aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2025-03-06 23:36:57 +0100
committerLinnea Gräf <nea@nea.moe>2025-03-08 16:01:00 +0100
commit6ad259ca405d58f1956d67d7daeb05ae8590ca62 (patch)
treef749625557c8eca45b1c7cc5f1f035f76935a7ff
parent8a4bfe24b364612e3e783ed9569082b130aa2bfc (diff)
downloadFirmament-6ad259ca405d58f1956d67d7daeb05ae8590ca62.tar.gz
Firmament-6ad259ca405d58f1956d67d7daeb05ae8590ca62.tar.bz2
Firmament-6ad259ca405d58f1956d67d7daeb05ae8590ca62.zip
feat: Add tool/harvestability indicator
-rw-r--r--gradle/libs.versions.toml2
-rw-r--r--src/compat/jade/java/moe/nea/firmament/compat/jade/DrillToolHandler.kt36
-rw-r--r--src/compat/jade/java/moe/nea/firmament/compat/jade/DrillToolProvider.kt90
-rw-r--r--src/compat/jade/java/moe/nea/firmament/compat/jade/FirmamentJadePlugin.kt2
-rw-r--r--src/compat/jade/java/moe/nea/firmament/compat/jade/GemstoneProvider.kt24
-rw-r--r--src/compat/jade/java/moe/nea/firmament/compat/jade/HardstoneProvider.kt24
-rw-r--r--src/compat/jade/java/moe/nea/firmament/compat/jade/MithrilProvider.kt24
-rw-r--r--src/compat/jade/java/moe/nea/firmament/mixins/compat/jade/ElementAccessor.java12
-rw-r--r--src/main/kotlin/repo/MiningRepoData.kt33
-rw-r--r--src/main/kotlin/util/MC.kt2
-rw-r--r--src/main/kotlin/util/SkyblockId.kt6
11 files changed, 115 insertions, 140 deletions
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 7b16a7b..dc1f36d 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -57,7 +57,7 @@ devauth = "1.2.1"
ktor = "3.0.3"
# Update from https://repo.nea.moe/#/releases/moe/nea/neurepoparser
-neurepoparser = "1.6.0"
+neurepoparser = "1.7.0"
# Update from https://github.com/HotswapProjects/HotswapAgent/releases
# TODO: bump to 2.0.1
diff --git a/src/compat/jade/java/moe/nea/firmament/compat/jade/DrillToolHandler.kt b/src/compat/jade/java/moe/nea/firmament/compat/jade/DrillToolHandler.kt
deleted file mode 100644
index d9e9436..0000000
--- a/src/compat/jade/java/moe/nea/firmament/compat/jade/DrillToolHandler.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-package moe.nea.firmament.compat.jade
-
-import com.google.common.collect.Lists
-import snownee.jade.addon.harvest.SimpleToolHandler
-import snownee.jade.addon.harvest.ToolHandler
-import net.minecraft.block.BlockState
-import net.minecraft.client.MinecraftClient
-import net.minecraft.item.Item
-import net.minecraft.item.ItemStack
-import net.minecraft.util.Identifier
-import net.minecraft.util.math.BlockPos
-import net.minecraft.world.World
-import moe.nea.firmament.util.SBData
-
-class DrillToolHandler(
- private val uid: Identifier,
- private val tools: MutableList<ItemStack>
-) : ToolHandler {
- override fun test(state: BlockState, world: World, pos: BlockPos): ItemStack {
- if (isOnMiningIsland()) {
- }
-
- // TODO: figure out how this work
- return SimpleToolHandler.create(uid, tools.map {
- return@map it.item
- }).test(state, world, pos)
- }
-
- override fun getTools(): List<ItemStack> {
- return this.tools
- }
-
- override fun getUid(): Identifier {
- return this.uid
- }
-}
diff --git a/src/compat/jade/java/moe/nea/firmament/compat/jade/DrillToolProvider.kt b/src/compat/jade/java/moe/nea/firmament/compat/jade/DrillToolProvider.kt
index 618ce4d..b944e8d 100644
--- a/src/compat/jade/java/moe/nea/firmament/compat/jade/DrillToolProvider.kt
+++ b/src/compat/jade/java/moe/nea/firmament/compat/jade/DrillToolProvider.kt
@@ -1,47 +1,79 @@
package moe.nea.firmament.compat.jade
-import com.google.common.cache.Cache
-import com.google.common.cache.CacheBuilder
-import com.google.common.collect.ImmutableList
-import com.google.common.collect.Maps
-import java.util.concurrent.TimeUnit
-import snownee.jade.addon.harvest.ToolHandler
+import java.util.Optional
+import java.util.function.UnaryOperator
import snownee.jade.api.BlockAccessor
import snownee.jade.api.IBlockComponentProvider
import snownee.jade.api.ITooltip
+import snownee.jade.api.JadeIds
import snownee.jade.api.config.IPluginConfig
-import net.minecraft.block.Block
-import net.minecraft.block.BlockState
+import snownee.jade.api.theme.IThemeHelper
+import snownee.jade.api.ui.IElement
+import snownee.jade.api.ui.IElementHelper
+import snownee.jade.impl.ui.ItemStackElement
+import snownee.jade.impl.ui.TextElement
+import kotlin.jvm.optionals.getOrDefault
import net.minecraft.item.ItemStack
-import net.minecraft.resource.ResourceManager
-import net.minecraft.resource.SynchronousResourceReloader
+import net.minecraft.item.Items
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import net.minecraft.util.math.Vec2f
+import moe.nea.firmament.Firmament
+import moe.nea.firmament.repo.ItemCache.asItemStack
+import moe.nea.firmament.repo.RepoManager
+import moe.nea.firmament.repo.SBItemStack
+import moe.nea.firmament.util.MC
-
-class DrillToolProvider : IBlockComponentProvider, SynchronousResourceReloader {
- val resultCache: Cache<BlockState, ImmutableList<ItemStack>> = CacheBuilder.newBuilder().expireAfterAccess(5, TimeUnit.MINUTES).build()
- val toolHandlers: MutableMap<Identifier, ToolHandler> = Maps.newLinkedHashMap()
- private val shearableBlocks: MutableList<Block> = mutableListOf()
- private val checkIcon: Text = Text.literal("✔")
- private val xIcon: Text = Text.literal("✕")
- private val itemSize = Vec2f(10f, 0f)
-
- @Synchronized
- fun registerHandler(handler: ToolHandler) {
- toolHandlers.put(handler.uid, handler)
+class DrillToolProvider : IBlockComponentProvider {
+ override fun appendTooltip(
+ tooltip: ITooltip,
+ accessor: BlockAccessor,
+ p2: IPluginConfig?
+ ) {
+ val customBlock = CustomFakeBlockProvider.getCustomBlock(accessor) ?: return
+ if (customBlock.breakingPower <= 0) return
+ val tool = RepoManager.miningData.getToolsThatCanBreak(customBlock.breakingPower).firstOrNull()
+ ?.asItemStack() ?: return
+ tooltip.replace(JadeIds.MC_HARVEST_TOOL, UnaryOperator { elements ->
+ elements.map { inner ->
+ val lastItemIndex = inner.indexOfLast { it is ItemStackElement }
+ if (lastItemIndex < 0) return@map inner
+ val innerMut = inner.toMutableList()
+ val harvestIndicator = innerMut.indexOfLast {
+ it is TextElement && it.cachedSize == Vec2f.ZERO && it.text.visit {
+ if (it.isEmpty()) Optional.empty() else Optional.of(true)
+ }.getOrDefault(false)
+ }
+ val canHarvest = (SBItemStack(MC.stackInHand).neuItem?.breakingPower ?: 0) >= customBlock.breakingPower
+ val lastItem = innerMut[lastItemIndex] as ItemStackElement
+ if (harvestIndicator < 0) {
+ innerMut.add(lastItemIndex + 1, canHarvestIndicator(canHarvest, lastItem.alignment))
+ } else {
+ innerMut.set(harvestIndicator, canHarvestIndicator(canHarvest, lastItem.alignment))
+ }
+ innerMut.set(lastItemIndex, IElementHelper.get()
+ .item(tool, 0.75f)
+ .translate(lastItem.translation)
+ .size(lastItem.size)
+ .message(null)
+ .align(lastItem.alignment))
+ innerMut.subList(0, lastItemIndex - 1).removeIf { it is ItemStackElement }
+ innerMut
+ }
+ })
}
- override fun appendTooltip(tooltip: ITooltip, accessor: BlockAccessor, config: IPluginConfig) {
- TODO("Not yet implemented")
+ fun canHarvestIndicator(canHarvest: Boolean, align: IElement.Align): IElement {
+ val t = IThemeHelper.get()
+ val text = if (canHarvest) t.success(CHECK) else t.danger(X)
+ return IElementHelper.get().text(text)
+ .scale(0.75F).zOffset(800).size(Vec2f.ZERO).translate(Vec2f(-3F, 3.25F)).align(align)
}
- override fun getUid(): Identifier {
- TODO("Not yet implemented")
- }
+ private val CHECK: Text = Text.literal("✔")
+ private val X: Text = Text.literal("✕")
- override fun reload(manager: ResourceManager) {
- TODO("Not yet implemented")
+ override fun getUid(): Identifier {
+ return Firmament.identifier("toolprovider")
}
}
diff --git a/src/compat/jade/java/moe/nea/firmament/compat/jade/FirmamentJadePlugin.kt b/src/compat/jade/java/moe/nea/firmament/compat/jade/FirmamentJadePlugin.kt
index 700aa70..6cb04b5 100644
--- a/src/compat/jade/java/moe/nea/firmament/compat/jade/FirmamentJadePlugin.kt
+++ b/src/compat/jade/java/moe/nea/firmament/compat/jade/FirmamentJadePlugin.kt
@@ -1,5 +1,6 @@
package moe.nea.firmament.compat.jade
+import snownee.jade.addon.harvest.HarvestToolProvider
import snownee.jade.api.IWailaClientRegistration
import snownee.jade.api.IWailaCommonRegistration
import snownee.jade.api.IWailaPlugin
@@ -18,6 +19,7 @@ class FirmamentJadePlugin : IWailaPlugin {
override fun registerClient(registration: IWailaClientRegistration) {
registration.registerBlockComponent(CustomMiningHardnessProvider, Block::class.java)
registration.registerProgressClient(SkyblockProgressProvider())
+ registration.registerBlockComponent(DrillToolProvider(), Block::class.java)
registration.addRayTraceCallback(CustomFakeBlockProvider(registration))
}
}
diff --git a/src/compat/jade/java/moe/nea/firmament/compat/jade/GemstoneProvider.kt b/src/compat/jade/java/moe/nea/firmament/compat/jade/GemstoneProvider.kt
deleted file mode 100644
index 883da12..0000000
--- a/src/compat/jade/java/moe/nea/firmament/compat/jade/GemstoneProvider.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package moe.nea.firmament.compat.jade
-
-import snownee.jade.api.BlockAccessor
-import snownee.jade.api.IBlockComponentProvider
-import snownee.jade.api.ITooltip
-import snownee.jade.api.config.IPluginConfig
-import net.minecraft.text.Text
-import net.minecraft.util.Identifier
-import moe.nea.firmament.util.SBData
-
-class GemstoneProvider(val type: String, val replacement: String) : IBlockComponentProvider {
- override fun appendTooltip(tooltip: ITooltip, accessor: BlockAccessor, config: IPluginConfig) {
- if (SBData.isOnSkyblock) {
- tooltip.add(drillIcon)
- // TODO: override jade breakability test to include breaking power of drills on mining islands
- tooltip.append(Text.of("Breaking Power 6/7/8/9/10")) // TODO: Use NEU API/add new data for breaking power
- tooltip.replace(Identifier.of("minecraft", type), Text.literal("Gemstone $type of $replacement y")) // this doesnt work
- }
- }
-
- override fun getUid(): Identifier {
- return "gemstone_${type}_${replacement}".jadeId()
- }
-}
diff --git a/src/compat/jade/java/moe/nea/firmament/compat/jade/HardstoneProvider.kt b/src/compat/jade/java/moe/nea/firmament/compat/jade/HardstoneProvider.kt
deleted file mode 100644
index 7e12d7f..0000000
--- a/src/compat/jade/java/moe/nea/firmament/compat/jade/HardstoneProvider.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package moe.nea.firmament.compat.jade
-
-import snownee.jade.api.BlockAccessor
-import snownee.jade.api.IBlockComponentProvider
-import snownee.jade.api.ITooltip
-import snownee.jade.api.config.IPluginConfig
-import net.minecraft.text.Text
-import net.minecraft.util.Identifier
-import moe.nea.firmament.util.SBData
-
-class HardstoneProvider : IBlockComponentProvider {
- override fun appendTooltip(tooltip: ITooltip, accessor: BlockAccessor, config: IPluginConfig) {
- if (SBData.isOnSkyblock) {
- tooltip.add(drillIcon)
- // TODO: override jade breakability test to include breaking power of drills on mining islands
- tooltip.append(Text.of("Breaking Power 5"))
- tooltip.replace(Identifier.of("minecraft", "stone"), Text.literal("Hard Stone")) // this doesnt work
- }
- }
-
- override fun getUid(): Identifier {
- return "hardstone".jadeId()
- }
-}
diff --git a/src/compat/jade/java/moe/nea/firmament/compat/jade/MithrilProvider.kt b/src/compat/jade/java/moe/nea/firmament/compat/jade/MithrilProvider.kt
deleted file mode 100644
index e5746fa..0000000
--- a/src/compat/jade/java/moe/nea/firmament/compat/jade/MithrilProvider.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package moe.nea.firmament.compat.jade
-
-import snownee.jade.api.BlockAccessor
-import snownee.jade.api.IBlockComponentProvider
-import snownee.jade.api.ITooltip
-import snownee.jade.api.config.IPluginConfig
-import net.minecraft.text.Text
-import net.minecraft.util.Identifier
-import moe.nea.firmament.util.SBData
-
-class MithrilProvider(val type: String) : IBlockComponentProvider {
- override fun appendTooltip(tooltip: ITooltip, accessor: BlockAccessor, config: IPluginConfig) {
- if (SBData.isOnSkyblock) { // why is there no utility to check if we are on an island with mithril am i dumb
- tooltip.add(drillIcon)
- tooltip.append(Text.of("Breaking Power 5"))
- tooltip.replace(Identifier.of("minecraft", type), Text.literal("Mithril $type")) // this doesnt work
- }
- }
-
- override fun getUid(): Identifier {
- return "mithril_$type".jadeId()
- }
-}
-
diff --git a/src/compat/jade/java/moe/nea/firmament/mixins/compat/jade/ElementAccessor.java b/src/compat/jade/java/moe/nea/firmament/mixins/compat/jade/ElementAccessor.java
new file mode 100644
index 0000000..1b58e3c
--- /dev/null
+++ b/src/compat/jade/java/moe/nea/firmament/mixins/compat/jade/ElementAccessor.java
@@ -0,0 +1,12 @@
+package moe.nea.firmament.mixins.compat.jade;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Accessor;
+import snownee.jade.api.ui.Element;
+import snownee.jade.api.ui.IElement;
+
+@Mixin(Element.class)
+public interface ElementAccessor {
+ @Accessor("align")
+ IElement.Align getAlign_firmament();
+}
diff --git a/src/main/kotlin/repo/MiningRepoData.kt b/src/main/kotlin/repo/MiningRepoData.kt
index 7c4a2ef..548c3e5 100644
--- a/src/main/kotlin/repo/MiningRepoData.kt
+++ b/src/main/kotlin/repo/MiningRepoData.kt
@@ -2,6 +2,10 @@ package moe.nea.firmament.repo
import io.github.moulberry.repo.IReloadable
import io.github.moulberry.repo.NEURepository
+import io.github.moulberry.repo.data.NEUItem
+import java.util.Collections
+import java.util.NavigableMap
+import java.util.TreeMap
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.serializer
@@ -19,14 +23,31 @@ import moe.nea.firmament.util.SkyBlockIsland
import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.util.mc.FirmamentDataComponentTypes
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
+import moe.nea.firmament.util.skyblockId
class MiningRepoData : IReloadable {
var customMiningAreas: Map<SkyBlockIsland, CustomMiningArea> = mapOf()
private set
var customMiningBlocks: List<CustomMiningBlock> = listOf()
private set
+ var toolsByBreakingPower: NavigableMap<BreakingPowerKey, NEUItem> = Collections.emptyNavigableMap()
+ private set
+ data class BreakingPowerKey(
+ val breakingPower: Int,
+ val itemId: SkyblockId? = null
+ ) {
+ companion object {
+ val COMPARATOR: Comparator<BreakingPowerKey> =
+ Comparator
+ .comparingInt<BreakingPowerKey> { it.breakingPower }
+ .thenComparing(Comparator.comparing(
+ { it.itemId },
+ nullsFirst(Comparator.naturalOrder<SkyblockId>())))
+ }
+ }
+
override fun reload(repo: NEURepository) {
customMiningAreas = repo.file("mining/custom_mining_areas.json")
?.kJson(serializer()) ?: mapOf()
@@ -35,6 +56,18 @@ class MiningRepoData : IReloadable {
.filter { it.path.endsWith(".json") }
.map { it.kJson(serializer<CustomMiningBlock>()) }
.toList()
+ toolsByBreakingPower = Collections.unmodifiableNavigableMap(
+ repo.items.items
+ .values
+ .asSequence()
+ .filter { it.breakingPower > 0 }
+ .associateTo(TreeMap<BreakingPowerKey, NEUItem>(BreakingPowerKey.COMPARATOR)) {
+ BreakingPowerKey(it.breakingPower, it.skyblockId) to it
+ })
+ }
+
+ fun getToolsThatCanBreak(breakingPower: Int): Collection<NEUItem> {
+ return toolsByBreakingPower.tailMap(BreakingPowerKey(breakingPower, null), true).values
}
@Serializable
diff --git a/src/main/kotlin/util/MC.kt b/src/main/kotlin/util/MC.kt
index ca3742d..a0d2fc0 100644
--- a/src/main/kotlin/util/MC.kt
+++ b/src/main/kotlin/util/MC.kt
@@ -99,7 +99,7 @@ object MC {
inline val soundManager get() = instance.soundManager
inline val player: ClientPlayerEntity? get() = TestUtil.unlessTesting { instance.player }
inline val camera: Entity? get() = instance.cameraEntity
- inline val stackInHand: ItemStack? get() = player?.inventory?.mainHandStack
+ inline val stackInHand: ItemStack get() = player?.inventory?.mainHandStack ?: ItemStack.EMPTY
inline val guiAtlasManager get() = instance.guiAtlasManager
inline val world: ClientWorld? get() = TestUtil.unlessTesting { instance.world }
inline val playerName: String? get() = player?.name?.unformattedString
diff --git a/src/main/kotlin/util/SkyblockId.kt b/src/main/kotlin/util/SkyblockId.kt
index a99afda..a31255c 100644
--- a/src/main/kotlin/util/SkyblockId.kt
+++ b/src/main/kotlin/util/SkyblockId.kt
@@ -34,7 +34,7 @@ import moe.nea.firmament.util.json.DashlessUUIDSerializer
*/
@JvmInline
@Serializable
-value class SkyblockId(val neuItem: String) {
+value class SkyblockId(val neuItem: String) : Comparable<SkyblockId> {
val identifier
get() = Identifier.of("skyblockitem",
neuItem.lowercase().replace(";", "__")
@@ -48,6 +48,10 @@ value class SkyblockId(val neuItem: String) {
return neuItem
}
+ override fun compareTo(other: SkyblockId): Int {
+ return neuItem.compareTo(other.neuItem)
+ }
+
/**
* A bazaar stock item id, as returned by the HyPixel bazaar api endpoint.
* These are not equivalent to the in-game ids, or the NEU repo ids, and in fact, do not refer to items, but instead