aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt33
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt48
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/SackData.java26
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/Storage.java12
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt16
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/BazaarConfig.java6
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/DamageIndicatorConfig.java4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/DevConfig.java3
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/DungeonConfig.java7
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/FishingConfig.java16
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/GardenConfig.java9
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/InventoryConfig.java14
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java138
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/SlayerConfig.java2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt18
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/ItemTipHelper.kt14
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/OwnInventoryData.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/ProfileStorageData.kt28
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/SackAPI.kt289
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/SlayerAPI.kt6
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/BazaarOpenedProductEvent.kt6
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/OwnInventoryItemUpdateEvent.kt (renamed from src/main/java/at/hannibal2/skyhanni/events/OwnInventorItemUpdateEvent.kt)2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/RenderEntityOutlineEvent.kt118
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/SackChangeEvent.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/bazaar/BazaarApi.kt18
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/bazaar/BazaarBestSellMethod.kt54
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/bazaar/BazaarOpenPriceWebsite.kt57
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/chat/Translator.kt8
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonTeammateOutlines.kt39
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/event/anniversary/ActivePlayerTimer.kt76
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/fishing/FishingTimer.kt9
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/fishing/SeaCreature.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/fishing/SeaCreatureFeatures.kt84
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/TrophyFishMessages.kt16
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/CropType.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/FarmingFortuneDisplay.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterDisplay.kt5
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterOverlay.kt12
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobFarmingContestsInventory.kt41
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/farming/FarmingWeightDisplay.kt35
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropMilestoneDisplay.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/farming/WildStrawberryDyeNotification.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/fortuneguide/FFGuideGUI.kt83
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/fortuneguide/FortuneUpgrades.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorDropStatistics.kt33
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorFeatures.kt72
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorReward.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/inventory/HideNotClickableItems.kt3
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/inventory/SackDisplay.kt295
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/itemabilities/abilitycooldown/ItemAbility.kt13
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/itemabilities/abilitycooldown/ItemAbilityCooldown.kt14
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/minion/MinionFeatures.kt50
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/EnderNodeTracker.kt171
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/PetCandyUsedDisplay.kt25
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/PetExpTooltip.kt19
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/PocketSackInASackDisplay.kt22
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/ghostcounter/GhostCounter.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedItemValue.kt155
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/items/GlowingDroppedItems.kt68
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/FeatureToggleProcessor.kt18
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/powdertracker/PowderChestReward.kt148
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/powdertracker/PowderTracker.kt356
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/tabcomplete/GetFromSacksTabComplete.kt45
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/tabcomplete/TabComplete.kt1
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/mobs/MobHighlight.kt5
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/nether/reputationhelper/dailyquest/DailyQuestHelper.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/rift/area/stillgorechateau/RiftBloodEffigies.kt6
-rw-r--r--src/main/java/at/hannibal2/skyhanni/mixins/hooks/RenderGlobalHook.kt20
-rw-r--r--src/main/java/at/hannibal2/skyhanni/mixins/hooks/RendererLivingEntityHook.kt20
-rw-r--r--src/main/java/at/hannibal2/skyhanni/mixins/transformers/CustomRenderGlobal.java17
-rw-r--r--src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinRenderGlobal.java42
-rw-r--r--src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinRendererLivingEntity.java23
-rw-r--r--src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt (renamed from src/main/java/at/hannibal2/skyhanni/test/SkyHanniTestCommand.kt)28
-rw-r--r--src/main/java/at/hannibal2/skyhanni/test/TestCopyBestiaryValues.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/test/TestCopyRngMeterValues.kt9
-rw-r--r--src/main/java/at/hannibal2/skyhanni/test/command/CopyErrorCommand.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/test/command/CopyItemCommand.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/EntityOutlineRenderer.kt408
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt68
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/LorenzRarity.kt46
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt35
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/RenderUtils.kt24
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/SoundUtils.kt1
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/TimeMark.kt16
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/jsonobjects/SackListJson.java11
-rw-r--r--src/main/resources/assets/skyhanni/sounds.json11
-rw-r--r--src/main/resources/assets/skyhanni/sounds/itstimetopunchwheat.oggbin0 -> 18944 bytes
-rw-r--r--src/main/resources/mixins.skyhanni.json3
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
new file mode 100644
index 000000000..b7bd98137
--- /dev/null
+++ b/src/main/resources/assets/skyhanni/sounds/itstimetopunchwheat.ogg
Binary files differ
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",