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" ktor = "3.0.3"
# Update from https://repo.nea.moe/#/releases/moe/nea/neurepoparser # 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 # Update from https://github.com/HotswapProjects/HotswapAgent/releases
# TODO: bump to 2.0.1 # 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 package moe.nea.firmament.compat.jade
import com.google.common.cache.Cache import java.util.Optional
import com.google.common.cache.CacheBuilder import java.util.function.UnaryOperator
import com.google.common.collect.ImmutableList
import com.google.common.collect.Maps
import java.util.concurrent.TimeUnit
import snownee.jade.addon.harvest.ToolHandler
import snownee.jade.api.BlockAccessor import snownee.jade.api.BlockAccessor
import snownee.jade.api.IBlockComponentProvider import snownee.jade.api.IBlockComponentProvider
import snownee.jade.api.ITooltip import snownee.jade.api.ITooltip
import snownee.jade.api.JadeIds
import snownee.jade.api.config.IPluginConfig import snownee.jade.api.config.IPluginConfig
import net.minecraft.block.Block import snownee.jade.api.theme.IThemeHelper
import net.minecraft.block.BlockState 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.item.ItemStack
import net.minecraft.resource.ResourceManager import net.minecraft.item.Items
import net.minecraft.resource.SynchronousResourceReloader
import net.minecraft.text.Text import net.minecraft.text.Text
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.math.Vec2f 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 {
class DrillToolProvider : IBlockComponentProvider, SynchronousResourceReloader { override fun appendTooltip(
val resultCache: Cache<BlockState, ImmutableList<ItemStack>> = CacheBuilder.newBuilder().expireAfterAccess(5, TimeUnit.MINUTES).build() tooltip: ITooltip,
val toolHandlers: MutableMap<Identifier, ToolHandler> = Maps.newLinkedHashMap() accessor: BlockAccessor,
private val shearableBlocks: MutableList<Block> = mutableListOf() p2: IPluginConfig?
private val checkIcon: Text = Text.literal("") ) {
private val xIcon: Text = Text.literal("") val customBlock = CustomFakeBlockProvider.getCustomBlock(accessor) ?: return
private val itemSize = Vec2f(10f, 0f) if (customBlock.breakingPower <= 0) return
val tool = RepoManager.miningData.getToolsThatCanBreak(customBlock.breakingPower).firstOrNull()
@Synchronized ?.asItemStack() ?: return
fun registerHandler(handler: ToolHandler) { tooltip.replace(JadeIds.MC_HARVEST_TOOL, UnaryOperator { elements ->
toolHandlers.put(handler.uid, handler) 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) { fun canHarvestIndicator(canHarvest: Boolean, align: IElement.Align): IElement {
TODO("Not yet implemented") 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 { override fun getUid(): Identifier {
TODO("Not yet implemented") return Firmament.identifier("toolprovider")
}
override fun reload(manager: ResourceManager) {
TODO("Not yet implemented")
} }
} }

View File

@@ -1,5 +1,6 @@
package moe.nea.firmament.compat.jade package moe.nea.firmament.compat.jade
import snownee.jade.addon.harvest.HarvestToolProvider
import snownee.jade.api.IWailaClientRegistration import snownee.jade.api.IWailaClientRegistration
import snownee.jade.api.IWailaCommonRegistration import snownee.jade.api.IWailaCommonRegistration
import snownee.jade.api.IWailaPlugin import snownee.jade.api.IWailaPlugin
@@ -18,6 +19,7 @@ class FirmamentJadePlugin : IWailaPlugin {
override fun registerClient(registration: IWailaClientRegistration) { override fun registerClient(registration: IWailaClientRegistration) {
registration.registerBlockComponent(CustomMiningHardnessProvider, Block::class.java) registration.registerBlockComponent(CustomMiningHardnessProvider, Block::class.java)
registration.registerProgressClient(SkyblockProgressProvider()) registration.registerProgressClient(SkyblockProgressProvider())
registration.registerBlockComponent(DrillToolProvider(), Block::class.java)
registration.addRayTraceCallback(CustomFakeBlockProvider(registration)) 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.IReloadable
import io.github.moulberry.repo.NEURepository 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.Serializable
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
import kotlinx.serialization.serializer 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.SkyblockId
import moe.nea.firmament.util.mc.FirmamentDataComponentTypes import moe.nea.firmament.util.mc.FirmamentDataComponentTypes
import moe.nea.firmament.util.mc.displayNameAccordingToNbt import moe.nea.firmament.util.mc.displayNameAccordingToNbt
import moe.nea.firmament.util.skyblockId
class MiningRepoData : IReloadable { class MiningRepoData : IReloadable {
var customMiningAreas: Map<SkyBlockIsland, CustomMiningArea> = mapOf() var customMiningAreas: Map<SkyBlockIsland, CustomMiningArea> = mapOf()
private set private set
var customMiningBlocks: List<CustomMiningBlock> = listOf() var customMiningBlocks: List<CustomMiningBlock> = listOf()
private set 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) { override fun reload(repo: NEURepository) {
customMiningAreas = repo.file("mining/custom_mining_areas.json") customMiningAreas = repo.file("mining/custom_mining_areas.json")
?.kJson(serializer()) ?: mapOf() ?.kJson(serializer()) ?: mapOf()
@@ -35,6 +56,18 @@ class MiningRepoData : IReloadable {
.filter { it.path.endsWith(".json") } .filter { it.path.endsWith(".json") }
.map { it.kJson(serializer<CustomMiningBlock>()) } .map { it.kJson(serializer<CustomMiningBlock>()) }
.toList() .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 @Serializable

View File

@@ -99,7 +99,7 @@ object MC {
inline val soundManager get() = instance.soundManager inline val soundManager get() = instance.soundManager
inline val player: ClientPlayerEntity? get() = TestUtil.unlessTesting { instance.player } inline val player: ClientPlayerEntity? get() = TestUtil.unlessTesting { instance.player }
inline val camera: Entity? get() = instance.cameraEntity 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 guiAtlasManager get() = instance.guiAtlasManager
inline val world: ClientWorld? get() = TestUtil.unlessTesting { instance.world } inline val world: ClientWorld? get() = TestUtil.unlessTesting { instance.world }
inline val playerName: String? get() = player?.name?.unformattedString inline val playerName: String? get() = player?.name?.unformattedString

View File

@@ -34,7 +34,7 @@ import moe.nea.firmament.util.json.DashlessUUIDSerializer
*/ */
@JvmInline @JvmInline
@Serializable @Serializable
value class SkyblockId(val neuItem: String) { value class SkyblockId(val neuItem: String) : Comparable<SkyblockId> {
val identifier val identifier
get() = Identifier.of("skyblockitem", get() = Identifier.of("skyblockitem",
neuItem.lowercase().replace(";", "__") neuItem.lowercase().replace(";", "__")
@@ -48,6 +48,10 @@ value class SkyblockId(val neuItem: String) {
return neuItem 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. * 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 * These are not equivalent to the in-game ids, or the NEU repo ids, and in fact, do not refer to items, but instead