diff options
author | Empa <42304516+ItsEmpa@users.noreply.github.com> | 2024-06-15 22:12:24 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-15 22:12:24 +0200 |
commit | bfe43b7ebffab551740d32cf919db27504d27ffb (patch) | |
tree | 7fb570635c88d98f122d2752be78f135868a9843 /src | |
parent | 8c91a9b8bf63059e1a8a099ccbdadea8a3676948 (diff) | |
download | skyhanni-bfe43b7ebffab551740d32cf919db27504d27ffb.tar.gz skyhanni-bfe43b7ebffab551740d32cf919db27504d27ffb.tar.bz2 skyhanni-bfe43b7ebffab551740d32cf919db27504d27ffb.zip |
Feature: Mineshaft Pity Display + OreMinedEvent (#1655)
Co-authored-by: J10a1n15 <45315647+j10a1n15@users.noreply.github.com>
Co-authored-by: Empa <42304516+ItsEmpa@users.noreply.github.com>
Co-authored-by: Thunderblade73 <gaidermarkus@gmail.com>
Co-authored-by: Thunderblade73 <85900443+Thunderblade73@users.noreply.github.com>
Co-authored-by: Cal <cwolfson58@gmail.com>
Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com>
Diffstat (limited to 'src')
20 files changed, 1318 insertions, 196 deletions
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 de4bc2585..22f7e2d28 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt @@ -52,6 +52,7 @@ import at.hannibal2.skyhanni.features.garden.pests.PestFinder import at.hannibal2.skyhanni.features.garden.pests.PestProfitTracker import at.hannibal2.skyhanni.features.garden.visitor.GardenVisitorDropStatistics import at.hannibal2.skyhanni.features.mining.KingTalismanHelper +import at.hannibal2.skyhanni.features.mining.MineshaftPityDisplay import at.hannibal2.skyhanni.features.mining.powdertracker.PowderTracker import at.hannibal2.skyhanni.features.minion.MinionFeatures import at.hannibal2.skyhanni.features.misc.CollectionTracker @@ -547,6 +548,10 @@ object Commands { "shtoggleegglocationdebug", "Shows Hoppity egg locations with their internal API names and status.", ) { HoppityEggLocations.toggleDebug() } + registerCommand( + "shresetmineshaftpitystats", + "Resets the mineshaft pity display stats" + ) { MineshaftPityDisplay.fullResetCounter() } } private fun internalCommands() { diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/dev/DebugConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/dev/DebugConfig.java index db2aa868b..9612ac3b7 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/dev/DebugConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/dev/DebugConfig.java @@ -127,6 +127,16 @@ public class DebugConfig { public boolean currentAreaDebug = true; @Expose + @ConfigOption(name = "Oreblock Name", desc = "Show the OreBlock you are currently looking at.") + @ConfigEditorBoolean + public boolean raytracedOreblock = true; + + @Expose + @ConfigOption(name = "Ore Event Messages", desc = "Shows debug messages every time the Ore Event happens.") + @ConfigEditorBoolean + public boolean oreEventMessages = false; + + @Expose @ConfigOption(name = "Assume Mayor", desc = "Select a mayor to assume.") @ConfigEditorDropdown public Property<Mayor> assumeMayor = Property.of(Mayor.DISABLED); diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/dev/DevConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/dev/DevConfig.java index 041134e41..a42da18c1 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/dev/DevConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/dev/DevConfig.java @@ -8,6 +8,7 @@ import io.github.notenoughupdates.moulconfig.annotations.Category; import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean; import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorKeybind; import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider; +import io.github.notenoughupdates.moulconfig.annotations.ConfigLink; import io.github.notenoughupdates.moulconfig.annotations.ConfigOption; import org.lwjgl.input.Keyboard; @@ -61,6 +62,10 @@ public class DevConfig { public Position debugItemPos = new Position(90, 70); @Expose + @ConfigLink(owner = DebugConfig.class, field = "raytracedOreblock") + public Position debugOrePos = new Position(1, 200, false, true); + + @Expose @ConfigOption( name = "Fancy Contributors", desc = "Marks §cSkyHanni's contributors §7fancy in the tab list. " + diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/mining/MineshaftPityDisplayConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/mining/MineshaftPityDisplayConfig.java new file mode 100644 index 000000000..ab73f060d --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/config/features/mining/MineshaftPityDisplayConfig.java @@ -0,0 +1,43 @@ +package at.hannibal2.skyhanni.config.features.mining; + +import at.hannibal2.skyhanni.config.FeatureToggle; +import at.hannibal2.skyhanni.config.core.config.Position; +import at.hannibal2.skyhanni.features.mining.MineshaftPityDisplay.MineshaftPityLines; +import com.google.gson.annotations.Expose; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDraggableList; +import io.github.notenoughupdates.moulconfig.annotations.ConfigLink; +import io.github.notenoughupdates.moulconfig.annotations.ConfigOption; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class MineshaftPityDisplayConfig { + @Expose + @ConfigOption(name = "Enable Display", desc = "Enable the Mineshaft Pity Display.") + @ConfigEditorBoolean + @FeatureToggle + public boolean enabled = true; + + @Expose + @ConfigOption(name = "Stats List", desc = "Drag text to change the appearance of the display.") + @ConfigEditorDraggableList + public List<MineshaftPityLines> mineshaftPityLines = new ArrayList<>(Arrays.asList( + MineshaftPityLines.TITLE, + MineshaftPityLines.COUNTER, + MineshaftPityLines.CHANCE, + MineshaftPityLines.NEEDED_TO_PITY, + MineshaftPityLines.TIME_SINCE_MINESHAFT + )); + + @Expose + @ConfigOption(name = "Modify Spawn Message", desc = "Modify the Mineshaft spawn message with more stats.") + @ConfigEditorBoolean + @FeatureToggle + public boolean modifyChatMessage = true; + + @Expose + @ConfigLink(owner = MineshaftPityDisplayConfig.class, field = "enabled") + public Position position = new Position(16, 192, false, true); +} diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java index 870c31929..adbfb32b7 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java @@ -69,6 +69,11 @@ public class MiningConfig { public MineshaftConfig mineshaft = new MineshaftConfig(); @Expose + @ConfigOption(name = "Mineshaft Pity Display", desc = "") + @Accordion + public MineshaftPityDisplayConfig mineshaftPityDisplay = new MineshaftPityDisplayConfig(); + + @Expose @ConfigOption(name = "Highlight Commission Mobs", desc = "Highlight Mobs that are part of active commissions.") @ConfigEditorBoolean @FeatureToggle diff --git a/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java b/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java index fe0cd58fb..2887d0230 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java +++ b/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java @@ -29,6 +29,7 @@ import at.hannibal2.skyhanni.features.garden.pests.PestProfitTracker; import at.hannibal2.skyhanni.features.garden.pests.VinylType; import at.hannibal2.skyhanni.features.garden.visitor.VisitorReward; import at.hannibal2.skyhanni.features.inventory.wardrobe.WardrobeAPI; +import at.hannibal2.skyhanni.features.mining.MineshaftPityDisplay; import at.hannibal2.skyhanni.features.mining.fossilexcavator.ExcavatorProfitTracker; import at.hannibal2.skyhanni.features.mining.powdertracker.PowderTracker; import at.hannibal2.skyhanni.features.misc.trevor.TrevorTracker; @@ -540,6 +541,21 @@ public class ProfileSpecificStorage { @Expose public int availableTokens; + + @Expose + public MineshaftStorage mineshaft = new MineshaftStorage(); + + public static class MineshaftStorage { + + @Expose + public long mineshaftTotalBlocks = 0L; + + @Expose + public int mineshaftTotalCount = 0; + + @Expose + public List<MineshaftPityDisplay.PityData> blocksBroken = new ArrayList<>(); + } } @Expose diff --git a/src/main/java/at/hannibal2/skyhanni/data/HotmData.kt b/src/main/java/at/hannibal2/skyhanni/data/HotmData.kt index 0b51dfa8d..d232e49bb 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/HotmData.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/HotmData.kt @@ -16,6 +16,7 @@ import at.hannibal2.skyhanni.features.gui.customscoreboard.ScoreboardPattern import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.test.command.ErrorManager import at.hannibal2.skyhanni.utils.ChatUtils +import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut import at.hannibal2.skyhanni.utils.ConditionalUtils.transformIf import at.hannibal2.skyhanni.utils.DelayedRun import at.hannibal2.skyhanni.utils.InventoryUtils @@ -36,11 +37,28 @@ import kotlin.math.ceil import kotlin.math.floor import kotlin.math.pow + +private fun calculatePeakOfTheMountainLoot(level: Int): Map<HotmReward, Double> = buildMap { + for (i in 1..level) { + when (i) { + 1, 5, 7 -> addOrPut(HotmReward.EXTRA_TOKENS, 1.0) + 2 -> addOrPut(HotmReward.EXTRA_FORGE_SLOTS, 1.0) + 3 -> addOrPut(HotmReward.EXTRA_COMMISSION_SLOTS, 1.0) + 4 -> addOrPut(HotmReward.MORE_BASE_MITHRIL_POWER, 1.0) + 6 -> addOrPut(HotmReward.MORE_BASE_GEMSTONE_POWER, 2.0) + 8 -> addOrPut(HotmReward.MORE_BASE_GLACITE_POWER, 3.0) + 9 -> addOrPut(HotmReward.MINESHAFT_CHANCE, 10.0) + 10 -> addOrPut(HotmReward.EXTRA_TOKENS, 2.0) + } + } +} + + enum class HotmData( val guiName: String, val maxLevel: Int, - val costFun: ((Int) -> (Double?)), - val rewardFun: ((Int) -> (Map<HotmReward, Double>)), + val costFun: (Int) -> (Double?), + val rewardFun: (Int) -> (Map<HotmReward, Double>), ) { MINING_SPEED( @@ -246,7 +264,10 @@ enum class HotmData( }, ), - PEAK_OF_THE_MOUNTAIN("Peak of the Mountain", 10, { null }, { emptyMap() }), + PEAK_OF_THE_MOUNTAIN( + "Peak of the Mountain", 10, { null }, + { level -> calculatePeakOfTheMountainLoot(level) }, + ), // Mining V3 DAILY_GRIND( @@ -291,7 +312,7 @@ enum class HotmData( "Surveyor", 20, { currentLevel -> (currentLevel + 1.0).pow(4) }, - { level -> mapOf(HotmReward.UNKNOWN to 0.75 * level) }, + { level -> mapOf(HotmReward.MINESHAFT_CHANCE to 0.75 * level) }, ), EAGER_ADVENTURER( "Eager Adventurer", @@ -343,7 +364,7 @@ enum class HotmData( get() = storage?.perks?.get(this.name)?.level ?: 0 var activeLevel: Int - get() = storage?.perks?.get(this.name)?.level?.plus(blueEgg()) ?: 0 + get() = if (enabled) storage?.perks?.get(this.name)?.level?.plus(blueEgg()) ?: 0 else 0 private set(value) { storage?.perks?.computeIfAbsent(this.name) { HotmTree.HotmPerk() }?.level = value.minus(blueEgg()) } @@ -370,7 +391,7 @@ enum class HotmData( fun getLevelUpCost() = costFun(rawLevel) - fun getReward() = rewardFun(activeLevel) + fun getReward() = if (enabled) rewardFun(activeLevel) else emptyMap() fun calculateTotalCost(desiredLevel: Int) = (1 until desiredLevel).sumOf { level -> costFun(level) ?: 0.0 }.toInt() @@ -382,8 +403,7 @@ enum class HotmData( val storage get() = ProfileStorageData.profileSpecific?.mining?.hotmTree - val abilities = - listOf(PICKOBULUS, MINING_SPEED_BOOST, VEIN_SEEKER, MANIAC_MINER, HAZARDOUS_MINER, GEMSTONE_INFUSION) + val abilities = listOf(PICKOBULUS, MINING_SPEED_BOOST, VEIN_SEEKER, MANIAC_MINER, HAZARDOUS_MINER, GEMSTONE_INFUSION) private val inventoryPattern by patternGroup.pattern( "inventory", @@ -407,7 +427,7 @@ enum class HotmData( private val disabledPattern by patternGroup.pattern( "perk.disabled", "§c§lDISABLED|§7§eClick to select!", - ) // unused for now since the assumption is when enabled isn't found it is disabled, + ) // unused for now since the assumption is when enabled isn't found it is disabled, // but the value might be useful in the future or for debugging val perkCostPattern by patternGroup.pattern( @@ -478,6 +498,21 @@ enum class HotmData( HotmAPI.MayhemPerk.entries.forEach { it.chatPattern } + (0..PEAK_OF_THE_MOUNTAIN.maxLevel).forEach { level -> + val map = mutableMapOf<HotmReward, Double>() + if (level >= 1) map.addOrPut(HotmReward.EXTRA_TOKENS, 1.0) + if (level >= 2) map.addOrPut(HotmReward.EXTRA_FORGE_SLOTS, 1.0) + if (level >= 3) map.addOrPut(HotmReward.EXTRA_COMMISSION_SLOTS, 1.0) + if (level >= 4) map.addOrPut(HotmReward.MORE_BASE_MITHRIL_POWER, 1.0) + if (level >= 5) map.addOrPut(HotmReward.EXTRA_TOKENS, 1.0) + if (level >= 6) map.addOrPut(HotmReward.MORE_BASE_GEMSTONE_POWER, 2.0) + if (level >= 7) map.addOrPut(HotmReward.EXTRA_TOKENS, 1.0) + if (level >= 8) map.addOrPut(HotmReward.MORE_BASE_GLACITE_POWER, 3.0) + if (level >= 9) map.addOrPut(HotmReward.MINESHAFT_CHANCE, 10.0) + if (level >= 10) map.addOrPut(HotmReward.EXTRA_TOKENS, 2.0) + + peakOfTheMountainPerks[level] = map + } } fun getPerkByNameOrNull(name: String): HotmData? = entries.find { it.guiName == name } @@ -724,6 +759,8 @@ enum class HotmData( } } +private val peakOfTheMountainPerks = mutableMapOf<Int, Map<HotmReward, Double>>() + private val patternGroup = RepoPattern.group("mining.hotm") enum class HotmReward { @@ -735,6 +772,7 @@ enum class HotmReward { DAILY_POWDER, MORE_BASE_MITHRIL_POWER, MORE_BASE_GEMSTONE_POWER, + MORE_BASE_GLACITE_POWER, MORE_MITHRIL_POWER, MORE_GEMSTONE_POWER, COMBAT_STAT_BOOST, @@ -748,6 +786,10 @@ enum class HotmReward { ABILITY_RADIUS, ABILITY_COOLDOWN, FOSSIL_DUST, + MINESHAFT_CHANCE, + EXTRA_TOKENS, + EXTRA_FORGE_SLOTS, + EXTRA_COMMISSION_SLOTS, UNKNOWN, COLD_RESISTANCE } diff --git a/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt index edb79b894..8fe946833 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt @@ -1,20 +1,34 @@ package at.hannibal2.skyhanni.data +import at.hannibal2.skyhanni.events.BlockClickEvent import at.hannibal2.skyhanni.events.ColdUpdateEvent +import at.hannibal2.skyhanni.events.DebugDataCollectEvent import at.hannibal2.skyhanni.events.LorenzChatEvent +import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent +import at.hannibal2.skyhanni.events.PlaySoundEvent import at.hannibal2.skyhanni.events.ScoreboardChangeEvent +import at.hannibal2.skyhanni.events.ServerBlockChangeEvent +import at.hannibal2.skyhanni.events.mining.OreMinedEvent import at.hannibal2.skyhanni.features.gui.customscoreboard.ScoreboardPattern +import at.hannibal2.skyhanni.features.mining.OreBlock import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.CollectionUtils.countBy +import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.LorenzUtils.inAnyIsland import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland +import at.hannibal2.skyhanni.utils.LorenzVec import at.hannibal2.skyhanni.utils.RegexUtils.matchFirst import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.RegexUtils.matches import at.hannibal2.skyhanni.utils.SimpleTimeMark +import at.hannibal2.skyhanni.utils.TimeUtils.format import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern +import net.minecraft.init.Blocks import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import kotlin.math.absoluteValue +import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds @SkyHanniModule @@ -22,24 +36,72 @@ object MiningAPI { private val group = RepoPattern.group("data.miningapi") private val glaciteAreaPattern by group.pattern("area.glacite", "Glacite Tunnels|Glacite Lake") - val coldReset by group.pattern( + private val dwarvenBaseCampPattern by group.pattern("area.basecamp", "Dwarven Base Camp") + private val coldReset by group.pattern( "cold.reset", "§6The warmth of the campfire reduced your §r§b❄ Cold §r§6to §r§a0§r§6!|§c ☠ §r§7You froze to death§r§7." ) - val coldResetDeath by group.pattern( + private val coldResetDeath by group.pattern( "cold.deathreset", "§c ☠ §r§7§r§.(?<name>.+)§r§7 (?<reason>.+)" ) - private var cold = 0 + data class MinedBlock(val ore: OreBlock, var position: LorenzVec, var confirmed: Boolean, val time: SimpleTimeMark) + + private var lastInitSound = SimpleTimeMark.farPast() + + private var waitingForInitBlock = false + private var waitingForInitBlockPos: LorenzVec? = null + private var waitingForInitSound = true + + private var waitingForEffMinerSound = false + private var waitingForEffMinerBlock = false + + var inGlacite = false + var inDwarvenMines = false + var inCrystalHollows = false + var inCrimsonIsle = false + var inEnd = false + var inSpidersDen = false + + var currentAreaOreBlocks = setOf<OreBlock>() + + private var lastSkyblockArea: String? = null + + private var recentClickedBlocks = mutableListOf<MinedBlock>() + private var surroundingMinedBlocks = mutableListOf<MinedBlock>() + private val allowedSoundNames = listOf("dig.glass", "dig.stone", "dig.gravel", "dig.cloth") + + var cold: Int = 0 + private set + var lastColdUpdate = SimpleTimeMark.farPast() var lastColdReset = SimpleTimeMark.farPast() - fun inGlaciteArea() = glaciteAreaPattern.matches(HypixelData.skyBlockArea) || IslandType.MINESHAFT.isInIsland() + fun inGlaciteArea() = inGlacialTunnels() || IslandType.MINESHAFT.isInIsland() + + fun inDwarvenBaseCamp() = + IslandType.DWARVEN_MINES.isInIsland() && dwarvenBaseCampPattern.matches(LorenzUtils.skyBlockArea) + + fun inRegularDwarven() = IslandType.DWARVEN_MINES.isInIsland() && !inGlacialTunnels() + + fun inCrystalHollows() = IslandType.CRYSTAL_HOLLOWS.isInIsland() - fun inColdIsland() = IslandType.DWARVEN_MINES.isInIsland() || IslandType.MINESHAFT.isInIsland() + fun inMineshaft() = IslandType.MINESHAFT.isInIsland() + + fun inGlacialTunnels() = + IslandType.DWARVEN_MINES.isInIsland() && glaciteAreaPattern.matches(LorenzUtils.skyBlockArea) + + fun inCustomMiningIsland() = inAnyIsland( + IslandType.DWARVEN_MINES, + IslandType.MINESHAFT, + IslandType.CRYSTAL_HOLLOWS, + IslandType.THE_END, + IslandType.CRIMSON_ISLE, + IslandType.SPIDER_DEN, + ) - fun getCold() = cold + fun inColdIsland() = inAnyIsland(IslandType.DWARVEN_MINES, IslandType.MINESHAFT) @SubscribeEvent fun onScoreboardChange(event: ScoreboardChangeEvent) { @@ -53,6 +115,16 @@ object MiningAPI { } @SubscribeEvent + fun onBlockClick(event: BlockClickEvent) { + if (!inCustomMiningIsland()) return + if (event.clickType != ClickType.LEFT_CLICK) return + val position = event.position + val blockState = event.getBlockState + val ore = OreBlock.getByStateOrNull(blockState) ?: return + recentClickedBlocks.add(MinedBlock(ore, position, false, SimpleTimeMark.now())) + } + + @SubscribeEvent fun onChat(event: LorenzChatEvent) { if (!inColdIsland()) return if (coldReset.matches(event.message)) { @@ -68,9 +140,125 @@ object MiningAPI { } @SubscribeEvent + fun onPlaySound(event: PlaySoundEvent) { + if (!inCustomMiningIsland()) return + if (waitingForInitSound) { + if (event.soundName in allowedSoundNames && event.pitch == 0.7936508f) { + val pos = event.location.roundLocationToBlock() + if (recentClickedBlocks.none { it.position == pos }) return + waitingForInitSound = false + waitingForInitBlock = true + waitingForInitBlockPos = event.location.roundLocationToBlock() + lastInitSound = SimpleTimeMark.now() + } + return + } + if (waitingForEffMinerSound) { + if (surroundingMinedBlocks.isEmpty()) return + if (event.soundName in allowedSoundNames || event.soundName == "random.orb") { + if (surroundingMinedBlocks.last().confirmed) return + waitingForEffMinerSound = false + surroundingMinedBlocks.last().confirmed = true + waitingForEffMinerBlock = true + } + } + } + + @SubscribeEvent + fun onBlockChange(event: ServerBlockChangeEvent) { + if (!inCustomMiningIsland()) return + if (event.newState.block != Blocks.air) return + if (event.oldState.block == Blocks.air) return + if (event.location.distanceToPlayer() > 7) return + + if (lastInitSound.passedSince() > 100.milliseconds) return + + val ore = OreBlock.getByStateOrNull(event.oldState) ?: return + + if (waitingForInitBlock) { + if (waitingForInitBlockPos != event.location) return + waitingForInitBlock = false + surroundingMinedBlocks.add(MinedBlock(ore, event.location, true, SimpleTimeMark.now())) + waitingForEffMinerBlock = true + return + } + if (waitingForEffMinerBlock) { + if (surroundingMinedBlocks.any { it.position == event.location }) return + waitingForEffMinerBlock = false + surroundingMinedBlocks.add(MinedBlock(ore, event.location, false, SimpleTimeMark.now())) + waitingForEffMinerSound = true + return + } + } + + @SubscribeEvent + fun onTick(event: LorenzTickEvent) { + if (!inCustomMiningIsland()) return + + if (LorenzUtils.lastWorldSwitch.passedSince() < 4.seconds) return + updateLocation() + + if (currentAreaOreBlocks.isEmpty()) return + + // if somehow you take more than 20 seconds to mine a single block, congrats + recentClickedBlocks.removeIf { it.time.passedSince() > 20.seconds } + surroundingMinedBlocks.removeIf { it.time.passedSince() > 20.seconds } + + if (surroundingMinedBlocks.isEmpty()) return + if (lastInitSound.passedSince() < 200.milliseconds) return + + resetOreEvent() + + val originalBlock = surroundingMinedBlocks.firstOrNull { it.confirmed } ?: run { + surroundingMinedBlocks = mutableListOf() + recentClickedBlocks = mutableListOf() + return + } + + val extraBlocks = surroundingMinedBlocks.filter { it.confirmed }.countBy { it.ore } + + OreMinedEvent(originalBlock.ore, extraBlocks).post() + + surroundingMinedBlocks = mutableListOf() + recentClickedBlocks.removeIf { it.time.passedSince() >= originalBlock.time.passedSince() } + } + + @SubscribeEvent fun onWorldChange(event: LorenzWorldChangeEvent) { if (cold != 0) updateCold(0) lastColdReset = SimpleTimeMark.now() + recentClickedBlocks = mutableListOf() + surroundingMinedBlocks = mutableListOf() + currentAreaOreBlocks = setOf() + resetOreEvent() + } + + private fun resetOreEvent() { + lastInitSound = SimpleTimeMark.farPast() + waitingForInitSound = true + waitingForInitBlock = false + waitingForInitBlockPos = null + waitingForEffMinerSound = false + waitingForEffMinerBlock = false + } + + @SubscribeEvent + fun onDebugDataCollect(event: DebugDataCollectEvent) { + event.title("Mining API") + if (!inCustomMiningIsland()) { + event.addIrrelevant("not in a mining island") + return + } + + event.addData { + add("lastInitSound: ${lastInitSound.passedSince().format()}") + add("waitingForInitSound: $waitingForInitSound") + add("waitingForInitBlock: $waitingForInitBlock") + add("waitingForInitBlockPos: $waitingForInitBlockPos") + add("waitingForEffMinerSound: $waitingForEffMinerSound") + add("waitingForEffMinerBlock: $waitingForEffMinerBlock") + add("recentClickedBlocks: ${recentClickedBlocks.joinToString { it.position.toCleanString() }}") + } } private fun updateCold(newCold: Int) { @@ -81,4 +269,18 @@ object MiningAPI { cold = newCold } + private fun updateLocation() { + val currentArea = LorenzUtils.skyBlockArea + if (currentArea == lastSkyblockArea) return + lastSkyblockArea = currentArea + + inGlacite = inGlaciteArea() + inDwarvenMines = inRegularDwarven() + inCrystalHollows = inCrystalHollows() + inCrimsonIsle = IslandType.CRIMSON_ISLE.isInIsland() + inEnd = IslandType.THE_END.isInIsland() + inSpidersDen = IslandType.SPIDER_DEN.isInIsland() + + currentAreaOreBlocks = OreBlock.entries.filter { it.checkArea() }.toSet() + } } diff --git a/src/main/java/at/hannibal2/skyhanni/events/ServerBlockChangeEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/ServerBlockChangeEvent.kt index 43941aa36..07aa7630e 100644 --- a/src/main/java/at/hannibal2/skyhanni/events/ServerBlockChangeEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/events/ServerBlockChangeEvent.kt @@ -1,6 +1,7 @@ package at.hannibal2.skyhanni.events import at.hannibal2.skyhanni.utils.BlockUtils.getBlockAt +import at.hannibal2.skyhanni.utils.BlockUtils.getBlockStateAt import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.toLorenzVec import net.minecraft.block.state.IBlockState @@ -10,7 +11,9 @@ class ServerBlockChangeEvent(private val blockPos: BlockPos, private val blockSt val location by lazy { blockPos.toLorenzVec() } val old by lazy { location.getBlockAt().toString().getName() } + val oldState by lazy { location.getBlockStateAt() } val new by lazy { blockState.block.toString().getName() } + val newState by lazy { blockState } companion object { diff --git a/src/main/java/at/hannibal2/skyhanni/events/mining/OreMinedEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/mining/OreMinedEvent.kt new file mode 100644 index 000000000..a97402a41 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/events/mining/OreMinedEvent.kt @@ -0,0 +1,6 @@ +package at.hannibal2.skyhanni.events.mining + +import at.hannibal2.skyhanni.api.event.SkyHanniEvent +import at.hannibal2.skyhanni.features.mining.OreBlock + +class OreMinedEvent(val originalOre: OreBlock, val extraBlocks: Map<OreBlock, Int>) : SkyHanniEvent() diff --git a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardElements.kt b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardElements.kt index aae1e7086..b73df6f14 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardElements.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardElements.kt @@ -10,7 +10,7 @@ import at.hannibal2.skyhanni.data.HypixelData.getPlayersOnCurrentServer import at.hannibal2.skyhanni.data.IslandType import at.hannibal2.skyhanni.data.MaxwellAPI import at.hannibal2.skyhanni.data.MayorAPI -import at.hannibal2.skyhanni.data.MiningAPI.getCold +import at.hannibal2.skyhanni.data.MiningAPI import at.hannibal2.skyhanni.data.PartyAPI import at.hannibal2.skyhanni.data.PurseAPI import at.hannibal2.skyhanni.data.QuiverAPI @@ -475,7 +475,7 @@ private fun getHeatShowWhen() = inAnyIsland(IslandType.CRYSTAL_HOLLOWS) && ScoreboardData.sidebarLinesFormatted.any { ScoreboardPattern.heatPattern.matches(it) } private fun getColdDisplayPair(): List<ScoreboardElementType> { - val cold = -getCold() + val cold = -MiningAPI.cold return listOf( when { diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/MineshaftPityDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/MineshaftPityDisplay.kt new file mode 100644 index 000000000..731a8ca4a --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/MineshaftPityDisplay.kt @@ -0,0 +1,340 @@ +package at.hannibal2.skyhanni.features.mining + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.api.event.HandleEvent +import at.hannibal2.skyhanni.data.HotmData +import at.hannibal2.skyhanni.data.HotmReward +import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.data.MiningAPI +import at.hannibal2.skyhanni.data.ProfileStorageData +import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.events.IslandChangeEvent +import at.hannibal2.skyhanni.events.LorenzChatEvent +import at.hannibal2.skyhanni.events.SecondPassedEvent +import at.hannibal2.skyhanni.events.mining.OreMinedEvent +import at.hannibal2.skyhanni.features.mining.MineshaftPityDisplay.PityBlock.Companion.getPity +import at.hannibal2.skyhanni.features.mining.MineshaftPityDisplay.PityBlock.Companion.getPityBlock +import at.hannibal2.skyhanni.features.mining.OreType.Companion.getOreType +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut +import at.hannibal2.skyhanni.utils.LorenzUtils.round +import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.RegexUtils.matches +import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables +import at.hannibal2.skyhanni.utils.SimpleTimeMark +import at.hannibal2.skyhanni.utils.TimeUtils.format +import at.hannibal2.skyhanni.utils.chat.Text +import at.hannibal2.skyhanni.utils.chat.Text.hover +import at.hannibal2.skyhanni.utils.renderables.Renderable +import com.google.gson.annotations.Expose +import net.minecraft.block.BlockStone +import net.minecraft.init.Blocks +import net.minecraft.item.EnumDyeColor +import net.minecraft.item.ItemStack +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +@SkyHanniModule +object MineshaftPityDisplay { + private val config get() = SkyHanniMod.feature.mining.mineshaftPityDisplay + + private val profileStorage get() = ProfileStorageData.profileSpecific?.mining?.mineshaft + + private var minedBlocks: MutableList<PityData> + get() = profileStorage?.blocksBroken ?: mutableListOf() + set(value) { + profileStorage?.blocksBroken = value + } + + private var PityBlock.efficientMiner: Int + get() = minedBlocks.firstOrNull { it.pityBlock == this }?.efficientMiner ?: 0 + set(value) { + minedBlocks.firstOrNull { it.pityBlock == this }?.let { it.efficientMiner = value } ?: run { + minedBlocks.add(PityData(this, efficientMiner = value)) + } + } + + private var PityBlock.blocksBroken: Int + get() = minedBlocks.firstOrNull { it.pityBlock == this }?.blocksBroken ?: 0 + set(value) { + minedBlocks.firstOrNull { it.pityBlock == this }?.let { it.blocksBroken = value } ?: run { + minedBlocks.add(PityData(this, blocksBroken = value)) + } + } + + private var mineshaftTotalBlocks: Long + get() = profileStorage?.mineshaftTotalBlocks ?: 0L + set(value) { + profileStorage?.mineshaftTotalBlocks = value + } + + private var mineshaftTotalCount: Int + get() = profileStorage?.mineshaftTotalCount ?: 0 + set(value) { + profileStorage?.mineshaftTotalCount = value + } + + var lastMineshaftSpawn = SimpleTimeMark.farPast() + + private var display = listOf<Renderable>() + + private const val MAX_COUNTER = 2000 + + @HandleEvent(onlyOnSkyblock = true) + fun onOreMined(event: OreMinedEvent) { + if (!isEnabled()) return + + event.originalOre.getOreType()?.getPityBlock()?.let { it.blocksBroken++ } + event.extraBlocks.toMutableMap() + .apply { addOrPut(event.originalOre, -1) } + .map { (block, amount) -> + block.getOreType()?.getPityBlock()?.let { it.efficientMiner += amount } + } + + update() + } + + @SubscribeEvent + fun onChat(event: LorenzChatEvent) { + if (!isEnabled()) return + if (MiningNotifications.mineshaftSpawn.matches(event.message)) { + val pityCounter = calculateCounter() + val chance = calculateChance(pityCounter) + val counterUntilPity = MAX_COUNTER - pityCounter + val totalBlocks = PityBlock.entries.sumOf { it.blocksBroken + it.efficientMiner } + + mineshaftTotalBlocks += totalBlocks + mineshaftTotalCount++ + + val message = event.message + " §e($counterUntilPity)" + + val hover = mutableListOf<String>() + hover.add("§7Blocks mined: §e$totalBlocks") + hover.add("§7Pity Counter: §e$pityCounter") + hover.add( + "§7Chance: " + + "§e1§6/§e${chance.round(1)} " + + "§7(§b${((1.0 / chance) * 100).addSeparators()}%§7)", + ) + minedBlocks.forEach { + hover.add( + " §7${it.pityBlock.displayName} mined: " + + "§e${it.blocksBroken.addSeparators()} [+${it.efficientMiner.addSeparators()} efficient miner]" + + " §6(${it.pityBlock.getPity().addSeparators()}/${counterUntilPity.addSeparators()})", + ) + } + hover.add("") + hover.add( + "§7Average Blocks/Mineshaft: " + + "§e${(mineshaftTotalBlocks / mineshaftTotalCount.toDouble()).addSeparators()}", + ) + + if (!lastMineshaftSpawn.isFarPast()) { + hover.add("") + hover.add("§7Time since Last Mineshaft: §e${lastMineshaftSpawn.passedSince().format()}") + } + + resetCounter() + + val newComponent = Text.text(message) { + this.hover = Text.multiline(hover) + } + + if (config.modifyChatMessage) event.chatComponent = newComponent + } + } + + @SubscribeEvent + fun onSecondPassed(event: SecondPassedEvent) { + if (!isDisplayEnabled()) return + update() + } + + private fun calculateCounter(): Int { + val counter = MAX_COUNTER + if (minedBlocks.isEmpty()) return counter + val difference = minedBlocks.sumOf { it.pityBlock.getPity() } + return (counter - difference).toInt().coerceAtLeast(0) + } + + // if the chance is 1/1500, it will return 1500 + private fun calculateChance(counter: Int): Double { + val surveyorPercent = HotmData.SURVEYOR.getReward()[HotmReward.MINESHAFT_CHANCE] ?: 0.0 + val peakMountainPercent = HotmData.PEAK_OF_THE_MOUNTAIN.getReward()[HotmReward.MINESHAFT_CHANCE] ?: 0.0 + val chance = counter / (1 + surveyorPercent / 100 + peakMountainPercent / 100) + return chance + } + + private fun update() { + val pityCounter = calculateCounter() + val chance = calculateChance(pityCounter) + val counterUntilPity = MAX_COUNTER - pityCounter + + val blocksToPityList = buildList { + val multipliers = PityBlock.entries.map { it.multiplier }.toSet().sorted() + + multipliers.forEach { multiplier -> + val iconsList = PityBlock.entries + .filter { it.multiplier == multiplier } + .map { Renderable.itemStack(it.displayItem) } + add( + Renderable.horizontalContainer( + listOf( + Renderable.horizontalContainer(iconsList), + Renderable.string("§b${pityCounter / multiplier}"), + ), + 2, + ), + ) + } + } + + + val neededToPityRenderable = Renderable.verticalContainer( + listOf( + Renderable.string("§3Needed to pity:"), + Renderable.horizontalContainer( + listOf( + Renderable.placeholder(10, 0), + Renderable.verticalContainer(blocksToPityList), + ), + ), + ), + ) + + val map = buildMap { + put(MineshaftPityLines.TITLE, Renderable.string("§9§lMineshaft Pity Counter")) + put(MineshaftPityLines.COUNTER, Renderable.string("§3Pity Counter: §e$counterUntilPity§6/§e$MAX_COUNTER")) + put( + MineshaftPityLines.CHANCE, + Renderable.string( + "§3Chance: §e1§6/§e${chance.round(1)} §7(§b${((1.0 / chance) * 100).addSeparators()}%§7)", + ), + ) + put(MineshaftPityLines.NEEDED_TO_PITY, neededToPityRenderable) + put( + MineshaftPityLines.TIME_SINCE_MINESHAFT, + Renderable.string("§3Last Mineshaft: §e${lastMineshaftSpawn.passedSince().format()}"), + ) + put( + MineshaftPityLines.AVERAGE_BLOCKS_MINESHAFT, + Renderable.string( + "§3Average Blocks/Mineshaft: §e${(mineshaftTotalBlocks / mineshaftTotalCount.toDouble()).addSeparators()}", + ), + ) + } + + display = config.mineshaftPityLines.filter { it.shouldDisplay() }.mapNotNull { map[it] } + } + + @SubscribeEvent + fun onRenderOverlay(event: GuiRenderEvent.GuiOverlayRenderEvent) { + if (!isDisplayEnabled()) return + display.ifEmpty { update() } + if (display.isEmpty()) return + config.position.renderRenderables( + listOf(Renderable.verticalContainer(display, 2)), + posLabel = "Mineshaft Pity Display", + ) + } + + private fun resetCounter() { + minedBlocks = mutableListOf() + lastMineshaftSpawn = SimpleTimeMark.now() + update() + } + + fun fullResetCounter() { + resetCounter() + mineshaftTotalBlocks = 0 + mineshaftTotalCount = 0 + lastMineshaftSpawn = SimpleTimeMark.farPast() + update() + } + + @SubscribeEvent + fun onIslandChange(event: IslandChangeEvent) { + if (event.newIsland == IslandType.MINESHAFT || event.oldIsland == IslandType.MINESHAFT) { + resetCounter() + } + } + + fun isEnabled() = MiningAPI.inGlacialTunnels() && config.enabled + + private fun isDisplayEnabled() = (MiningAPI.inGlacialTunnels() || MiningAPI.inDwarvenBaseCamp()) && config.enabled + + enum class MineshaftPityLines(private val display: String, val shouldDisplay: () -> Boolean = { true }) { + TITLE("§3§lMineshaft Pity Counter"), + COUNTER("§3Counter: §e561§6/§e2000"), + CHANCE("§3Chance: §e1§6/§e1439 §7(§b0.069%§7)"), + NEEDED_TO_PITY("§3Needed to pity:\n§7 <blocks>"), + TIME_SINCE_MINESHAFT("§3Last Mineshaft: §e21m 5s", { !lastMineshaftSpawn.isFarPast() }), + AVERAGE_BLOCKS_MINESHAFT("§3Average Blocks/Mineshaft: §e361.5", { mineshaftTotalCount != 0 }) + ; + + override fun toString() = display + } + + data class PityData( + @Expose val pityBlock: PityBlock, + @Expose var blocksBroken: Int = 0, + @Expose var efficientMiner: Int = 0, + ) + + enum class PityBlock( + val displayName: String, + val oreTypes: List<OreType>, + val multiplier: Int, + val displayItem: ItemStack, + ) { + MITHRIL( + "Mithril", + listOf(OreType.MITHRIL), + 2, + ItemStack(Blocks.wool, 1, EnumDyeColor.LIGHT_BLUE.metadata), + ), + + GEMSTONE( + "Gemstone", + listOf( + OreType.RUBY, OreType.AMBER, OreType.AMETHYST, OreType.JADE, + OreType.SAPPHIRE, OreType.TOPAZ, OreType.JASPER, OreType.OPAL, + OreType.AQUAMARINE, OreType.CITRINE, OreType.ONYX, OreType.PERIDOT, + ), + 4, + ItemStack(Blocks.stained_glass, 1, EnumDyeColor.BLUE.metadata), + ), + GLACITE( + "Glacite", + listOf(OreType.GLACITE), + 4, + ItemStack(Blocks.packed_ice), + ), + TUNGSTEN( + "Tungsten", + listOf(OreType.TUNGSTEN), + 4, + ItemStack(Blocks.clay), + ), + UMBER( + "Umber", + listOf(OreType.UMBER), + 4, + ItemStack(Blocks.red_sandstone), + ), + + TITANIUM( + "Titanium", + listOf(OreType.TITANIUM), + 8, + ItemStack(Blocks.stone, 1, BlockStone.EnumType.DIORITE_SMOOTH.metadata), + ), + ; + + companion object { + + fun OreType.getPityBlock() = entries.firstOrNull { this in it.oreTypes } + + fun PityBlock.getPity() = (blocksBroken + efficientMiner / 2.0) * multiplier + } + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/MiningCommissionsBlocksColor.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/MiningCommissionsBlocksColor.kt index 92ba5fc13..bf7c8c2ac 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/MiningCommissionsBlocksColor.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/MiningCommissionsBlocksColor.kt @@ -1,20 +1,18 @@ package at.hannibal2.skyhanni.features.mining import at.hannibal2.skyhanni.SkyHanniMod -import at.hannibal2.skyhanni.data.HypixelData -import at.hannibal2.skyhanni.data.IslandType -import at.hannibal2.skyhanni.data.MiningAPI +import at.hannibal2.skyhanni.data.MiningAPI.inCrystalHollows +import at.hannibal2.skyhanni.data.MiningAPI.inDwarvenMines +import at.hannibal2.skyhanni.data.MiningAPI.inGlacite import at.hannibal2.skyhanni.events.ConfigLoadEvent import at.hannibal2.skyhanni.events.DebugDataCollectEvent import at.hannibal2.skyhanni.events.LorenzChatEvent import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent import at.hannibal2.skyhanni.events.TabListUpdateEvent +import at.hannibal2.skyhanni.features.mining.OreType.Companion.isGemstone import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule -import at.hannibal2.skyhanni.utils.CollectionUtils.equalsOneOf import at.hannibal2.skyhanni.utils.ConditionalUtils.onToggle -import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.TimeLimitedSet import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern @@ -41,68 +39,39 @@ object MiningCommissionsBlocksColor { */ private val commissionCompletePattern by patternGroup.pattern( "complete", - "§a§l(?<name>.*) §r§eCommission Complete! Visit the King §r§eto claim your rewards!" + "§a§l(?<name>.*) §r§eCommission Complete! Visit the King §r§eto claim your rewards!", ) var color: EnumDyeColor = EnumDyeColor.RED - private fun glass() = { state: IBlockState, result: Boolean -> + private fun glass(state: IBlockState, result: Boolean): IBlockState = if (result) { state.withProperty(BlockCarpet.COLOR, color) } else { state.withProperty(BlockCarpet.COLOR, EnumDyeColor.GRAY) } - } - private fun block() = { _: IBlockState, result: Boolean -> + private fun block(result: Boolean): IBlockState { val wool = Blocks.wool.defaultState - if (result) { + return if (result) { wool.withProperty(BlockCarpet.COLOR, color) } else { wool.withProperty(BlockCarpet.COLOR, EnumDyeColor.GRAY) } } - val aquamarine = Blocks.stained_glass - val aquamarine_2 = Blocks.stained_glass_pane - - val citrine = Blocks.stained_glass - val citrine_2 = Blocks.stained_glass_pane - - val glacite = Blocks.packed_ice - - val onyx = Blocks.stained_glass - val onyx_2 = Blocks.stained_glass_pane - - val umber = Blocks.hardened_clay - val umber_2 = Blocks.stained_hardened_clay - val umber_3 = Blocks.double_stone_slab2 // red sandstone - - val peridot = Blocks.stained_glass - val peridot_2 = Blocks.stained_glass_pane - - val tungston = Blocks.cobblestone - val tungston_2 = Blocks.stone_stairs - val tungston_3 = Blocks.clay - - val mithril = Blocks.stained_hardened_clay - val mithril_2 = Blocks.wool - val mithril_3 = Blocks.prismarine - private var oldSneakState = false private var dirty = false private var forceDirty = false - private var inDwarvenMines = false - private var inCrystalHollows = false - private var inGlaciteArea = false + var replaceBlocksMapCache = mutableMapOf<IBlockState, IBlockState>() - // TODO Commissin API + // TODO Commission API @SubscribeEvent fun onTabListUpdate(event: TabListUpdateEvent) { - for (block in MiningBlock.entries) { - val tabList = " §r§f${block.displayName}: " + for (block in CommissionBlock.entries) { + val tabList = " §r§f${block.commissionName}: " val newValue = event.tabList.any { it.startsWith(tabList) && !it.contains("DONE") } if (block.highlight != newValue) { if (newValue && block in ignoredTabListCommissions) continue @@ -112,15 +81,15 @@ object MiningCommissionsBlocksColor { } } - private val ignoredTabListCommissions = TimeLimitedSet<MiningBlock>(5.seconds) + private val ignoredTabListCommissions = TimeLimitedSet<CommissionBlock>(5.seconds) - // TODO Commissin API + // TODO Commission API @SubscribeEvent fun onChat(event: LorenzChatEvent) { if (!enabled) return commissionCompletePattern.matchMatcher(event.message) { val name = group("name") - val block = MiningBlock.entries.find { it.displayName.equals(name, ignoreCase = true) } ?: return + val block = CommissionBlock.entries.find { it.commissionName.equals(name, ignoreCase = true) } ?: return block.highlight = false dirty = true ignoredTabListCommissions.add(block) @@ -129,16 +98,7 @@ object MiningCommissionsBlocksColor { @SubscribeEvent fun onTick(event: LorenzTickEvent) { - if (LorenzUtils.lastWorldSwitch.passedSince() > 4.seconds) { - inGlaciteArea = MiningAPI.inGlaciteArea() && !IslandType.MINESHAFT.isInIsland() - inDwarvenMines = IslandType.DWARVEN_MINES.isInIsland() && - !(inGlaciteArea || HypixelData.skyBlockArea.equalsOneOf("Dwarven Base Camp", "Fossil Research Center")) - inCrystalHollows = IslandType.CRYSTAL_HOLLOWS.isInIsland() && HypixelData.skyBlockArea != "Crystal Nucleus" - } - - // TODO add dwarven mines support -// val newEnabled = (inDwarvenMines || inCrystalHollows || inGlaciteArea) && config.enabled - val newEnabled = (inCrystalHollows || inGlaciteArea) && config.enabled + val newEnabled = (inCrystalHollows || inGlacite) && config.enabled var reload = false if (newEnabled != enabled) { enabled = newEnabled @@ -165,6 +125,7 @@ object MiningCommissionsBlocksColor { } if (reload) { + replaceBlocksMapCache = mutableMapOf() Minecraft.getMinecraft().renderGlobal.loadRenderers() dirty = false } @@ -191,7 +152,8 @@ object MiningCommissionsBlocksColor { enabled = false inDwarvenMines = false inCrystalHollows = false - inGlaciteArea = false + inGlacite = false + replaceBlocksMapCache = mutableMapOf() } @SubscribeEvent @@ -205,157 +167,82 @@ object MiningCommissionsBlocksColor { event.addData { add("inDwarvenMines: $inDwarvenMines") add("inCrystalHollows: $inCrystalHollows") - add("inGlaciteArea: $inGlaciteArea") + add("inGlaciteArea: $inGlacite") add("active: $active") } } - enum class MiningBlock( - val displayName: String, - val onCheck: (IBlockState) -> Boolean, - val onColor: (IBlockState, Boolean) -> IBlockState, + enum class CommissionBlock( + val commissionName: String, + val oreType: OreType, var highlight: Boolean = false, - val checkIsland: () -> Boolean, ) { // Dwarven Mines MITHRIL( "Mithril Everywhere", - onCheck = { state -> - (state.block == mithril && state.getValue(BlockCarpet.COLOR).equalsOneOf(EnumDyeColor.CYAN)) || - (state.block == mithril_2 && (state.getValue(BlockCarpet.COLOR).equalsOneOf(EnumDyeColor.GRAY) || - state.getValue(BlockCarpet.COLOR).equalsOneOf(EnumDyeColor.LIGHT_BLUE))) || - state.block == mithril_3 - }, - onColor = block(), - checkIsland = { true } + OreType.MITHRIL, ), // Crystal Hollows AMBER( "Amber Gemstone Collector", - onCheck = { state -> - (state.block == aquamarine || state.block == aquamarine_2) && - state.getValue(BlockCarpet.COLOR).equalsOneOf(EnumDyeColor.ORANGE) - }, - onColor = glass(), - checkIsland = { !inDwarvenMines } + OreType.AMBER, ), TOPAZ( "Topaz Gemstone Collector", - onCheck = { state -> - (state.block == aquamarine || state.block == aquamarine_2) && - state.getValue(BlockCarpet.COLOR).equalsOneOf(EnumDyeColor.YELLOW) - }, - onColor = glass(), - checkIsland = { !inDwarvenMines } + OreType.TOPAZ, ), AMETHYST( "Amethyst Gemstone Collector", - onCheck = { state -> - (state.block == aquamarine || state.block == aquamarine_2) && - state.getValue(BlockCarpet.COLOR).equalsOneOf(EnumDyeColor.PURPLE) - }, - onColor = glass(), - checkIsland = { !inDwarvenMines } + OreType.AMETHYST, ), RUBY( "Ruby Gemstone Collector", - onCheck = { state -> - (state.block == aquamarine || state.block == aquamarine_2) && - state.getValue(BlockCarpet.COLOR).equalsOneOf(EnumDyeColor.RED) - }, - onColor = glass(), - checkIsland = { !inDwarvenMines } + OreType.RUBY, ), JADE( "Jade Gemstone Collector", - onCheck = { state -> - (state.block == aquamarine || state.block == aquamarine_2) && - state.getValue(BlockCarpet.COLOR).equalsOneOf(EnumDyeColor.LIME) - }, - onColor = glass(), - checkIsland = { !inDwarvenMines } + OreType.JADE, ), SAPPHIRE( "Sapphire Gemstone Collector", - onCheck = { state -> - (state.block == aquamarine || state.block == aquamarine_2) && - state.getValue(BlockCarpet.COLOR).equalsOneOf(EnumDyeColor.LIGHT_BLUE) - }, - onColor = glass(), - checkIsland = { !inDwarvenMines } - ), - HARD_STONE( - "Hardstone Miner", - onCheck = { state -> - state.block == glacite - }, - onColor = block(), - checkIsland = { !inDwarvenMines } + OreType.SAPPHIRE, ), // Glacite Tunnels GLACITE( "Glacite Collector", - onCheck = { state -> - state.block == glacite - }, - onColor = block(), - checkIsland = { inGlaciteArea } + OreType.GLACITE, ), UMBER( "Umber Collector", - onCheck = { state -> - (state.block == umber || state.block == umber_3) || - (state.block == umber_2 && state.getValue(BlockCarpet.COLOR).equalsOneOf(EnumDyeColor.BROWN)) - }, - onColor = block(), - checkIsland = { inGlaciteArea } + OreType.UMBER, ), TUNGSTON( "Tungsten Collector", - onCheck = { state -> - state.block == tungston || state.block == tungston_2 || state.block == tungston_3 - }, - onColor = block(), - checkIsland = { inGlaciteArea } + OreType.TUNGSTEN, ), PERIDOT( "Peridot Gemstone Collector", - onCheck = { state -> - (state.block == peridot || state.block == peridot_2) && - state.getValue(BlockCarpet.COLOR).equalsOneOf(EnumDyeColor.GREEN) - }, - onColor = glass(), - checkIsland = { inGlaciteArea } + OreType.PERIDOT, ), AQUAMARINE( "Aquamarine Gemstone Collector", - onCheck = { state -> - (state.block == aquamarine || state.block == aquamarine_2) && - state.getValue(BlockCarpet.COLOR).equalsOneOf(EnumDyeColor.BLUE) - }, - onColor = glass(), - checkIsland = { inGlaciteArea } + OreType.AQUAMARINE, ), CITRINE( "Citrine Gemstone Collector", - onCheck = { state -> - (state.block == citrine || state.block == citrine_2) && - state.getValue(BlockCarpet.COLOR).equalsOneOf(EnumDyeColor.BROWN) - }, - onColor = glass(), - checkIsland = { inGlaciteArea } + OreType.CITRINE, ), ONYX( "Onyx Gemstone Collector", - onCheck = { state -> - (state.block == onyx || state.block == onyx_2) && - state.getValue(BlockCarpet.COLOR).equalsOneOf(EnumDyeColor.BLACK) - }, - onColor = glass(), - checkIsland = { inGlaciteArea } + OreType.ONYX, ), ; + + companion object { + fun CommissionBlock.onColor(state: IBlockState): IBlockState = + if (oreType.isGemstone()) glass(state, highlight) else block(highlight) + } } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/MiningNotifications.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/MiningNotifications.kt index 527c9d4c7..ae50308e9 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/MiningNotifications.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/MiningNotifications.kt @@ -2,9 +2,8 @@ package at.hannibal2.skyhanni.features.mining import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.api.GetFromSackAPI -import at.hannibal2.skyhanni.data.IslandType -import at.hannibal2.skyhanni.data.MiningAPI.getCold -import at.hannibal2.skyhanni.data.MiningAPI.inColdIsland +import at.hannibal2.skyhanni.data.MiningAPI +import at.hannibal2.skyhanni.data.MiningAPI.inGlaciteArea import at.hannibal2.skyhanni.data.MiningAPI.lastColdReset import at.hannibal2.skyhanni.events.ColdUpdateEvent import at.hannibal2.skyhanni.events.ConfigLoadEvent @@ -14,7 +13,6 @@ import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.ConditionalUtils import at.hannibal2.skyhanni.utils.DelayedRun import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import at.hannibal2.skyhanni.utils.PrimitiveItemStack.Companion.makePrimitiveStack import at.hannibal2.skyhanni.utils.RegexUtils.matches @@ -27,7 +25,7 @@ import kotlin.time.Duration.Companion.seconds @SkyHanniModule object MiningNotifications { - private val ASCENSION_ROPE = "ASCENSION_ROPE".asInternalName().makePrimitiveStack(1) + private val ASCENSION_ROPE by lazy { "ASCENSION_ROPE".asInternalName().makePrimitiveStack(1) } enum class MiningNotificationList(val str: String, val notification: String) { MINESHAFT_SPAWN("§bGlacite Mineshaft", "§bMineshaft"), @@ -40,26 +38,27 @@ object MiningNotifications { } private val patternGroup = RepoPattern.group("mining.notifications") - private val mineshaftSpawn by patternGroup.pattern( + val mineshaftSpawn by patternGroup.pattern( "mineshaft.spawn", - "§5§lWOW! §r§aYou found a §r§bGlacite Mineshaft §r§aportal!" + "§5§lWOW! §r§aYou found a §r§bGlacite Mineshaft §r§aportal!", ) private val scrapDrop by patternGroup.pattern( "scrapdrop", - "§6§lEXCAVATOR! §r§fYou found a §r§9Suspicious Scrap§r§f!" + "§6§lEXCAVATOR! §r§fYou found a §r§9Suspicious Scrap§r§f!", ) val goldenGoblinSpawn by patternGroup.pattern( "goblin.goldspawn", - "§6A Golden Goblin has spawned!" + "§6A Golden Goblin has spawned!", ) val diamondGoblinSpawn by patternGroup.pattern( "goblin.diamondspawn", - "§6A §r§bDiamond Goblin §r§6has spawned!" + "§6A §r§bDiamond Goblin §r§6has spawned!", ) private val config get() = SkyHanniMod.feature.mining.notifications private var hasSentCold = false + private var hasSentAscensionRope = false @SubscribeEvent fun onChat(event: LorenzChatEvent) { @@ -76,7 +75,7 @@ object MiningNotifications { @SubscribeEvent fun onColdUpdate(event: ColdUpdateEvent) { - if (!inColdIsland()) return + if (!inGlaciteArea()) return if (!config.enabled) return if (lastColdReset.passedSince() < 1.seconds) return @@ -84,7 +83,8 @@ object MiningNotifications { hasSentCold = true sendNotification(MiningNotificationList.COLD) } - if (IslandType.MINESHAFT.isInIsland() && config.getAscensionRope && config.coldAmount == event.cold) { + if (MiningAPI.inMineshaft() && config.getAscensionRope && event.cold >= config.coldAmount && !hasSentAscensionRope) { + hasSentAscensionRope = true DelayedRun.runDelayed(0.5.seconds) { GetFromSackAPI.getFromChatMessageSackItems(ASCENSION_ROPE) } @@ -94,17 +94,18 @@ object MiningNotifications { @SubscribeEvent fun onWorldChange(event: LorenzWorldChangeEvent) { hasSentCold = false + hasSentAscensionRope = false } @SubscribeEvent fun onConfigLoad(event: ConfigLoadEvent) { ConditionalUtils.onToggle(config.coldThreshold) { - if (getCold() != config.coldThreshold.get()) hasSentCold = false + if (MiningAPI.cold != config.coldThreshold.get()) hasSentCold = false } } private fun sendNotification(type: MiningNotificationList) { - if (!config.notifications.contains(type)) return + if (type !in config.notifications) return LorenzUtils.sendTitle(type.notification, 1500.milliseconds) if (config.playSound) SoundUtils.playPlingSound() } diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt new file mode 100644 index 000000000..3457d2566 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt @@ -0,0 +1,279 @@ +package at.hannibal2.skyhanni.features.mining + +import at.hannibal2.skyhanni.data.MiningAPI.currentAreaOreBlocks +import at.hannibal2.skyhanni.data.MiningAPI.inCrimsonIsle +import at.hannibal2.skyhanni.data.MiningAPI.inCrystalHollows +import at.hannibal2.skyhanni.data.MiningAPI.inDwarvenMines +import at.hannibal2.skyhanni.data.MiningAPI.inEnd +import at.hannibal2.skyhanni.data.MiningAPI.inGlacite +import at.hannibal2.skyhanni.data.MiningAPI.inSpidersDen +import at.hannibal2.skyhanni.utils.CollectionUtils.equalsOneOf +import net.minecraft.block.BlockColored +import net.minecraft.block.BlockSand +import net.minecraft.block.BlockStainedGlass +import net.minecraft.block.BlockStainedGlassPane +import net.minecraft.block.BlockStone +import net.minecraft.block.BlockStoneSlabNew +import net.minecraft.block.state.IBlockState +import net.minecraft.init.Blocks +import net.minecraft.item.EnumDyeColor + +enum class OreBlock( + val checkBlock: (IBlockState) -> Boolean, + val checkArea: () -> Boolean, +) { + // MITHRIL + LOW_TIER_MITHRIL( + checkBlock = ::isLowTierMithril, + checkArea = { inGlacite }, + ), + MID_TIER_MITHRIL( + checkBlock = { it.block == Blocks.prismarine }, + checkArea = { inDwarvenMines || inCrystalHollows || inGlacite }, + ), + HIGH_TIER_MITHRIL( + checkBlock = ::isHighTierMithril, + checkArea = { inDwarvenMines || inCrystalHollows || inGlacite }, + ), + + // TITANIUM + TITANIUM( + checkBlock = ::isTitanium, + checkArea = { inDwarvenMines || inGlacite }, + ), + + // VANILLA ORES + STONE( + checkBlock = ::isStone, + checkArea = { inDwarvenMines }, + ), + COBBLESTONE( + checkBlock = { it.block == Blocks.cobblestone }, + checkArea = { inDwarvenMines }, + ), + COAL_ORE( + checkBlock = { it.block == Blocks.coal_ore }, + checkArea = { inDwarvenMines || inCrystalHollows }, + ), + IRON_ORE( + checkBlock = { it.block == Blocks.iron_ore }, + checkArea = { inDwarvenMines || inCrystalHollows }, + ), + GOLD_ORE( + checkBlock = { it.block == Blocks.gold_ore }, + checkArea = { inDwarvenMines || inCrystalHollows }, + ), + LAPIS_ORE( + checkBlock = { it.block == Blocks.lapis_ore }, + checkArea = { inDwarvenMines || inCrystalHollows }, + ), + REDSTONE_ORE( + checkBlock = { it.block.equalsOneOf(Blocks.redstone_ore, Blocks.lit_redstone_ore) }, + checkArea = { inDwarvenMines || inCrystalHollows }, + ), + EMERALD_ORE( + checkBlock = { it.block == Blocks.emerald_ore }, + checkArea = { inDwarvenMines || inCrystalHollows }, + ), + DIAMOND_ORE( + checkBlock = { it.block == Blocks.diamond_ore }, + checkArea = { inDwarvenMines || inCrystalHollows }, + ), + + // NETHER + NETHERRACK( + checkBlock = { it.block == Blocks.netherrack }, + checkArea = { inCrimsonIsle }, + ), + QUARTZ_ORE( + checkBlock = { it.block == Blocks.quartz_ore }, + checkArea = { inCrystalHollows || inCrimsonIsle }, + ), + GLOWSTONE( + checkBlock = { it.block == Blocks.glowstone }, + checkArea = { inCrimsonIsle }, + ), + MYCELIUM( + checkBlock = { it.block == Blocks.mycelium }, + checkArea = { inCrimsonIsle }, + ), + RED_SAND( + checkBlock = ::isRedSand, + checkArea = { inCrimsonIsle }, + ), + SULPHUR( + checkBlock = { it.block == Blocks.sponge }, + checkArea = { inCrimsonIsle }, + ), + + // SPIDER'S DEN + GRAVEL( + checkBlock = { it.block == Blocks.gravel }, + checkArea = { inSpidersDen }, + ), + + //END + END_STONE( + checkBlock = { it.block == Blocks.end_stone }, + checkArea = { inEnd }, + ), + OBSIDIAN( + checkBlock = { it.block == Blocks.obsidian }, + checkArea = { inCrystalHollows || inEnd }, + ), + + // HARD STONE + HARD_STONE_HOLLOWS( + checkBlock = ::isHardStoneHollows, + checkArea = { inCrystalHollows }, + ), + HARD_STONE_GLACIAL( + checkBlock = ::isHardstoneGlacite, + checkArea = { inGlacite }, + ), + + // DWARVEN BLOCKS + DWARVEN_REDSTONE( + checkBlock = { it.block == Blocks.redstone_block }, + checkArea = { inCrystalHollows }, + ), + DWARVEN_GOLD( + checkBlock = { it.block == Blocks.gold_block }, + checkArea = { inDwarvenMines || inCrystalHollows }, + ), + DWARVEN_EMERALD( + checkBlock = { it.block == Blocks.emerald_block }, + checkArea = { inCrystalHollows }, + ), + DWARVEN_DIAMOND( + checkBlock = { it.block == Blocks.diamond_block }, + checkArea = { inCrystalHollows }, + ), + + // GEMSTONES + RUBY( + checkBlock = { it.isGemstoneWithColor(EnumDyeColor.RED) }, + checkArea = { inCrystalHollows || inGlacite }, + ), + AMBER( + checkBlock = { it.isGemstoneWithColor(EnumDyeColor.ORANGE) }, + checkArea = { inCrystalHollows || inGlacite }, + ), + AMETHYST( + checkBlock = { it.isGemstoneWithColor(EnumDyeColor.PURPLE) }, + checkArea = { inCrystalHollows || inGlacite }, + ), + JADE( + checkBlock = { it.isGemstoneWithColor(EnumDyeColor.LIME) }, + checkArea = { inCrystalHollows || inGlacite }, + ), + SAPPHIRE( + checkBlock = { it.isGemstoneWithColor(EnumDyeColor.LIGHT_BLUE) }, + checkArea = { inCrystalHollows || inGlacite }, + ), + TOPAZ( + checkBlock = { it.isGemstoneWithColor(EnumDyeColor.YELLOW) }, + checkArea = { inCrystalHollows || inGlacite }, + ), + JASPER( + checkBlock = { it.isGemstoneWithColor(EnumDyeColor.PINK) }, + checkArea = { inCrystalHollows || inGlacite }, + ), + OPAL( + checkBlock = { it.isGemstoneWithColor(EnumDyeColor.WHITE) }, + checkArea = { inGlacite || inCrimsonIsle }, + ), + AQUAMARINE( + checkBlock = { it.isGemstoneWithColor(EnumDyeColor.BLUE) }, + checkArea = { inGlacite }, + ), + CITRINE( + checkBlock = { it.isGemstoneWithColor(EnumDyeColor.BROWN) }, + checkArea = { inGlacite }, + ), + ONYX( + checkBlock = { it.isGemstoneWithColor(EnumDyeColor.BLACK) }, + checkArea = { inGlacite }, + ), + PERIDOT( + checkBlock = { it.isGemstoneWithColor(EnumDyeColor.GREEN) }, + checkArea = { inGlacite }, + ), + + // GLACIAL + LOW_TIER_UMBER( + checkBlock = ::isLowTierUmber, + checkArea = { inGlacite }, + ), + HIGH_TIER_UMBER( + checkBlock = ::isHighTierUmber, + checkArea = { inGlacite }, + ), + LOW_TIER_TUNGSTEN( + checkBlock = { it.block == Blocks.cobblestone }, + checkArea = { inGlacite }, + ), + HIGH_TIER_TUNGSTEN( + checkBlock = { it.block == Blocks.clay }, + checkArea = { inGlacite }, + ), + GLACITE( + checkBlock = { it.block == Blocks.packed_ice }, + checkArea = { inGlacite }, + ), + ; + + companion object { + fun getByStateOrNull(state: IBlockState): OreBlock? = currentAreaOreBlocks.firstOrNull { it.checkBlock(state) } + } +} + +private fun isLowTierMithril(state: IBlockState): Boolean { + return when (state.block) { + Blocks.wool -> state.getValue(BlockColored.COLOR) == EnumDyeColor.GRAY + Blocks.stained_hardened_clay -> state.getValue(BlockColored.COLOR) == EnumDyeColor.CYAN + else -> false + } +} + +private fun isHighTierMithril(state: IBlockState): Boolean { + return (state.block == Blocks.wool && state.getValue(BlockColored.COLOR) == EnumDyeColor.LIGHT_BLUE) +} + +private fun isTitanium(state: IBlockState): Boolean { + return (state.block == Blocks.stone && state.getValue(BlockStone.VARIANT) == BlockStone.EnumType.DIORITE_SMOOTH) +} + +private fun isStone(state: IBlockState): Boolean { + return (state.block == Blocks.stone && state.getValue(BlockStone.VARIANT) == BlockStone.EnumType.STONE) +} + +private fun isHardStoneHollows(state: IBlockState): Boolean { + return when (state.block) { + Blocks.wool -> (state.getValue(BlockColored.COLOR) == EnumDyeColor.GRAY) + Blocks.stained_hardened_clay -> (state.getValue(BlockColored.COLOR) == EnumDyeColor.CYAN) + Blocks.stone -> (state.getValue(BlockStone.VARIANT) == BlockStone.EnumType.STONE) + else -> false + } +} + +private fun isHardstoneGlacite(state: IBlockState): Boolean = + (state.block == Blocks.stone && state.getValue(BlockStone.VARIANT) == BlockStone.EnumType.STONE) || + (state.block == Blocks.wool && state.getValue(BlockColored.COLOR) == EnumDyeColor.GRAY) + +private fun isRedSand(state: IBlockState): Boolean = + (state.block == Blocks.stone && state.getValue(BlockSand.VARIANT) == BlockSand.EnumType.RED_SAND) + +private fun isLowTierUmber(state: IBlockState): Boolean = state.block == Blocks.hardened_clay || + (state.block == Blocks.stained_hardened_clay && state.getValue(BlockColored.COLOR) == EnumDyeColor.BROWN) + +private fun isHighTierUmber(state: IBlockState): Boolean = + (state.block == Blocks.double_stone_slab2 && state.getValue(BlockStoneSlabNew.VARIANT) == BlockStoneSlabNew.EnumType.RED_SANDSTONE) + +private fun IBlockState.isGemstoneWithColor(color: EnumDyeColor): Boolean { + return when (this.block) { + Blocks.stained_glass -> color == this.getValue(BlockStainedGlass.COLOR) + Blocks.stained_glass_pane -> color == this.getValue(BlockStainedGlassPane.COLOR) + else -> false + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/OreType.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/OreType.kt new file mode 100644 index 000000000..9e47a5a45 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/OreType.kt @@ -0,0 +1,213 @@ +package at.hannibal2.skyhanni.features.mining + +import at.hannibal2.skyhanni.data.MiningAPI +import at.hannibal2.skyhanni.utils.CollectionUtils.equalsOneOf +import at.hannibal2.skyhanni.utils.NEUInternalName +import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName +import net.minecraft.block.state.IBlockState + +enum class OreType( + val oreName: String, + private val internalNameString: String, + val oreBlocks: List<OreBlock>, + val internalName: NEUInternalName = internalNameString.asInternalName() +) { + MITHRIL( + "Mithril", + "MITHRIL_ORE", + listOf(OreBlock.LOW_TIER_MITHRIL, OreBlock.MID_TIER_MITHRIL, OreBlock.HIGH_TIER_MITHRIL) + ), + TITANIUM( + "Titanium", + "TITANIUM_ORE", + listOf(OreBlock.TITANIUM) + ), + COBBLESTONE( + "Cobblestone", + "COBBLESTONE", + listOf(OreBlock.STONE, OreBlock.COBBLESTONE) + ), + COAL( + "Coal", + "COAL", + listOf(OreBlock.COAL_ORE) + ), + IRON( + "Iron", + "IRON_INGOT", + listOf(OreBlock.IRON_ORE) + ), + GOLD( + "Gold", + "GOLD_INGOT", + listOf(OreBlock.GOLD_ORE, OreBlock.DWARVEN_GOLD) + ), + LAPIS( + "Lapis Lazuli", + "INK_SACK-4", + listOf(OreBlock.LAPIS_ORE) + ), + REDSTONE( + "Redstone", + "REDSTONE", + listOf(OreBlock.REDSTONE_ORE, OreBlock.DWARVEN_REDSTONE) + ), + EMERALD( + "Emerald", + "EMERALD", + listOf(OreBlock.EMERALD_ORE, OreBlock.DWARVEN_EMERALD) + ), + DIAMOND( + "Diamond", + "DIAMOND", + listOf(OreBlock.DIAMOND_ORE, OreBlock.DWARVEN_DIAMOND) + ), + NETHERRACK( + "Netherrack", + "NETHERRACK", + listOf(OreBlock.NETHERRACK) + ), + QUARTZ( + "Nether Quartz", + "QUARTZ", + listOf(OreBlock.QUARTZ_ORE) + ), + GLOWSTONE( + "Glowstone", + "GLOWSTONE_DUST", + listOf(OreBlock.GLOWSTONE) + ), + MYCELIUM( + "Mycelium", + "MYCEL", + listOf(OreBlock.MYCELIUM) + ), + RED_SAND( + "Red Sand", + "SAND-1", + listOf(OreBlock.RED_SAND) + ), + SULPHUR( + "Sulphur", + "SULPHUR_ORE", + listOf(OreBlock.SULPHUR) + ), + GRAVEL( + "Gravel", + "GRAVEL", + listOf(OreBlock.GRAVEL) + ), + END_STONE( + "End Stone", + "ENDER_STONE", + listOf(OreBlock.END_STONE) + ), + OBSIDIAN( + "Obsidian", + "OBSIDIAN", + listOf(OreBlock.OBSIDIAN) + ), + HARD_STONE( + "Hard Stone", + "HARD_STONE", + listOf(OreBlock.HARD_STONE_HOLLOWS, OreBlock.HARD_STONE_GLACIAL) + ), + RUBY( + "Ruby", + "ROUGH_RUBY_GEM", + listOf(OreBlock.RUBY) + ), + AMBER( + "Amber", + "ROUGH_AMBER_GEM", + listOf(OreBlock.AMBER) + ), + AMETHYST( + "Amethyst", + "ROUGH_AMETHYST_GEM", + listOf(OreBlock.AMETHYST) + ), + JADE( + "Jade", + "ROUGH_JADE_GEM", + listOf(OreBlock.JADE) + ), + SAPPHIRE( + "Sapphire", + "ROUGH_SAPPHIRE_GEM", + listOf(OreBlock.SAPPHIRE) + ), + TOPAZ( + "Topaz", + "ROUGH_TOPAZ_GEM", + listOf(OreBlock.TOPAZ) + ), + JASPER( + "Jasper", + "ROUGH_JASPER_GEM", + listOf(OreBlock.JASPER) + ), + OPAL( + "Opal", + "ROUGH_OPAL_GEM", + listOf(OreBlock.OPAL) + ), + AQUAMARINE( + "Aquamarine", + "ROUGH_AQUAMARINE_GEM", + listOf(OreBlock.AQUAMARINE) + ), + CITRINE( + "Citrine", + "ROUGH_CITRINE_GEM", + listOf(OreBlock.CITRINE) + ), + ONYX( + "Onyx", + "ROUGH_ONYX_GEM", + listOf(OreBlock.ONYX) + ), + PERIDOT( + "Peridot", + "ROUGH_PERIDOT_GEM", + listOf(OreBlock.PERIDOT) + ), + UMBER( + "Umber", + "UMBER", + listOf(OreBlock.LOW_TIER_UMBER, OreBlock.HIGH_TIER_UMBER) + ), + TUNGSTEN( + "Tungsten", + "TUNGSTEN", + listOf(OreBlock.LOW_TIER_TUNGSTEN, OreBlock.HIGH_TIER_TUNGSTEN) + ), + GLACITE( + "Glacite", + "GLACITE", + listOf(OreBlock.GLACITE) + ), + ; + + companion object { + + fun IBlockState.isOreType(oreType: OreType): Boolean { + return oreType.oreBlocks.intersect(MiningAPI.currentAreaOreBlocks) + .any { it.checkBlock.invoke(this) } + } + + fun OreType.isGemstone(): Boolean { + return this.equalsOneOf( + RUBY, AMBER, AMETHYST, JADE, + SAPPHIRE, TOPAZ, JASPER, OPAL, + AQUAMARINE, CITRINE, ONYX, PERIDOT + ) + } + + fun OreBlock.getOreType(): OreType? { + return OreType.entries.firstOrNull { + it.oreBlocks.contains(this) + } + } + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/hooks/BlockRendererDispatcherHook.kt b/src/main/java/at/hannibal2/skyhanni/mixins/hooks/BlockRendererDispatcherHook.kt index 3f8f803b0..2d15d3cb7 100644 --- a/src/main/java/at/hannibal2/skyhanni/mixins/hooks/BlockRendererDispatcherHook.kt +++ b/src/main/java/at/hannibal2/skyhanni/mixins/hooks/BlockRendererDispatcherHook.kt @@ -1,6 +1,9 @@ package at.hannibal2.skyhanni.mixins.hooks import at.hannibal2.skyhanni.features.mining.MiningCommissionsBlocksColor +import at.hannibal2.skyhanni.features.mining.MiningCommissionsBlocksColor.CommissionBlock.Companion.onColor +import at.hannibal2.skyhanni.features.mining.MiningCommissionsBlocksColor.replaceBlocksMapCache +import at.hannibal2.skyhanni.features.mining.OreType.Companion.isOreType import at.hannibal2.skyhanni.utils.LorenzUtils import net.minecraft.block.state.IBlockState import net.minecraft.client.renderer.BlockRendererDispatcher @@ -23,10 +26,10 @@ fun modifyGetModelFromBlockState( if (!LorenzUtils.inSkyBlock) return if (MiningCommissionsBlocksColor.enabled && MiningCommissionsBlocksColor.active) { - for (block in MiningCommissionsBlocksColor.MiningBlock.entries) { - if (block.checkIsland() && block.onCheck(state)) { - returnState = block.onColor(state, block.highlight) - } + returnState = replaceBlocksMapCache.getOrPut(state) { + MiningCommissionsBlocksColor.CommissionBlock.entries.firstOrNull { + state.isOreType(it.oreType) + }?.onColor(state) ?: state } } diff --git a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt index 4e1c51de3..ce1b5eea7 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt @@ -1,6 +1,7 @@ package at.hannibal2.skyhanni.test import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.api.event.HandleEvent import at.hannibal2.skyhanni.config.ConfigFileType import at.hannibal2.skyhanni.config.ConfigGuiManager import at.hannibal2.skyhanni.config.ConfigManager @@ -13,10 +14,14 @@ import at.hannibal2.skyhanni.events.LorenzChatEvent import at.hannibal2.skyhanni.events.LorenzRenderWorldEvent import at.hannibal2.skyhanni.events.LorenzToolTipEvent import at.hannibal2.skyhanni.events.ReceiveParticleEvent +import at.hannibal2.skyhanni.events.mining.OreMinedEvent import at.hannibal2.skyhanni.features.garden.GardenNextJacobContest import at.hannibal2.skyhanni.features.garden.visitor.GardenVisitorColorNames import at.hannibal2.skyhanni.features.inventory.bazaar.BazaarApi.getBazaarData +import at.hannibal2.skyhanni.features.mining.OreBlock import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.BlockUtils +import at.hannibal2.skyhanni.utils.BlockUtils.getBlockStateAt import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.CollectionUtils.editCopy import at.hannibal2.skyhanni.utils.InventoryUtils @@ -504,13 +509,27 @@ object SkyHanniDebugsAndTests { itemRenderDebug() } - if (Minecraft.getMinecraft().gameSettings.showDebugInfo && debugConfig.currentAreaDebug) { - config.debugLocationPos.renderString( - "Current Area: ${HypixelData.skyBlockArea}", - posLabel = "SkyBlock Area (Debug)", - ) + if (Minecraft.getMinecraft().gameSettings.showDebugInfo) { + if (debugConfig.currentAreaDebug) { + config.debugLocationPos.renderString( + "Current Area: ${HypixelData.skyBlockArea}", + posLabel = "SkyBlock Area (Debug)", + ) + } + + if (debugConfig.raytracedOreblock) { + BlockUtils.getBlockLookingAt(50.0)?.let { pos -> + OreBlock.getByStateOrNull(pos.getBlockStateAt())?.let { ore -> + config.debugOrePos.renderString( + "Looking at: ${ore.name} (${pos.toCleanString()})", + posLabel = "OreBlock" + ) + } + } + } } + if (!debugConfig.enabled) return if (displayLine.isNotEmpty()) { @@ -549,6 +568,14 @@ object SkyHanniDebugsAndTests { ) } + @HandleEvent(onlyOnSkyblock = true) + fun onOreMined(event: OreMinedEvent) { + if (!debugConfig.oreEventMessages) return + val originalOre = event.originalOre + val extraBlocks = event.extraBlocks.map { "${it.key.name}: ${it.value}" } + ChatUtils.debug("Mined: $originalOre (${extraBlocks.joinToString()})") + } + @SubscribeEvent fun onReceiveParticle(event: ReceiveParticleEvent) { // val particleType = event.type diff --git a/src/main/java/at/hannibal2/skyhanni/utils/CollectionUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/CollectionUtils.kt index 87261b6d0..d540daffe 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/CollectionUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/CollectionUtils.kt @@ -92,6 +92,16 @@ object CollectionUtils { } } + /** Returns a map containing the count of occurrences of each distinct result of the [selector] function. */ + inline fun <T, K> Iterable<T>.countBy(selector: (T) -> K): Map<K, Int> { + val map = mutableMapOf<K, Int>() + for (item in this) { + val key = selector(item) + map[key] = map.getOrDefault(key, 0) + 1 + } + return map + } + fun List<String>.nextAfter(after: String, skip: Int = 1) = nextAfter({ it == after }, skip) fun List<String>.nextAfter(after: (String) -> Boolean, skip: Int = 1): String? { @@ -210,6 +220,31 @@ object CollectionUtils { return collection } + /** Removes the first element that matches the given [predicate] in the list. */ + fun <T> List<T>.removeFirst(predicate: (T) -> Boolean): List<T> { + val mutableList = this.toMutableList() + val iterator = mutableList.iterator() + while (iterator.hasNext()) { + if (predicate(iterator.next())) { + iterator.remove() + break + } + } + return mutableList.toList() + } + + /** Removes the first element that matches the given [predicate] in the map. */ + fun <K, V> Map<K, V>.removeFirst(predicate: (Map.Entry<K, V>) -> Boolean): Map<K, V> { + val mutableMap = this.toMutableMap() + val iterator = mutableMap.entries.iterator() + while (iterator.hasNext()) { + if (predicate(iterator.next())) { + iterator.remove() + break + } + } + return mutableMap.toMap() + } /** Updates a value if it is present in the set (equals), useful if the newValue is not reference equal with the value in the set */ inline fun <reified T> MutableSet<T>.refreshReference(newValue: T) = if (this.contains(newValue)) { diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt index e369a69ca..474d6dee5 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt @@ -121,7 +121,7 @@ data class LorenzVec( } } - fun toCleanString(): String = "$x $y $z" + fun toCleanString(separator: String = ", "): String = listOf(x, y, z).joinToString(separator) fun lengthSquared(): Double = x * x + y * y + z * z fun length(): Double = sqrt(this.lengthSquared()) |