aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEmpa <42304516+ItsEmpa@users.noreply.github.com>2024-06-15 22:12:24 +0200
committerGitHub <noreply@github.com>2024-06-15 22:12:24 +0200
commitbfe43b7ebffab551740d32cf919db27504d27ffb (patch)
tree7fb570635c88d98f122d2752be78f135868a9843 /src
parent8c91a9b8bf63059e1a8a099ccbdadea8a3676948 (diff)
downloadskyhanni-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')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt5
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/dev/DebugConfig.java10
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/dev/DevConfig.java5
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/mining/MineshaftPityDisplayConfig.java43
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java5
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java16
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/HotmData.kt60
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt214
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/ServerBlockChangeEvent.kt3
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/mining/OreMinedEvent.kt6
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardElements.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/mining/MineshaftPityDisplay.kt340
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/mining/MiningCommissionsBlocksColor.kt197
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/mining/MiningNotifications.kt29
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt279
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/mining/OreType.kt213
-rw-r--r--src/main/java/at/hannibal2/skyhanni/mixins/hooks/BlockRendererDispatcherHook.kt11
-rw-r--r--src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt37
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/CollectionUtils.kt35
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt2
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())