diff options
author | Linnea Gräf <nea@nea.moe> | 2025-03-06 23:36:57 +0100 |
---|---|---|
committer | Linnea Gräf <nea@nea.moe> | 2025-03-08 16:01:00 +0100 |
commit | 6ad259ca405d58f1956d67d7daeb05ae8590ca62 (patch) | |
tree | f749625557c8eca45b1c7cc5f1f035f76935a7ff | |
parent | 8a4bfe24b364612e3e783ed9569082b130aa2bfc (diff) | |
download | Firmament-6ad259ca405d58f1956d67d7daeb05ae8590ca62.tar.gz Firmament-6ad259ca405d58f1956d67d7daeb05ae8590ca62.tar.bz2 Firmament-6ad259ca405d58f1956d67d7daeb05ae8590ca62.zip |
feat: Add tool/harvestability indicator
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 |