diff options
Diffstat (limited to 'src/main')
88 files changed, 3025 insertions, 672 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt index 7628a2bfb..d7c4a566a 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt @@ -3,15 +3,13 @@ package at.hannibal2.skyhanni import at.hannibal2.skyhanni.api.CollectionAPI import at.hannibal2.skyhanni.config.ConfigManager import at.hannibal2.skyhanni.config.Features +import at.hannibal2.skyhanni.config.SackData import at.hannibal2.skyhanni.config.commands.Commands.init import at.hannibal2.skyhanni.data.* import at.hannibal2.skyhanni.data.repo.RepoManager import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.features.anvil.AnvilCombineHelper -import at.hannibal2.skyhanni.features.bazaar.BazaarApi -import at.hannibal2.skyhanni.features.bazaar.BazaarBestSellMethod -import at.hannibal2.skyhanni.features.bazaar.BazaarCancelledBuyOrderClipboard -import at.hannibal2.skyhanni.features.bazaar.BazaarOrderHelper +import at.hannibal2.skyhanni.features.bazaar.* import at.hannibal2.skyhanni.features.bingo.* import at.hannibal2.skyhanni.features.chat.* import at.hannibal2.skyhanni.features.chat.playerchat.PlayerChatFilter @@ -23,6 +21,7 @@ import at.hannibal2.skyhanni.features.commands.WikiCommand import at.hannibal2.skyhanni.features.cosmetics.CosmeticFollowingLine import at.hannibal2.skyhanni.features.damageindicator.DamageIndicatorManager import at.hannibal2.skyhanni.features.dungeon.* +import at.hannibal2.skyhanni.features.event.anniversary.ActivePlayerTimer import at.hannibal2.skyhanni.features.event.diana.* import at.hannibal2.skyhanni.features.fame.AccountUpgradeReminder import at.hannibal2.skyhanni.features.fame.CityProjectFeatures @@ -57,7 +56,10 @@ import at.hannibal2.skyhanni.features.misc.discordrpc.DiscordRPCManager import at.hannibal2.skyhanni.features.misc.ghostcounter.GhostCounter import at.hannibal2.skyhanni.features.misc.items.EstimatedItemValue import at.hannibal2.skyhanni.features.misc.items.EstimatedWardrobePrice +import at.hannibal2.skyhanni.features.misc.items.GlowingDroppedItems import at.hannibal2.skyhanni.features.misc.massconfiguration.DefaultConfigFeatures +import at.hannibal2.skyhanni.features.misc.powdertracker.PowderTracker +import at.hannibal2.skyhanni.features.misc.tabcomplete.GetFromSacksTabComplete import at.hannibal2.skyhanni.features.misc.tabcomplete.PlayerTabComplete import at.hannibal2.skyhanni.features.misc.tabcomplete.WarpTabComplete import at.hannibal2.skyhanni.features.misc.teleportpad.TeleportPadCompactName @@ -104,6 +106,7 @@ import at.hannibal2.skyhanni.features.summonings.SummoningSoulsName import at.hannibal2.skyhanni.mixins.hooks.RenderLivingEntityHelper import at.hannibal2.skyhanni.test.* import at.hannibal2.skyhanni.test.command.CopyNearbyParticlesCommand +import at.hannibal2.skyhanni.utils.EntityOutlineRenderer import at.hannibal2.skyhanni.utils.MinecraftConsoleFilter.Companion.initLogging import at.hannibal2.skyhanni.utils.NEUVersionCheck.checkIfNeuIsLoaded import at.hannibal2.skyhanni.utils.TabListData @@ -128,7 +131,7 @@ import org.apache.logging.log4j.Logger clientSideOnly = true, useMetadata = true, guiFactory = "at.hannibal2.skyhanni.config.ConfigGuiForgeInterop", - version = "0.20.Beta.14", + version = "0.20.Beta.18", ) class SkyHanniMod { @Mod.EventHandler @@ -141,12 +144,14 @@ class SkyHanniMod { loadModule(HypixelData()) loadModule(DungeonData()) loadModule(ScoreboardData()) + loadModule(SeaCreatureFeatures()) loadModule(SeaCreatureManager()) loadModule(ItemRenderBackground()) loadModule(EntityData()) loadModule(EntityMovementData()) loadModule(TestExportTools) loadModule(ItemClickData()) + loadModule(ActivePlayerTimer) loadModule(MinecraftData()) loadModule(TitleUtils()) loadModule(ItemTipHelper()) @@ -171,6 +176,7 @@ class SkyHanniMod { loadModule(TitleData()) loadModule(BlockData()) loadModule(DefaultConfigFeatures) + loadModule(EntityOutlineRenderer) // APIs loadModule(BazaarApi()) @@ -182,7 +188,7 @@ class SkyHanniMod { loadModule(SlayerAPI) loadModule(PurseAPI()) loadModule(RiftAPI) - loadModule(SackAPI()) + loadModule(SackAPI) // features loadModule(BazaarOrderHelper()) @@ -206,6 +212,7 @@ class SkyHanniMod { loadModule(TrophyFishFillet()) loadModule(TrophyFishMessages()) loadModule(BazaarBestSellMethod()) + loadModule(BazaarOpenPriceWebsite()) loadModule(AnvilCombineHelper()) loadModule(SeaCreatureMessageShortener()) // registerEvent(new GriffinBurrowFinder()); @@ -328,7 +335,7 @@ class SkyHanniMod { loadModule(BingoCardTips()) loadModule(GardenVisitorDropStatistics) loadModule(CaptureFarmingGear()) - loadModule(SackDisplay()) + loadModule(SackDisplay) loadModule(GardenStartLocation) loadModule(PetCandyUsedDisplay()) loadModule(ServerRestartTitle()) @@ -338,6 +345,7 @@ class SkyHanniMod { loadModule(ShowFishingItemName()) loadModule(WarpTabComplete) loadModule(PlayerTabComplete) + loadModule(GetFromSacksTabComplete) loadModule(SlayerItemProfitTracker) loadModule(SlayerItemsOnGround()) loadModule(RestorePieceOfWizardPortalLore()) @@ -389,11 +397,14 @@ class SkyHanniMod { loadModule(GardenPlotBorders()) loadModule(CosmeticFollowingLine()) loadModule(SuperpairsClicksAlert()) + loadModule(PowderTracker()) + loadModule(GlowingDroppedItems()) + loadModule(DungeonTeammateOutlines()) init() // test stuff - loadModule(SkyHanniTestCommand()) + loadModule(SkyHanniDebugsAndTests()) loadModule(CopyNearbyParticlesCommand) loadModule(ButtonOnPause()) loadModule(PacketTest()) @@ -410,7 +421,10 @@ class SkyHanniMod { configManager = ConfigManager() configManager.firstLoad() initLogging() - Runtime.getRuntime().addShutdownHook(Thread { configManager.saveConfig("shutdown-hook") }) + Runtime.getRuntime().addShutdownHook(Thread { + configManager.saveConfig("shutdown-hook") + configManager.saveSackData("shutdown-hook") + }) repo = RepoManager(configManager.configDirectory) try { repo.loadRepoInformation() @@ -445,6 +459,7 @@ class SkyHanniMod { @JvmStatic val feature: Features get() = configManager.features + val sackData: SackData get() = configManager.sackData lateinit var repo: RepoManager lateinit var configManager: ConfigManager val logger: Logger = LogManager.getLogger("SkyHanni") diff --git a/src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt b/src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt index 31c02efab..a882af5d4 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt @@ -81,11 +81,14 @@ class ConfigManager { } lateinit var features: Features + lateinit var sackData: SackData + private set private val logger = LorenzLogger("config_manager") var configDirectory = File("config/skyhanni") private var configFile: File? = null + private var sackFile: File? = null lateinit var processor: MoulConfigProcessor<Features> fun firstLoad() { @@ -95,6 +98,7 @@ class ConfigManager { configDirectory.mkdir() configFile = File(configDirectory, "config.json") + sackFile = File(configDirectory, "sacks.json") fixedRateTimer(name = "skyhanni-config-auto-save", period = 60_000L, initialDelay = 60_000L) { saveConfig("auto-save-60s") @@ -134,12 +138,40 @@ class ConfigManager { } } + if (sackFile!!.exists()) { + try { + val inputStreamReader = InputStreamReader(FileInputStream(sackFile!!), StandardCharsets.UTF_8) + val bufferedReader = BufferedReader(inputStreamReader) + val builder = StringBuilder() + for (line in bufferedReader.lines()) { + builder.append(line) + builder.append("\n") + } + + + logger.log("load-sacks-now") + sackData = gson.fromJson( + builder.toString(), + SackData::class.java + ) + logger.log("Loaded sacks from file") + } catch (error: Exception) { + error.printStackTrace() + } + } + if (!::features.isInitialized) { logger.log("Creating blank config and saving to file") features = Features() saveConfig("blank config") } + if (!::sackData.isInitialized) { + logger.log("Creating blank sack data and saving") + sackData = SackData() + saveSackData("blank config") + } + val features = SkyHanniMod.feature processor = MoulConfigProcessor(SkyHanniMod.feature) BuiltinMoulConfigGuis.addProcessors(processor) @@ -179,4 +211,20 @@ class ConfigManager { e.printStackTrace() } } + + fun saveSackData(reason: String) { + logger.log("saveSackData: $reason") + val file = sackFile ?: throw Error("Can not save sacks, sackFile is null!") + try { + logger.log("Saving sack file") + file.parentFile.mkdirs() + file.createNewFile() + BufferedWriter(OutputStreamWriter(FileOutputStream(file), StandardCharsets.UTF_8)).use { writer -> + writer.write(gson.toJson(SkyHanniMod.sackData)) + } + } catch (e: IOException) { + logger.log("Could not save sacks file to $file") + e.printStackTrace() + } + } }
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/config/SackData.java b/src/main/java/at/hannibal2/skyhanni/config/SackData.java new file mode 100644 index 000000000..61febed74 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/config/SackData.java @@ -0,0 +1,26 @@ +package at.hannibal2.skyhanni.config; + +import at.hannibal2.skyhanni.data.SackItem; +import at.hannibal2.skyhanni.utils.NEUInternalName; +import com.google.gson.annotations.Expose; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class SackData { + + @Expose + public Map<UUID, PlayerSpecific> players = new HashMap<>(); + + public static class PlayerSpecific { + @Expose + public Map<String, ProfileSpecific> profiles = new HashMap<>(); + } + + public static class ProfileSpecific { + + @Expose + public Map<NEUInternalName, SackItem> sackContents = new HashMap<>(); + } +}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/config/Storage.java b/src/main/java/at/hannibal2/skyhanni/config/Storage.java index 2b9755926..9ec45a70c 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/Storage.java +++ b/src/main/java/at/hannibal2/skyhanni/config/Storage.java @@ -10,6 +10,7 @@ import at.hannibal2.skyhanni.features.garden.visitor.VisitorReward; import at.hannibal2.skyhanni.features.misc.EnderNode; import at.hannibal2.skyhanni.features.misc.FrozenTreasure; import at.hannibal2.skyhanni.features.misc.ghostcounter.GhostData; +import at.hannibal2.skyhanni.features.misc.powdertracker.PowderChestReward; import at.hannibal2.skyhanni.features.rift.area.westvillage.KloonTerminal; import at.hannibal2.skyhanni.utils.LorenzVec; import at.hannibal2.skyhanni.utils.NEUInternalName; @@ -273,6 +274,17 @@ public class Storage { } @Expose + public Map<Integer, PowderTracker> powderTracker = new HashMap<>(); + + public static class PowderTracker { + @Expose + public int totalChestPicked = 0; + + @Expose + public Map<PowderChestReward, Long> rewards = new HashMap<>(); + } + + @Expose public FrozenTreasureTracker frozenTreasureTracker = new FrozenTreasureTracker(); public static class FrozenTreasureTracker { 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 acd280967..04eee744b 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt @@ -31,7 +31,7 @@ import at.hannibal2.skyhanni.features.misc.massconfiguration.DefaultConfigFeatur import at.hannibal2.skyhanni.features.slayer.SlayerItemProfitTracker import at.hannibal2.skyhanni.test.PacketTest import at.hannibal2.skyhanni.test.SkyHanniConfigSearchResetCommand -import at.hannibal2.skyhanni.test.SkyHanniTestCommand +import at.hannibal2.skyhanni.test.SkyHanniDebugsAndTests import at.hannibal2.skyhanni.test.TestBingo import at.hannibal2.skyhanni.test.command.* import at.hannibal2.skyhanni.utils.APIUtil @@ -183,11 +183,11 @@ object Commands { registerCommand( "shdebugdata", "Prints debug data in the clipboard" - ) { SkyHanniTestCommand.debugData(it) } + ) { SkyHanniDebugsAndTests.debugData(it) } registerCommand( "shversion", "Prints the SkyHanni version in the chat" - ) { SkyHanniTestCommand.debugVersion() } + ) { SkyHanniDebugsAndTests.debugVersion() } registerCommand( "shcarrot", "Toggles receiving the 12 fortune from carrots" @@ -198,7 +198,7 @@ object Commands { registerCommand("shtestbingo", "dev command") { TestBingo.toggle() } registerCommand("shprintbingohelper", "dev command") { BingoNextStepHelper.command() } registerCommand("shreloadbingodata", "dev command") { BingoCardDisplay.command() } - registerCommand("shtestgardenvisitors", "dev command") { SkyHanniTestCommand.testGardenVisitors() } + registerCommand("shtestgardenvisitors", "dev command") { SkyHanniDebugsAndTests.testGardenVisitors() } registerCommand("shtestcomposter", "dev command") { ComposterOverlay.onCommand(it) } registerCommand("shtestinquisitor", "dev command") { InquisitorWaypointShare.test() } registerCommand("shshowcropmoneycalculation", "dev command") { CropMoneyDisplay.toggleShowCalculation() } @@ -210,21 +210,21 @@ object Commands { } private fun developersCodingHelp() { - registerCommand("shtest", "Unused test command.") { SkyHanniTestCommand.testCommand(it) } + registerCommand("shtest", "Unused test command.") { SkyHanniDebugsAndTests.testCommand(it) } registerCommand("shreloadlocalrepo", "Reloading the local repo data") { SkyHanniMod.repo.reloadLocalRepo() } registerCommand("shchathistory", "Show the unfiltered chat history") { ChatManager.openChatFilterGUI() } registerCommand( "shstoplisteners", "Unregistering all loaded forge event listeners" - ) { SkyHanniTestCommand.stopListeners() } + ) { SkyHanniDebugsAndTests.stopListeners() } registerCommand( "shreloadlisteners", "Trying to load all forge event listeners again. Might not work at all" - ) { SkyHanniTestCommand.reloadListeners() } + ) { SkyHanniDebugsAndTests.reloadListeners() } registerCommand( "shcopylocation", "Copies the player location as LorenzVec format to the clipboard" - ) { SkyHanniTestCommand.copyLocation(it) } + ) { SkyHanniDebugsAndTests.copyLocation(it) } registerCommand( "shcopyentities", "Copies entities in the specified radius around the player to the clipboard" diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/BazaarConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/BazaarConfig.java index 8d3565765..8f60e5b28 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/BazaarConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/BazaarConfig.java @@ -34,4 +34,10 @@ public class BazaarConfig { @ConfigEditorBoolean @FeatureToggle public boolean cancelledBuyOrderClipboard = false; + + @Expose + @ConfigOption(name = "Price Website", desc = "Adds a button to the bazaar product inventory that will open the item page in §cskyblock.bz§7.") + @ConfigEditorBoolean + @FeatureToggle + public boolean openPriceWebsite = false; } diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/DamageIndicatorConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/DamageIndicatorConfig.java index 94ff6f497..d2e7cfed5 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/DamageIndicatorConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/DamageIndicatorConfig.java @@ -111,12 +111,12 @@ public class DamageIndicatorConfig { public static class VampireSlayerConfig { @Expose - @ConfigOption(name = "HP untill Steak", desc = "Show the amount of HP miss1ing untill the steak can be used on the vampire slayer on top of the boss.") + @ConfigOption(name = "HP until Steak", desc = "Show the amount of HP missing until the steak can be used on the vampire slayer on top of the boss.") @ConfigEditorBoolean public boolean hpTillSteak = false; @Expose - @ConfigOption(name = "Mania Circles", desc = "Show a timer until the boss leaves the invicible Mania Circles state.") + @ConfigOption(name = "Mania Circles", desc = "Show a timer until the boss leaves the invincible Mania Circles state.") @ConfigEditorBoolean public boolean maniaCircles = false; diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/DevConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/DevConfig.java index 751e48a1c..798bbce1d 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/DevConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/DevConfig.java @@ -121,6 +121,9 @@ public class DevConfig { public Position debugPos = new Position(10, 10, false, true); @Expose + public Position debugLocationPos = new Position(1, 160, false, true); + + @Expose @ConfigOption(name = "Minecraft Console", desc = "") @ConfigEditorAccordion(id = 1) public boolean minecraftConsole = false; diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/DungeonConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/DungeonConfig.java index b0677f462..f1e62efc1 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/DungeonConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/DungeonConfig.java @@ -65,6 +65,13 @@ public class DungeonConfig { @FeatureToggle public boolean highlightDeathmites = true; + @Expose + @ConfigOption(name = "Highlight Teammates", desc = "Highlight dungeon teammates with a glowing outline.") + @ConfigEditorBoolean + @FeatureToggle + public boolean highlightTeammates = true; + + @ConfigOption(name = "Object Hider", desc = "Hide various things in dungeons.") @ConfigEditorAccordion(id = 3) public boolean objectHider = false; diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/FishingConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/FishingConfig.java index 3dccd1b3c..64ab6cdb9 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/FishingConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/FishingConfig.java @@ -119,11 +119,19 @@ public class FishingConfig { ) @ConfigEditorBoolean @ConfigAccordionId(id = 2) - @FeatureToggle public boolean barnTimerCrystalHollows = true; @Expose @ConfigOption( + name = "Stranded Fishing", + desc = "Show the barn fishing timer even on all the different islands stranded players can visit." + ) + @ConfigEditorBoolean + @ConfigAccordionId(id = 2) + public boolean barnTimerForStranded = true; + + @Expose + @ConfigOption( name = "Worm Cap Alert", desc = "Alerts you with sound if you hit the Worm Sea Creature limit of 60." ) @@ -192,6 +200,12 @@ public class FishingConfig { } @Expose + @ConfigOption(name = "Highlight Rare", desc = "Highlight rare sea creatures in blue color.") + @ConfigEditorBoolean + @FeatureToggle + public boolean rareSeaCreatureHighlight = false; + + @Expose @ConfigOption( name = "Shark Fish Counter", desc = "Counts how many sharks have been caught." diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/GardenConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/GardenConfig.java index 59867fb8a..cddb1dfad 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/GardenConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/GardenConfig.java @@ -295,6 +295,11 @@ public class GardenConfig { "§b1 §cSpace Helmet", " ", // If they want another empty row "§212,735 Garden EXP", + "§b1 §9Cultivating I", + "§b1 §9Replenish I", + "§b11,056 Bits", + "§250,556 Mithril Powder", + "§d50,556 Gemstone Powder", } ) public List<Integer> textFormat = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12)); @@ -551,7 +556,7 @@ public class GardenConfig { public boolean keybind = false; @Expose - @ConfigOption(name = "Enabled", desc = "Use custom keybinds while holding a farming tool or daedalus axe in the hand. §cOnly updates after scrolling in the hotbar.") + @ConfigOption(name = "Enabled", desc = "Use custom keybinds while holding a farming tool or daedalus axe in the hand.") @ConfigEditorBoolean @ConfigAccordionId(id = 8) @FeatureToggle @@ -1180,7 +1185,7 @@ public class GardenConfig { @ConfigEditorBoolean @ConfigAccordionId(id = 21) @FeatureToggle - public boolean composterNotifyLowEnabled = true; + public boolean composterNotifyLowEnabled = false; @Expose @ConfigOption(name = "Show Title", desc = "Send a title to notify.") diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/InventoryConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/InventoryConfig.java index 571a9a15b..2091f7f4d 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/InventoryConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/InventoryConfig.java @@ -131,6 +131,20 @@ public class InventoryConfig { public boolean jacobFarmingContestRealTime = true; @Expose + @ConfigOption(name = "Medal Icon", desc = "Adds a symbol that shows what medal you received in this contest. " + + "§eIf you use a texture pack this may cause conflicting icons.") + @ConfigEditorBoolean + @ConfigAccordionId(id = 3) + @FeatureToggle + public boolean jacobFarmingContestMedalIcon = true; + + @Expose + @ConfigOption(name = "Finnegan Icon", desc = "Uses a different indicator for when the contest happened during Mayor Finnegan.") + @ConfigEditorBoolean + @ConfigAccordionId(id = 3) + public boolean jacobFarmingContestFinneganIcon = true; + + @Expose @ConfigOption(name = "Sack Items Display", desc = "") @Accordion public SackDisplay sackDisplay = new SackDisplay(); diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java index 6a8bc4576..bef52f688 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java @@ -26,7 +26,7 @@ public class MiscConfig { public boolean petDisplay = false; @Expose - @ConfigOption(name = "Pet Experience Tooltip", desc = "Show the currently active pet.") + @ConfigOption(name = "Pet Experience Tooltip", desc = "") @ConfigAccordionId(id = 0) @Accordion public PetExperienceToolTipConfig petExperienceToolTip = new PetExperienceToolTipConfig(); @@ -44,6 +44,11 @@ public class MiscConfig { @ConfigEditorBoolean public boolean showAlways = false; + @Expose + @ConfigOption(name = "GDrag 200", desc = "Show for Golden Dragon the exp needed for level 200.") + @ConfigEditorBoolean + public boolean goldenDragon200 = true; + } @Expose @@ -540,6 +545,12 @@ public class MiscConfig { @ConfigEditorBoolean @FeatureToggle public boolean vipVisits = true; + + @Expose + @ConfigOption(name = "/gfs Sack", desc = "Tab complete /gfs sack items.") + @ConfigEditorBoolean + @FeatureToggle + public boolean gfsSack = true; } @ConfigOption(name = "Pocket Sack-In-A-Sack", desc = "") @@ -808,6 +819,79 @@ public class MiscConfig { } @Expose + @ConfigOption(name = "Powder Tracker", desc = "") + @Accordion + public PowderTrackerConfig powderTrackerConfig = new PowderTrackerConfig(); + + public static class PowderTrackerConfig { + + @Expose + @ConfigOption(name = "Enabled", desc = "Enable the Powder Tracker overlay for mining.") + @ConfigEditorBoolean + @FeatureToggle + public boolean enabled = false; + + @Expose + @ConfigOption(name = "Only when Grinding", desc = "Only show the overlay when powder grinding.") + @ConfigEditorBoolean + public boolean onlyWhenPowderGrinding = false; + + @Expose + @ConfigOption(name = "Great Explorer", desc = "Enable this if your Great Explorer perk is maxed.") + @ConfigEditorBoolean + public boolean greatExplorerMaxed = false; + + @Expose + @ConfigOption( + name = "Text Format", + desc = "Drag text to change the appearance of the overlay." + ) + @ConfigEditorDraggableList( + exampleText = { + "§b§lPowder Tracker", + "§7Display Mode: §a[Total] §e[This Session]", + "§d852 Total chests Picked §7(950/h)", + "§bx2 Powder: §aActive!", + "§b250,420 §aMithril Powder §7(350,000/h)", + "§b250,420 §dGemstone Powder §7(350,000/h)", + "", + "§50§7-§90§7-§a0§f-0 §cRuby Gemstone", + "§50§7-§90§7-§a0§f-0 §bSapphire Gemstone", + "§50§7-§90§7-§a0§f-0 §6Amber Gemstone", + "§50§7-§90§7-§a0§f-0 §5Amethyst Gemstone", + "§50§7-§90§7-§a0§f-0 §aJade Gemstone", + "§50§7-§90§7-§a0§f-0 §eTopaz Gemstone", + + "§b14 §9FTX 3070", + "§b14 §9Electron Transmitter", + "§b14 §9Robotron Reflector", + "§b14 §9Superlite Motor", + "§b14 §9Control Switch", + "§b14 §9Synthetic Heart", + "§b14 §9Total Robot Parts", + + "§90§7-§a0§7-§c0§f-§e0§f-§30 §fGoblin Egg", + + "§b12 §aWishing Compass", + + "§b320 §aSludge Juice", + "§b2 §9Ascension Rope", + "§b6 §5Treasurite", + "§b4 §6Jungle Heart", + "§b1 §5Pickonimbus 2000", + "§b14 §aYoggie", + "§b9 §fPrehistoric Egg", + "§b25 §aOil Barrel" + } + ) + public Property<List<Integer>> textFormat = Property.of(new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18))); + + @Expose + public Position position = new Position(-274, 0, false, true); + + } + + @Expose @ConfigOption(name = "Cosmetic", desc = "") @Accordion public CosmeticConfig cosmeticConfig = new CosmeticConfig(); @@ -849,6 +933,32 @@ public class MiscConfig { } } + + @Expose + @ConfigOption(name = "Glowing Dropped Items", desc = "") + @Accordion + public GlowingDroppedItems glowingDroppedItems = new GlowingDroppedItems(); + + public static class GlowingDroppedItems { + + @Expose + @ConfigOption(name = "Enabled", desc = "Draws a glowing outline around all dropped items on the ground.") + @ConfigEditorBoolean + @FeatureToggle + public boolean enabled = false; + + @Expose + @ConfigOption(name = "Highlight Showcase Items", desc = "Draws a glowing outline around showcase items.") + @ConfigEditorBoolean + public boolean highlightShowcase = false; + + @Expose + @ConfigOption(name = "Highlight Fishing Bait", desc = "Draws a glowing outline around fishing bait.") + @ConfigEditorBoolean + public boolean highlightFishingBait = false; + + } + @Expose @ConfigOption(name = "Exp Bottles", desc = "Hides all the experience orbs lying on the ground.") @ConfigEditorBoolean @@ -954,6 +1064,7 @@ public class MiscConfig { @Expose @ConfigOption(name = "Superpairs Clicks Alert", desc = "Display an alert when you reach the maximum clicks gained from Chronomatron or Ultrasequencer.") @ConfigEditorBoolean + @FeatureToggle public boolean superpairsClicksAlert = false; @Expose @@ -964,4 +1075,29 @@ public class MiscConfig { @Expose public Position inventoryLoadPos = new Position(394, 124, false, true); + + + @ConfigOption(name = "300þ Anniversary Celebration", desc = "Features for the 300þ year of SkyBlock") + @Accordion + @Expose + public Century century = new Century(); + + public static class Century { + + @ConfigOption(name = "Enable Active Player Timer", desc = "Show a HUD telling you how much longer you have to wait to be eligible for another free ticket") + @Expose + @ConfigEditorBoolean + @FeatureToggle + public boolean enableActiveTimer = true; + + @Expose + public Position activeTimerPosition = new Position(100, 100, false, true); + + @ConfigOption(name = "Enable Active Player Alert", desc = "Loudly proclaim when it is time to break some wheat") + @Expose + @ConfigEditorBoolean + public boolean enableActiveAlert = false; + } + + } diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/SlayerConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/SlayerConfig.java index d6af72b41..de8db100c 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/SlayerConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/SlayerConfig.java @@ -34,7 +34,7 @@ public class SlayerConfig { public String beaconColor = "0:255:255:0:88"; @Expose - @ConfigOption(name = "Show Warning", desc = "Displays a warning mid-screen then the Enderman Slayer throws a Yang Glyph (beacon).") + @ConfigOption(name = "Show Warning", desc = "Displays a warning mid-screen when the Enderman Slayer throws a Yang Glyph (beacon).") @ConfigEditorBoolean @FeatureToggle public boolean showWarning = false; diff --git a/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt b/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt index f8b0cfed3..329b6b1b3 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt @@ -1,17 +1,24 @@ package at.hannibal2.skyhanni.data +import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.events.* +import at.hannibal2.skyhanni.utils.LocationUtils.isPlayerInside import at.hannibal2.skyhanni.utils.LorenzLogger import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher import at.hannibal2.skyhanni.utils.StringUtils.removeColor import at.hannibal2.skyhanni.utils.TabListData import net.minecraft.client.Minecraft +import net.minecraft.util.AxisAlignedBB import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import net.minecraftforge.fml.common.network.FMLNetworkEvent class HypixelData { + private val config get() = SkyHanniMod.feature.dev private val tabListProfilePattern = "§e§lProfile: §r§a(?<profile>.*)".toPattern() + private val westVillageFarmArea = AxisAlignedBB(-54.0, 69.0, -115.0, -40.0, 75.0, -127.0) + private val howlingCaveArea = AxisAlignedBB(-401.0, 50.0, -104.0, -337.0, 90.0, 36.0) + private val zealotBruiserHideoutArea = AxisAlignedBB(-520.0, 66.0, -332.0, -558.0, 85.0, -280.0) companion object { var hypixelLive = false @@ -69,10 +76,19 @@ class HypixelData { fun onTick(event: LorenzTickEvent) { if (event.isMod(2)) { if (LorenzUtils.inSkyBlock) { - skyBlockArea = ScoreboardData.sidebarLinesFormatted + val originalLocation = ScoreboardData.sidebarLinesFormatted .firstOrNull { it.startsWith(" §7⏣ ") || it.startsWith(" §5ф ") } ?.substring(5)?.removeColor() ?: "?" + + skyBlockArea = when { + skyBlockIsland == IslandType.THE_RIFT && westVillageFarmArea.isPlayerInside() -> "Dreadfarm" + skyBlockIsland == IslandType.THE_PARK && howlingCaveArea.isPlayerInside() -> "Howling Cave" + skyBlockIsland == IslandType.THE_END && zealotBruiserHideoutArea.isPlayerInside() -> "The End" + + else -> originalLocation + } + checkProfileName() } } diff --git a/src/main/java/at/hannibal2/skyhanni/data/ItemTipHelper.kt b/src/main/java/at/hannibal2/skyhanni/data/ItemTipHelper.kt index ee699b161..30f2b51b7 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/ItemTipHelper.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/ItemTipHelper.kt @@ -7,6 +7,7 @@ import at.hannibal2.skyhanni.events.RenderItemTipEvent import at.hannibal2.skyhanni.mixins.transformers.gui.AccessorGuiContainer import at.hannibal2.skyhanni.utils.InventoryUtils.getInventoryName import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.RenderUtils.drawSlotText import net.minecraft.client.Minecraft import net.minecraft.client.gui.inventory.GuiChest import net.minecraft.client.renderer.GlStateManager @@ -26,20 +27,13 @@ class ItemTipHelper { if (itemTipEvent.renderObjects.isEmpty()) return - GlStateManager.disableLighting() - GlStateManager.disableDepth() - GlStateManager.disableBlend() - for (renderObject in itemTipEvent.renderObjects) { - val fontRenderer = event.fontRenderer val text = renderObject.text - val x = event.x + 17 - fontRenderer.getStringWidth(text) + renderObject.offsetX + val x = event.x + 17 + renderObject.offsetX val y = event.y + 9 + renderObject.offsetY - fontRenderer.drawStringWithShadow(text, x.toFloat(), y.toFloat(), 16777215) - } - GlStateManager.enableLighting() - GlStateManager.enableDepth() + event.drawSlotText(x, y, text, 1f) + } } @SubscribeEvent(priority = EventPriority.HIGHEST) diff --git a/src/main/java/at/hannibal2/skyhanni/data/OwnInventoryData.kt b/src/main/java/at/hannibal2/skyhanni/data/OwnInventoryData.kt index dd9ccef71..93d85d1a6 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/OwnInventoryData.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/OwnInventoryData.kt @@ -2,7 +2,7 @@ package at.hannibal2.skyhanni.data import at.hannibal2.skyhanni.api.CollectionAPI import at.hannibal2.skyhanni.events.InventoryCloseEvent -import at.hannibal2.skyhanni.events.OwnInventorItemUpdateEvent +import at.hannibal2.skyhanni.events.OwnInventoryItemUpdateEvent import at.hannibal2.skyhanni.events.PacketEvent import at.hannibal2.skyhanni.features.bazaar.BazaarApi import at.hannibal2.skyhanni.utils.ItemUtils.getInternalNameOrNull @@ -29,7 +29,7 @@ class OwnInventoryData { val windowId = packet.func_149175_c() if (windowId == 0) { val item = packet.func_149174_e() ?: return - OwnInventorItemUpdateEvent(item).postAndCatch() + OwnInventoryItemUpdateEvent(item).postAndCatch() } } if (packet is S2FPacketSetSlot) { diff --git a/src/main/java/at/hannibal2/skyhanni/data/ProfileStorageData.kt b/src/main/java/at/hannibal2/skyhanni/data/ProfileStorageData.kt index f9eb6b024..3524f03db 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/ProfileStorageData.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/ProfileStorageData.kt @@ -1,6 +1,7 @@ package at.hannibal2.skyhanni.data import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.config.SackData import at.hannibal2.skyhanni.config.Storage import at.hannibal2.skyhanni.events.* import at.hannibal2.skyhanni.utils.LorenzUtils @@ -17,6 +18,10 @@ object ProfileStorageData { private var nextProfile: String? = null + + private var sackPlayers: SackData.PlayerSpecific? = null + var sackProfiles: SackData.ProfileSpecific? = null + @SubscribeEvent(priority = EventPriority.HIGHEST) fun onChat(event: LorenzChatEvent) { "§7Switching to profile (?<name>.*)\\.\\.\\.".toPattern().matchMatcher(event.message) { @@ -32,25 +37,35 @@ object ProfileStorageData { nextProfile = null val playerSpecific = playerSpecific + val sackPlayers = sackPlayers if (playerSpecific == null) { LorenzUtils.error("profileSpecific after profile swap can not be set: playerSpecific is null!") return } - loadProfileSpecific(playerSpecific, profileName, "profile swap (chat message)") + if (sackPlayers == null) { + LorenzUtils.error("sackPlayers after profile swap can not be set: sackPlayers is null!") + return + } + loadProfileSpecific(playerSpecific, sackPlayers, profileName, "profile swap (chat message)") ConfigLoadEvent().postAndCatch() } @SubscribeEvent(priority = EventPriority.HIGHEST) fun onProfileJoin(event: ProfileJoinEvent) { val playerSpecific = playerSpecific + val sackPlayers = sackPlayers if (playerSpecific == null) { LorenzUtils.error("playerSpecific is null in ProfileJoinEvent!") return } + if (sackPlayers == null) { + LorenzUtils.error("sackPlayers is null in sackPlayers!") + return + } if (profileSpecific == null) { val profileName = event.name - loadProfileSpecific(playerSpecific, profileName, "first join (chat message)") + loadProfileSpecific(playerSpecific, sackPlayers, profileName, "first join (chat message)") } } @@ -58,11 +73,12 @@ object ProfileStorageData { fun onTabListUpdate(event: TabListUpdateEvent) { if (profileSpecific != null) return val playerSpecific = playerSpecific ?: return + val sackPlayers = sackPlayers ?: return for (line in event.tabList) { val pattern = "§e§lProfile: §r§a(?<name>.*)".toPattern() pattern.matchMatcher(line) { val profileName = group("name").lowercase() - loadProfileSpecific(playerSpecific, profileName, "tab list") + loadProfileSpecific(playerSpecific, sackPlayers, profileName, "tab list") nextProfile = null return } @@ -87,18 +103,20 @@ object ProfileStorageData { } } - private fun loadProfileSpecific(playerSpecific: Storage.PlayerSpecific, profileName: String, reason: String) { + private fun loadProfileSpecific(playerSpecific: Storage.PlayerSpecific, sackProfile: SackData.PlayerSpecific, profileName: String, reason: String) { noTabListTime = -1 profileSpecific = playerSpecific.profiles.getOrPut(profileName) { Storage.ProfileSpecific() } + sackProfiles = sackProfile.profiles.getOrPut(profileName) { SackData.ProfileSpecific() } tryMigrateProfileSpecific() - ConfigLoadEvent().postAndCatch() loaded = true + ConfigLoadEvent().postAndCatch() } @SubscribeEvent fun onHypixelJoin(event: HypixelJoinEvent) { val playerUuid = LorenzUtils.getRawPlayerUuid() playerSpecific = SkyHanniMod.feature.storage.players.getOrPut(playerUuid) { Storage.PlayerSpecific() } + sackPlayers = SkyHanniMod.sackData.players.getOrPut(playerUuid) { SackData.PlayerSpecific() } migratePlayerSpecific() ConfigLoadEvent().postAndCatch() } diff --git a/src/main/java/at/hannibal2/skyhanni/data/SackAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/SackAPI.kt index 3990e13d6..e2102c6ec 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/SackAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/SackAPI.kt @@ -1,13 +1,192 @@ package at.hannibal2.skyhanni.data +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.events.InventoryCloseEvent +import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent import at.hannibal2.skyhanni.events.LorenzChatEvent import at.hannibal2.skyhanni.events.SackChangeEvent +import at.hannibal2.skyhanni.features.fishing.trophy.TrophyFishManager +import at.hannibal2.skyhanni.features.fishing.trophy.TrophyRarity +import at.hannibal2.skyhanni.features.inventory.SackDisplay +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.LorenzUtils.editCopy import at.hannibal2.skyhanni.utils.NEUInternalName +import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName +import at.hannibal2.skyhanni.utils.NEUItems.getNpcPriceOrNull +import at.hannibal2.skyhanni.utils.NEUItems.getPrice +import at.hannibal2.skyhanni.utils.NumberUtil.formatNumber +import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import com.google.gson.annotations.Expose +import net.minecraft.item.ItemStack import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -class SackAPI { +object SackAPI { + private val sackDisplayConfig get() = SkyHanniMod.feature.inventory.sackDisplay + private var lastOpenedInventory = "" + + var inSackInventory = false + private val sackPattern = "^(.* Sack|Enchanted .* Sack)$".toPattern() + private val numPattern = + "(?:(?:§[0-9a-f](?<level>I{1,3})§7:)?|(?:§7Stored:)?) (?<color>§[0-9a-f])(?<stored>[0-9.,kKmMbB]+)§7/(?<total>\\d+(?:[0-9.,]+)?[kKmMbB]?)".toPattern() + private val gemstonePattern = + " §[0-9a-f](?<gemrarity>[A-z]*): §[0-9a-f](?<stored>\\d+(?:\\.\\d+)?(?:(?:,\\d+)?)+[kKmM]?)(?: §[0-9a-f]\\(\\d+(?:\\.\\d+)?(?:(?:,\\d+)?)+[kKmM]?\\))?".toPattern() + + private var isRuneSack = false + private var isGemstoneSack = false + var isTrophySack = false + private var sackRarity: TrophyRarity? = null + + val sackItem = mutableMapOf<String, SackOtherItem>() + val runeItem = mutableMapOf<String, SackRune>() + val gemstoneItem = mutableMapOf<String, SackGemstone>() + private val stackList = mutableMapOf<Int, ItemStack>() + + @SubscribeEvent + fun onInventoryClose(event: InventoryCloseEvent) { + inSackInventory = false + isRuneSack = false + isGemstoneSack = false + isTrophySack = false + runeItem.clear() + gemstoneItem.clear() + sackItem.clear() + stackList.clear() + } + + @SubscribeEvent + fun onInventoryOpen(event: InventoryFullyOpenedEvent) { + val inventoryName = event.inventoryName + val isNewInventory = inventoryName != lastOpenedInventory + lastOpenedInventory = inventoryName + val match = sackPattern.matcher(inventoryName).matches() + if (!match) return + val stacks = event.inventoryItems + isRuneSack = inventoryName == "Runes Sack" + isGemstoneSack = inventoryName == "Gemstones Sack" + isTrophySack = inventoryName.contains("Trophy Fishing Sack") + sackRarity = inventoryName.getTrophyRarity() + inSackInventory = true + stackList.putAll(stacks) + SackDisplay.update(isNewInventory) + } + + private fun String.getTrophyRarity(): TrophyRarity? { + return if (this.startsWith("Bronze")) + TrophyRarity.BRONZE + else + if (this.startsWith("Silver")) + TrophyRarity.SILVER + else null + } + + private fun NEUInternalName.sackPrice(stored: String) = when (sackDisplayConfig.priceFrom) { + 0 -> (getPrice(true) * stored.formatNumber()).toInt().let { if (it < 0) 0 else it } + + 1 -> try { + val npcPrice = getNpcPriceOrNull() ?: 0.0 + (npcPrice * stored.formatNumber()).toInt() + } catch (e: Exception) { + 0 + } + + else -> 0 + } + + fun getSacksData(savingSacks: Boolean) { + if (savingSacks) sackData = ProfileStorageData.sackProfiles?.sackContents ?: return + for ((_, stack) in stackList) { + val name = stack.name ?: continue + val lore = stack.getLore() + val gem = SackGemstone() + val rune = SackRune() + val item = SackOtherItem() + loop@ for (line in lore) { + if (isGemstoneSack) { + gemstonePattern.matchMatcher(line) { + val rarity = group("gemrarity") + val stored = group("stored") + gem.internalName = gemstoneMap[name.removeColor()] ?: NEUInternalName.NONE + if (gemstoneMap.containsKey(name.removeColor())) { + val internalName = "${rarity.uppercase()}_${ + name.uppercase().split(" ")[0].removeColor() + }_GEM".asInternalName() + + when (rarity) { + "Rough" -> { + gem.rough = stored + gem.roughPrice = internalName.sackPrice(stored) + if (savingSacks) setSackItem(internalName, stored.formatNumber().toInt()) + } + + "Flawed" -> { + gem.flawed = stored + gem.flawedPrice = internalName.sackPrice(stored) + if (savingSacks) setSackItem(internalName, stored.formatNumber().toInt()) + } + + "Fine" -> { + gem.fine = stored + gem.finePrice = internalName.sackPrice(stored) + if (savingSacks) setSackItem(internalName, stored.formatNumber().toInt()) + } + + "Flawless" -> { + gem.flawless = stored + gem.flawlessPrice = internalName.sackPrice(stored) + if (savingSacks) setSackItem(internalName, stored.formatNumber().toInt()) + } + } + gemstoneItem[name] = gem + } + } + } else { + numPattern.matchMatcher(line) { + val stored = group("stored") + val internalName = stack.getInternalName() + item.internalName = internalName + item.colorCode = group("color") + item.stored = stored + item.total = group("total") + if (savingSacks) setSackItem(item.internalName, item.stored.formatNumber().toInt()) + item.price = if (isTrophySack) { + val trophyName = + internalName.asString().lowercase().substringBeforeLast("_").replace("_", "") + val filletValue = + TrophyFishManager.getInfoByName(trophyName)?.getFilletValue(sackRarity!!) ?: 0 + val storedNumber = stored.formatNumber().toInt() + "MAGMA_FISH".asInternalName().sackPrice((filletValue * storedNumber).toString()) + } else internalName.sackPrice(stored).coerceAtLeast(0) + + if (isRuneSack) { + val level = group("level") + rune.stack = stack + if (level == "I") { + rune.lvl1 = stored + continue@loop + } + if (level == "II") { + rune.lvl2 = stored + continue@loop + } + if (level == "III") { + rune.lvl3 = stored + } + runeItem.put(name, rune) + } else { + sackItem.put(name, item) + } + } + } + } + } + if (savingSacks) saveSackData() + } + + private var sackData = mapOf<NEUInternalName, SackItem>() data class SackChange(val delta: Int, val internalName: NEUInternalName, val sacks: List<String>) @@ -45,4 +224,112 @@ class SackAPI { } SackChangeEvent(sackChanges, otherItemsAdded, otherItemsRemoved).postAndCatch() } + + @SubscribeEvent + fun sackChange(event: SackChangeEvent) { + sackData = ProfileStorageData.sackProfiles?.sackContents ?: return + + // if it gets added and subtracted but only 1 shows it will be outdated + val justChanged = mutableMapOf<NEUInternalName, Int>() + + for (change in event.sackChanges) { + if (change.internalName in justChanged) { + justChanged[change.internalName] = (justChanged[change.internalName] ?: 0) + change.delta + } else { + justChanged[change.internalName] = change.delta + } + } + + for (item in justChanged) { + if (sackData.containsKey(item.key)) { + val oldData = sackData[item.key] + var newAmount = oldData!!.amount + item.value + var changed = newAmount - oldData.amount + if (newAmount < 0) { + newAmount = 0 + changed = 0 + } + sackData = sackData.editCopy { this[item.key] = SackItem(newAmount, changed, oldData.outdatedStatus) } + } else { + val newAmount = if (item.value > 0) item.value else 0 + sackData = sackData.editCopy { this[item.key] = SackItem(newAmount, newAmount, 2) } + } + } + + if (event.otherItemsAdded || event.otherItemsRemoved) { + for (item in sackData) { + if (item.key in justChanged) continue + val oldData = sackData[item.key] + sackData = sackData.editCopy { this[item.key] = SackItem(oldData!!.amount, 0, 1) } + } + } + saveSackData() + } + + private fun setSackItem(item: NEUInternalName, amount: Int) { + sackData = sackData.editCopy { this[item] = SackItem(amount, 0, 0) } + } + + fun fetchSackItem(item: NEUInternalName): SackItem? { + sackData = ProfileStorageData.sackProfiles?.sackContents ?: return SackItem(0, 0, -1) + + if (sackData.containsKey(item)) { + return sackData[item] + } + + sackData = sackData.editCopy { this[item] = SackItem(0, 0, 2) } + return sackData[item] + } + + private fun saveSackData() { + ProfileStorageData.sackProfiles?.sackContents = sackData + SkyHanniMod.configManager.saveSackData("shutdown-hook") + } + + data class SackGemstone( + var internalName: NEUInternalName = NEUInternalName.NONE, + var rough: String = "0", + var flawed: String = "0", + var fine: String = "0", + var flawless: String = "0", + var roughPrice: Int = 0, + var flawedPrice: Int = 0, + var finePrice: Int = 0, + var flawlessPrice: Int = 0, + ) + + data class SackRune( + var stack: ItemStack? = null, + var lvl1: String = "0", + var lvl2: String = "0", + var lvl3: String = "0", + ) + + data class SackOtherItem( + var internalName: NEUInternalName = NEUInternalName.NONE, + var colorCode: String = "", + var stored: String = "0", + var total: String = "0", + var price: Int = 0, + ) } + +// status -1 = fetching data failed, 0 = < 1% of being wrong, 1 = 10% of being wrong, 2 = is 100% wrong +// lastChange is set to 0 when value is refreshed in the sacks gui and when being set initially +// if it didn't change in an update the lastChange value will stay the same and not be set to 0 +data class SackItem( + @Expose val amount: Int, + @Expose val lastChange: Int, + @Expose val outdatedStatus: Int +) + +private val gemstoneMap = mapOf( + "Jade Gemstones" to "ROUGH_JADE_GEM".asInternalName(), + "Amber Gemstones" to "ROUGH_AMBER_GEM".asInternalName(), + "Topaz Gemstones" to "ROUGH_TOPAZ_GEM".asInternalName(), + "Sapphire Gemstones" to "ROUGH_SAPPHIRE_GEM".asInternalName(), + "Amethyst Gemstones" to "ROUGH_AMETHYST_GEM".asInternalName(), + "Jasper Gemstones" to "ROUGH_JASPER_GEM".asInternalName(), + "Ruby Gemstones" to "ROUGH_RUBY_GEM".asInternalName(), + "Opal Gemstones" to "ROUGH_OPAL_GEM".asInternalName(), +) diff --git a/src/main/java/at/hannibal2/skyhanni/data/SlayerAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/SlayerAPI.kt index 3a3bd8b33..2675c27c3 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/SlayerAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/SlayerAPI.kt @@ -121,7 +121,11 @@ object SlayerAPI { } if (event.isMod(5)) { - isInSlayerArea = SlayerType.getByArea(LorenzUtils.skyBlockArea) != null + isInSlayerArea = if (LorenzUtils.isStrandedProfile) { + true + } else { + SlayerType.getByArea(LorenzUtils.skyBlockArea) != null + } } } }
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/events/BazaarOpenedProductEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/BazaarOpenedProductEvent.kt new file mode 100644 index 000000000..a746d7468 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/events/BazaarOpenedProductEvent.kt @@ -0,0 +1,6 @@ +package at.hannibal2.skyhanni.events + +import at.hannibal2.skyhanni.utils.NEUInternalName + +class BazaarOpenedProductEvent(val openedProduct: NEUInternalName, val inventoryOpenEvent: InventoryFullyOpenedEvent) : + LorenzEvent()
\ 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/OwnInventoryItemUpdateEvent.kt index 837cbf93b..a7a5ba491 100644 --- a/src/main/java/at/hannibal2/skyhanni/events/OwnInventorItemUpdateEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/events/OwnInventoryItemUpdateEvent.kt @@ -5,4 +5,4 @@ import net.minecraft.item.ItemStack /** * Note: This event is async and may not be executed on the main minecraft thread. */ -data class OwnInventorItemUpdateEvent(val itemStack: ItemStack) : LorenzEvent()
\ No newline at end of file +data class OwnInventoryItemUpdateEvent(val itemStack: ItemStack) : LorenzEvent()
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/events/RenderEntityOutlineEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/RenderEntityOutlineEvent.kt new file mode 100644 index 000000000..21e18bd6c --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/events/RenderEntityOutlineEvent.kt @@ -0,0 +1,118 @@ +package at.hannibal2.skyhanni.events
+
+import net.minecraft.client.Minecraft
+import net.minecraft.entity.Entity
+import net.minecraft.entity.item.EntityArmorStand
+import net.minecraft.entity.item.EntityItemFrame
+import java.util.function.Consumer
+
+class RenderEntityOutlineEvent(theType: Type?, potentialEntities: HashSet<Entity>?) :
+ LorenzEvent() {
+
+ /**
+ * The phase of the event (see [Type]
+ */
+ var type: Type? = null
+
+ /**
+ * The entities to outline. This is progressively cumulated from [.entitiesToChooseFrom]
+ */
+ var entitiesToOutline: HashMap<Entity, Int>? = null
+
+ /**
+ * The entities we can outline. Note that this set and [.entitiesToOutline] are disjoint at all times.
+ */
+ var entitiesToChooseFrom: HashSet<Entity>? = null
+
+ /**
+ * Constructs the event, given the type and optional entities to outline.
+ *
+ *
+ * This will modify {@param potentialEntities} internally, so make a copy before passing it if necessary.
+ *
+ * @param theType of the event (see [Type]
+ */
+ init {
+ type = theType
+ entitiesToChooseFrom = potentialEntities
+ if (potentialEntities != null) {
+ entitiesToOutline = HashMap(potentialEntities.size)
+ }
+ }
+
+ /**
+ * Conditionally queue entities around which to render entities
+ * Selects from the pool of [.entitiesToChooseFrom] to speed up the predicate testing on subsequent calls.
+ * Is more efficient (theoretically) than calling [.queueEntityToOutline] for each entity because lists are handled internally.
+ *
+ *
+ * This function loops through all entities and so is not very efficient.
+ * It's advisable to encapsulate calls to this function with global checks (those not dependent on an individual entity) for efficiency purposes.
+ *
+ * @param outlineColor a function to test
+ */
+ fun queueEntitiesToOutline(outlineColor: ((entity: Entity) -> Int?)? = null) {
+ if (outlineColor == null) {
+ return
+ }
+ if (entitiesToChooseFrom == null) {
+ computeAndCacheEntitiesToChooseFrom()
+ }
+ val itr: MutableIterator<Entity> = entitiesToChooseFrom!!.iterator()
+ while (itr.hasNext()) {
+ val e: Entity = itr.next()
+ val i: Int? = outlineColor(e)
+ if (i != null) {
+ entitiesToOutline!![e] = i
+ itr.remove()
+ }
+ }
+ }
+
+ /**
+ * Adds a single entity to the list of the entities to outline
+ *
+ * @param entity the entity to add
+ * @param outlineColor the color with which to outline
+ */
+ fun queueEntityToOutline(entity: Entity?, outlineColor: Int) {
+ if (entity == null) {
+ return
+ }
+ if (entitiesToChooseFrom == null) {
+ computeAndCacheEntitiesToChooseFrom()
+ }
+ if (!entitiesToChooseFrom!!.contains(entity)) {
+ return
+ }
+ entitiesToOutline!![entity] = outlineColor
+ entitiesToChooseFrom!!.remove(entity)
+ }
+
+ /**
+ * Used for on-the-fly generation of entities. Driven by event handlers in a decentralized fashion
+ */
+ private fun computeAndCacheEntitiesToChooseFrom() {
+ val entities: List<Entity> = Minecraft.getMinecraft().theWorld.getLoadedEntityList()
+ // Only render outlines around non-null entities within the camera frustum
+ entitiesToChooseFrom = HashSet(entities.size)
+ // Only consider entities that aren't invisible armorstands to increase FPS significantly
+ entities.forEach(Consumer<Entity> { e: Entity? ->
+ if (e != null && !(e is EntityArmorStand && e.isInvisible()) && e !is EntityItemFrame) {
+ entitiesToChooseFrom!!.add(e)
+ }
+ })
+ entitiesToOutline = HashMap(entitiesToChooseFrom!!.size)
+ }
+
+ /**
+ * The phase of the event.
+ * [.XRAY] means that this directly precedes entities whose outlines are rendered through walls (Vanilla 1.9+)
+ * [.NO_XRAY] means that this directly precedes entities whose outlines are rendered only when visible to the client
+ */
+ enum class Type {
+ XRAY,
+ NO_XRAY
+ }
+
+}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/events/SackChangeEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/SackChangeEvent.kt index b2006bc0d..aa6101a62 100644 --- a/src/main/java/at/hannibal2/skyhanni/events/SackChangeEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/events/SackChangeEvent.kt @@ -3,7 +3,7 @@ package at.hannibal2.skyhanni.events import at.hannibal2.skyhanni.data.SackAPI class SackChangeEvent( - val sackChanged: List<SackAPI.SackChange>, + val sackChanges: List<SackAPI.SackChange>, val otherItemsAdded: Boolean, val otherItemsRemoved: Boolean ) : LorenzEvent() 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 d17a3dc6b..dfc69dee8 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/bazaar/BazaarApi.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/bazaar/BazaarApi.kt @@ -21,6 +21,8 @@ class BazaarApi { var inBazaarInventory = false private var currentSearchedItem = "" + var currentlyOpenedProduct: NEUInternalName? = null + fun getBazaarDataByName(name: String): BazaarData? = NEUItems.getInternalNameOrNull(name)?.getBazaarData() fun NEUInternalName.getBazaarData() = if (isBazaarItem()) { @@ -45,6 +47,21 @@ class BazaarApi { @SubscribeEvent fun onInventoryOpen(event: InventoryFullyOpenedEvent) { inBazaarInventory = checkIfInBazaar(event) + if (inBazaarInventory) { + val openedProduct = getOpenedProduct(event.inventoryItems) ?: return + currentlyOpenedProduct = openedProduct + BazaarOpenedProductEvent(openedProduct, event).postAndCatch() + } + } + + private fun getOpenedProduct(inventoryItems: Map<Int, ItemStack>): NEUInternalName? { + val buyInstantly = inventoryItems[10] ?: return null + + if (buyInstantly.displayName != "§aBuy Instantly") return null + val bazaarItem = inventoryItems[13] ?: return null + + val itemName = bazaarItem.displayName + return NEUItems.getInternalNameOrNull(itemName) } @SubscribeEvent @@ -118,5 +135,6 @@ class BazaarApi { @SubscribeEvent fun onInventoryClose(event: InventoryCloseEvent) { inBazaarInventory = false + currentlyOpenedProduct = null } }
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/bazaar/BazaarBestSellMethod.kt b/src/main/java/at/hannibal2/skyhanni/features/bazaar/BazaarBestSellMethod.kt index d6145fb63..b42a331fe 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/bazaar/BazaarBestSellMethod.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/bazaar/BazaarBestSellMethod.kt @@ -1,16 +1,18 @@ package at.hannibal2.skyhanni.features.bazaar import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.events.BazaarOpenedProductEvent import at.hannibal2.skyhanni.events.InventoryCloseEvent import at.hannibal2.skyhanni.features.bazaar.BazaarApi.Companion.getBazaarData +import at.hannibal2.skyhanni.utils.InventoryUtils import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName import at.hannibal2.skyhanni.utils.ItemUtils.getNameWithEnchantment import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.NEUItems +import at.hannibal2.skyhanni.utils.NEUInternalName import at.hannibal2.skyhanni.utils.NumberUtil import at.hannibal2.skyhanni.utils.RenderUtils.renderString -import net.minecraft.client.gui.inventory.GuiChest -import net.minecraft.inventory.ContainerChest +import io.github.moulberry.notenoughupdates.events.SlotClickEvent +import net.minecraft.item.ItemStack import net.minecraftforge.client.event.GuiScreenEvent import net.minecraftforge.fml.common.eventhandler.EventPriority import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @@ -18,40 +20,36 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent class BazaarBestSellMethod { private var display = "" + // Working with the last clicked item manually because + // the open inventory event happen while the recent clicked item in the inventory is not in the inventory or in the cursor slot + private var lastClickedItem: ItemStack? = null + private var nextCloseWillResetItem = false + @SubscribeEvent fun onInventoryClose(event: InventoryCloseEvent) { display = "" + if (lastClickedItem != null) { + if (nextCloseWillResetItem) { + lastClickedItem = null + } + nextCloseWillResetItem = !nextCloseWillResetItem + } } @SubscribeEvent - fun onGuiDraw(event: GuiScreenEvent.DrawScreenEvent.Post) { + fun onBazaarOpenedProduct(event: BazaarOpenedProductEvent) { if (!isEnabled()) return - display = getNewText(event) + display = updateDisplay(event.openedProduct) } - private fun getNewText(event: GuiScreenEvent.DrawScreenEvent.Post): String { + private fun updateDisplay(internalName: NEUInternalName): String { try { - if (event.gui !is GuiChest) return "" - val chest = (event.gui as GuiChest).inventorySlots as ContainerChest - - val inv = chest.lowerChestInventory ?: return "" - - val buyInstantly = inv.getStackInSlot(10) - if (buyInstantly == null || buyInstantly.displayName != "§aBuy Instantly") return "" - val bazaarItem = inv.getStackInSlot(13) ?: return "" - - val internalName = NEUItems.getInternalNameOrNull(bazaarItem.displayName) ?: return "" - - var having = 0 - for (slot in chest.inventorySlots) { - if (slot == null) continue - if (slot.slotNumber == slot.slotIndex) continue - val stack = slot.stack ?: continue - if (internalName == stack.getInternalName()) { - having += stack.stackSize + var having = InventoryUtils.countItemsInLowerInventory { it.getInternalName() == internalName } + lastClickedItem?.let { + if (it.getInternalName() == internalName) { + having += it.stackSize } } - if (having <= 0) return "" val data = internalName.getBazaarData() ?: return "" @@ -73,5 +71,11 @@ class BazaarBestSellMethod { SkyHanniMod.feature.bazaar.bestSellMethodPos.renderString(display, posLabel = "Bazaar Best Sell Method") } + @SubscribeEvent(priority = EventPriority.HIGH) + fun onStackClick(event: SlotClickEvent) { + lastClickedItem = event.slot?.stack + nextCloseWillResetItem = false + } + private fun isEnabled() = LorenzUtils.inSkyBlock && SkyHanniMod.feature.bazaar.bestSellMethod }
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/bazaar/BazaarOpenPriceWebsite.kt b/src/main/java/at/hannibal2/skyhanni/features/bazaar/BazaarOpenPriceWebsite.kt new file mode 100644 index 000000000..92f5ec78f --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/bazaar/BazaarOpenPriceWebsite.kt @@ -0,0 +1,57 @@ +package at.hannibal2.skyhanni.features.bazaar + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.utils.NEUInternalName +import at.hannibal2.skyhanni.utils.NEUItems +import at.hannibal2.skyhanni.utils.OSUtils +import io.github.moulberry.notenoughupdates.events.ReplaceItemEvent +import io.github.moulberry.notenoughupdates.events.SlotClickEvent +import io.github.moulberry.notenoughupdates.util.Utils +import net.minecraftforge.fml.common.eventhandler.EventPriority +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class BazaarOpenPriceWebsite { + private val config get() = SkyHanniMod.feature.bazaar + + private val item by lazy { + val neuItem = NEUItems.getItemStack("PAPER", true) + Utils.createItemStack( + neuItem.item, + "§bPrice History", + "§7Click here to open", + "§7the price history", + "§7on §cskyblock.bz" + ) + } + + @SubscribeEvent + fun replaceItem(event: ReplaceItemEvent) { + if (!isEnabled()) return + BazaarApi.currentlyOpenedProduct ?: return + + if (event.slotNumber == 22) { + event.replaceWith(item) + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + fun onStackClick(event: SlotClickEvent) { + if (!isEnabled()) return + val lastItem = BazaarApi.currentlyOpenedProduct ?: return + + if (event.slotId == 22) { + event.isCanceled = true + val name = getSkyBlockBzName(lastItem) + OSUtils.openBrowser("https://www.skyblock.bz/product/$name") + } + } + + private fun getSkyBlockBzName(internalName: NEUInternalName): String { + val name = internalName.asString() + return if (name.contains(";")) { + "ENCHANTMENT_" + name.replace(";", "_") + } else name + } + + fun isEnabled() = config.openPriceWebsite +}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/chat/Translator.kt b/src/main/java/at/hannibal2/skyhanni/features/chat/Translator.kt index f39905876..1274fbdf2 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/chat/Translator.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/chat/Translator.kt @@ -34,13 +34,11 @@ class Translator { if (e.type != 0.toByte()) return // If this is not a player-sent message, return val chatComponent = e.message - // If you want to help me debug, experience the bug while this line is uncommented (spams logs) -// consoleLog(chatComponent.toString()) val message = chatComponent.unformattedText if (!messageContentRegex.matches(message.removeColor())) return val clickStyle = createClickStyle(message) - chatComponent.setChatStyle(clickStyle) + chatComponent.siblings.last().setChatStyle(clickStyle) } private fun createClickStyle(message: String): ChatStyle { @@ -180,7 +178,7 @@ class Translator { val sentenceWithoutQuotes = sentence.substring(1, sentence.length - 1) messageToSend = "$messageToSend$sentenceWithoutQuotes" } // The first translated sentence only has 1 extra char at the end, but sentences after it need 1 at the front and 1 at the end removed in the substring - messageToSend = messageToSend.substring(1, messageToSend.length - 1) + messageToSend = messageToSend.substring(1, messageToSend.length) return URLDecoder.decode(messageToSend, "UTF-8").replace("\\", "") } @@ -210,7 +208,7 @@ class Translator { } val translation = getTranslationFromEnglish(message, language) - LorenzUtils.chat("§6[SkyHanni] §eCopied translation to clipboard: $translation") + LorenzUtils.chat("§e[SkyHanni] Copied translation to clipboard: $translation") OSUtils.copyToClipboard(translation) } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonTeammateOutlines.kt b/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonTeammateOutlines.kt new file mode 100644 index 000000000..de2ca07e2 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonTeammateOutlines.kt @@ -0,0 +1,39 @@ +package at.hannibal2.skyhanni.features.dungeon
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.RenderEntityOutlineEvent
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import net.minecraft.client.Minecraft
+import net.minecraft.client.entity.EntityOtherPlayerMP
+import net.minecraft.client.gui.FontRenderer
+import net.minecraft.entity.Entity
+import net.minecraft.scoreboard.ScorePlayerTeam
+import net.minecraft.scoreboard.Team
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+class DungeonTeammateOutlines {
+ private val config get() = SkyHanniMod.feature.dungeon
+
+ @SubscribeEvent
+ fun onRenderEntityOutlines(event: RenderEntityOutlineEvent) {
+ if (isEnabled() && event.type === RenderEntityOutlineEvent.Type.XRAY) {
+ event.queueEntitiesToOutline { entity -> getEntityOutlineColor(entity) }
+ }
+ }
+
+ private fun isEnabled() = LorenzUtils.inSkyBlock && LorenzUtils.inDungeons && config.highlightTeammates
+
+ private fun getEntityOutlineColor(entity: Entity): Int? {
+ if (entity !is EntityOtherPlayerMP || entity.team == null) return null
+
+ // Must be visible on the scoreboard
+ val team = entity.team as ScorePlayerTeam
+ if (team.nameTagVisibility == Team.EnumVisible.NEVER) return null
+
+ val colorFormat = FontRenderer.getFormatFromString(team.colorPrefix)
+ return if (colorFormat.length >= 2)
+ Minecraft.getMinecraft().fontRendererObj.getColorCode(colorFormat[1]);
+ else null
+ }
+
+}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/anniversary/ActivePlayerTimer.kt b/src/main/java/at/hannibal2/skyhanni/features/event/anniversary/ActivePlayerTimer.kt new file mode 100644 index 000000000..a83def532 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/event/anniversary/ActivePlayerTimer.kt @@ -0,0 +1,76 @@ +package at.hannibal2.skyhanni.features.event.anniversary + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.events.LorenzChatEvent +import at.hannibal2.skyhanni.events.LorenzTickEvent +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.NEUItems +import at.hannibal2.skyhanni.utils.RenderUtils.renderSingleLineWithItems +import at.hannibal2.skyhanni.utils.SoundUtils +import at.hannibal2.skyhanni.utils.SoundUtils.playSound +import at.hannibal2.skyhanni.utils.TimeMark +import at.hannibal2.skyhanni.utils.TimeUtils.format +import at.hannibal2.skyhanni.utils.renderables.Renderable +import io.github.moulberry.notenoughupdates.util.SkyBlockTime +import net.minecraft.init.Items +import net.minecraft.item.ItemStack +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.time.Instant +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds + +object ActivePlayerTimer { + val displayItem by lazy { NEUItems.getItemStackOrNull("EPOCH_CAKE_ORANGE") ?: ItemStack(Items.clock) } + + private var lastTimerReceived = TimeMark.never() + private var lastTimeAlerted = TimeMark.never() + + private var overlay: List<Any>? = null + + @SubscribeEvent + fun onChat(event: LorenzChatEvent) { + if (event.message == "§6§lACTIVE PLAYER! §eYou gained §b+1 Raffle Ticket§e!") { + lastTimerReceived = TimeMark.now() + } + } + + fun isEnabled(): Boolean { + return SkyHanniMod.feature.misc.century.enableActiveTimer && + Instant.now().isBefore(SkyBlockTime(301).toInstant()) && + LorenzUtils.inSkyBlock + } + + + @SubscribeEvent + fun onRender(event: GuiRenderEvent.GameOverlayRenderEvent) { + SkyHanniMod.feature.misc.century.activeTimerPosition.renderSingleLineWithItems( + overlay ?: return, + posLabel = "300þ Anniversary Active Timer" + ) + } + + @SubscribeEvent + fun onTick(event: LorenzTickEvent) { + if (!isEnabled()) { + overlay = null + return + } + val p = lastTimerReceived.passedTime() + val timeLeft = if (p > 20.minutes) { + 0.seconds + } else { + 20.minutes - p + } + if (p.isFinite() && timeLeft < 1.seconds && lastTimeAlerted.passedTime() > 5.minutes && SkyHanniMod.feature.misc.century.enableActiveAlert) { + SoundUtils.centuryActiveTimerAlert.playSound() + lastTimeAlerted = TimeMark.now() + } + overlay = listOf( + Renderable.itemStack(displayItem), + Renderable.string("§eTime Left: ${timeLeft.format()}") + ) + } + + +}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingTimer.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingTimer.kt index f5f87ee2c..fce359672 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingTimer.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingTimer.kt @@ -65,17 +65,22 @@ class FishingTimer { private fun countMobs() = EntityUtils.getEntities<EntityArmorStand>() .map { entity -> val name = entity.name - if (SeaCreatureManager.allFishingMobNames.any { name.contains(it) }) { + val isSummonedSoul = name.contains("'") + val hasFishingMobName = SeaCreatureManager.allFishingMobNames.any { name.contains(it) } + if (hasFishingMobName && !isSummonedSoul) { if (name == "Sea Emperor" || name == "Rider of the Deep") 2 else 1 } else 0 }.sum() private fun isRightLocation(): Boolean { + inHollows = false + + if (config.barnTimerForStranded && LorenzUtils.isStrandedProfile) return true + if (config.barnTimerCrystalHollows && IslandType.CRYSTAL_HOLLOWS.isInIsland()) { inHollows = true return true } - inHollows = false if (!IslandType.THE_FARMING_ISLANDS.isInIsland()) { return LocationUtils.playerLocation().distance(barnLocation) < 50 diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/SeaCreature.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/SeaCreature.kt index c2b452cbe..987a57d00 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/fishing/SeaCreature.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/SeaCreature.kt @@ -8,7 +8,7 @@ data class SeaCreature( ) { override fun toString(): String { - return chatColor + rare() + displayName + return chatColor + rare() + displayName } private fun rare(): String { diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/SeaCreatureFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/SeaCreatureFeatures.kt new file mode 100644 index 000000000..90e433725 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/SeaCreatureFeatures.kt @@ -0,0 +1,84 @@ +package at.hannibal2.skyhanni.features.fishing + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.events.EntityMaxHealthUpdateEvent +import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent +import at.hannibal2.skyhanni.events.RenderEntityOutlineEvent +import at.hannibal2.skyhanni.events.withAlpha +import at.hannibal2.skyhanni.features.damageindicator.DamageIndicatorManager +import at.hannibal2.skyhanni.mixins.hooks.RenderLivingEntityHelper +import at.hannibal2.skyhanni.utils.EntityUtils.hasMaxHealth +import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer +import at.hannibal2.skyhanni.utils.LorenzColor +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.LorenzUtils.editCopy +import net.minecraft.entity.Entity +import net.minecraft.entity.EntityLivingBase +import net.minecraft.entity.monster.EntityGuardian +import net.minecraft.entity.monster.EntityZombie +import net.minecraft.entity.player.EntityPlayer +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + + +class SeaCreatureFeatures { + private val config get() = SkyHanniMod.feature.fishing + private var rareSeaCreatures = listOf<EntityLivingBase>() + + @SubscribeEvent + fun onEntityHealthUpdate(event: EntityMaxHealthUpdateEvent) { + if (!isEnabled()) return + val entity = event.entity as? EntityLivingBase ?: return + if (DamageIndicatorManager.isBoss(entity)) return + + val maxHealth = event.maxHealth + for (creatureType in RareSeaCreatureType.entries) { + if (!creatureType.health.any { entity.hasMaxHealth(it, false, maxHealth) }) continue + if (!creatureType.clazz.isInstance(entity)) continue + + if (creatureType.nametag.isNotBlank() && EntityPlayer::class.java.isInstance(entity) && (entity as EntityPlayer).name != creatureType.nametag) { + continue + } + + rareSeaCreatures = rareSeaCreatures.editCopy { add(entity) } + RenderLivingEntityHelper.setEntityColor(entity, LorenzColor.RED.toColor().withAlpha(50)) + { config.rareSeaCreatureHighlight } + RenderLivingEntityHelper.setNoHurtTime(entity) { config.rareSeaCreatureHighlight } + } + } + + @SubscribeEvent + fun onWorldChange(event: LorenzWorldChangeEvent) { + rareSeaCreatures = emptyList() + } + + @SubscribeEvent + fun onRenderEntityOutlines(event: RenderEntityOutlineEvent) { + if (isEnabled() && config.rareSeaCreatureHighlight && event.type === RenderEntityOutlineEvent.Type.XRAY) { + event.queueEntitiesToOutline(getEntityOutlineColor) + } + } + + private fun isEnabled() = LorenzUtils.inSkyBlock && !LorenzUtils.inDungeons && !LorenzUtils.inKuudraFight + + private val getEntityOutlineColor: (entity: Entity) -> Int? = { entity -> + if (EntityLivingBase::class.java.isInstance(entity) && entity in rareSeaCreatures && entity.distanceToPlayer() < 30) { + LorenzColor.GREEN.toColor().rgb + } else null + } + + enum class RareSeaCreatureType( + val clazz: Class<out EntityLivingBase>, + val nametag: String, + vararg val health: Int + ) { + WATER_HYDRA(EntityZombie::class.java, "Water Hydra", 500_000, 1_500_000), + SEA_EMPEROR(EntityGuardian::class.java, "The Sea Emperors", 750_000, 800_000, 2_250_000, 2_400_000), + ZOMBIE_MINER(EntityPlayer::class.java, "", 2_000_000, 6_000_000), + PHANTOM_FISHERMAN(EntityPlayer::class.java, "Phantom Fisher", 1_000_000, 3_000_000), + GRIM_REAPER(EntityPlayer::class.java, "Grim Reaper", 3_000_000, 9_000_000), + YETI(EntityPlayer::class.java, "", 2_000_000, 6_000_000), + NUTCRACKER(EntityPlayer::class.java, "", 4_000_000, 12_000_000), + GREAT_WHITE_SHARK(EntityPlayer::class.java, "GWS ", 1_500_000, 4_500_000), + ; + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/TrophyFishMessages.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/TrophyFishMessages.kt index 423a393f1..4a0b6080e 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/TrophyFishMessages.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/TrophyFishMessages.kt @@ -48,13 +48,15 @@ class TrophyFishMessages { " §7(${total.addSeparators()}. total)" } else "" - val component = ChatComponentText(if (config.trophyCounter) { - "§6§lTROPHY FISH! " + when (config.trophyDesign) { - 0 -> "§7$amount. §r$displayRarity $displayName$totalText" - 1 -> "§bYou caught a $displayName $displayRarity§b. §7(${amount.addSeparators()})$totalText" - else -> "§bYou caught your ${amount.addSeparators()}${amount.ordinal()} $displayRarity $displayName§b.$totalText" - } - } else event.message) + val component = ChatComponentText( + if (config.trophyCounter) { + "§6§lTROPHY FISH! " + when (config.trophyDesign) { + 0 -> "§7$amount. §r$displayRarity $displayName$totalText" + 1 -> "§bYou caught a $displayName $displayRarity§b. §7(${amount.addSeparators()})$totalText" + else -> "§bYou caught your ${amount.addSeparators()}${amount.ordinal()} $displayRarity $displayName§b.$totalText" + } + } else event.message + ) if (config.trophyFishTooltip) { TrophyFishManager.getInfo(internalName)?.let { diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/CropType.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/CropType.kt index 084c8d9e9..3bb3534e3 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/CropType.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/CropType.kt @@ -39,7 +39,7 @@ enum class CropType( return entries.firstOrNull { it.cropName == itemName } } - fun getByName(name: String) = getByNameOrNull(name) ?: throw RuntimeException("No valid crop type '$name'") + fun getByName(name: String) = getByNameOrNull(name) ?: error("No valid crop type '$name'") fun IBlockState.getCropType(): CropType? { return when (block) { diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/FarmingFortuneDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/FarmingFortuneDisplay.kt index d2c1b2c4d..4a45ed901 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/FarmingFortuneDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/FarmingFortuneDisplay.kt @@ -49,7 +49,7 @@ class FarmingFortuneDisplay { } @SubscribeEvent(priority = EventPriority.LOW) - fun onInventoryUpdate(event: OwnInventorItemUpdateEvent) { + fun onInventoryUpdate(event: OwnInventoryItemUpdateEvent) { if (!GardenAPI.inGarden()) return if (event.itemStack.getCropType() == null) return updateToolFortune(event.itemStack) diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterDisplay.kt index 2f88c52de..aab3b082b 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterDisplay.kt @@ -16,6 +16,7 @@ import java.util.* import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds import kotlin.time.DurationUnit +import kotlin.math.floor class ComposterDisplay { private val config get() = SkyHanniMod.feature.garden @@ -69,8 +70,8 @@ class ComposterDisplay { val organicMatterRequired = ComposterAPI.organicMatterRequiredPer(null) val fuelRequired = ComposterAPI.fuelRequiredPer(null) - val organicMatterRemaining = organicMatter / organicMatterRequired - val fuelRemaining = fuel / fuelRequired + val organicMatterRemaining = floor(organicMatter / organicMatterRequired) + val fuelRemaining = floor(fuel / fuelRequired) val endOfOrganicMatter = timePerCompost * organicMatterRemaining val endOfFuel = timePerCompost * fuelRemaining 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 66c7b2c41..e86d4124a 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 @@ -201,9 +201,9 @@ class ComposterOverlay { val matterPer = ComposterAPI.organicMatterRequiredPer(null) val matterPerPreview = ComposterAPI.organicMatterRequiredPer(upgrade) - val matterMaxDuration = ComposterAPI.timePerCompost(null) * (maxOrganicMatter / matterPer) + val matterMaxDuration = ComposterAPI.timePerCompost(null) * floor(maxOrganicMatter / matterPer) val matterMaxDurationPreview = - ComposterAPI.timePerCompost(upgrade) * (maxOrganicMatterPreview / matterPerPreview) + ComposterAPI.timePerCompost(upgrade) * floor(maxOrganicMatterPreview / matterPerPreview) var format = formatTime(matterMaxDuration) var formatPreview = @@ -217,9 +217,9 @@ class ComposterOverlay { val fuelRequiredPer = ComposterAPI.fuelRequiredPer(null) val fuelRequiredPerPreview = ComposterAPI.fuelRequiredPer(upgrade) - val fuelMaxDuration = ComposterAPI.timePerCompost(null) * (maxFuel / fuelRequiredPer) + val fuelMaxDuration = ComposterAPI.timePerCompost(null) * floor(maxFuel / fuelRequiredPer) val fuelMaxDurationPreview = - ComposterAPI.timePerCompost(upgrade) * (maxFuelPreview / fuelRequiredPerPreview) + ComposterAPI.timePerCompost(upgrade) * floor(maxFuelPreview / fuelRequiredPerPreview) format = formatTime(fuelMaxDuration) formatPreview = @@ -350,8 +350,8 @@ class ComposterOverlay { val priceCompost = getPrice("COMPOST") - val profit = (priceCompost - (fuelPricePer + organicMatterPricePer)) * multiplier - val profitPreview = (priceCompost - (fuelPricePerPreview + organicMatterPricePerPreview)) * multiplierPreview + val profit = ((priceCompost * multiDropFactor) - (fuelPricePer + organicMatterPricePer)) * timeMultiplier + val profitPreview = ((priceCompost * multiDropFactorPreview) - (fuelPricePerPreview + organicMatterPricePerPreview)) * timeMultiplierPreview val profitFormatPreview = if (profit != profitPreview) " §c➜ §6" + NumberUtil.format(profitPreview) else "" val profitFormat = " §7Profit per $timeText: §6${NumberUtil.format(profit)}$profitFormatPreview" diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobFarmingContestsInventory.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobFarmingContestsInventory.kt index 61bd56198..29333d205 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobFarmingContestsInventory.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobFarmingContestsInventory.kt @@ -1,15 +1,13 @@ package at.hannibal2.skyhanni.features.garden.contest import at.hannibal2.skyhanni.SkyHanniMod -import at.hannibal2.skyhanni.events.GuiContainerEvent -import at.hannibal2.skyhanni.events.InventoryCloseEvent -import at.hannibal2.skyhanni.events.InventoryUpdatedEvent -import at.hannibal2.skyhanni.events.LorenzToolTipEvent +import at.hannibal2.skyhanni.events.* import at.hannibal2.skyhanni.utils.InventoryUtils 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.RenderUtils.drawSlotText import at.hannibal2.skyhanni.utils.RenderUtils.highlight import net.minecraft.client.gui.inventory.GuiChest import net.minecraft.inventory.ContainerChest @@ -18,7 +16,6 @@ import java.text.SimpleDateFormat import java.util.* class JacobFarmingContestsInventory { - private val duplicateSlots = mutableListOf<Int>() private val realTime = mutableMapOf<Int, String>() @@ -28,6 +25,7 @@ class JacobFarmingContestsInventory { // Render the contests a tick delayed to feel smoother private var hideEverything = true + private val contestEarnedPattern = "§7You earned a §(?<medalColour>.*)§l.* §7medal!".toPattern() @SubscribeEvent fun onInventoryClose(event: InventoryCloseEvent) { @@ -142,4 +140,37 @@ class JacobFarmingContestsInventory { } } } + + @SubscribeEvent + fun onRenderItemOverlayPost(event: GuiRenderItemEvent.RenderOverlayEvent.GuiRenderItemPost) { + if (!LorenzUtils.inSkyBlock) return + if (!config.jacobFarmingContestMedalIcon) return + if (!InventoryUtils.openInventoryName().contains("Your Contests")) return + + val stack = event.stack ?: return + var finneganContest = false + + for (line in stack.getLore()) { + if (line.contains("Contest boosted by Finnegan!")) finneganContest = true + + val matcher = contestEarnedPattern.matcher(line) + if (matcher.matches()) { + val medalEarned = ContestBracket.entries.find { it.color == matcher.group("medalColour") } ?: return + + var stackTip = "§${medalEarned.color}✦" + var x = event.x + 9 + var y = event.y + 1 + var scale = .7f + + if (finneganContest && config.jacobFarmingContestFinneganIcon) { + stackTip = "§${medalEarned.color}▲" + x = event.x + 5 + y = event.y - 2 + scale = 1.3f + } + + event.drawSlotText(x, y, stackTip, scale) + } + } + } }
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/FarmingWeightDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/FarmingWeightDisplay.kt index 71b69107b..7a993f0de 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/FarmingWeightDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/FarmingWeightDisplay.kt @@ -66,6 +66,11 @@ class FarmingWeightDisplay { if (!isEnabled()) return if (!event.isMod(5)) return update() + + SkyHanniMod.coroutineScope.launch { + getCropWeights() + hasFetchedCropWeights = true + } } companion object { @@ -403,15 +408,35 @@ class FarmingWeightDisplay { private fun CropType.getLocalCounter() = localCounter[this] ?: 0L - private fun CropType.getFactor() = factorPerCrop[this]!! + private fun CropType.getFactor(): Double { + return factorPerCrop[this] ?: backupFactors[this] ?: error("Crop $this not in backupFactors!") + } fun lookUpCommand(it: Array<String>) { val name = if (it.size == 1) it[0] else LorenzUtils.getPlayerName() OSUtils.openBrowser("https://elitebot.dev/@$name/") - LorenzUtils.chat("§e[SkyHanni] Opening Farming Profile from §b$name") + LorenzUtils.chat("§e[SkyHanni] Opening Farming Profile of player §b$name") + } + + private val factorPerCrop = mutableMapOf<CropType, Double>() + private var attemptingCropWeightFetch = false + private var hasFetchedCropWeights = false + + private suspend fun getCropWeights() { + if (attemptingCropWeightFetch || hasFetchedCropWeights) return + attemptingCropWeightFetch = true + + val url = "https://api.elitebot.dev/weights" + val result = withContext(Dispatchers.IO) { APIUtil.getJSONResponse(url) }.asJsonObject + + for (crop in result.entrySet()) { + val cropType = CropType.getByName(crop.key) + factorPerCrop[cropType] = crop.value.asDouble + } } - private val factorPerCrop by lazy { + // still needed when first joining garden and if they cant make https requests + private val backupFactors by lazy { mapOf( CropType.WHEAT to 100_000.0, CropType.CARROT to 302_061.86, @@ -425,7 +450,7 @@ class FarmingWeightDisplay { CropType.CACTUS to 177_254.45, ) } - } - class UpcomingPlayer(val name: String, val weight: Double) + class UpcomingPlayer(val name: String, val weight: Double) + } }
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropMilestoneDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropMilestoneDisplay.kt index 03b9ea809..3975e2471 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropMilestoneDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropMilestoneDisplay.kt @@ -83,7 +83,7 @@ object GardenCropMilestoneDisplay { } @SubscribeEvent - fun onOwnInventoryItemUpdate(event: OwnInventorItemUpdateEvent) { + fun onOwnInventoryItemUpdate(event: OwnInventoryItemUpdateEvent) { if (!GardenAPI.inGarden()) return try { @@ -104,7 +104,7 @@ object GardenCropMilestoneDisplay { } cultivatingData[crop] = counter } catch (e: Throwable) { - LorenzUtils.error("[SkyHanni] Error in OwnInventorItemUpdateEvent") + LorenzUtils.error("[SkyHanni] Error in OwnInventoryItemUpdateEvent") e.printStackTrace() } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/WildStrawberryDyeNotification.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/WildStrawberryDyeNotification.kt index 171f3c989..1e5736f91 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/WildStrawberryDyeNotification.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/WildStrawberryDyeNotification.kt @@ -3,7 +3,7 @@ package at.hannibal2.skyhanni.features.garden.farming import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.data.TitleUtils import at.hannibal2.skyhanni.events.GuiContainerEvent -import at.hannibal2.skyhanni.events.OwnInventorItemUpdateEvent +import at.hannibal2.skyhanni.events.OwnInventoryItemUpdateEvent import at.hannibal2.skyhanni.features.garden.GardenAPI import at.hannibal2.skyhanni.utils.ItemBlink import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName_old @@ -24,7 +24,7 @@ class WildStrawberryDyeNotification { } @SubscribeEvent - fun onOwnInventoryItemUpdate(event: OwnInventorItemUpdateEvent) { + fun onOwnInventoryItemUpdate(event: OwnInventoryItemUpdateEvent) { if (!GardenAPI.inGarden()) return if (!SkyHanniMod.feature.garden.wildStrawberryDyeNotification) return diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/fortuneguide/FFGuideGUI.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/fortuneguide/FFGuideGUI.kt index 76b0fda29..11465df66 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/fortuneguide/FFGuideGUI.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/fortuneguide/FFGuideGUI.kt @@ -28,6 +28,7 @@ open class FFGuideGUI : GuiScreen() { var selectedPage = FortuneGuidePage.OVERVIEW var currentCrop: CropType? = null + //todo set this to what they have equip var currentPet = FarmingItems.ELEPHANT var currentArmor = 0 @@ -163,22 +164,30 @@ open class FFGuideGUI : GuiScreen() { ) GuiRenderUtils.renderItemAndTip( - FarmingItems.HELMET.getItem(), guiLeft + 162, guiTop + 80, mouseX, mouseY) + FarmingItems.HELMET.getItem(), guiLeft + 162, guiTop + 80, mouseX, mouseY + ) GuiRenderUtils.renderItemAndTip( - FarmingItems.CHESTPLATE.getItem(), guiLeft + 162, guiTop + 100, mouseX, mouseY) + FarmingItems.CHESTPLATE.getItem(), guiLeft + 162, guiTop + 100, mouseX, mouseY + ) GuiRenderUtils.renderItemAndTip( - FarmingItems.LEGGINGS.getItem(), guiLeft + 162, guiTop + 120, mouseX, mouseY) + FarmingItems.LEGGINGS.getItem(), guiLeft + 162, guiTop + 120, mouseX, mouseY + ) GuiRenderUtils.renderItemAndTip( - FarmingItems.BOOTS.getItem(), guiLeft + 162, guiTop + 140, mouseX, mouseY) + FarmingItems.BOOTS.getItem(), guiLeft + 162, guiTop + 140, mouseX, mouseY + ) GuiRenderUtils.renderItemAndTip( - FarmingItems.NECKLACE.getItem(), guiLeft + 182, guiTop + 80, mouseX, mouseY) + FarmingItems.NECKLACE.getItem(), guiLeft + 182, guiTop + 80, mouseX, mouseY + ) GuiRenderUtils.renderItemAndTip( - FarmingItems.CLOAK.getItem(), guiLeft + 182, guiTop + 100, mouseX, mouseY) + FarmingItems.CLOAK.getItem(), guiLeft + 182, guiTop + 100, mouseX, mouseY + ) GuiRenderUtils.renderItemAndTip( - FarmingItems.BELT.getItem(), guiLeft + 182, guiTop + 120, mouseX, mouseY) + FarmingItems.BELT.getItem(), guiLeft + 182, guiTop + 120, mouseX, mouseY + ) GuiRenderUtils.renderItemAndTip( - FarmingItems.BRACELET.getItem(), guiLeft + 182, guiTop + 140, mouseX, mouseY) + FarmingItems.BRACELET.getItem(), guiLeft + 182, guiTop + 140, mouseX, mouseY + ) } } pages[selectedPage]?.drawPage(mouseX, mouseY, partialTicks) @@ -281,23 +290,27 @@ open class FFGuideGUI : GuiScreen() { if (selectedPage != FortuneGuidePage.UPGRADES) { if (currentCrop == null) { - if (GuiRenderUtils.isPointInRect(mouseX, mouseY, guiLeft + 142, guiTop + 130, - 16, 16) && currentPet != FarmingItems.ELEPHANT) { + if (GuiRenderUtils.isPointInRect(mouseX, mouseY, guiLeft + 142, guiTop + 130, 16, 16) && + currentPet != FarmingItems.ELEPHANT + ) { SoundUtils.playClickSound() currentPet = FarmingItems.ELEPHANT FFStats.getTotalFF() - } else if (GuiRenderUtils.isPointInRect(mouseX, mouseY, guiLeft + 162, guiTop + 130, - 16, 16) && currentPet != FarmingItems.MOOSHROOM_COW) { + } else if (GuiRenderUtils.isPointInRect(mouseX, mouseY, guiLeft + 162, guiTop + 130, 16, 16) && + currentPet != FarmingItems.MOOSHROOM_COW + ) { SoundUtils.playClickSound() currentPet = FarmingItems.MOOSHROOM_COW FFStats.getTotalFF() - } else if (GuiRenderUtils.isPointInRect(mouseX, mouseY, guiLeft + 182, guiTop + 130, - 16, 16) && currentPet != FarmingItems.RABBIT) { + } else if (GuiRenderUtils.isPointInRect(mouseX, mouseY, guiLeft + 182, guiTop + 130, 16, 16) && + currentPet != FarmingItems.RABBIT + ) { SoundUtils.playClickSound() currentPet = FarmingItems.RABBIT FFStats.getTotalFF() - } else if (GuiRenderUtils.isPointInRect(mouseX, mouseY, guiLeft + 202, guiTop + 130, - 16, 16) && currentPet != FarmingItems.BEE) { + } else if (GuiRenderUtils.isPointInRect(mouseX, mouseY, guiLeft + 202, guiTop + 130, 16, 16) && + currentPet != FarmingItems.BEE + ) { SoundUtils.playClickSound() currentPet = FarmingItems.BEE FFStats.getTotalFF() @@ -327,23 +340,27 @@ open class FFGuideGUI : GuiScreen() { currentEquipment = if (currentEquipment == 4) 0 else 4 } } else { - if (GuiRenderUtils.isPointInRect(mouseX, mouseY, guiLeft + 142, guiTop + 160, - 16, 16) && currentPet != FarmingItems.ELEPHANT) { + if (GuiRenderUtils.isPointInRect(mouseX, mouseY, guiLeft + 142, guiTop + 160, 16, 16) && + currentPet != FarmingItems.ELEPHANT + ) { SoundUtils.playClickSound() currentPet = FarmingItems.ELEPHANT FFStats.getTotalFF() - } else if (GuiRenderUtils.isPointInRect(mouseX, mouseY, guiLeft + 162, guiTop + 160, - 16, 16) && currentPet != FarmingItems.MOOSHROOM_COW) { + } else if (GuiRenderUtils.isPointInRect(mouseX, mouseY, guiLeft + 162, guiTop + 160, 16, 16) && + currentPet != FarmingItems.MOOSHROOM_COW + ) { SoundUtils.playClickSound() currentPet = FarmingItems.MOOSHROOM_COW FFStats.getTotalFF() - } else if (GuiRenderUtils.isPointInRect(mouseX, mouseY, guiLeft + 182, guiTop + 160, - 16, 16) && currentPet != FarmingItems.RABBIT) { + } else if (GuiRenderUtils.isPointInRect(mouseX, mouseY, guiLeft + 182, guiTop + 160, 16, 16) && + currentPet != FarmingItems.RABBIT + ) { SoundUtils.playClickSound() currentPet = FarmingItems.RABBIT FFStats.getTotalFF() - } else if (GuiRenderUtils.isPointInRect(mouseX, mouseY, guiLeft + 202, guiTop + 160, - 16, 16) && currentPet != FarmingItems.BEE) { + } else if (GuiRenderUtils.isPointInRect(mouseX, mouseY, guiLeft + 202, guiTop + 160, 16, 16) && + currentPet != FarmingItems.BEE + ) { SoundUtils.playClickSound() currentPet = FarmingItems.BEE FFStats.getTotalFF() @@ -359,7 +376,9 @@ open class FFGuideGUI : GuiScreen() { private fun renderTabs() { var x = guiLeft + 15 var y = guiTop - 28 - drawRect(x, y, x + 25, y + 28, if (currentCrop == null) 0x50555555 else 0x50000000) + val selectedColor = 0x50000000 + val notSelectedColor = 0x50303030 + drawRect(x, y, x + 25, y + 28, if (currentCrop == null) selectedColor else notSelectedColor) GuiRenderUtils.renderItemStack(ItemStack(Blocks.grass), x + 5, y + 5) if (GuiRenderUtils.isPointInRect(mouseX, mouseY, x, y, 25, 28)) { tooltipToDisplay.add("§eOverview") @@ -367,7 +386,7 @@ open class FFGuideGUI : GuiScreen() { for (crop in CropType.entries) { x += 30 - drawRect(x, y, x + 25, y + 28, if (currentCrop == crop) 0x50555555 else 0x50000000) + drawRect(x, y, x + 25, y + 28, if (currentCrop == crop) selectedColor else notSelectedColor) GuiRenderUtils.renderItemStack(crop.icon, x + 5, y + 5) if (GuiRenderUtils.isPointInRect(mouseX, mouseY, x, y, 25, 28)) { tooltipToDisplay.add("§e${crop.cropName}") @@ -377,13 +396,21 @@ open class FFGuideGUI : GuiScreen() { x = guiLeft - 28 y = guiTop + 15 - drawRect(x, y, x + 28, y + 25, if (selectedPage != FortuneGuidePage.UPGRADES) 0x50555555 else 0x50000000) + drawRect( + x, y, + x + 28, y + 25, + if (selectedPage != FortuneGuidePage.UPGRADES) selectedColor else notSelectedColor + ) GuiRenderUtils.renderItemStack(ItemStack(Items.gold_ingot), x + 5, y + 5) if (GuiRenderUtils.isPointInRect(mouseX, mouseY, x, y, 28, 25)) { tooltipToDisplay.add("§eBreakdown") } y += 30 - drawRect(x, y, x + 28, y + 25, if (selectedPage == FortuneGuidePage.UPGRADES) 0x50555555 else 0x50000000) + drawRect( + x, y, + x + 28, y + 25, + if (selectedPage == FortuneGuidePage.UPGRADES) selectedColor else notSelectedColor + ) GuiRenderUtils.renderItemStack(ItemStack(Items.map), x + 5, y + 5) if (GuiRenderUtils.isPointInRect(mouseX, mouseY, x, y, 28, 25)) { tooltipToDisplay.add("§eUpgrades") diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/fortuneguide/FortuneUpgrades.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/fortuneguide/FortuneUpgrades.kt index c4f0848fe..63b32ebb4 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/fortuneguide/FortuneUpgrades.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/fortuneguide/FortuneUpgrades.kt @@ -254,7 +254,7 @@ object FortuneUpgrades { } ?: return FarmingFortuneDisplay.loadFortuneLineData(item, 0.0) - val increase = reforge[item.getItemRarity() + 1, FarmingFortuneDisplay.reforgeFortune] ?: return + val increase = reforge[item.getItemRarity().id + 1, FarmingFortuneDisplay.reforgeFortune] ?: return list.add( FortuneUpgrade("§7Recombobulate your ${item.displayName}", null, "RECOMBOBULATOR_3000", 1, increase) ) @@ -267,7 +267,7 @@ object FortuneUpgrades { copperPrice: Int? = null ) { FarmingFortuneDisplay.loadFortuneLineData(item, 0.0) - val increase = reforge[item.getItemRarity(), FarmingFortuneDisplay.reforgeFortune] ?: return + val increase = reforge[item.getItemRarity().id, FarmingFortuneDisplay.reforgeFortune] ?: return list.add( FortuneUpgrade( "§7Reforge your ${item.displayName} §7to ${reforge.reforgeName}", diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorDropStatistics.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorDropStatistics.kt index eabae9ee0..5c456e53a 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorDropStatistics.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorDropStatistics.kt @@ -30,6 +30,9 @@ object GardenVisitorDropStatistics { private var copper = 0 private var gardenExp = 0 private var farmingExp = 0L + private var bits = 0 + private var mithrilPowder = 0 + private var gemstonePowder = 0 var coinsSpent = 0L var lastAccept = 0L @@ -38,6 +41,9 @@ object GardenVisitorDropStatistics { private val copperPattern = "[+](?<amount>.*) Copper".toPattern() private val gardenExpPattern = "[+](?<amount>.*) Garden Experience".toPattern() private val farmingExpPattern = "[+](?<amount>.*) Farming XP".toPattern() + private val bitsPattern = "[+](?<amount>.*) Bits".toPattern() + private val mithrilPowderPattern = "[+](?<amount>.*) Mithril Powder".toPattern() + private val gemstonePowderPattern = "[+](?<amount>.*) Gemstone Powder".toPattern() private var rewardsCount = mapOf<VisitorReward, Int>() private fun formatDisplay(map: List<List<Any>>): List<List<Any>> { @@ -76,6 +82,21 @@ object GardenVisitorDropStatistics { gardenExp += amount saveAndUpdate() } + bitsPattern.matchMatcher(message) { + val amount = group("amount").formatNumber().toInt() + bits += amount + saveAndUpdate() + } + mithrilPowderPattern.matchMatcher(message) { + val amount = group("amount").formatNumber().toInt() + mithrilPowder += amount + saveAndUpdate() + } + gemstonePowderPattern.matchMatcher(message) { + val amount = group("amount").formatNumber().toInt() + gemstonePowder += amount + saveAndUpdate() + } acceptPattern.matchMatcher(message) { setRarities(group("rarity")) saveAndUpdate() @@ -129,7 +150,7 @@ object GardenVisitorDropStatistics { //8 addAsSingletonList(format(coinsSpent, "Coins Spent", "§6", "")) - //9 – 14 + //9 – 16 for (reward in VisitorReward.entries) { val count = rewardsCount[reward] ?: 0 if (config.displayIcons) {// Icons @@ -141,10 +162,16 @@ object GardenVisitorDropStatistics { addAsSingletonList(format(count, reward.displayName, "§b")) } } - //15 + //17 addAsSingletonList("") - //16 + //18 addAsSingletonList(format(gardenExp, "Garden EXP", "§2", "§7")) + //19 + addAsSingletonList(format(bits, "Bits", "§b", "§b")) + //20 + addAsSingletonList(format(mithrilPowder, "Mithril Powder", "§2", "§2")) + //21 + addAsSingletonList(format(gemstonePowder, "Gemstone Powder", "§d", "§d")) } fun format(amount: Number, name: String, color: String, amountColor: String = color) = 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 98975b61d..8cbf48d66 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 @@ -18,6 +18,7 @@ import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.ItemUtils.name import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer import at.hannibal2.skyhanni.utils.LorenzUtils.addAsSingletonList +import at.hannibal2.skyhanni.utils.LorenzUtils.editCopy import at.hannibal2.skyhanni.utils.NEUItems.getItemStack import at.hannibal2.skyhanni.utils.NEUItems.getPrice import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators @@ -49,7 +50,7 @@ import kotlin.time.Duration.Companion.seconds private val config get() = SkyHanniMod.feature.garden class GardenVisitorFeatures { - private val visitors = mutableMapOf<String, Visitor>() + private var visitors = mapOf<String, Visitor>() private var display = emptyList<List<Any>>() private var lastClickedNpc = 0 private val newVisitorArrivedMessage = ".* §r§ehas arrived on your §r§bGarden§r§e!".toPattern() @@ -68,7 +69,7 @@ class GardenVisitorFeatures { @SubscribeEvent fun onPreProfileSwitch(event: PreProfileSwitchEvent) { display = emptyList() - visitors.clear() + visitors = emptyMap() } @SubscribeEvent @@ -97,17 +98,7 @@ class GardenVisitorFeatures { name = name.substring(2) } - val visitor = visitors[name] - if (visitor == null) { - println("visitors: $visitors") - println("name: $name") - println("npcItem.name: ${npcItem.name}") - CopyErrorCommand.logError( - RuntimeException("visitor is null! '$name'"), - "Error finding the visitor `$name§c`. Try to reopen the inventory" - ) - return - } + val visitor = getOrCreateVisitor(name) ?: return visitor.entityId = lastClickedNpc for (line in offerItem.getLore()) { @@ -148,6 +139,27 @@ class GardenVisitorFeatures { } } + private fun getOrCreateVisitor(name: String): Visitor? { + var visitor = visitors[name] + if (visitor == null) { + // workaround if the tab list has not yet updated when opening the visitor + addVisitor(name) + LorenzUtils.debug("Found visitor from npc that is not in tab list. Adding it still.") + updateDisplay() + visitor = visitors[name] + } + + if (visitor != null) return visitor + + println("visitors: $visitors") + println("name: $name") + CopyErrorCommand.logErrorState( + "Error finding the visitor `$name§c`. Try to reopen the inventory", + "visitor is null! name='$name', visitors=`$visitors`" + ) + return null + } + private fun readReward(offerItem: ItemStack): VisitorReward? { for (line in offerItem.getLore()) { for (reward in VisitorReward.entries) { @@ -259,7 +271,7 @@ class GardenVisitorFeatures { } @SubscribeEvent - fun onOwnInventoryItemUpdate(event: OwnInventorItemUpdateEvent) { + fun onOwnInventoryItemUpdate(event: OwnInventoryItemUpdateEvent) { if (GardenAPI.onBarnPlot) { MinecraftExecutor.OnThread.execute { update() @@ -511,26 +523,37 @@ class GardenVisitorFeatures { visitorsInTab.add(name) } } - if (visitors.keys.removeIf { - val time = System.currentTimeMillis() - LorenzUtils.lastWorldSwitch - val removed = it !in visitorsInTab && time > 2_000 - if (removed) { - logger.log("Removed old visitor: '$it'") - } - removed - }) { - updateDisplay() + val removedVisitors = mutableListOf<String>() + visitors.forEach { + val name = it.key + val time = System.currentTimeMillis() - LorenzUtils.lastWorldSwitch + val removed = name !in visitorsInTab && time > 2_000 + if (removed) { + logger.log("Removed old visitor: '$name'") + removedVisitors.add(name) + } + } + var dirty = false + if (removedVisitors.isNotEmpty()) { + visitors = visitors.editCopy { + keys.removeIf { it in removedVisitors } + } + dirty = true } for (name in visitorsInTab) { if (!visitors.containsKey(name)) { addVisitor(name) + dirty = true } } + if (dirty) { + updateDisplay() + } } private fun addVisitor(name: String) { val visitor = Visitor(name, status = VisitorStatus.NEW) - visitors[name] = visitor + visitors = visitors.editCopy { this[name] = visitor } VisitorArrivalEvent(visitor).postAndCatch() logger.log("New visitor detected: '$name'") @@ -542,7 +565,6 @@ class GardenVisitorFeatures { val displayName = GardenVisitorColorNames.getColoredName(name) LorenzUtils.chat("§e[SkyHanni] $displayName §eis visiting your garden!") } - updateDisplay() if (System.currentTimeMillis() > LorenzUtils.lastWorldSwitch + 2_000) { if (name.removeColor().contains("Jerry")) { diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorReward.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorReward.kt index 123faaca7..81419939b 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorReward.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorReward.kt @@ -9,8 +9,6 @@ enum class VisitorReward(val displayName: String, val internalName: String, val DEDICATION("§9Dedication IV", "DEDICATION;4", "Dedication (IV|4) Book".toPattern()), MUSIC_RUNE("§9Music Rune", "MUSIC_RUNE;1", "◆ Music Rune [1I]".toPattern()), SPACE_HELMET("§cSpace Helmet", "DCTR_SPACE_HELM", "Space Helmet".toPattern()), - - // Pretty sure that the symbol is ◆ but not 100% CULTIVATING( "§9Cultivating I", "CULTIVATING;1", "Cultivating ([I1]) Book".toPattern() diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/HideNotClickableItems.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/HideNotClickableItems.kt index 18147fb2f..d0e51d176 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/HideNotClickableItems.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/HideNotClickableItems.kt @@ -17,6 +17,7 @@ import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName_old import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.ItemUtils.isEnchanted import at.hannibal2.skyhanni.utils.ItemUtils.isVanilla +import at.hannibal2.skyhanni.utils.LorenzUtils.equalsOneOf import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.isRiftExportable import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.isRiftTransferable import at.hannibal2.skyhanni.utils.StringUtils.removeColor @@ -419,7 +420,7 @@ class HideNotClickableItems { } private fun hideSalvage(chestName: String, stack: ItemStack): Boolean { - if (chestName != "Salvage Item") return false + if (!chestName.equalsOneOf("Salvage Item", "Salvage Items")) return false reverseColor = true if (ItemUtils.isRecombobulated(stack)) { 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 b9cd5fdeb..757d9e741 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/SackDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/SackDisplay.kt @@ -1,186 +1,59 @@ package at.hannibal2.skyhanni.features.inventory import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.data.SackAPI import at.hannibal2.skyhanni.events.GuiRenderEvent -import at.hannibal2.skyhanni.events.InventoryCloseEvent -import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent import at.hannibal2.skyhanni.features.bazaar.BazaarApi -import at.hannibal2.skyhanni.features.fishing.trophy.TrophyFishManager -import at.hannibal2.skyhanni.features.fishing.trophy.TrophyRarity -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.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzUtils.addAsSingletonList import at.hannibal2.skyhanni.utils.LorenzUtils.addSelector -import at.hannibal2.skyhanni.utils.NEUInternalName -import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import at.hannibal2.skyhanni.utils.NEUItems import at.hannibal2.skyhanni.utils.NEUItems.getItemStack -import at.hannibal2.skyhanni.utils.NEUItems.getNpcPriceOrNull -import at.hannibal2.skyhanni.utils.NEUItems.getPrice 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.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.item.ItemStack import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -class SackDisplay { - - companion object { - var inInventory = false - var isRuneSack = false - var isGemstoneSack = false - var isTrophySack = false - var sackRarity: TrophyRarity? = null - } - - private val config get() = SkyHanniMod.feature.inventory.sackDisplay +object SackDisplay { private var display = emptyList<List<Any>>() - private val sackItem = mutableMapOf<String, Item>() - private val runeItem = mutableMapOf<String, Rune>() - private val gemstoneItem = mutableMapOf<String, Gemstone>() - private val sackPattern = "^(.* Sack|Enchanted .* Sack)$".toPattern() - private val stackList = mutableMapOf<Int, ItemStack>() - private val gemstoneMap = mapOf( - "Jade Gemstones" to "ROUGH_JADE_GEM".asInternalName(), - "Amber Gemstones" to "ROUGH_AMBER_GEM".asInternalName(), - "Topaz Gemstones" to "ROUGH_TOPAZ_GEM".asInternalName(), - "Sapphire Gemstones" to "ROUGH_SAPPHIRE_GEM".asInternalName(), - "Amethyst Gemstones" to "ROUGH_AMETHYST_GEM".asInternalName(), - "Jasper Gemstones" to "ROUGH_JASPER_GEM".asInternalName(), - "Ruby Gemstones" to "ROUGH_RUBY_GEM".asInternalName(), - "Opal Gemstones" to "ROUGH_OPAL_GEM".asInternalName(), - ) - private val MAGMA_FISH = "MAGMA_FISH".asInternalName() - - private val numPattern = - "(?:(?:§[0-9a-f](?<level>I{1,3})§7:)?|(?:§7Stored:)?) (?<color>§[0-9a-f])(?<stored>[0-9.,kKmMbB]+)§7/(?<total>\\d+(?:[0-9.,]+)?[kKmMbB]?)".toPattern() - private val gemstonePattern = - " §[0-9a-f](?<gemrarity>[A-z]*): §[0-9a-f](?<stored>\\d+(?:\\.\\d+)?(?:(?:,\\d+)?)+[kKmM]?)(?: §[0-9a-f]\\(\\d+(?:\\.\\d+)?(?:(?:,\\d+)?)+[kKmM]?\\))?".toPattern() + private val config get() = SkyHanniMod.feature.inventory.sackDisplay @SubscribeEvent fun onBackgroundDraw(event: GuiRenderEvent.ChestBackgroundRenderEvent) { - if (inInventory) { + if (SackAPI.inSackInventory) { + if (!isEnabled()) return config.position.renderStringsAndItems( - display, - extraSpace = config.extraSpace, - itemScale = 1.3, - posLabel = "Sacks Items" + display, extraSpace = config.extraSpace, itemScale = 1.3, posLabel = "Sacks Items" ) } } - private fun update() { - display = drawDisplay() - } - - private fun init() { - for ((_, stack) in stackList) { - val name = stack.name ?: continue - val lore = stack.getLore() - val gem = Gemstone() - val rune = Rune() - val item = Item() - loop@ for (line in lore) { - if (isGemstoneSack) { - gemstonePattern.matchMatcher(line) { - val rarity = group("gemrarity") - val stored = group("stored") - gem.internalName = gemstoneMap[name.removeColor()] ?: NEUInternalName.NONE - if (gemstoneMap.containsKey(name.removeColor())) { - val internalName = "${rarity.uppercase()}_${ - name.uppercase().split(" ")[0].removeColor() - }_GEM".asInternalName() - - when (rarity) { - "Rough" -> { - gem.rough = stored - gem.roughPrice = internalName.sackPrice(stored) - } - - "Flawed" -> { - gem.flawed = stored - gem.flawedPrice = internalName.sackPrice(stored) - } - - "Fine" -> { - gem.fine = stored - gem.finePrice = internalName.sackPrice(stored) - } - - "Flawless" -> { - gem.flawless = stored - gem.flawlessPrice = internalName.sackPrice(stored) - } - } - gemstoneItem[name] = gem - } - } - } else { - numPattern.matchMatcher(line) { - val stored = group("stored") - val internalName = stack.getInternalName() - item.internalName = internalName - item.colorCode = group("color") - item.stored = stored - item.total = group("total") - item.price = if (isTrophySack) { - val trophyName = - internalName.asString().lowercase().substringBeforeLast("_").replace("_", "") - val filletValue = - TrophyFishManager.getInfoByName(trophyName)?.getFilletValue(sackRarity!!) ?: 0 - val storedNumber = stored.formatNumber().toInt() - MAGMA_FISH.sackPrice((filletValue * storedNumber).toString()) - } else internalName.sackPrice(stored).coerceAtLeast(0) - - if (isRuneSack) { - val level = group("level") - rune.stack = stack - if (level == "I") { - rune.lvl1 = stored - continue@loop - } - if (level == "II") { - rune.lvl2 = stored - continue@loop - } - if (level == "III") { - rune.lvl3 = stored - } - runeItem.put(name, rune) - } else { - sackItem.put(name, item) - } - } - } - } - } + fun update(savingSacks: Boolean) { + display = drawDisplay(savingSacks) } - private fun drawDisplay(): List<List<Any>> { + private fun drawDisplay(savingSacks: Boolean): List<List<Any>> { val newDisplay = mutableListOf<List<Any>>() var totalPrice = 0 var rendered = 0 - init() - - if (sackItem.isNotEmpty()) { - val sortedPairs: MutableMap<String, Item> = when (config.sortingType) { - 0 -> sackItem.toList().sortedByDescending { it.second.stored.formatNumber() }.toMap().toMutableMap() - 1 -> sackItem.toList().sortedBy { it.second.stored.formatNumber() }.toMap().toMutableMap() - 2 -> sackItem.toList().sortedByDescending { it.second.price }.toMap().toMutableMap() - 3 -> sackItem.toList().sortedBy { it.second.price }.toMap().toMutableMap() - else -> sackItem.toList().sortedByDescending { it.second.stored.formatNumber() }.toMap().toMutableMap() - } - - sortedPairs.toList().forEach { - if (it.second.stored == "0" && !config.showEmpty) { - sortedPairs.remove(it.first) + SackAPI.getSacksData(savingSacks) + + val sackItems = SackAPI.sackItem.toList() + if (sackItems.isNotEmpty()) { + val sortedPairs: MutableMap<String, SackAPI.SackOtherItem> = when (config.sortingType) { + 0 -> sackItems.sortedByDescending { it.second.stored.formatNumber() } + 1 -> sackItems.sortedBy { it.second.stored.formatNumber() } + 2 -> sackItems.sortedByDescending { it.second.price } + 3 -> sackItems.sortedBy { it.second.price } + else -> sackItems.sortedByDescending { it.second.stored.formatNumber() } + }.toMap().toMutableMap() + + sortedPairs.toList().forEach { (k, v) -> + if (v.stored == "0" && !config.showEmpty) { + sortedPairs.remove(k) } } @@ -195,12 +68,10 @@ class SackDisplay { newDisplay.add(buildList { add(" §7- ") add(itemStack) - if (!isTrophySack) - add(Renderable.optionalLink("${itemName.replace("§k", "")}: ", { - BazaarApi.searchForBazaarItem(itemName) - }) { !NEUItems.neuHasFocus() }) - else - add("${itemName.replace("§k", "")}: ") + if (!SackAPI.isTrophySack) add(Renderable.optionalLink("${itemName.replace("§k", "")}: ", { + BazaarApi.searchForBazaarItem(itemName) + }) { !NEUItems.neuHasFocus() }) + else add("${itemName.replace("§k", "")}: ") add( when (config.numberFormat) { @@ -211,10 +82,8 @@ class SackDisplay { } ) - if (colorCode == "§a") - add(" §c§l(Full!)") - if (config.showPrice && price != 0) - add(" §7(§6${format(price)}§7)") + if (colorCode == "§a") add(" §c§l(Full!)") + if (config.showPrice && price != 0) add(" §7(§6${format(price)}§7)") }) rendered++ } @@ -222,31 +91,29 @@ class SackDisplay { val name = SortType.entries[config.sortingType].longName newDisplay.addAsSingletonList("§7Sorted By: §c$name") - newDisplay.addSelector<SortType>( - " ", + newDisplay.addSelector<SortType>(" ", getName = { type -> type.shortName }, isCurrent = { it.ordinal == config.sortingType }, onChange = { config.sortingType = it.ordinal - update() + update(false) }) if (config.showPrice) { newDisplay.addAsSingletonList("§cTotal price: §6${format(totalPrice)}") - newDisplay.addSelector<PriceFrom>( - " ", + newDisplay.addSelector<PriceFrom>(" ", getName = { type -> type.displayName }, isCurrent = { it.ordinal == config.priceFrom }, onChange = { config.priceFrom = it.ordinal - update() + update(false) }) } } - if (runeItem.isNotEmpty()) { + if (SackAPI.runeItem.isNotEmpty()) { newDisplay.addAsSingletonList("§7Runes:") - for ((name, rune) in runeItem) { + for ((name, rune) in SackAPI.runeItem) { val list = mutableListOf<Any>() val (stack, lv1, lv2, lv3) = rune list.add(" §7- ") @@ -257,9 +124,9 @@ class SackDisplay { } } - if (gemstoneItem.isNotEmpty()) { + if (SackAPI.gemstoneItem.isNotEmpty()) { newDisplay.addAsSingletonList("§7Gemstones:") - for ((name, gem) in gemstoneItem) { + for ((name, gem) in SackAPI.gemstoneItem) { val (internalName, rough, flawed, fine, flawless, roughprice, flawedprice, fineprice, flawlessprice) = gem newDisplay.add(buildList { add(" §7- ") @@ -270,89 +137,18 @@ class SackDisplay { add(" ($rough-§a$flawed-§9$fine-§5$flawless)") val price = (roughprice + flawedprice + fineprice + flawlessprice) totalPrice += price - if (config.showPrice && price != 0) - add(" §7(§6${format(price)}§7)") + if (config.showPrice && price != 0) add(" §7(§6${format(price)}§7)") }) } - if (config.showPrice) - newDisplay.addAsSingletonList("§eTotal price: §6${format(totalPrice)}") + if (config.showPrice) newDisplay.addAsSingletonList("§eTotal price: §6${format(totalPrice)}") } return newDisplay } private fun format(price: Int) = if (config.priceFormat == 0) NumberUtil.format(price) else price.addSeparators() - @SubscribeEvent - fun onInventoryClose(event: InventoryCloseEvent) { - inInventory = false - isRuneSack = false - isGemstoneSack = false - isTrophySack = false - runeItem.clear() - gemstoneItem.clear() - sackItem.clear() - stackList.clear() - } - - @SubscribeEvent - fun onInventoryOpen(event: InventoryFullyOpenedEvent) { - if (!isEnabled()) return - val inventoryName = event.inventoryName - val match = sackPattern.matcher(inventoryName).matches() - if (!match) return - val stacks = event.inventoryItems - isRuneSack = inventoryName == "Runes Sack" - isGemstoneSack = inventoryName == "Gemstones Sack" - isTrophySack = inventoryName.contains("Trophy Fishing Sack") - sackRarity = inventoryName.getTrophyRarity() - inInventory = true - stackList.putAll(stacks) - update() - } - - - data class Gemstone( - var internalName: NEUInternalName = NEUInternalName.NONE, - var rough: String = "0", - var flawed: String = "0", - var fine: String = "0", - var flawless: String = "0", - var roughPrice: Int = 0, - var flawedPrice: Int = 0, - var finePrice: Int = 0, - var flawlessPrice: Int = 0, - ) - - data class Rune( - var stack: ItemStack? = null, - var lvl1: String = "0", - var lvl2: String = "0", - var lvl3: String = "0", - ) - - data class Item( - var internalName: NEUInternalName = NEUInternalName.NONE, - var colorCode: String = "", - var stored: String = "0", - var total: String = "0", - var price: Int = 0, - ) - private fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled - private fun NEUInternalName.sackPrice(stored: String) = when (config.priceFrom) { - 0 -> (getPrice(true) * stored.formatNumber()).toInt().let { if (it < 0) 0 else it } - - 1 -> try { - val npcPrice = getNpcPriceOrNull() ?: 0.0 - (npcPrice * stored.formatNumber()).toInt() - } catch (e: Exception) { - 0 - } - - else -> 0 - } - enum class SortType(val shortName: String, val longName: String) { STORED_DESC("Stored D", "Stored Descending"), STORED_ASC("Stored A", "Stored Ascending"), @@ -366,13 +162,4 @@ class SackDisplay { NPC("Npc Price"), ; } - - private fun String.getTrophyRarity(): TrophyRarity? { - return if (this.startsWith("Bronze")) - TrophyRarity.BRONZE - else - if (this.startsWith("Silver")) - TrophyRarity.SILVER - else null - } -} +}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/itemabilities/abilitycooldown/ItemAbility.kt b/src/main/java/at/hannibal2/skyhanni/features/itemabilities/abilitycooldown/ItemAbility.kt index 81ce2ac2d..6ab81e0c9 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/itemabilities/abilitycooldown/ItemAbility.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/itemabilities/abilitycooldown/ItemAbility.kt @@ -2,6 +2,8 @@ package at.hannibal2.skyhanni.features.itemabilities.abilitycooldown import at.hannibal2.skyhanni.utils.LorenzColor import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.NEUInternalName +import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName enum class ItemAbility( val abilityName: String, @@ -46,12 +48,13 @@ enum class ItemAbility( LIVID_DAGGER("Throw", 5, "Livid Dagger"), FIRE_VEIL("Fire Veil", 5, "Fire Veil Wand"), INK_WAND("Ink Bomb", 30, "Ink Wand"), + FIRE_FURY_STAFF("Firestorm", 20, "Fire Fury Staff"), // doesn't have a consistent sound ECHO("Echo", 3, "Ancestral Spade"); var newVariant = false - var internalNames = mutableListOf<String>() + var internalNames = mutableListOf<NEUInternalName>() constructor( cooldownInSeconds: Int, @@ -59,8 +62,10 @@ enum class ItemAbility( alternativePosition: Boolean = false, ) : this("no name", cooldownInSeconds, actionBarDetection = false, alternativePosition = alternativePosition) { newVariant = true - internalNames.addAll(alternateInternalNames) - internalNames.add(name) + alternateInternalNames.forEach { + internalNames.add(it.asInternalName()) + } + internalNames.add(name.asInternalName()) } fun activate(color: LorenzColor? = null, customCooldown: Int = (cooldownInSeconds * 1000)) { @@ -92,7 +97,7 @@ enum class ItemAbility( } companion object { - fun getByInternalName(internalName: String): ItemAbility? { + fun getByInternalName(internalName: NEUInternalName): ItemAbility? { return entries.firstOrNull { it.newVariant && internalName in it.internalNames } } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/itemabilities/abilitycooldown/ItemAbilityCooldown.kt b/src/main/java/at/hannibal2/skyhanni/features/itemabilities/abilitycooldown/ItemAbilityCooldown.kt index f09e86164..a0fd45a13 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/itemabilities/abilitycooldown/ItemAbilityCooldown.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/itemabilities/abilitycooldown/ItemAbilityCooldown.kt @@ -6,7 +6,7 @@ import at.hannibal2.skyhanni.events.* import at.hannibal2.skyhanni.utils.InventoryUtils import at.hannibal2.skyhanni.utils.ItemUtils import at.hannibal2.skyhanni.utils.ItemUtils.cleanName -import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName_old +import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName import at.hannibal2.skyhanni.utils.LorenzColor import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzUtils.between @@ -46,8 +46,12 @@ class ItemAbilityCooldown { ItemAbility.GYROKINETIC_WAND_LEFT.sound() } if (event.pitch == 1f && event.volume == 1f) { - val internalName = InventoryUtils.getItemInHand()?.getInternalName_old() ?: return - if (!internalName.equalsOneOf("SHADOW_FURY", "STARRED_SHADOW_FURY")) return + val internalName = InventoryUtils.getItemInHand()?.getInternalName() ?: return + if (!internalName.equalsOneOf( + "SHADOW_FURY".asInternalName(), + "STARRED_SHADOW_FURY".asInternalName() + ) + ) return ItemAbility.SHADOW_FURY.sound() } @@ -157,7 +161,7 @@ class ItemAbilityCooldown { private fun handleItemClick(itemInHand: ItemStack?) { if (!LorenzUtils.inSkyBlock) return - itemInHand?.getInternalName_old()?.run { + itemInHand?.getInternalName()?.run { ItemAbility.getByInternalName(this)?.setItemClick() } } @@ -321,7 +325,7 @@ class ItemAbilityCooldown { private fun hasAbility(stack: ItemStack): MutableList<ItemAbility> { val itemName: String = stack.cleanName() - val internalName = stack.getInternalName_old() + val internalName = stack.getInternalName() val list = mutableListOf<ItemAbility>() for (ability in ItemAbility.entries) { diff --git a/src/main/java/at/hannibal2/skyhanni/features/minion/MinionFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/minion/MinionFeatures.kt index c06235daf..b12eb2f3e 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/minion/MinionFeatures.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/minion/MinionFeatures.kt @@ -7,6 +7,8 @@ import at.hannibal2.skyhanni.data.ProfileStorageData import at.hannibal2.skyhanni.events.* import at.hannibal2.skyhanni.test.GriffinUtils.drawWaypointFilled import at.hannibal2.skyhanni.utils.* +import at.hannibal2.skyhanni.utils.BlockUtils.getBlockStateAt +import at.hannibal2.skyhanni.utils.ItemUtils.cleanName import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.ItemUtils.name import at.hannibal2.skyhanni.utils.LorenzUtils.editCopy @@ -21,9 +23,11 @@ import net.minecraft.client.Minecraft import net.minecraft.client.gui.inventory.GuiChest import net.minecraft.entity.EntityLivingBase import net.minecraft.entity.item.EntityArmorStand +import net.minecraft.init.Blocks import net.minecraftforge.client.event.GuiScreenEvent import net.minecraftforge.client.event.RenderLivingEvent import net.minecraftforge.client.event.RenderWorldLastEvent +import net.minecraftforge.event.entity.player.PlayerInteractEvent import net.minecraftforge.fml.common.eventhandler.EventPriority import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import net.minecraftforge.fml.common.gameevent.InputEvent @@ -34,15 +38,34 @@ class MinionFeatures { private val config get() = SkyHanniMod.feature.minions private var lastClickedEntity: LorenzVec? = null private var lastMinion: LorenzVec? = null + private var newMinion: LorenzVec? = null + private var newMinionName: String? = null private var lastMinionOpened = 0L private var minionInventoryOpen = false private var lastInventoryClosed = 0L - private var lastMinionPickedUp = 0L private var coinsPerDay = "" private val minionUpgradePattern = "§aYou have upgraded your Minion to Tier (?<tier>.*)".toPattern() @SubscribeEvent + fun onPlayerInteract(event: PlayerInteractEvent) { + if (!LorenzUtils.inSkyBlock) return + if (LorenzUtils.skyBlockIsland != IslandType.PRIVATE_ISLAND) return + if (event.action != PlayerInteractEvent.Action.RIGHT_CLICK_BLOCK) return + + val lookingAt = event.pos.offset(event.face).toLorenzVec() + val equipped = InventoryUtils.getItemInHand() ?: return + + if (equipped.displayName.contains(" Minion ") && lookingAt.getBlockStateAt().block == Blocks.air) { + newMinion = lookingAt.add(0.5, 0.0, 0.5) + newMinionName = getMinionName(equipped.cleanName()) + } else { + newMinion = null + newMinionName = null + } + } + + @SubscribeEvent fun onClick(event: InputEvent.MouseInputEvent) { if (!LorenzUtils.inSkyBlock) return if (LorenzUtils.skyBlockIsland != IslandType.PRIVATE_ISLAND) return @@ -123,7 +146,7 @@ class MinionFeatures { @SubscribeEvent fun onInventoryClose(event: InventoryCloseEvent) { if (!minionInventoryOpen) return - var minions = minions ?: return + val minions = minions ?: return minionInventoryOpen = false lastMinionOpened = System.currentTimeMillis() @@ -135,10 +158,6 @@ class MinionFeatures { if (location !in minions) { minions[location]!!.lastClicked = 0 } - - if (System.currentTimeMillis() - lastMinionPickedUp < 2_000) { - MinionFeatures.minions = minions.editCopy { remove(location) } - } } @SubscribeEvent @@ -210,7 +229,24 @@ class MinionFeatures { } if (message.startsWith("§aYou picked up a minion!")) { - lastMinionPickedUp = System.currentTimeMillis() + if (lastMinion != null) { + minions = minions?.editCopy { remove(lastMinion) } + lastClickedEntity = null + lastMinion = null + lastMinionOpened = 0L + } + } + if (message.startsWith("§bYou placed a minion!")) { + if (newMinion != null) { + minions = minions?.editCopy { + this[newMinion!!] = Storage.ProfileSpecific.MinionConfig().apply { + displayName = newMinionName + lastClicked = 0 + } + } + newMinion = null + newMinionName = null + } } minionUpgradePattern.matchMatcher(message) { diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/EnderNodeTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/EnderNodeTracker.kt index 9205b0154..133e12ab4 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/EnderNodeTracker.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/EnderNodeTracker.kt @@ -1,42 +1,39 @@ package at.hannibal2.skyhanni.features.misc import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.config.Storage import at.hannibal2.skyhanni.data.IslandType import at.hannibal2.skyhanni.data.ProfileStorageData -import at.hannibal2.skyhanni.data.ScoreboardData -import at.hannibal2.skyhanni.events.ConfigLoadEvent -import at.hannibal2.skyhanni.events.GuiRenderEvent -import at.hannibal2.skyhanni.events.LorenzChatEvent -import at.hannibal2.skyhanni.events.PlaySoundEvent -import at.hannibal2.skyhanni.features.bazaar.BazaarApi.Companion.getBazaarData +import at.hannibal2.skyhanni.events.* +import at.hannibal2.skyhanni.utils.ItemUtils.getInternalNameOrNull import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzUtils.addAsSingletonList import at.hannibal2.skyhanni.utils.LorenzUtils.afterChange import at.hannibal2.skyhanni.utils.LorenzUtils.editCopy import at.hannibal2.skyhanni.utils.NEUItems.getNpcPriceOrNull -import at.hannibal2.skyhanni.utils.NEUItems.getPrice +import at.hannibal2.skyhanni.utils.NEUItems.getPriceOrNull import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.NumberUtil.format import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems +import io.github.moulberry.notenoughupdates.util.MinecraftExecutor +import net.minecraft.client.Minecraft import net.minecraftforge.fml.common.eventhandler.SubscribeEvent class EnderNodeTracker { private val config get() = SkyHanniMod.feature.misc.enderNodeTracker + private val storage get() = ProfileStorageData.profileSpecific?.enderNodeTracker - private var totalNodesMined = 0 - private var totalEndermiteNests = 0 private var totalEnderArmor = 0 + private var miteGelInInventory = 0 private var display = emptyList<List<Any>>() - private var lootCount = mapOf<EnderNode, Int>() private var lootProfit = mapOf<EnderNode, Double>() private val enderNodeRegex = Regex("""ENDER NODE!.+You found (\d+x )?§r(.+)§r§f!""") private val endermanRegex = Regex("""(RARE|PET) DROP! §r(.+) §r§b\(""") - private var lastEndermiteTime = 0L - @SubscribeEvent fun onChat(event: LorenzChatEvent) { + if (!config.enabled) return if (!ProfileStorageData.loaded) return if (!isInTheEnd()) return @@ -44,10 +41,11 @@ class EnderNodeTracker { val message = event.message.trim() var item: String? = null var amount = 1 + val storage = storage ?: return // check whether the loot is from an ender node or an enderman enderNodeRegex.find(message)?.let { - totalNodesMined++ + storage.totalNodesMined++ amount = it.groups[1]?.value?.substringBefore("x")?.toIntOrNull() ?: 1 item = it.groups[2]?.value } ?: endermanRegex.find(message)?.let { @@ -59,37 +57,67 @@ class EnderNodeTracker { item == null -> return isEnderArmor(item) -> totalEnderArmor++ item == "§cEndermite Nest" -> { - lastEndermiteTime = System.currentTimeMillis() - totalEndermiteNests++ + storage.totalEndermiteNests++ } } // increment the count of the specific item found EnderNode.entries.find { it.displayName == item }?.let { - val old = lootCount[it] ?: 0 - lootCount = lootCount.editCopy { + val old = storage.lootCount[it] ?: 0 + storage.lootCount = storage.lootCount.editCopy { this[it] = old + amount } } - saveAndUpdate() + update() + } + + @SubscribeEvent + fun onIslandChange(event: IslandChangeEvent) { + if (!config.enabled) return + if (event.newIsland != IslandType.THE_END) return + miteGelInInventory = Minecraft.getMinecraft().thePlayer.inventory.mainInventory + .filter { it?.getInternalNameOrNull() == EnderNode.MITE_GEL.internalName } + .sumOf { it.stackSize } + } + + @SubscribeEvent + fun onSackChange(event: SackChangeEvent) { + if (!config.enabled) return + if (!ProfileStorageData.loaded) return + if (!isInTheEnd()) return + val storage = storage ?: return + + val change = event.sackChanges + .firstOrNull { it.internalName == EnderNode.MITE_GEL.internalName && it.delta > 0 } + ?: return + val old = storage.lootCount[EnderNode.MITE_GEL] ?: 0 + storage.lootCount = storage.lootCount.editCopy { + this[EnderNode.MITE_GEL] = old + change.delta + } + update() } @SubscribeEvent - fun onSoundPlay(event: PlaySoundEvent) { + fun onInventoryUpdate(event: OwnInventoryItemUpdateEvent) { + if (!config.enabled) return if (!isInTheEnd()) return - if (event.soundName != "mob.silverfish.kill") return - if (event.distanceToPlayer > 15) return - if (System.currentTimeMillis() - lastEndermiteTime > 7500) return - - // listen for nearby endermite death sounds within 7.5s of mining an endermite nest - // this is a fairly accurate approximation for mite gel drops - val oldEndStone = lootCount[EnderNode.ENCHANTED_ENDSTONE] ?: 0 - val oldMiteGel = lootCount[EnderNode.MITE_GEL] ?: 0 - lootCount = lootCount.editCopy { - this[EnderNode.ENCHANTED_ENDSTONE] = oldEndStone + (1 + Math.random() * 2).toInt() - this[EnderNode.MITE_GEL] = oldMiteGel + (1 + Math.random() * 2).toInt() + if (!ProfileStorageData.loaded) return + val storage = storage ?: return + + MinecraftExecutor.OnThread.execute { + val newMiteGelInInventory = Minecraft.getMinecraft().thePlayer.inventory.mainInventory + .filter { it?.getInternalNameOrNull() == EnderNode.MITE_GEL.internalName } + .sumOf { it.stackSize } + val change = newMiteGelInInventory - miteGelInInventory + if (change > 0) { + val old = storage.lootCount[EnderNode.MITE_GEL] ?: 0 + storage.lootCount = storage.lootCount.editCopy { + this[EnderNode.MITE_GEL] = old + change + } + update() + } + miteGelInInventory = newMiteGelInInventory } - saveAndUpdate() } @SubscribeEvent @@ -102,60 +130,49 @@ class EnderNodeTracker { @SubscribeEvent fun onConfigLoad(event: ConfigLoadEvent) { config.textFormat.afterChange { - saveAndUpdate() + update() } + val storage = storage ?: return - val hidden = ProfileStorageData.profileSpecific?.enderNodeTracker ?: return - totalNodesMined = hidden.totalNodesMined - totalEndermiteNests = hidden.totalEndermiteNests - lootCount = hidden.lootCount - totalEnderArmor = hidden.lootCount.filter { isEnderArmor(it.key.displayName) }.map { it.value }.sum() - saveAndUpdate() + totalEnderArmor = storage.lootCount.filter { isEnderArmor(it.key.displayName) } + .map { it.value } + .sum() + update() } - private fun calculateProfit(): Map<EnderNode, Double> { + private fun calculateProfit(storage: Storage.ProfileSpecific.EnderNodeTracker): Map<EnderNode, Double> { + if (!ProfileStorageData.loaded) return emptyMap() + val newProfit = mutableMapOf<EnderNode, Double>() - lootCount.forEach { (key, _) -> - val price = if (isEnderArmor(key.displayName)) { + storage.lootCount.forEach { (item, amount) -> + val price = if (isEnderArmor(item.displayName)) { 10_000.0 } else { - val internalName = key.internalName - val npcPrice = internalName.getNpcPriceOrNull() - val bazaarData = internalName.getBazaarData() - if (LorenzUtils.noTradeMode || bazaarData == null) { - npcPrice ?: georgePrice(key) ?: 0.0 - } else { - npcPrice - ?.coerceAtLeast(bazaarData.sellPrice) - ?.coerceAtLeast(georgePrice(key) ?: 0.0) - ?: internalName.getPrice() - } + (if (!LorenzUtils.noTradeMode) item.internalName.getPriceOrNull() else 0.0) + ?.coerceAtLeast(item.internalName.getNpcPriceOrNull() ?: 0.0) + ?.coerceAtLeast(georgePrice(item) ?: 0.0) + ?: 0.0 } - newProfit[key] = price * (lootCount[key] ?: 0) + newProfit[item] = price * amount } return newProfit } - private fun saveAndUpdate() { - val hidden = ProfileStorageData.profileSpecific?.enderNodeTracker ?: return - hidden.totalNodesMined = totalNodesMined - hidden.totalEndermiteNests = totalEndermiteNests - hidden.lootCount = lootCount - - lootProfit = calculateProfit() - display = formatDisplay(drawDisplay()) + private fun update() { + val storage = storage ?: return + lootProfit = calculateProfit(storage) + display = formatDisplay(drawDisplay(storage)) } - private fun isInTheEnd() = LorenzUtils.inIsland(IslandType.THE_END) - && ScoreboardData.sidebarLines.any { it.contains("The End") } + private fun isInTheEnd() = LorenzUtils.skyBlockArea == "The End" private fun isEnderArmor(displayName: String?) = when (displayName) { - "§5Ender Helmet", - "§5Ender Chestplate", - "§5Ender Leggings", - "§5Ender Boots", - "§5Ender Necklace", - "§5Ender Gauntlet" -> true + EnderNode.END_HELMET.displayName, + EnderNode.END_CHESTPLATE.displayName, + EnderNode.END_LEGGINGS.displayName, + EnderNode.END_BOOTS.displayName, + EnderNode.ENDER_NECKLACE.displayName, + EnderNode.ENDER_GAUNTLET.displayName -> true else -> false } @@ -169,15 +186,17 @@ class EnderNodeTracker { else -> null } - private fun drawDisplay() = buildList<List<Any>> { + private fun drawDisplay(storage: Storage.ProfileSpecific.EnderNodeTracker) = buildList<List<Any>> { + if (!ProfileStorageData.loaded) return emptyList<List<Any>>() + addAsSingletonList("§5§lEnder Node Tracker") - addAsSingletonList("§d${totalNodesMined.addSeparators()} Ender Nodes mined") + addAsSingletonList("§d${storage.totalNodesMined.addSeparators()} Ender Nodes mined") addAsSingletonList("§6${format(lootProfit.values.sum())} Coins made") addAsSingletonList(" ") - addAsSingletonList("§b${totalEndermiteNests.addSeparators()} §cEndermite Nest") + addAsSingletonList("§b${storage.totalEndermiteNests.addSeparators()} §cEndermite Nest") for (item in EnderNode.entries.subList(0, 11)) { - val count = (lootCount[item] ?: 0).addSeparators() + val count = (storage.lootCount[item] ?: 0).addSeparators() val profit = format(lootProfit[item] ?: 0.0) addAsSingletonList("§b$count ${item.displayName} §7(§6$profit§7)") } @@ -187,17 +206,19 @@ class EnderNodeTracker { "§7(§6${format(totalEnderArmor * 10_000)}§7)" ) for (item in EnderNode.entries.subList(11, 16)) { - val count = (lootCount[item] ?: 0).addSeparators() + val count = (storage.lootCount[item] ?: 0).addSeparators() val profit = format(lootProfit[item] ?: 0.0) addAsSingletonList("§b$count ${item.displayName} §7(§6$profit§7)") } // enderman pet rarities - val (c, u, r, e, l) = EnderNode.entries.subList(16, 21).map { (lootCount[it] ?: 0).addSeparators() } + val (c, u, r, e, l) = EnderNode.entries.subList(16, 21).map { (storage.lootCount[it] ?: 0).addSeparators() } val profit = format(EnderNode.entries.subList(16, 21).sumOf { lootProfit[it] ?: 0.0 }) addAsSingletonList("§f$c§7-§a$u§7-§9$r§7-§5$e§7-§6$l §fEnderman Pet §7(§6$profit§7)") } private fun formatDisplay(map: List<List<Any>>): List<List<Any>> { + if (!ProfileStorageData.loaded) return emptyList() + val newList = mutableListOf<List<Any>>() for (index in config.textFormat.get()) { newList.add(map[index]) diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/PetCandyUsedDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/PetCandyUsedDisplay.kt index 5cfcd881d..351e3123d 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/PetCandyUsedDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/PetCandyUsedDisplay.kt @@ -3,8 +3,8 @@ package at.hannibal2.skyhanni.features.misc import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.events.GuiRenderItemEvent import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.RenderUtils.drawSlotText import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.getPetCandyUsed -import net.minecraft.client.renderer.GlStateManager import net.minecraftforge.fml.common.eventhandler.SubscribeEvent class PetCandyUsedDisplay { @@ -20,26 +20,9 @@ class PetCandyUsedDisplay { if (petCandyUsed == 0) return val stackTip = "§c$petCandyUsed" - - GlStateManager.disableLighting() - GlStateManager.disableDepth() - GlStateManager.disableBlend() - - val fontRenderer = event.fontRenderer - val x = event.x + 13 - fontRenderer.getStringWidth(stackTip) + val x = event.x + 13 val y = event.y + 1 - val scale = 0.9 - GlStateManager.pushMatrix() - GlStateManager.translate(x.toFloat(), y.toFloat(), 0f) - GlStateManager.scale(scale, scale, scale) - fontRenderer.drawStringWithShadow(stackTip, 0f, 0f, 16777215) - val reverseScale = 1 / 0.7 - GlStateManager.scale(reverseScale, reverseScale, reverseScale) - GlStateManager.popMatrix() - - GlStateManager.enableLighting() - GlStateManager.enableDepth() + event.drawSlotText(x, y, stackTip, .9f) } - -} +}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/PetExpTooltip.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/PetExpTooltip.kt index 7b54d9d95..7a96c86d2 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/PetExpTooltip.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/PetExpTooltip.kt @@ -1,15 +1,13 @@ package at.hannibal2.skyhanni.features.misc import at.hannibal2.skyhanni.SkyHanniMod -import at.hannibal2.skyhanni.utils.ItemUtils +import at.hannibal2.skyhanni.utils.* +import at.hannibal2.skyhanni.utils.ItemUtils.getItemRarityOrNull import at.hannibal2.skyhanni.utils.ItemUtils.name -import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzUtils.indexOfFirst import at.hannibal2.skyhanni.utils.LorenzUtils.round -import at.hannibal2.skyhanni.utils.NumberUtil import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.getPetExp -import at.hannibal2.skyhanni.utils.StringUtils import net.minecraftforge.event.entity.player.ItemTooltipEvent import net.minecraftforge.fml.common.eventhandler.EventPriority import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @@ -34,7 +32,7 @@ class PetExpTooltip { ) ?: return val maxLevel = ItemUtils.maxPetLevel(name) - val maxXp = if (maxLevel == 200) 210255385 else 25353230L + val maxXp = maxPetExp(name) // lvl 100 legendary val percentage = petExperience / maxXp val percentageFormat = LorenzUtils.formatPercentage(percentage) @@ -44,8 +42,17 @@ class PetExpTooltip { event.toolTip.add(index, "§7Total experience: §e${NumberUtil.format(petExperience)}") } else { val progressBar = StringUtils.progressBar(percentage) + val isBelowLegendary = itemStack.getItemRarityOrNull()?.let { it < LorenzRarity.LEGENDARY } ?: false + val addLegendaryColor = if (isBelowLegendary) "§6" else "" event.toolTip.add(index, "$progressBar §e${petExperience.addSeparators()}§6/§e${NumberUtil.format(maxXp)}") - event.toolTip.add(index, "§7Progress to Level $maxLevel: §e$percentageFormat") + event.toolTip.add(index, "§7Progress to ${addLegendaryColor}Level $maxLevel: §e$percentageFormat") } } + + private fun maxPetExp(petName: String) = when { + petName.contains("Golden Dragon") && config.goldenDragon200 -> 210_255_385 // lvl 200 legendary + petName.contains("Bingo") -> 5_624_785 // lvl 100 common + + else -> 25_353_230 // lvl 100 legendary + } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/PocketSackInASackDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/PocketSackInASackDisplay.kt index 0ed156362..58b7924b1 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/PocketSackInASackDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/PocketSackInASackDisplay.kt @@ -5,8 +5,8 @@ import at.hannibal2.skyhanni.events.GuiRenderItemEvent import at.hannibal2.skyhanni.events.LorenzToolTipEvent import at.hannibal2.skyhanni.utils.ItemUtils import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.RenderUtils.drawSlotText import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.getAppliedPocketSackInASack -import net.minecraft.client.renderer.GlStateManager import net.minecraftforge.fml.common.eventhandler.SubscribeEvent class PocketSackInASackDisplay { @@ -22,26 +22,10 @@ class PocketSackInASackDisplay { val pocketSackInASackApplied = stack.getAppliedPocketSackInASack() ?: return val stackTip = "§a$pocketSackInASackApplied" - - GlStateManager.disableLighting() - GlStateManager.disableDepth() - GlStateManager.disableBlend() - - val fontRenderer = event.fontRenderer - val x = event.x + 13 - fontRenderer.getStringWidth(stackTip) + val x = event.x + 13 val y = event.y + 1 - val scale = 0.9 - GlStateManager.pushMatrix() - GlStateManager.translate(x.toFloat(), y.toFloat(), 0f) - GlStateManager.scale(scale, scale, scale) - fontRenderer.drawStringWithShadow(stackTip, 0f, 0f, 16777215) - val reverseScale = 1 / 0.7 - GlStateManager.scale(reverseScale, reverseScale, reverseScale) - GlStateManager.popMatrix() - - GlStateManager.enableLighting() - GlStateManager.enableDepth() + event.drawSlotText(x, y, stackTip, .9f) } @SubscribeEvent diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/ghostcounter/GhostCounter.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/ghostcounter/GhostCounter.kt index e8e539553..19482141d 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/ghostcounter/GhostCounter.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/ghostcounter/GhostCounter.kt @@ -41,7 +41,6 @@ import at.hannibal2.skyhanni.utils.StringUtils.removeColor import at.hannibal2.skyhanni.utils.renderables.Renderable import io.github.moulberry.notenoughupdates.util.Utils import io.github.moulberry.notenoughupdates.util.XPInformation -import net.minecraft.client.Minecraft import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import java.io.File import java.text.NumberFormat @@ -452,7 +451,8 @@ object GhostCounter { val stacks = event.inventoryItems val ghostStack = stacks[10] ?: return val bestiaryNextLevel = - if (ghostStack.displayName == "§cGhost") 1 else ghostStack.displayName.substring(8).romanToDecimal() + 1 + if ("§\\wGhost".toRegex().matches(ghostStack.displayName)) 1 else ghostStack.displayName.substring(8) + .romanToDecimal() + 1 hidden?.bestiaryNextLevel = bestiaryNextLevel.toDouble() var kills = 0.0 for (line in ghostStack.getLore()) { diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedItemValue.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedItemValue.kt index 4e29ca596..82ba33392 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedItemValue.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedItemValue.kt @@ -1,28 +1,29 @@ package at.hannibal2.skyhanni.features.misc.items import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.config.ConfigManager import at.hannibal2.skyhanni.events.ConfigLoadEvent import at.hannibal2.skyhanni.events.GuiRenderEvent import at.hannibal2.skyhanni.events.InventoryCloseEvent import at.hannibal2.skyhanni.events.RenderItemTooltipEvent +import at.hannibal2.skyhanni.test.command.CopyErrorCommand +import at.hannibal2.skyhanni.utils.* import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName import at.hannibal2.skyhanni.utils.ItemUtils.getInternalNameOrNull import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName_old import at.hannibal2.skyhanni.utils.ItemUtils.getItemName +import at.hannibal2.skyhanni.utils.ItemUtils.getItemRarityOrNull import at.hannibal2.skyhanni.utils.ItemUtils.getLore -import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.ItemUtils.name import at.hannibal2.skyhanni.utils.LorenzUtils.addAsSingletonList import at.hannibal2.skyhanni.utils.LorenzUtils.onToggle import at.hannibal2.skyhanni.utils.LorenzUtils.sortedDesc -import at.hannibal2.skyhanni.utils.NEUInternalName import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import at.hannibal2.skyhanni.utils.NEUItems.getItemStackOrNull import at.hannibal2.skyhanni.utils.NEUItems.getPrice import at.hannibal2.skyhanni.utils.NEUItems.getPriceOrNull -import at.hannibal2.skyhanni.utils.NumberUtil import at.hannibal2.skyhanni.utils.NEUItems.manager import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators -import at.hannibal2.skyhanni.utils.OSUtils import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.GemstoneSlotType import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.getAbilityScrolls @@ -52,7 +53,7 @@ import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.hasWoodSingularity import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.isRecombobulated import at.hannibal2.skyhanni.utils.StringUtils.firstLetterUppercase import at.hannibal2.skyhanni.utils.StringUtils.removeColor -import com.google.gson.Gson +import com.google.gson.JsonObject import com.google.gson.reflect.TypeToken import io.github.moulberry.notenoughupdates.events.RepositoryReloadEvent import io.github.moulberry.notenoughupdates.recipes.Ingredient @@ -62,7 +63,6 @@ import net.minecraft.item.ItemStack import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import java.io.File import java.util.* -import kotlin.collections.HashMap import kotlin.math.roundToLong object EstimatedItemValue { @@ -70,15 +70,19 @@ object EstimatedItemValue { private var display = emptyList<List<Any>>() private val cache = mutableMapOf<ItemStack, List<List<Any>>>() private var lastToolTipTime = 0L - private var gemstoneUnlockCosts = HashMap<String, HashMap<String, List<String>>>() + private var gemstoneUnlockCosts = HashMap<NEUInternalName, HashMap<String, List<String>>>() @SubscribeEvent fun onRepoReload(event: RepositoryReloadEvent) { val data = manager.getJsonFromFile(File(manager.repoLocation, "constants/gemstonecosts.json")) if (data != null) - // item_internal_names -> gemstone_slots -> ingredients_array - gemstoneUnlockCosts = Gson().fromJson(data, object : TypeToken<HashMap<String, HashMap<String, List<String>>>>() {}.getType()) + // item_internal_names -> gemstone_slots -> ingredients_array + gemstoneUnlockCosts = + ConfigManager.gson.fromJson( + data, + object : TypeToken<HashMap<NEUInternalName, HashMap<String, List<String>>>>() {}.type + ) else LorenzUtils.error("Gemstone Slot Unlock Costs failed to load") } @@ -280,22 +284,63 @@ object EstimatedItemValue { val rawReforgeName = stack.getReforgeName() ?: return 0.0 for ((rawInternalName, values) in Constants.REFORGESTONES.entrySet()) { - val stone = values.asJsonObject - val reforgeName = stone.get("reforgeName").asString + val stoneJson = values.asJsonObject + val reforgeName = stoneJson.get("reforgeName").asString if (rawReforgeName == reforgeName.lowercase() || rawReforgeName == rawInternalName.lowercase()) { val internalName = rawInternalName.asInternalName() - val price = internalName.getPrice() - val name = internalName.getItemName() + val reforgeStonePrice = internalName.getPrice() + val reforgeStoneName = internalName.getItemName() + + val reforgeCosts = stoneJson.get("reforgeCosts").asJsonObject + val applyCost = getReforgeStoneApplyCost(stack, reforgeCosts, internalName) ?: return 0.0 + val realReforgeName = if (reforgeName.equals("Warped")) "Hyper" else reforgeName list.add("§7Reforge: §9$realReforgeName") - list.add(" §7($name §6" + NumberUtil.format(price) + "§7)") - return price + list.add(" §7Stone $reforgeStoneName §7(§6" + NumberUtil.format(reforgeStonePrice) + "§7)") + list.add(" §7Apply cost: (§6" + NumberUtil.format(applyCost)) + return reforgeStonePrice + applyCost } } return 0.0 } + private fun getReforgeStoneApplyCost( + stack: ItemStack, + reforgeCosts: JsonObject, + reforgeStone: NEUInternalName + ): Int? { + var itemRarity = stack.getItemRarityOrNull() ?: return null + + // Catch cases of special or very special + if (itemRarity > LorenzRarity.MYTHIC) { + itemRarity = LorenzRarity.LEGENDARY + } else { + if (stack.isRecombobulated()) { + val oneBelow = itemRarity.oneBelow(logError = false) + if (oneBelow == null) { + CopyErrorCommand.logErrorState( + "Wrong item rarity detected in estimated item value for item ${stack.name}", + "Recombobulated item is common: ${stack.getInternalName()}, name:${stack.name}" + ) + return null + } + itemRarity = oneBelow + } + } + val rarityName = itemRarity.name + if (!reforgeCosts.has(rarityName)) { + val reforgesFound = reforgeCosts.entrySet().map { it.key } + CopyErrorCommand.logErrorState( + "Can not calculate reforge cost for item ${stack.name}", + "item rarity '$itemRarity' is not in NEU repo reforge cost for reforge stone$reforgeStone ($reforgesFound)" + ) + return null + } + + return reforgeCosts[rarityName].asInt + } + private fun addRecomb(stack: ItemStack, list: MutableList<String>): Double { if (!stack.isRecombobulated()) return 0.0 @@ -406,8 +451,8 @@ object EstimatedItemValue { private fun addSilex(stack: ItemStack, list: MutableList<String>): Double { val tier = stack.getSilexCount() ?: return 0.0 - val internalName = stack.getInternalName_old() - val maxTier = if (internalName == "STONK_PICKAXE") 4 else 5 + val internalName = stack.getInternalName() + val maxTier = if (internalName == "STONK_PICKAXE".asInternalName()) 4 else 5 val wtfHardcodedSilex = "SIL_EX".asInternalName() val price = wtfHardcodedSilex.getPrice() * tier @@ -666,50 +711,56 @@ object EstimatedItemValue { } private fun addGemstoneSlotUnlockCost(stack: ItemStack, list: MutableList<String>): Double { - val internalName = stack.getInternalName_old() + val internalName = stack.getInternalName() // item have to contains gems.unlocked_slots NBT array for unlocked slot detection - val unlockedSlots = stack.getExtraAttributes()?.getCompoundTag("gems")?.getTag("unlocked_slots").toString() + val unlockedSlots = + stack.getExtraAttributes()?.getCompoundTag("gems")?.getTag("unlocked_slots")?.toString() ?: return 0.0 + + // TODO detection for old items which doesnt have gems.unlocked_slots NBT array +// if (unlockedSlots == "null") return 0.0 - var totalPrice = 0.0 val priceMap = mutableMapOf<String, Double>() + if (gemstoneUnlockCosts.isEmpty()) return 0.0 + + if (internalName !in gemstoneUnlockCosts) { + CopyErrorCommand.logErrorState( + "Could not find gemstone slot price for ${stack.name}", + "EstimatedItemValue has no gemstoneUnlockCosts for $internalName" + ) + return 0.0 + } - if (gemstoneUnlockCosts.isNotEmpty() && gemstoneUnlockCosts.contains(internalName)) { - for (slot in gemstoneUnlockCosts.get(internalName)!!) { - if (unlockedSlots.contains(slot.key)) { - val previousTotal = totalPrice - - for (ingredients in slot.value) { - val ingredient = Ingredient(manager, ingredients) - - totalPrice += if (ingredient.isCoins) { - ingredient.count - } else { - getPrice(ingredient.internalItemId) * ingredient.count - } - } - - val splitSlot = slot.key.split("_") // eg. SAPPHIRE_1 - val colorCode = GemstoneSlotType.getColorCode(splitSlot[0]) - val formattedPrice = NumberUtil.format(totalPrice - previousTotal) - - // eg. SAPPHIRE_1 -> Sapphire Slot 2 - val displayName = splitSlot[0].lowercase(Locale.ENGLISH).replaceFirstChar(Char::uppercase) + " Slot" + - // If the slot index is 0, we don't need to specify - if (!splitSlot[1].equals("0")) { - " " + (splitSlot[1].toInt() + 1) - } else { "" } - - priceMap[" §$colorCode $displayName §7(§6$formattedPrice§7)"] = totalPrice - previousTotal + var totalPrice = 0.0 + val slots = gemstoneUnlockCosts[internalName] ?: return 0.0 + for (slot in slots) { + if (!unlockedSlots.contains(slot.key)) continue + + val previousTotal = totalPrice + for (ingredients in slot.value) { + val ingredient = Ingredient(manager, ingredients) + + totalPrice += if (ingredient.isCoins) { + ingredient.count + } else { + getPrice(ingredient.internalItemId) * ingredient.count } } - // TODO detection for old items which doesnt have gems.unlocked_slots NBT array - if (!unlockedSlots.equals("null")) { - list.add("§7Gemstone Slot Unlock Cost: §6" + NumberUtil.format(totalPrice)) - list += priceMap.sortedDesc().keys - } + val splitSlot = slot.key.split("_") // eg. SAPPHIRE_1 + val colorCode = GemstoneSlotType.getColorCode(splitSlot[0]) + val formattedPrice = NumberUtil.format(totalPrice - previousTotal) + + // eg. SAPPHIRE_1 -> Sapphire Slot 2 + val displayName = splitSlot[0].lowercase(Locale.ENGLISH).replaceFirstChar(Char::uppercase) + " Slot" + + // If the slot index is 0, we don't need to specify + if (splitSlot[1] != "0") " " + (splitSlot[1].toInt() + 1) else "" + + priceMap[" §$colorCode $displayName §7(§6$formattedPrice§7)"] = totalPrice - previousTotal } + + list.add("§7Gemstone Slot Unlock Cost: §6" + NumberUtil.format(totalPrice)) + list += priceMap.sortedDesc().keys return totalPrice } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/items/GlowingDroppedItems.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/items/GlowingDroppedItems.kt new file mode 100644 index 000000000..4e7afc376 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/items/GlowingDroppedItems.kt @@ -0,0 +1,68 @@ +package at.hannibal2.skyhanni.features.misc.items
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.data.IslandType
+import at.hannibal2.skyhanni.events.RenderEntityOutlineEvent
+import at.hannibal2.skyhanni.utils.ItemUtils.getItemRarityOrNull
+import at.hannibal2.skyhanni.utils.ItemUtils.name
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils.equalsOneOf
+import net.minecraft.entity.Entity
+import net.minecraft.entity.item.EntityArmorStand
+import net.minecraft.entity.item.EntityItem
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+class GlowingDroppedItems {
+
+ private val config get() = SkyHanniMod.feature.misc.glowingDroppedItems
+
+ /**
+ * List of skyblock locations where we might see items in showcases
+ */
+ private val showcaseItemLocations = setOf(
+ "The End",
+ "Jerry's Workshop"
+ )
+
+ @SubscribeEvent
+ fun onRenderEntityOutlines(event: RenderEntityOutlineEvent) {
+ if (isEnabled() && event.type === RenderEntityOutlineEvent.Type.XRAY) {
+ event.queueEntitiesToOutline(getEntityOutlineColor)
+ }
+ }
+
+ private fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled
+
+ private val getEntityOutlineColor: (entity: Entity) -> Int? = { entity ->
+ if (entity is EntityItem && !shouldHideShowcaseItem(entity)) {
+ val rarity = entity.entityItem.getItemRarityOrNull()
+
+ if (config.highlightFishingBait || entity.entityItem.name?.endsWith(" Bait") != true) {
+ rarity?.color?.toColor()?.rgb
+ } else null
+ } else null
+ }
+
+ private fun isShowcaseArea() =
+ showcaseItemLocations.contains(LorenzUtils.skyBlockArea) ||
+ LorenzUtils.skyBlockIsland.equalsOneOf(
+ IslandType.HUB,
+ IslandType.PRIVATE_ISLAND,
+ IslandType.PRIVATE_ISLAND_GUEST
+ )
+
+ private fun shouldHideShowcaseItem(entity: EntityItem): Boolean {
+ if (!isShowcaseArea() || config.highlightShowcase) return false
+
+ for (entityArmorStand in entity.worldObj.getEntitiesWithinAABB(
+ EntityArmorStand::class.java,
+ entity.entityBoundingBox
+ )) {
+ if (entityArmorStand.isInvisible) {
+ return true
+ }
+ }
+
+ return false
+ }
+}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/FeatureToggleProcessor.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/FeatureToggleProcessor.kt index fc8c4bd92..b3d3f74b7 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/FeatureToggleProcessor.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/FeatureToggleProcessor.kt @@ -11,8 +11,9 @@ import java.util.* class FeatureToggleProcessor : ConfigStructureReader { - var latestCategory: Category? = null - val pathStack = Stack<String>() + private var latestCategory: Category? = null + private val pathStack = Stack<String>() + private val accordionStack = Stack<String>() val allOptions = mutableListOf<FeatureToggleableOption>() val orderedOptions by lazy { @@ -26,10 +27,13 @@ class FeatureToggleProcessor : ConfigStructureReader { override fun endCategory() { } - override fun beginAccordion(baseObject: Any?, field: Field?, option: ConfigOption?, id: Int) { + override fun beginAccordion(baseObject: Any?, field: Field?, o: ConfigOption?, id: Int) { + val option = o ?: return + accordionStack.push(option.name) } override fun endAccordion() { + accordionStack.pop() } override fun pushPath(fieldPath: String) { @@ -63,9 +67,15 @@ class FeatureToggleProcessor : ConfigStructureReader { else -> error("Invalid FeatureToggle type: $field") } + + var name = option.name + if ((name == "Enable" || name == "Enabled") && !accordionStack.empty()) { + name = accordionStack.peek() + } + allOptions.add( FeatureToggleableOption( - option.name, + name, option.desc, value, featureToggle.trueIsEnabled, diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/powdertracker/PowderChestReward.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/powdertracker/PowderChestReward.kt new file mode 100644 index 000000000..9a9dcae90 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/powdertracker/PowderChestReward.kt @@ -0,0 +1,148 @@ +package at.hannibal2.skyhanni.features.misc.powdertracker + +import java.util.regex.Pattern + +enum class PowderChestReward(val displayName: String, val pattern: Pattern) { + + + MITHRIL_POWDER("§aMithril Powder", "§aYou received §r§b[+](?<amount>.*) §r§aMithril Powder.".toPattern()), + GEMSTONE_POWDER("§dGemstone Powder", "§aYou received §r§b[+](?<amount>.*) §r§aGemstone Powder.".toPattern()), + + ROUGH_RUBY_GEMSTONE( + "§fRough Ruby Gemstone", + "§aYou received §r§f(?<amount>.*) §r§f❤ §r§fRough Ruby Gemstone§r§a.".toPattern() + ), + FLAWED_RUBY_GEMSTONE( + "§aFlawed Sapphire Gemstone", + "§aYou received §r§f(?<amount>.*) §r§a❤ §r§aFlawed RubyGemstone§r§a.".toPattern() + ), + FINE_RUBY_GEMSTONE( + "§9Fine Ruby Gemstone", + "§aYou received §r§f(?<amount>.*) §r§9❤ §r§9Fine Ruby Gemstone§r§a.".toPattern() + ), + FLAWLESS_RUBY_GEMSTONE( + "§5Flawless Ruby Gemstone", + "§aYou received §r§f(?<amount>.*) §r§9❤ §r§5Flawless Ruby Gemstone§r§a.".toPattern() + ), + + ROUGH_SAPPHIRE_GEMSTONE( + "§fRough Sapphire Gemstone", + "§aYou received §r§f(?<amount>.*) §r§f✎ §r§fRough Sapphire Gemstone§r§a.".toPattern() + ), + FLAWED_SAPPHIRE_GEMSTONE( + "§aFlawed Sapphire Gemstone", + "§aYou received §r§f(?<amount>.*) §r§a✎ §r§aFlawed Sapphire Gemstone§r§a.".toPattern() + ), + FINE_SAPPHIRE_GEMSTONE( + "§9Fine Sapphire Gemstone", + "§aYou received §r§f(?<amount>.*) §r§9✎ §r§9Fine Sapphire Gemstone§r§a.".toPattern() + ), + FLAWLESS_SAPPHIRE_GEMSTONE( + "§5Flawless Sapphire Gemstone", + "§aYou received §r§f(?<amount>.*) §r§9✎ §r§5Flawless Sapphire Gemstone§r§a.".toPattern() + ), + + ROUGH_AMBER_GEMSTONE( + "§fRough Amber Gemstone", + "§aYou received §r§f(?<amount>.*) §r§f⸕ §r§fRough Amber Gemstone§r§a.".toPattern() + ), + FLAWED_AMBER_GEMSTONE( + "§aFlawed Amber Gemstone", + "§aYou received §r§f(?<amount>.*) §r§a⸕ §r§aFlawed Amber Gemstone§r§a.".toPattern() + ), + FINE_AMBER_GEMSTONE( + "§9Fine Amber Gemstone", + "§aYou received §r§f(?<amount>.*) §r§9⸕ §r§9Fine Amber Gemstone§r§a.".toPattern() + ), + FLAWLESS_AMBER_GEMSTONE( + "§5Flawless Amber Gemstone", + "§aYou received §r§f(?<amount>.*) §r§9⸕ §r§5Flawless Amber Gemstone§r§a.".toPattern() + ), + + ROUGH_AMETHYST_GEMSTONE( + "§fRough Amethyst Gemstone", + "§aYou received §r§f(?<amount>.*) §r§f❈ §r§fRough Amethyst Gemstone§r§a.".toPattern() + ), + FLAWED_AMETHYST_GEMSTONE( + "§aFlawed Amethyst Gemstone", + "§aYou received §r§f(?<amount>.*) §r§a❈ §r§aFlawed Amethyst Gemstone§r§a.".toPattern() + ), + FINE_AMETHYST_GEMSTONE( + "§9Fine Amethyst Gemstone", + "§aYou received §r§f(?<amount>.*) §r§9❈ §r§9Fine Amethyst Gemstone§r§a.".toPattern() + ), + FLAWLESS_AMETHYST_GEMSTONE( + "§5Flawless Amethyst Gemstone", + "§aYou received §r§f(?<amount>.*) §r§9❈ §r§5Flawless Amethyst Gemstone§r§a.".toPattern() + ), + + ROUGH_JADE_GEMSTONE( + "§fRough Jade Gemstone", + "§aYou received §r§f(?<amount>.*) §r§f☘ §r§fRough Jade Gemstone§r§a.".toPattern() + ), + FLAWED_JADE_GEMSTONE( + "§aFlawed Jade Gemstone", + "§aYou received §r§f(?<amount>.*) §r§a☘ §r§aFlawed Jade Gemstone§r§a.".toPattern() + ), + FINE_JADE_GEMSTONE( + "§9Fine Jade Gemstone", + "§aYou received §r§f(?<amount>.*) §r§9☘ §r§9Fine Jade Gemstone§r§a.".toPattern() + ), + FLAWLESS_JADE_GEMSTONE( + "§5Flawless Jade Gemstone", + "§aYou received §r§f(?<amount>.*) §r§9☘ §r§5Flawless Jade Gemstone§r§a.".toPattern() + ), + + ROUGH_TOPAZ_GEMSTONE( + "§fRough Topaz Gemstone", + "§aYou received §r§f(?<amount>.*) §r§f✧ §r§fRough Topaz Gemstone§r§a.".toPattern() + ), + FLAWED_TOPAZ_GEMSTONE( + "§aFlawed Topaz Gemstone", + "§aYou received §r§f(?<amount>.*) §r§a✧ §r§aFlawed Topaz Gemstone§r§a.".toPattern() + ), + FINE_TOPAZ_GEMSTONE( + "§9Fine Topaz Gemstone", + "§aYou received §r§f(?<amount>.*) §r§9✧ §r§9Fine Topaz Gemstone§r§a.".toPattern() + ), + FLAWLESS_TOPAZ_GEMSTONE( + "§5Flawless Topaz Gemstone", + "§aYou received §r§f(?<amount>.*) §r§9✧ §r§5Flawless Topaz Gemstone§r§a.".toPattern() + ), + + FTX_3070("§9FTX 3070", "§aYou received §r§f(?<amount>.*) §r§9FTX 3070§r§a.".toPattern()), + ELECTRON_TRANSIMTTER( + "§9Electron Transmitter", + "§aYou received §r§f(?<amount>.*) §r§9Electron Transmitter§r§a.".toPattern() + ), + ROBOTRON_REFLECTOR( + "§9Robotron Reflector", + "§aYou received §r§f(?<amount>.*) §r§9Robotron Reflector§r§a.".toPattern() + ), + SUPERLITE_MOTOR("§9Superlite Motor", "§aYou received §r§f(?<amount>.*) §r§9Superlite Motor§r§a.".toPattern()), + CONTROL_SWITCH("§9Control Switch", "§aYou received §r§f(?<amount>.*) §r§9Control Switch§r§a.".toPattern()), + SYNTHETIC_HEART("§9Synthetic Heart", "§aYou received §r§f(?<amount>.*) §r§9Synthetic Heart§r§a.".toPattern()), + + GOBLIN_EGG("§9Goblin Egg", "§aYou received §r§f(?<amount>.*) §r§9Goblin Egg§r§a.".toPattern()), + GREEN_GOBLIN_EGG( + "§aGreen Goblin Egg", + "§aYou received §r§f(?<amount>.*) §r§a§r§aGreen Goblin Egg§r§a.".toPattern() + ), + RED_GOBLIN_EGG("§cRed Goblin Egg", "§aYou received §r§f(?<amount>.*) §r§9§r§cRed Goblin Egg§r§a.".toPattern()), + YELLOW_GOBLIN_EGG( + "§eYellow Goblin Egg", + "§aYou received §r§f(?<amount>.*) §r§9§r§eYellow Goblin Egg§r§a.".toPattern() + ), + BLUE_GOBLIN_EGG("§3Blue Goblin Egg", "§aYou received §r§f(?<amount>.*) §r§9§r§3Blue Goblin Egg§r§a.".toPattern()), + + WISHING_COMPASS("§aWishing Compass", "§aYou received §r§f(?<amount>.*) §r§aWishing Compass§r§a.".toPattern()), + + SLUDGE_JUICE("§aSludge Juice", "§aYou received §r§f(?<amount>.*) §r§aSludge Juice§r§a.".toPattern()), + ASCENSION_ROPE("§9Ascension Rope", "§aYou received §r§f(?<amount>.*) §r§9Ascension Rope§r§a.".toPattern()), + TREASURITE("§5Treasurite", "§aYou received §r§f(?<amount>.*) §r§5Treasurite§r§a.".toPattern()), + JUNGLE_HEART("§6Jungle Heart", "§aYou received §r§f(?<amount>.*) §r§6Jungle Heart§r§a.".toPattern()), + PICKONIMBUS_2000("§5Pickonimbus 2000", "§aYou received §r§f(?<amount>.*) §r§5Pickonimbus 2000§r§a.".toPattern()), + YOGGIE("§aYoggie", "§aYou received §r§f(?<amount>.*) §r§aYoggie§r§a.".toPattern()), + PREHISTORIC_EGG("§fPrehistoric Egg", "§aYou received §r§f(?<amount>.*) §r§fPrehistoric Egg§r§a.".toPattern()), + OIL_BARREL("§aOil Barrel", "§aYou received §r§f(?<amount>.*) §r§aOil Barrel§r§a.".toPattern()), +}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/powdertracker/PowderTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/powdertracker/PowderTracker.kt new file mode 100644 index 000000000..ce3d4a1c1 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/powdertracker/PowderTracker.kt @@ -0,0 +1,356 @@ +package at.hannibal2.skyhanni.features.misc.powdertracker + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.config.Storage +import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.data.ProfileStorageData +import at.hannibal2.skyhanni.events.* +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.afterChange +import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.formatNumber +import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems +import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.inventory.GuiInventory +import net.minecraft.entity.boss.BossStatus +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import kotlin.concurrent.fixedRateTimer + +class PowderTracker { + + private val config get() = SkyHanniMod.feature.misc.powderTrackerConfig + private var display = emptyList<List<Any>>() + private val picked = "§6You have successfully picked the lock on this chest!".toPattern() + private val uncovered = "§aYou uncovered a treasure chest!".toPattern() + private val powderEvent = ".*§r§b§l2X POWDER STARTED!.*".toPattern() + private val powderEnded = ".*§r§b§l2X POWDER ENDED!.*".toPattern() + private val powderBossBar = "§e§lPASSIVE EVENT §b§l2X POWDER §e§lRUNNING FOR §a§l(?<time>.*)§r".toPattern() + private var lastChestPicked = 0L + private var isGrinding = false + private val gemstoneInfo = ResourceInfo(0L, 0L, 0, 0.0, mutableListOf()) + private val mithrilInfo = ResourceInfo(0L, 0L, 0, 0.0, mutableListOf()) + private val chestInfo = ResourceInfo(0L, 0L, 0, 0.0, mutableListOf()) + private var doublePowder = false + private var powderTimer = "" + private var currentDisplayMode = DisplayMode.TOTAL + private var inventoryOpen = false + private var currentSessionData = mutableMapOf<Int, Storage.ProfileSpecific.PowderTracker>() + private val gemstones = listOf( + "Ruby" to "§c", + "Sapphire" to "§b", + "Amber" to "§6", + "Amethyst" to "§5", + "Jade" to "§a", + "Topaz" to "§e" + ) + + init { + fixedRateTimer(name = "skyhanni-powder-tracker", period = 1000) { + if (!isEnabled()) return@fixedRateTimer + calculateResourceHour(gemstoneInfo) + calculateResourceHour(mithrilInfo) + calculateResourceHour(chestInfo) + } + } + + @SubscribeEvent + fun onRenderOverlay(event: GuiRenderEvent) { + if (!isEnabled()) return + + val currentlyOpen = Minecraft.getMinecraft().currentScreen is GuiInventory + if (inventoryOpen != currentlyOpen) { + inventoryOpen = currentlyOpen + saveAndUpdate() + } + + if (config.onlyWhenPowderGrinding && !isGrinding) return + + config.position.renderStringsAndItems( + display, + posLabel = "Powder Chest Tracker" + ) + } + + @SubscribeEvent + fun onChat(event: LorenzChatEvent) { + if (!isEnabled()) return + val msg = event.message + val both = currentLog() ?: return + + if (config.greatExplorerMaxed) { + uncovered.matchMatcher(msg) { + both.modify { + it.totalChestPicked += 1 + } + isGrinding = true + lastChestPicked = System.currentTimeMillis() + } + } + + picked.matchMatcher(msg) { + both.modify { + it.totalChestPicked += 1 + } + isGrinding = true + lastChestPicked = System.currentTimeMillis() + } + + powderEvent.matchMatcher(msg) { doublePowder = true } + powderEnded.matchMatcher(msg) { doublePowder = false } + + for (reward in PowderChestReward.entries) { + reward.pattern.matchMatcher(msg) { + both.modify { + val count = it.rewards[reward] ?: 0 + var amount = group("amount").formatNumber() + if ((reward == PowderChestReward.MITHRIL_POWDER || reward == PowderChestReward.GEMSTONE_POWDER) && doublePowder) + amount *= 2 + it.rewards[reward] = count + amount + } + } + } + saveAndUpdate() + } + + @SubscribeEvent + fun onTick(event: LorenzTickEvent) { + if (!isEnabled()) return + if (event.repeatSeconds(1)) { + doublePowder = powderBossBar.matcher(BossStatus.bossName).find() + powderBossBar.matchMatcher(BossStatus.bossName) { + powderTimer = group("time") + doublePowder = powderTimer != "00:00" + + saveAndUpdate() + } + } + if (System.currentTimeMillis() - lastChestPicked > 60_000) { + isGrinding = false + } + } + + @SubscribeEvent + fun onConfigLoad(event: ConfigLoadEvent) { + config.textFormat.afterChange { saveAndUpdate() } + } + + @SubscribeEvent + fun onWorldChange(event: LorenzWorldChangeEvent) { + if (!isEnabled()) return + gemstoneInfo.perHour = 0.0 + gemstoneInfo.stoppedChecks = 0 + gemstoneInfo.perMin.clear() + mithrilInfo.perHour = 0.0 + mithrilInfo.stoppedChecks = 0 + mithrilInfo.perMin.clear() + chestInfo.perHour = 0.0 + chestInfo.stoppedChecks = 0 + chestInfo.perMin.clear() + doublePowder = false + saveAndUpdate() + } + + private fun saveAndUpdate() { + calculateGemstone() + calculateMithril() + calculateChest() + display = formatDisplay(drawDisplay()) + } + + private fun formatDisplay(map: List<List<Any>>) = buildList { + if (map.isEmpty()) return@buildList + for (index in config.textFormat.get()) { + add(map[index]) + } + } + + private fun drawDisplay() = buildList<List<Any>> { + addAsSingletonList("§b§lPowder Tracker") + if (inventoryOpen){ + addSelector<DisplayMode>( + "§7Display Mode: ", + getName = { type -> type.displayName }, + isCurrent = { it == currentDisplayMode }, + onChange = { + currentDisplayMode = it + saveAndUpdate() + } + ) + }else{ + addAsSingletonList("") + } + + val both = currentLog() ?: return@buildList + val display = both.get(currentDisplayMode) + val rewards = display.rewards + + val chestPerHour = if (chestInfo.perHour < 0) 0 else chestInfo.perHour.toInt().addSeparators() + addAsSingletonList("§d${display.totalChestPicked.addSeparators()} Total Chests Picked §7($chestPerHour/h)") + addAsSingletonList("§bDouble Powder: ${if (doublePowder) "§aActive! §7($powderTimer)" else "§cInactive!"}") + + val mithril = PowderChestReward.entries[0] + val mithrilCount = rewards.getOrDefault(mithril, 0).addSeparators() + val mithrilPerHour = if (mithrilInfo.perHour < 0) 0 else mithrilInfo.perHour.toInt().addSeparators() + addAsSingletonList("§b$mithrilCount ${mithril.displayName} §7($mithrilPerHour/h)") + + val gemstone = PowderChestReward.entries[1] + val gemstoneCount = rewards.getOrDefault(gemstone, 0).addSeparators() + val gemstonePerHour = if (gemstoneInfo.perHour < 0) 0 else gemstoneInfo.perHour.toInt().addSeparators() + addAsSingletonList("§b$gemstoneCount ${gemstone.displayName} §7($gemstonePerHour/h)") + + addAsSingletonList("") + + for ((gem, color) in gemstones) { + var totalGemstone = 0L + + for (quality in arrayOf("ROUGH", "FLAWED", "FINE", "FLAWLESS")) { + val gemstoneType = PowderChestReward.valueOf("${quality}_${gem.uppercase()}_GEMSTONE") + val count = rewards.getOrDefault(gemstoneType, 0) + val multiplier = when (quality) { + "FLAWED" -> 80 + "FINE" -> 6400 + "FLAWLESS" -> 512000 + else -> 1 + } + totalGemstone += count * multiplier + } + + val (flawless, fine, flawed, rough) = convert(totalGemstone) + addAsSingletonList("§5${flawless}§7-§9${fine}§7-§a${flawed}§f-${rough} $color$gem Gemstone") + } + + var totalParts = 0L + for (reward in PowderChestReward.entries.subList(26, 32)) { // robots part + val count = rewards.getOrDefault(reward, 0) + totalParts += count + addAsSingletonList("§b${count.addSeparators()} ${reward.displayName}") + } + addAsSingletonList("§b${totalParts.addSeparators()} §9Total Robot Parts") + + val goblinEgg = rewards.getOrDefault(PowderChestReward.GOBLIN_EGG, 0) + val greenEgg = rewards.getOrDefault(PowderChestReward.GREEN_GOBLIN_EGG, 0) + val redEgg = rewards.getOrDefault(PowderChestReward.RED_GOBLIN_EGG, 0) + val yellowEgg = rewards.getOrDefault(PowderChestReward.YELLOW_GOBLIN_EGG, 0) + val blueEgg = rewards.getOrDefault(PowderChestReward.BLUE_GOBLIN_EGG, 0) + addAsSingletonList("§9$goblinEgg§7-§a$greenEgg§7-§c$redEgg§f-§e$yellowEgg§f-§3$blueEgg §fGoblin Egg") + + for (reward in PowderChestReward.entries.subList(37, 46)) { + val count = rewards.getOrDefault(reward, 0).addSeparators() + addAsSingletonList("§b$count ${reward.displayName}") + } + + + } + + private fun calculateResourceHour(resourceInfo: ResourceInfo) { + val difference = resourceInfo.estimated - resourceInfo.lastEstimated + resourceInfo.lastEstimated = resourceInfo.estimated + + if (difference == resourceInfo.estimated) { + return + } + + resourceInfo.perHour = resourceInfo.perMin.average() * 3600 + resourceInfo.perMin.add(difference) + + if (difference == 0L) { + resourceInfo.stoppedChecks += 1 + + if (resourceInfo.stoppedChecks == 60) { + resourceInfo.stoppedChecks = 0 + resourceInfo.perMin.clear() + resourceInfo.perHour = 0.0 + } + return + } + resourceInfo.stoppedChecks = 0 + } + + private fun calculateGemstone() { + val both = currentLog() ?: return + val display = both.get(currentDisplayMode) + val rewards = display.rewards + gemstoneInfo.estimated = 0 + gemstoneInfo.estimated += rewards.getOrDefault(PowderChestReward.GEMSTONE_POWDER, 0) + } + + private fun calculateMithril() { + val both = currentLog() ?: return + val display = both.get(currentDisplayMode) + val rewards = display.rewards + mithrilInfo.estimated = 0 + mithrilInfo.estimated += rewards.getOrDefault(PowderChestReward.MITHRIL_POWDER, 0) + } + + private fun calculateChest() { + val both = currentLog() ?: return + val display = both.get(currentDisplayMode) + chestInfo.estimated = 0 + chestInfo.estimated += display.totalChestPicked + } + + private fun convert(roughCount: Long): Gem { + val flawlessRatio = 512000 + val fineRatio = 6400 + val flawedRatio = 80 + + val flawlessCount = roughCount / flawlessRatio + val remainingAfterFlawless = roughCount % flawlessRatio + + val fineCount = remainingAfterFlawless / fineRatio + val remainingAfterFine = remainingAfterFlawless % fineRatio + + val flawedCount = remainingAfterFine / flawedRatio + val remainingRoughCount = remainingAfterFine % flawedRatio + + return Gem(flawlessCount, fineCount, flawedCount, remainingRoughCount) + } + + data class Gem(val flawless: Long, val fine: Long, val flawed: Long, val rough: Long) + + private data class ResourceInfo( + var estimated: Long, + var lastEstimated: Long, + var stoppedChecks: Int, + var perHour: Double, + val perMin: MutableList<Long> + ) + + enum class DisplayMode(val displayName: String) { + TOTAL("Total"), + CURRENT("This Session"), + ; + } + + + private fun currentLog(): AbstractPowderTracker? { + val profileSpecific = ProfileStorageData.profileSpecific ?: return null + + return AbstractPowderTracker( + profileSpecific.powderTracker.getOrPut(0) { Storage.ProfileSpecific.PowderTracker() }, + currentSessionData.getOrPut(0) { Storage.ProfileSpecific.PowderTracker() } + ) + } + + class AbstractPowderTracker( + private val total: Storage.ProfileSpecific.PowderTracker, + private val currentSession: Storage.ProfileSpecific.PowderTracker, + ) { + + fun modify(modifyFunction: (Storage.ProfileSpecific.PowderTracker) -> Unit) { + modifyFunction(total) + modifyFunction(currentSession) + } + + fun get(displayMode: DisplayMode) = when (displayMode) { + DisplayMode.TOTAL -> total + DisplayMode.CURRENT -> currentSession + } + } + + private fun isEnabled() = + LorenzUtils.inSkyBlock && LorenzUtils.skyBlockIsland == IslandType.CRYSTAL_HOLLOWS && config.enabled +}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/tabcomplete/GetFromSacksTabComplete.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/tabcomplete/GetFromSacksTabComplete.kt new file mode 100644 index 000000000..1fc7521d2 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/tabcomplete/GetFromSacksTabComplete.kt @@ -0,0 +1,45 @@ +package at.hannibal2.skyhanni.features.misc.tabcomplete + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.events.PacketEvent +import at.hannibal2.skyhanni.events.RepositoryReloadEvent +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.jsonobjects.SackListJson +import net.minecraft.network.play.client.C01PacketChatMessage +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +object GetFromSacksTabComplete { + private val config get() = SkyHanniMod.feature.misc.tabCompleteCommands + private var sackList = emptyList<String>() + private val commands = arrayOf("gfs", "getfromsacks") + + @SubscribeEvent + fun onRepoReload(event: RepositoryReloadEvent) { + sackList = event.getConstant<SackListJson>("Sacks")?.sackList ?: return + } + + fun handleTabComplete(command: String): List<String>? { + if (!isEnabled()) return null + if (command !in commands) return null + + return sackList.map { it.replace(" ", "_") } + } + + @SubscribeEvent + fun onSendPacket(event: PacketEvent.SendEvent) { + if (!isEnabled()) return + + val packet = event.packet as? C01PacketChatMessage ?: return + val message = packet.message + if (commands.any { message.startsWith("/$it ") }) { + val rawName = message.split(" ")[1] + val realName = rawName.replace("_", " ") + if (realName in sackList) { + event.isCanceled = true + LorenzUtils.sendMessageToServer(message.replace(rawName, realName)) + } + } + } + + fun isEnabled() = LorenzUtils.inSkyBlock && config.gfsSack +}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/tabcomplete/TabComplete.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/tabcomplete/TabComplete.kt index 8e13d3a1d..b003de699 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/tabcomplete/TabComplete.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/tabcomplete/TabComplete.kt @@ -20,6 +20,7 @@ object TabComplete { } private fun customTabComplete(command: String): List<String>? { + GetFromSacksTabComplete.handleTabComplete(command)?.let { return it } WarpTabComplete.handleTabComplete(command)?.let { return it } PlayerTabComplete.handleTabComplete(command)?.let { return it } CollectionTracker.handleTabComplete(command)?.let { return it } diff --git a/src/main/java/at/hannibal2/skyhanni/features/mobs/MobHighlight.kt b/src/main/java/at/hannibal2/skyhanni/features/mobs/MobHighlight.kt index a50479f6b..8d8a3ca40 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mobs/MobHighlight.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mobs/MobHighlight.kt @@ -13,6 +13,7 @@ import net.minecraft.client.entity.EntityOtherPlayerMP import net.minecraft.entity.EntityLivingBase import net.minecraft.entity.monster.EntityEnderman import net.minecraft.entity.monster.EntitySpider +import net.minecraft.entity.monster.EntityCaveSpider import net.minecraftforge.fml.common.eventhandler.SubscribeEvent class MobHighlight { @@ -40,7 +41,7 @@ class MobHighlight { val entity = event.entity val maxHealth = event.maxHealth if (config.arachneKeeperHighlight) { - if (maxHealth == 3_000 && entity is EntitySpider) { + if (maxHealth == 3_000 && entity is EntityCaveSpider) { RenderLivingEntityHelper.setEntityColor(entity, LorenzColor.DARK_BLUE.toColor().withAlpha(127)) { config.arachneKeeperHighlight } RenderLivingEntityHelper.setNoHurtTime(entity) { config.arachneKeeperHighlight } @@ -106,4 +107,4 @@ class MobHighlight { { config.arachneBossHighlighter } RenderLivingEntityHelper.setNoHurtTime(entity) { config.arachneBossHighlighter } } -}
\ No newline at end of file +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/nether/reputationhelper/dailyquest/DailyQuestHelper.kt b/src/main/java/at/hannibal2/skyhanni/features/nether/reputationhelper/dailyquest/DailyQuestHelper.kt index 14e5b131a..1f5c7c081 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/nether/reputationhelper/dailyquest/DailyQuestHelper.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/nether/reputationhelper/dailyquest/DailyQuestHelper.kt @@ -60,7 +60,7 @@ class DailyQuestHelper(val reputationHelper: CrimsonIsleReputationHelper) { } } - // TODO use OwnInventorItemUpdateEvent + // TODO use OwnInventoryItemUpdateEvent private fun checkInventoryForTrophyFish() { val fishQuest = getQuest<TrophyFishQuest>() ?: return if (fishQuest.state != QuestState.ACCEPTED && fishQuest.state != QuestState.READY_TO_COLLECT) return diff --git a/src/main/java/at/hannibal2/skyhanni/features/rift/area/stillgorechateau/RiftBloodEffigies.kt b/src/main/java/at/hannibal2/skyhanni/features/rift/area/stillgorechateau/RiftBloodEffigies.kt index c613aa3a1..179c99365 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/rift/area/stillgorechateau/RiftBloodEffigies.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/rift/area/stillgorechateau/RiftBloodEffigies.kt @@ -68,12 +68,12 @@ class RiftBloodEffigies { if (diff < 0L) { if (s == "7") { if (time != 0L) { - LorenzUtils.chat("§e[SkyHanni] Effigies #${index + 1} respawned!") + LorenzUtils.chat("§e[SkyHanni] Effigy #${index + 1} respawned!") effigiesTimes = effigiesTimes.editCopy { this[index] = 0L } } } else { if (time != -1L) { - LorenzUtils.chat("§e[SkyHanni] Effigies #${index + 1} is broken!") + LorenzUtils.chat("§e[SkyHanni] Effigy #${index + 1} is broken!") val endTime = System.currentTimeMillis() + 1_000 * 60 * 20 effigiesTimes = effigiesTimes.editCopy { this[index] = endTime } } @@ -104,7 +104,7 @@ class RiftBloodEffigies { if (!isEnabled()) return for ((index, location) in locations.withIndex()) { - val name = "Effigies #${index + 1}" + val name = "Effigy #${index + 1}" val duration = effigiesTimes[index]!! if (duration == -1L) { diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/hooks/RenderGlobalHook.kt b/src/main/java/at/hannibal2/skyhanni/mixins/hooks/RenderGlobalHook.kt new file mode 100644 index 000000000..57305c5bd --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/mixins/hooks/RenderGlobalHook.kt @@ -0,0 +1,20 @@ +package at.hannibal2.skyhanni.mixins.hooks
+
+import at.hannibal2.skyhanni.utils.EntityOutlineRenderer
+import at.hannibal2.skyhanni.utils.RenderUtils
+import net.minecraft.client.Minecraft
+import net.minecraft.client.renderer.culling.ICamera
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable
+
+class RenderGlobalHook {
+ fun renderEntitiesOutlines(camera: ICamera?, partialTicks: Float): Boolean {
+ val vec = RenderUtils.exactLocation(Minecraft.getMinecraft().renderViewEntity, partialTicks)
+ return EntityOutlineRenderer.renderEntityOutlines(camera!!, partialTicks, vec)
+ }
+
+ fun shouldRenderEntityOutlines(cir: CallbackInfoReturnable<Boolean?>) {
+ if (EntityOutlineRenderer.shouldRenderEntityOutlines()) {
+ cir.returnValue = true
+ }
+ }
+}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/hooks/RendererLivingEntityHook.kt b/src/main/java/at/hannibal2/skyhanni/mixins/hooks/RendererLivingEntityHook.kt new file mode 100644 index 000000000..c91252c8a --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/mixins/hooks/RendererLivingEntityHook.kt @@ -0,0 +1,20 @@ +package at.hannibal2.skyhanni.mixins.hooks
+
+import at.hannibal2.skyhanni.utils.EntityOutlineRenderer
+import net.minecraft.client.renderer.GlStateManager
+import net.minecraft.entity.EntityLivingBase
+
+class RendererLivingEntityHook {
+ fun setOutlineColor(red: Float, green: Float, blue: Float, alpha: Float, entity: EntityLivingBase) {
+ val color = EntityOutlineRenderer.getCustomOutlineColor(entity)
+
+ if (color != null) {
+ val colorRed = (color shr 16 and 255).toFloat() / 255.0f
+ val colorGreen = (color shr 8 and 255).toFloat() / 255.0f
+ val colorBlue = (color and 255).toFloat() / 255.0f
+ GlStateManager.color(colorRed, colorGreen, colorBlue, alpha)
+ } else {
+ GlStateManager.color(red, green, blue, alpha)
+ }
+ }
+}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/CustomRenderGlobal.java b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/CustomRenderGlobal.java new file mode 100644 index 000000000..bd440d3ea --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/CustomRenderGlobal.java @@ -0,0 +1,17 @@ +package at.hannibal2.skyhanni.mixins.transformers;
+
+import net.minecraft.client.renderer.RenderGlobal;
+import net.minecraft.client.shader.Framebuffer;
+import net.minecraft.client.shader.ShaderGroup;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+@Mixin(RenderGlobal.class)
+public interface CustomRenderGlobal {
+ @Accessor("entityOutlineFramebuffer")
+ Framebuffer getEntityOutlineFramebuffer_skyhanni();
+
+ @Accessor("entityOutlineShader")
+ ShaderGroup getEntityOutlineShader_skyhanni();
+
+}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinRenderGlobal.java b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinRenderGlobal.java new file mode 100644 index 000000000..630fb1381 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinRenderGlobal.java @@ -0,0 +1,42 @@ +package at.hannibal2.skyhanni.mixins.transformers;
+
+import at.hannibal2.skyhanni.mixins.hooks.RenderGlobalHook;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.RenderGlobal;
+import at.hannibal2.skyhanni.utils.EntityOutlineRenderer;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.culling.ICamera;
+import net.minecraft.entity.Entity;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.Redirect;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+@Mixin(RenderGlobal.class)
+public abstract class MixinRenderGlobal {
+
+ @Shadow
+ abstract boolean isRenderEntityOutlines();
+
+ @Unique
+ private final RenderGlobalHook skyHanni$hook = new RenderGlobalHook();
+
+ @Redirect(method = "renderEntities", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;isRenderEntityOutlines()Z"))
+ public boolean renderEntitiesOutlines(RenderGlobal self, Entity renderViewEntity, ICamera camera, float partialTicks) {
+ return skyHanni$hook.renderEntitiesOutlines(camera, partialTicks) && this.isRenderEntityOutlines();
+ }
+
+ @Inject(method = "isRenderEntityOutlines", at = @At(value = "HEAD"), cancellable = true)
+ public void isRenderEntityOutlinesWrapper(CallbackInfoReturnable<Boolean> cir) {
+ skyHanni$hook.shouldRenderEntityOutlines(cir);
+ }
+
+ @Inject(method = "renderEntityOutlineFramebuffer", at = @At(value = "RETURN"))
+ public void afterFramebufferDraw(CallbackInfo callbackInfo) {
+ GlStateManager.enableDepth();
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinRendererLivingEntity.java b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinRendererLivingEntity.java new file mode 100644 index 000000000..281137ae5 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinRendererLivingEntity.java @@ -0,0 +1,23 @@ +package at.hannibal2.skyhanni.mixins.transformers;
+
+import at.hannibal2.skyhanni.mixins.hooks.RendererLivingEntityHook;
+import at.hannibal2.skyhanni.utils.EntityOutlineRenderer;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.entity.RendererLivingEntity;
+import net.minecraft.entity.EntityLivingBase;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Redirect;
+import org.spongepowered.asm.mixin.Unique;
+
+@Mixin(RendererLivingEntity.class)
+public class MixinRendererLivingEntity {
+
+ @Unique
+ private final RendererLivingEntityHook skyHanni$hook = new RendererLivingEntityHook();
+
+ @Redirect(method = "setScoreTeamColor", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;color(FFFF)V"))
+ public void setOutlineColor(float colorRed, float colorGreen, float colorBlue, float colorAlpha, EntityLivingBase entity) {
+ skyHanni$hook.setOutlineColor(colorRed, colorGreen, colorBlue, colorAlpha, entity);
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniTestCommand.kt b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt index 63bc5f471..d330cc1d0 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniTestCommand.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt @@ -15,15 +15,17 @@ import at.hannibal2.skyhanni.utils.NEUItems.getNpcPriceOrNull import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.RenderUtils.renderString import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems +import net.minecraft.client.Minecraft import net.minecraft.nbt.NBTTagCompound import net.minecraftforge.common.MinecraftForge import net.minecraftforge.event.entity.player.ItemTooltipEvent import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import java.io.File -class SkyHanniTestCommand { +class SkyHanniDebugsAndTests { companion object { + private val config get() = SkyHanniMod.feature.dev var displayLine = "" var displayList = emptyList<List<Any>>() @@ -198,7 +200,7 @@ class SkyHanniTestCommand { builder.append("\n") builder.append("player name: '${LorenzUtils.getPlayerName()}'\n") builder.append("player uuid: '${LorenzUtils.getPlayerUuid()}'\n") - builder.append("repoAutoUpdate: ${SkyHanniMod.feature.dev.repoAutoUpdate}\n") + builder.append("repoAutoUpdate: ${config.repoAutoUpdate}\n") builder.append("\n") builder.append("onHypixel: ${LorenzUtils.onHypixel}\n") @@ -232,16 +234,16 @@ class SkyHanniTestCommand { @SubscribeEvent fun onShowInternalName(event: ItemTooltipEvent) { - if (!SkyHanniMod.feature.dev.showInternalName) return + if (!config.showInternalName) return val itemStack = event.itemStack ?: return val internalName = itemStack.getInternalName() - if ((internalName == NEUInternalName.NONE) && !SkyHanniMod.feature.dev.showEmptyNames) return + if ((internalName == NEUInternalName.NONE) && !config.showEmptyNames) return event.toolTip.add("Internal Name: '${internalName.asString()}'") } @SubscribeEvent fun onSHowNpcPrice(event: ItemTooltipEvent) { - if (!SkyHanniMod.feature.dev.showNpcPrice) return + if (!config.showNpcPrice) return val itemStack = event.itemStack ?: return val internalName = itemStack.getInternalNameOrNull() ?: return @@ -250,6 +252,16 @@ class SkyHanniTestCommand { } @SubscribeEvent + fun onRenderLocation(event: GuiRenderEvent.GameOverlayRenderEvent) { + if (LorenzUtils.inSkyBlock && Minecraft.getMinecraft().gameSettings.showDebugInfo) { + config.debugLocationPos.renderString( + "Current Area: ${HypixelData.skyBlockArea}", + posLabel = "SkyBlock Area (Debug)" + ) + } + } + + @SubscribeEvent fun onChatMessage(event: LorenzChatEvent) { } @@ -257,12 +269,12 @@ class SkyHanniTestCommand { @SubscribeEvent fun onRenderOverlay(event: GuiRenderEvent.GameOverlayRenderEvent) { if (!LorenzUtils.inSkyBlock) return - if (!SkyHanniMod.feature.dev.debugEnabled) return + if (!config.debugEnabled) return if (displayLine.isNotEmpty()) { - SkyHanniMod.feature.dev.debugPos.renderString("test: $displayLine", posLabel = "Test") + config.debugPos.renderString("test: $displayLine", posLabel = "Test") } - SkyHanniMod.feature.dev.debugPos.renderStringsAndItems(displayList, posLabel = "Test Display") + config.debugPos.renderStringsAndItems(displayList, posLabel = "Test Display") } @SubscribeEvent diff --git a/src/main/java/at/hannibal2/skyhanni/test/TestCopyBestiaryValues.kt b/src/main/java/at/hannibal2/skyhanni/test/TestCopyBestiaryValues.kt index 870752a25..fdb4a58c3 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/TestCopyBestiaryValues.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/TestCopyBestiaryValues.kt @@ -44,7 +44,7 @@ object TestCopyBestiaryValues { @SubscribeEvent(priority = EventPriority.LOW) fun onLateInventoryOpen(event: InventoryUpdatedEvent) { if (!SkyHanniMod.feature.dev.copyBestiaryData) return - SkyHanniTestCommand.displayLine = "" + SkyHanniDebugsAndTests.displayLine = "" val backItem = event.inventoryItems[3 + 9 * 5 + 3] if (backItem == null) { @@ -112,6 +112,6 @@ object TestCopyBestiaryValues { val text = gson.toJson(obj) OSUtils.copyToClipboard(text) - SkyHanniTestCommand.displayLine = "Bestiary for $titleName" + SkyHanniDebugsAndTests.displayLine = "Bestiary for $titleName" } }
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/test/TestCopyRngMeterValues.kt b/src/main/java/at/hannibal2/skyhanni/test/TestCopyRngMeterValues.kt index b243c9991..a556a7254 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/TestCopyRngMeterValues.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/TestCopyRngMeterValues.kt @@ -3,9 +3,10 @@ package at.hannibal2.skyhanni.test import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.config.ConfigManager import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent -import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName_old +import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.NEUInternalName import at.hannibal2.skyhanni.utils.NumberUtil.formatNumber import at.hannibal2.skyhanni.utils.OSUtils import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher @@ -17,16 +18,16 @@ object TestCopyRngMeterValues { fun onInventoryOpen(event: InventoryFullyOpenedEvent) { if (!SkyHanniMod.feature.dev.copyRngMeter) return - val map = mutableMapOf<String, Long>() + val map = mutableMapOf<NEUInternalName, Long>() val slayerPattern = "§7Slayer XP: §d.*§5/§d(?<xp>.*)".toPattern() val dungeonPattern = "§7Dungeon Score: §d.*§5/§d(?<xp>.*)".toPattern() for (item in event.inventoryItems.values) { for (line in item.getLore()) { slayerPattern.matchMatcher(line) { - map[item.getInternalName_old()] = group("xp").formatNumber() + map[item.getInternalName()] = group("xp").formatNumber() } dungeonPattern.matchMatcher(line) { - map[item.getInternalName_old()] = group("xp").formatNumber() + map[item.getInternalName()] = group("xp").formatNumber() } } } diff --git a/src/main/java/at/hannibal2/skyhanni/test/command/CopyErrorCommand.kt b/src/main/java/at/hannibal2/skyhanni/test/command/CopyErrorCommand.kt index 946255996..dd830571f 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/command/CopyErrorCommand.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/command/CopyErrorCommand.kt @@ -42,6 +42,10 @@ object CopyErrorCommand { } ?: "§c[SkyHanni] Error id not found!") } + fun logErrorState(userMessage: String, internalMessage: String) { + logError(IllegalStateException(internalMessage), userMessage) + } + fun logError(throwable: Throwable, message: String) { val error = Error(message, throwable) Minecraft.getMinecraft().thePlayer ?: throw error diff --git a/src/main/java/at/hannibal2/skyhanni/test/command/CopyItemCommand.kt b/src/main/java/at/hannibal2/skyhanni/test/command/CopyItemCommand.kt index 2b33c8e82..d2f0b60ab 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/command/CopyItemCommand.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/command/CopyItemCommand.kt @@ -1,6 +1,6 @@ package at.hannibal2.skyhanni.test.command -import at.hannibal2.skyhanni.test.SkyHanniTestCommand +import at.hannibal2.skyhanni.test.SkyHanniDebugsAndTests import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName_old import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.LorenzUtils @@ -32,7 +32,7 @@ object CopyItemCommand { resultList.add("") resultList.add("ExtraAttributes") val extraAttributes = tagCompound.getCompoundTag("ExtraAttributes") - SkyHanniTestCommand.runn(extraAttributes, " . ") + SkyHanniDebugsAndTests.runn(extraAttributes, " . ") } } diff --git a/src/main/java/at/hannibal2/skyhanni/utils/EntityOutlineRenderer.kt b/src/main/java/at/hannibal2/skyhanni/utils/EntityOutlineRenderer.kt new file mode 100644 index 000000000..6ce9e4b38 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/EntityOutlineRenderer.kt @@ -0,0 +1,408 @@ +package at.hannibal2.skyhanni.utils + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.events.LorenzTickEvent +import at.hannibal2.skyhanni.events.RenderEntityOutlineEvent +import at.hannibal2.skyhanni.mixins.transformers.CustomRenderGlobal +import at.hannibal2.skyhanni.test.command.CopyErrorCommand +import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.renderer.OpenGlHelper +import net.minecraft.client.renderer.RenderHelper +import net.minecraft.client.renderer.culling.ICamera +import net.minecraft.client.shader.Framebuffer +import net.minecraft.entity.Entity +import net.minecraft.entity.EntityLivingBase +import net.minecraft.util.BlockPos +import net.minecraftforge.client.MinecraftForgeClient +import net.minecraftforge.fml.common.eventhandler.EventPriority +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import org.lwjgl.opengl.GL11 +import org.lwjgl.opengl.GL13 +import org.lwjgl.opengl.GL30 +import java.lang.reflect.InvocationTargetException +import java.lang.reflect.Method + +/** + * Class to handle all entity outlining, including xray and no-xray rendering + * Features that include entity outlining should subscribe to the {@link RenderEntityOutlineEvent}. + * + * Credit to SkyblockAddons and Biscuit Development + * https://github.com/BiscuitDevelopment/SkyblockAddons/blob/main/src/main/java/codes/biscuit/skyblockaddons/features/EntityOutlines/EntityOutlineRenderer.java + * + */ +object EntityOutlineRenderer { + private val entityRenderCache: CachedInfo = CachedInfo(null, null, null) + private var stopLookingForOptifine = false + private var isMissingMixin = false + private var isFastRender: Method? = null + private var isShaders: Method? = null + private var isAntialiasing: Method? = null + private var emptyLastTick = false + private val swapBuffer by lazy { initSwapBuffer() } + private val logger = LorenzLogger("entity_outline_renderer") + private val mc get() = Minecraft.getMinecraft() + private val BUF_FLOAT_4: java.nio.FloatBuffer = org.lwjgl.BufferUtils.createFloatBuffer(4) + + private val CustomRenderGlobal.frameBuffer get() = entityOutlineFramebuffer_skyhanni + private val CustomRenderGlobal.shader get() = entityOutlineShader_skyhanni + + /** + * @return a new framebuffer with the size of the main framebuffer + */ + private fun initSwapBuffer(): Framebuffer { + val main = mc.framebuffer + val framebuffer = Framebuffer(main.framebufferTextureWidth, main.framebufferTextureHeight, true) + framebuffer.setFramebufferFilter(GL11.GL_NEAREST) + framebuffer.setFramebufferColor(0.0f, 0.0f, 0.0f, 0.0f) + return framebuffer + } + + private fun updateFramebufferSize() { + val width = mc.displayWidth + val height = mc.displayHeight + if (swapBuffer.framebufferWidth != width || swapBuffer.framebufferHeight != height) { + swapBuffer.createBindFramebuffer(width, height) + } + val renderGlobal = mc.renderGlobal as CustomRenderGlobal + val outlineBuffer = renderGlobal.frameBuffer + if (outlineBuffer.framebufferWidth != width || outlineBuffer.framebufferHeight != height) { + outlineBuffer.createBindFramebuffer(width, height) + renderGlobal.shader.createBindFramebuffers(width, height) + } + } + + /** + * Renders xray and no-xray entity outlines. + * + * @param camera the current camera + * @param partialTicks the progress to the next tick + * @param x the camera x position + * @param y the camera y position + * @param z the camera z position + */ + @JvmStatic + fun renderEntityOutlines(camera: ICamera, partialTicks: Float, vector: LorenzVec): Boolean { + val shouldRenderOutlines = shouldRenderEntityOutlines() + + if (!(shouldRenderOutlines && !isCacheEmpty() && MinecraftForgeClient.getRenderPass() == 0)) { + return !shouldRenderOutlines + } + + val renderGlobal = mc.renderGlobal as CustomRenderGlobal + val renderManager = mc.renderManager + mc.theWorld.theProfiler.endStartSection("entityOutlines") + updateFramebufferSize() + + // Clear and bind the outline framebuffer + renderGlobal.frameBuffer.framebufferClear() + renderGlobal.frameBuffer.bindFramebuffer(false) + + // Vanilla options + RenderHelper.disableStandardItemLighting() + GlStateManager.disableFog() + mc.renderManager.setRenderOutlines(true) + + // Enable outline mode + GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL13.GL_COMBINE); + GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_RGB, GL11.GL_REPLACE); + GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_SOURCE0_RGB, GL13.GL_CONSTANT); + GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_OPERAND0_RGB, GL11.GL_SRC_COLOR); + GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_ALPHA, GL11.GL_REPLACE); + GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_SOURCE0_ALPHA, GL11.GL_TEXTURE); + GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_OPERAND0_ALPHA, GL11.GL_SRC_ALPHA); + + // Render x-ray outlines first, ignoring the depth buffer bit + if (!isXrayCacheEmpty()) { + // Xray is enabled by disabling depth testing + GlStateManager.depthFunc(GL11.GL_ALWAYS) + + entityRenderCache.xrayCache?.forEach { (key, value) -> + // Test if the entity should render, given the player's camera position + if (!shouldRender(camera, key, vector)) return@forEach + + try { + if (key !is EntityLivingBase) outlineColor(value) + renderManager.renderEntityStatic(key, partialTicks, true) + } catch (ignored: Exception) { + } + } + + // Reset depth function + GlStateManager.depthFunc(GL11.GL_LEQUAL) + } + + // Render no-xray outlines second, taking into consideration the depth bit + if (!isNoXrayCacheEmpty()) { + if (!isNoOutlineCacheEmpty()) { + // Render other entities + terrain that may occlude an entity outline into a depth buffer + swapBuffer.framebufferClear() + copyBuffers(mc.framebuffer, swapBuffer, GL11.GL_DEPTH_BUFFER_BIT) + swapBuffer.bindFramebuffer(false) + + // Copy terrain + other entities depth into outline frame buffer to now switch to no-xray outlines + entityRenderCache.noOutlineCache?.forEach { entity -> + // Test if the entity should render, given the player's instantaneous camera position + if (!shouldRender(camera, entity, vector)) return@forEach + + try { + renderManager.renderEntityStatic(entity, partialTicks, true) + } catch (ignored: Exception) { + } + } + + // Copy the entire depth buffer of everything that might occlude outline to outline framebuffer + copyBuffers(swapBuffer, renderGlobal.frameBuffer, GL11.GL_DEPTH_BUFFER_BIT) + renderGlobal.frameBuffer.bindFramebuffer(false) + } else { + copyBuffers(mc.framebuffer, renderGlobal.frameBuffer, GL11.GL_DEPTH_BUFFER_BIT) + } + + // Xray disabled by re-enabling traditional depth testing + entityRenderCache.noXrayCache?.forEach { (key, value) -> + // Test if the entity should render, given the player's instantaneous camera position + if (!shouldRender(camera, key, vector)) return@forEach + + try { + if (key !is EntityLivingBase) outlineColor(value) + renderManager.renderEntityStatic(key, partialTicks, true) + } catch (ignored: Exception) { + } + } + } + + // Disable outline mode + with(GL11.GL_TEXTURE_ENV) { + GL11.glTexEnvi(this, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE) + GL11.glTexEnvi(this, GL13.GL_COMBINE_RGB, GL11.GL_MODULATE) + GL11.glTexEnvi(this, GL13.GL_SOURCE0_RGB, GL11.GL_TEXTURE) + GL11.glTexEnvi(this, GL13.GL_OPERAND0_RGB, GL11.GL_SRC_COLOR) + GL11.glTexEnvi(this, GL13.GL_COMBINE_ALPHA, GL11.GL_MODULATE) + GL11.glTexEnvi(this, GL13.GL_SOURCE0_ALPHA, GL11.GL_TEXTURE) + GL11.glTexEnvi(this, GL13.GL_OPERAND0_ALPHA, GL11.GL_SRC_ALPHA) + } + + // Vanilla options + RenderHelper.enableStandardItemLighting() + mc.renderManager.setRenderOutlines(false) + + // Load the outline shader + GlStateManager.depthMask(false) + renderGlobal.shader.loadShaderGroup(partialTicks) + GlStateManager.depthMask(true) + + // Reset GL/framebuffers for next render layers + GlStateManager.enableLighting() + mc.framebuffer.bindFramebuffer(false) + GlStateManager.enableFog() + GlStateManager.enableBlend() + GlStateManager.enableColorMaterial() + GlStateManager.enableDepth() + GlStateManager.enableAlpha() + + return !shouldRenderOutlines + } + + @JvmStatic + fun getCustomOutlineColor(entity: Entity?): Int? { + if (entityRenderCache.xrayCache?.containsKey(entity) == true) { + return entityRenderCache.xrayCache!![entity] + } + return if (entityRenderCache.noXrayCache?.containsKey(entity) == true) { + entityRenderCache.noXrayCache!![entity] + } else null + } + + /** + * Caches optifine settings and determines whether outlines should be rendered + * + * @return `true` iff outlines should be rendered + */ + @JvmStatic + fun shouldRenderEntityOutlines(): Boolean { + // Skyblock Conditions + if (!LorenzUtils.inSkyBlock) { + return false + } + + // Main toggle for outlines features + if (!isEnabled()) { + return false + } + + // Vanilla Conditions + val renderGlobal = mc.renderGlobal as CustomRenderGlobal + if (renderGlobal.frameBuffer == null || renderGlobal.shader == null || mc.thePlayer == null) return false + + // Optifine Conditions + if (!stopLookingForOptifine && isFastRender == null) { + try { + val config = Class.forName("Config") + try { + isFastRender = config.getMethod("isFastRender") + isShaders = config.getMethod("isShaders") + isAntialiasing = config.getMethod("isAntialiasing") + } catch (ex: Exception) { + logger.log("Couldn't find Optifine methods for entity outlines.") + stopLookingForOptifine = true + } + } catch (ex: Exception) { + logger.log("Couldn't find Optifine for entity outlines.") + stopLookingForOptifine = true + } + } + var isFastRenderValue = false + var isShadersValue = false + var isAntialiasingValue = false + if (isFastRender != null) { + try { + isFastRenderValue = isFastRender!!.invoke(null) as Boolean + isShadersValue = isShaders!!.invoke(null) as Boolean + isAntialiasingValue = isAntialiasing!!.invoke(null) as Boolean + } catch (ex: IllegalAccessException) { + logger.log("An error occurred while calling Optifine methods for entity outlines... $ex") + } catch (ex: InvocationTargetException) { + logger.log("An error occurred while calling Optifine methods for entity outlines... $ex") + } + } + return !isFastRenderValue && !isShadersValue && !isAntialiasingValue + } + + // Add new features that need the entity outline logic here + private fun isEnabled(): Boolean { + if (isMissingMixin) return false + if (SkyHanniMod.feature.fishing.rareSeaCreatureHighlight) return true + if (SkyHanniMod.feature.misc.glowingDroppedItems.enabled) return true + if (SkyHanniMod.feature.dungeon.highlightTeammates) return true + + return false + } + + /** + * Apply the same rendering standards as in [net.minecraft.client.renderer.RenderGlobal.renderEntities] lines 659 to 669 + * + * @param camera the current camera + * @param entity the entity to render + * @param x the camera x position + * @param y the camera y position + * @param z the camera z position + * @return whether the entity should be rendered + */ + private fun shouldRender(camera: ICamera, entity: Entity, vector: LorenzVec): Boolean = + // Only render the view entity when sleeping or in 3rd person mode + if (entity === mc.renderViewEntity && + !(mc.renderViewEntity is EntityLivingBase && (mc.renderViewEntity as EntityLivingBase).isPlayerSleeping || + mc.gameSettings.thirdPersonView != 0) + ) { + false + } else mc.theWorld.isBlockLoaded(BlockPos(entity)) && (mc.renderManager.shouldRender( + entity, + camera, + vector.x, + vector.y, + vector.z + ) || entity.riddenByEntity === mc.thePlayer) + // Only render if renderManager would render and the world is loaded at the entity + + private fun outlineColor(color: Int) { + BUF_FLOAT_4.put(0, (color shr 16 and 255).toFloat() / 255.0f) + BUF_FLOAT_4.put(1, (color shr 8 and 255).toFloat() / 255.0f) + BUF_FLOAT_4.put(2, (color and 255).toFloat() / 255.0f) + BUF_FLOAT_4.put(3, 1f) + GL11.glTexEnv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, BUF_FLOAT_4) + } + + /** + * Function that copies a portion of a framebuffer to another framebuffer. + * + * + * Note that this requires GL3.0 to function properly + * + * + * The major use of this function is to copy the depth-buffer portion of the world framebuffer to the entity outline framebuffer. + * This enables us to perform no-xray outlining on entities, as we can use the world framebuffer's depth testing on the outline frame buffer + * + * @param frameToCopy the framebuffer from which we are copying data + * @param frameToPaste the framebuffer onto which we are copying the data + * @param buffersToCopy the bit mask indicating the sections to copy (see [GL11.GL_DEPTH_BUFFER_BIT], [GL11.GL_COLOR_BUFFER_BIT], [GL11.GL_STENCIL_BUFFER_BIT]) + */ + private fun copyBuffers(frameToCopy: Framebuffer?, frameToPaste: Framebuffer?, buffersToCopy: Int) { + if (OpenGlHelper.isFramebufferEnabled()) { + OpenGlHelper.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, frameToCopy!!.framebufferObject) + OpenGlHelper.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, frameToPaste!!.framebufferObject) + GL30.glBlitFramebuffer( + 0, 0, frameToCopy.framebufferWidth, frameToCopy.framebufferHeight, + 0, 0, frameToPaste.framebufferWidth, frameToPaste.framebufferHeight, + buffersToCopy, GL11.GL_NEAREST + ) + } + } + + fun isCacheEmpty() = isXrayCacheEmpty() && isNoXrayCacheEmpty() + + private fun isXrayCacheEmpty() = entityRenderCache.xrayCache?.isEmpty() ?: true + private fun isNoXrayCacheEmpty() = entityRenderCache.noXrayCache?.isEmpty() ?: true + private fun isNoOutlineCacheEmpty() = entityRenderCache.noOutlineCache?.isEmpty() ?: true + + /** + * Updates the cache at the start of every minecraft tick to improve efficiency. + * Identifies and caches all entities in the world that should be outlined. + * + * + * Calls to [.shouldRender] are frustum based, rely on partialTicks, + * and so can't be updated on a per-tick basis without losing information. + * + * + * This works since entities are only updated once per tick, so the inclusion or exclusion of an entity + * to be outlined can be cached each tick with no loss of data + * + * @param event the client tick event + */ + @SubscribeEvent + fun onTick(event: LorenzTickEvent) { + if (!(event.phase == EventPriority.NORMAL && isEnabled())) return; + + val renderGlobal = try { + mc.renderGlobal as CustomRenderGlobal + } catch (e: NoClassDefFoundError) { + CopyErrorCommand.logError(e, "Unable to enable entity outlines, the required mixin is not loaded") + isMissingMixin = true + return + } + + if (mc.theWorld != null && shouldRenderEntityOutlines()) { + // These events need to be called in this specific order for the xray to have priority over the no xray + // Get all entities to render xray outlines + val xrayOutlineEvent = RenderEntityOutlineEvent(RenderEntityOutlineEvent.Type.XRAY, null) + xrayOutlineEvent.postAndCatch() + // Get all entities to render no xray outlines, using pre-filtered entities (no need to test xray outlined entities) + val noxrayOutlineEvent = RenderEntityOutlineEvent( + RenderEntityOutlineEvent.Type.NO_XRAY, + xrayOutlineEvent.entitiesToChooseFrom + ) + noxrayOutlineEvent.postAndCatch() + // Cache the entities for future use + entityRenderCache.xrayCache = xrayOutlineEvent.entitiesToOutline + entityRenderCache.noXrayCache = noxrayOutlineEvent.entitiesToOutline + entityRenderCache.noOutlineCache = noxrayOutlineEvent.entitiesToChooseFrom + emptyLastTick = if (isCacheEmpty()) { + if (!emptyLastTick) { + renderGlobal.frameBuffer.framebufferClear() + } + true + } else false + } else if (!emptyLastTick) { + entityRenderCache.xrayCache = null + entityRenderCache.noXrayCache = null + entityRenderCache.noOutlineCache = null + if (renderGlobal.frameBuffer != null) renderGlobal.frameBuffer.framebufferClear() + emptyLastTick = true + } + } + + private class CachedInfo( + var xrayCache: HashMap<Entity, Int>?, + var noXrayCache: HashMap<Entity, Int>?, + var noOutlineCache: HashSet<Entity>? + ) +}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt index bedbebc46..25d5f9df4 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt @@ -1,5 +1,6 @@ package at.hannibal2.skyhanni.utils +import at.hannibal2.skyhanni.test.command.CopyErrorCommand import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import at.hannibal2.skyhanni.utils.NEUItems.getItemStack import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.isRecombobulated @@ -152,19 +153,39 @@ object ItemUtils { return nbt.getCompoundTag("SkullOwner").getString("Id") } - fun ItemStack.getItemRarity(): Int { - //todo make into an enum in future - return when (this.getLore().lastOrNull()?.take(4)) { - "§f§l" -> 0 // common - "§a§l" -> 1 // uncommon - "§9§l" -> 2 // rare - "§5§l" -> 3 // epic - "§6§l" -> 4 // legendary - "§d§l" -> 5 // mythic - "§b§l" -> 6 // divine - "§4§l" -> 7 // supreme - "§c§l" -> 8 // special/very special - else -> -1 // unknown + fun ItemStack.getItemRarity() = getItemRarityOrNull() ?: error("item rarity not detected for item '$name'") + + fun ItemStack.getItemRarityOrNull(): LorenzRarity? { + if (isPet(cleanName())) { + return getPetRarity(this) + } + + val lore = getLore() + var lastLine = lore.lastOrNull() ?: return null + if (lastLine == "§eClick to inspect!") { + // Assuming inside ah browser + val index = lore.indexOfFirst { it.startsWith("§7Seller: ") } - 2 + if (index > 0) { + lastLine = lore[index] + } + } + return when (lastLine.take(4)) { + "§f§l" -> LorenzRarity.COMMON + "§a§l" -> LorenzRarity.UNCOMMON + "§9§l" -> LorenzRarity.RARE + "§5§l" -> LorenzRarity.EPIC + "§6§l" -> LorenzRarity.LEGENDARY + "§d§l" -> LorenzRarity.MYTHIC + "§b§l" -> LorenzRarity.DIVINE + "§4§l" -> LorenzRarity.SUPREME + "§c§l" -> LorenzRarity.SPECIAL + else -> { + CopyErrorCommand.logErrorState( + "Could not read rarity for item $name", + "getItemRarityOrNull not found for: ${getInternalName()}, name:'$name', lastLine:'$lastLine'" + ) + return null + } } } @@ -234,10 +255,23 @@ object ItemUtils { return getItemStack().nameWithEnchantment ?: error("Could not find item name for $this") } + // TODO: Replace entirely some day fun getPetRarityOld(petStack: ItemStack?): Int { - val petInternalName = petStack?.getInternalName_old() - if (petInternalName == "NONE" || petInternalName == null) return -1 - val split = petInternalName.split(";") - return split.last().toInt() + val rarity = petStack?.getItemRarityOrNull() ?: return -1 + + return rarity.id + } + + private fun getPetRarity(pet: ItemStack): LorenzRarity? { + val rarityId = pet.getInternalName().asString().split(";").last().toInt() + val rarity = LorenzRarity.getById(rarityId) + val name = pet.name + if (rarity == null) { + CopyErrorCommand.logErrorState( + "Could not read rarity for pet $name", + "getPetRarity not found for: ${pet.getInternalName()}, name:'$name'" + ) + } + return rarity } }
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzRarity.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzRarity.kt new file mode 100644 index 000000000..4af8f7e2f --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzRarity.kt @@ -0,0 +1,46 @@ +package at.hannibal2.skyhanni.utils
+
+import at.hannibal2.skyhanni.test.command.CopyErrorCommand
+
+
+// TODO: replace id with ordinal
+enum class LorenzRarity(val color: LorenzColor, val id: Int) {
+ COMMON(LorenzColor.WHITE, 0),
+ UNCOMMON(LorenzColor.GREEN, 1),
+ RARE(LorenzColor.BLUE, 2),
+ EPIC(LorenzColor.DARK_PURPLE, 3),
+ LEGENDARY(LorenzColor.GOLD, 4),
+ MYTHIC(LorenzColor.LIGHT_PURPLE, 5),
+ DIVINE(LorenzColor.AQUA, 6),
+ SUPREME(LorenzColor.DARK_RED, 7),
+ SPECIAL(LorenzColor.RED, 8),
+ VERY_SPECIAL(LorenzColor.RED, 9),
+ ;
+
+ fun oneBelow(logError: Boolean = true): LorenzRarity? {
+ val rarityBelow = getById(ordinal - 1)
+ if (rarityBelow == null && logError) {
+ CopyErrorCommand.logErrorState(
+ "Problem with item rarity detected.",
+ "Trying to get an item rarity below common"
+ )
+ }
+ return rarityBelow
+ }
+
+ fun oneAbove(logError: Boolean = true): LorenzRarity? {
+ val rarityBelow = getById(ordinal + 1)
+ if (rarityBelow == null && logError) {
+ CopyErrorCommand.logErrorState(
+ "Problem with item rarity detected.",
+ "Trying to get an item rarity above special"
+ )
+ }
+ return rarityBelow
+ }
+
+ companion object {
+ fun getById(id: Int) = entries.firstOrNull { it.ordinal == id }
+ }
+
+}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt index 4e01f5da6..9a4348d39 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt @@ -29,6 +29,7 @@ import java.text.DecimalFormat import java.text.NumberFormat import java.text.SimpleDateFormat import java.util.* +import java.util.Timer import kotlin.properties.ReadWriteProperty import kotlin.reflect.KMutableProperty1 import kotlin.reflect.KProperty @@ -54,6 +55,8 @@ object LorenzUtils { val noTradeMode get() = HypixelData.noTrade + val isStrandedProfile get() = HypixelData.stranded + val isBingoProfile get() = inSkyBlock && (HypixelData.bingo || TestBingo.testBingo) val lastWorldSwitch get() = HypixelData.joinedWorld @@ -427,17 +430,25 @@ object LorenzUtils { fun IslandType.isInIsland() = inIsland(this) - fun <K, N : Number> MutableMap<K, N>.addOrPut(item: K, amount: N): N { - val old = this[item] ?: 0 - val new = when (old) { - is Double -> old + amount.toDouble() - is Float -> old + amount.toFloat() - is Long -> old + amount.toLong() - else -> old.toInt() + amount.toInt() - } - @Suppress("UNCHECKED_CAST") - this[item] = new as N - return new + fun <K> MutableMap<K, Int>.addOrPut(key: K, number: Int): Int { + val currentValue = this[key] ?: 0 + val newValue = currentValue + number + this[key] = newValue + return newValue + } + + fun <K> MutableMap<K, Long>.addOrPut(key: K, number: Long): Long { + val currentValue = this[key] ?: 0L + val newValue = currentValue + number + this[key] = newValue + return newValue + } + + fun <K> MutableMap<K, Double>.addOrPut(key: K, number: Double): Double { + val currentValue = this[key] ?: 0.0 + val newValue = currentValue + number + this[key] = newValue + return newValue } fun <K, N : Number> MutableMap<K, N>.sumAllValues(): Double { @@ -469,7 +480,7 @@ object LorenzUtils { // Taken and modified from Skytils @JvmStatic - fun Any.equalsOneOf(vararg other: Any): Boolean { + fun <T> T.equalsOneOf(vararg other: T): Boolean { for (obj in other) { if (this == obj) return true } diff --git a/src/main/java/at/hannibal2/skyhanni/utils/RenderUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/RenderUtils.kt index 7544e7934..760c4b946 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/RenderUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/RenderUtils.kt @@ -4,6 +4,7 @@ import at.hannibal2.skyhanni.config.core.config.Position import at.hannibal2.skyhanni.data.GuiEditManager import at.hannibal2.skyhanni.data.GuiEditManager.Companion.getAbsX import at.hannibal2.skyhanni.data.GuiEditManager.Companion.getAbsY +import at.hannibal2.skyhanni.events.GuiRenderItemEvent import at.hannibal2.skyhanni.utils.renderables.Renderable import io.github.moulberry.moulconfig.internal.TextRenderUtils import net.minecraft.client.Minecraft @@ -970,4 +971,25 @@ object RenderUtils { ) ) } -} + + fun GuiRenderItemEvent.RenderOverlayEvent.GuiRenderItemPost.drawSlotText(xPos: Int, yPos: Int, text: String, scale: Float) { + val fontRenderer = Minecraft.getMinecraft().fontRendererObj + + GlStateManager.disableLighting() + GlStateManager.disableDepth() + GlStateManager.disableBlend() + + GlStateManager.pushMatrix() + GlStateManager.translate((xPos - fontRenderer.getStringWidth(text)).toFloat(), yPos.toFloat(), 0f) + GlStateManager.scale(scale, scale, 1f) + fontRenderer.drawStringWithShadow(text, 0f, 0f, 16777215) + + val reverseScale = 1 / scale + + GlStateManager.scale(reverseScale, reverseScale, 1f) + GlStateManager.popMatrix() + + GlStateManager.enableLighting() + GlStateManager.enableDepth() + } +}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/utils/SoundUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/SoundUtils.kt index 7fd9d228f..64f10d514 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/SoundUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/SoundUtils.kt @@ -9,6 +9,7 @@ import net.minecraft.util.ResourceLocation object SoundUtils { private val beepSound by lazy { createSound("random.orb", 1f) } private val clickSound by lazy { createSound("gui.button.press", 1f) } + val centuryActiveTimerAlert by lazy { createSound("skyhanni:centurytimer.active", 1f) } fun ISound.playSound() { Minecraft.getMinecraft().addScheduledTask { diff --git a/src/main/java/at/hannibal2/skyhanni/utils/TimeMark.kt b/src/main/java/at/hannibal2/skyhanni/utils/TimeMark.kt new file mode 100644 index 000000000..83285550d --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/TimeMark.kt @@ -0,0 +1,16 @@ +package at.hannibal2.skyhanni.utils + +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds + + +data class TimeMark(val long: Long) { + + fun hasNeverHappened() = long == 0L + fun passedTime() = if (long == 0L) Duration.Companion.INFINITE else (System.currentTimeMillis() - long).milliseconds + + companion object { + fun never() = TimeMark(0) + fun now() = TimeMark(System.currentTimeMillis()) + } +}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/utils/jsonobjects/SackListJson.java b/src/main/java/at/hannibal2/skyhanni/utils/jsonobjects/SackListJson.java new file mode 100644 index 000000000..c554ddb24 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/jsonobjects/SackListJson.java @@ -0,0 +1,11 @@ +package at.hannibal2.skyhanni.utils.jsonobjects; + +import com.google.gson.annotations.Expose; + +import java.util.List; + +public class SackListJson { + + @Expose + public List<String> sackList; +} diff --git a/src/main/resources/assets/skyhanni/sounds.json b/src/main/resources/assets/skyhanni/sounds.json new file mode 100644 index 000000000..7c017cb0d --- /dev/null +++ b/src/main/resources/assets/skyhanni/sounds.json @@ -0,0 +1,11 @@ +{ + "centurytimer.active": { + "category": "neutral", + "sounds": [ + { + "name": "itstimetopunchwheat", + "stream": false + } + ] + } +}
\ No newline at end of file diff --git a/src/main/resources/assets/skyhanni/sounds/itstimetopunchwheat.ogg b/src/main/resources/assets/skyhanni/sounds/itstimetopunchwheat.ogg Binary files differnew file mode 100644 index 000000000..b7bd98137 --- /dev/null +++ b/src/main/resources/assets/skyhanni/sounds/itstimetopunchwheat.ogg diff --git a/src/main/resources/mixins.skyhanni.json b/src/main/resources/mixins.skyhanni.json index ed342797f..3982dd3c8 100644 --- a/src/main/resources/mixins.skyhanni.json +++ b/src/main/resources/mixins.skyhanni.json @@ -12,6 +12,8 @@ "MixinItemStack", "MixinNetHandlerPlayClient", "MixinNetworkManager", + "MixinRenderGlobal", + "MixinRendererLivingEntity", "MixinRenderItem", "MixinRenderManager", "gui.AccessorGuiContainer", @@ -20,6 +22,7 @@ ], "client": [ "AccessorKeyBinding", + "CustomRenderGlobal", "MixinEntityRenderer", "MixinGuiChat", "MixinGuiIngame", |