feat: Add tool/harvestability indicator

This commit is contained in:
Linnea Gräf
2025-03-06 23:36:57 +01:00
parent 8a4bfe24b3
commit 6ad259ca40
11 changed files with 115 additions and 140 deletions

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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)
}
private val CHECK: Text = Text.literal("")
private val X: Text = Text.literal("")
override fun getUid(): Identifier {
TODO("Not yet implemented")
}
override fun reload(manager: ResourceManager) {
TODO("Not yet implemented")
return Firmament.identifier("toolprovider")
}
}

View File

@@ -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))
}
}

View File

@@ -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()
}
}

View File

@@ -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()
}
}

View File

@@ -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()
}
}

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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

View File

@@ -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