@@ -23,32 +23,57 @@ import com.lambda.interaction.material.container.containers.EnderChestContainer
2323import com.lambda.module.Module
2424import com.lambda.module.tag.ModuleTag
2525import com.lambda.threading.runSafe
26+ import com.lambda.util.Describable
2627import com.lambda.util.InputUtils.isSatisfied
2728import com.lambda.util.KeyCode
29+ import com.lambda.util.NamedEnum
30+ import com.lambda.util.item.ItemStackUtils.bundleContents
2831import com.lambda.util.item.ItemStackUtils.shulkerBoxContents
32+ import com.lambda.util.item.ItemUtils.bundles
2933import com.lambda.util.item.ItemUtils.shulkerBoxes
34+ import com.lambda.util.text.buildText
35+ import com.lambda.util.text.literal
3036import net.minecraft.block.ShulkerBoxBlock
3137import net.minecraft.client.font.TextRenderer
32- import net.minecraft.client.gui.DrawContext
33- import net.minecraft.client.gui.tooltip.TooltipComponent
3438import net.minecraft.client.gl.RenderPipelines
39+ import net.minecraft.client.gui.DrawContext
40+ import net.minecraft.client.gui.render.state.GuiRenderState
41+ import net.minecraft.client.gui.render.state.ItemGuiElementRenderState
42+ import net.minecraft.client.gui.render.state.TextGuiElementRenderState
3543import net.minecraft.client.gui.screen.ingame.HandledScreen
44+ import net.minecraft.client.gui.tooltip.TooltipComponent
45+ import net.minecraft.client.render.item.KeyedItemRenderState
46+ import net.minecraft.entity.LivingEntity
3647import net.minecraft.item.BlockItem
48+ import net.minecraft.item.ItemDisplayContext
3749import net.minecraft.item.ItemStack
3850import net.minecraft.item.Items
3951import net.minecraft.item.tooltip.TooltipData
4052import net.minecraft.screen.slot.Slot
4153import net.minecraft.util.Colors
4254import net.minecraft.util.DyeColor
4355import net.minecraft.util.Identifier
56+ import net.minecraft.world.World
57+ import org.joml.Matrix3x2f
58+ import kotlin.math.max
59+
4460
4561object ContainerPreview : Module(
4662 name = " ContainerPreview" ,
4763 description = " Renders shulker box contents visually in tooltips" ,
4864 tag = ModuleTag .RENDER ,
4965) {
50- private val lockKey by setting(" Lock Key" , Bind (KeyCode .LeftShift .code, 0 , - 1 ), " Key to lock the tooltip in place for item interaction" )
51- private val colorTint by setting(" Color Tint" , true , " Tint the background with the shulker box color" )
66+ private val lockKey by setting(" Lock Key" , Bind (KeyCode .LeftShift .code, 0 , - 1 ), " Key to lock the tooltip in place for item interaction" ).group(Group .ContainerTooltip )
67+ private val colorTint by setting(" Color Tint" , true , " Tint the background with the shulker box color" ).group(Group .ContainerTooltip )
68+
69+ private val contentPreview by setting(" Content Preview" , true , " Show a preview of the most common item in a container on the container item in inventories" ).group(Group .ContentPreview )
70+ private val previewItemScale by setting(" Item Scale" , 13f , 1f .. 32f , 0.1f , " Scale of the item icons on a container item" ) { contentPreview }.group(Group .ContentPreview )
71+ private val previewItemXOffset by setting(" Item X Offset" , 0f , - 32f .. 32f , 0.1f , " X offset of the item icons on a container item" ) { contentPreview }.group(Group .ContentPreview )
72+ private val previewItemYOffset by setting(" Item Y Offset" , 0f , - 32f .. 32f , 0.1f , " Y offset of the item icons on a container item" ) { contentPreview }.group(Group .ContentPreview )
73+ private val previewItemWeightedCount by setting(" Weighted Count" , true , description = " Count items for preview in containers relative to max stack size" ) { contentPreview }.group(Group .ContentPreview )
74+ .onValueChange { _, _ ->
75+ containerCache.clear()
76+ }
5277
5378 private val background = Identifier .ofVanilla(" textures/gui/container/shulker_box.png" )
5479
@@ -65,6 +90,13 @@ object ContainerPreview : Module(
6590 var isRenderingSubTooltip: Boolean = false
6691 private set
6792
93+ // Cache for container contents summary // Cache size is limited to 200 entries
94+ val containerCache = object : LinkedHashMap <Int , ContainerPreviewInfo >(16 , 0.75f , true ) {
95+ override fun removeEldestEntry (eldest : MutableMap .MutableEntry <Int , ContainerPreviewInfo >): Boolean {
96+ return size > 200
97+ }
98+ }
99+
68100 private const val ROWS = 3
69101 private const val COLS = 9
70102 private const val SLOT_SIZE = 18
@@ -325,6 +357,28 @@ object ContainerPreview : Module(
325357 return null
326358 }
327359
360+ private fun getPreviewItemForContainer (container : ItemStack ): ContainerPreviewInfo {
361+ val hash = container.hashCode()
362+
363+ return containerCache.computeIfAbsent(hash) {
364+ val contents = container.shulkerBoxContents + container.bundleContents
365+ if (contents.isEmpty()) return @computeIfAbsent ContainerPreviewInfo (null , false )
366+
367+ val group = contents.filter { stack -> stack.item != Items .AIR }
368+ .groupBy { stack -> stack.item }
369+ .map { (item, stacks) ->
370+ val stackWeight = if (previewItemWeightedCount) 64f / item.maxCount else 1f
371+ stacks.first() to (stacks.sumOf { it.count } * stackWeight)
372+ }
373+ val unique = group.size
374+ val mostCommon = group.maxByOrNull { (_, weightedCount) -> weightedCount }?.let { (stack, count) ->
375+ stack.copyWithCount(max(1 , count.toInt().coerceAtMost(stack.maxCount)))
376+ }
377+
378+ ContainerPreviewInfo (mostCommon, unique > 1 )
379+ }
380+ }
381+
328382 @JvmStatic
329383 fun isShulkerBox (stack : ItemStack ) = stack.item in shulkerBoxes
330384
@@ -334,9 +388,57 @@ object ContainerPreview : Module(
334388 @JvmStatic
335389 fun isPreviewableContainer (stack : ItemStack ) = isShulkerBox(stack) || isEnderChest(stack)
336390
391+ @JvmStatic
392+ fun isBundle (stack : ItemStack ) = stack.item in bundles
393+
394+ @JvmStatic
395+ fun drawOnItem (drawContext : DrawContext , state : GuiRenderState , entity : LivingEntity ? , world : World ? , stack : ItemStack , x : Int , y : Int , seed : Int ) {
396+ if (! contentPreview) return
397+ if (! isShulkerBox(stack) && ! isBundle(stack)) return
398+ val preview = getPreviewItemForContainer(stack)
399+ if (preview.stack == null ) return
400+
401+ // Apply scaling
402+ val scale = previewItemScale / 16.0f
403+ val itemMatrix = Matrix3x2f (drawContext.matrices)
404+
405+ // Required to center the icon correctly, due to how the item gets centered by the renderer
406+ val shift = 8 * (1 - scale) // 0 at scale 1.0, 8 at scale 0.0
407+
408+ val newScreenX = ((x + previewItemXOffset + shift) / scale).toInt()
409+ val newScreenY = ((y + previewItemYOffset + shift) / scale).toInt()
410+
411+ itemMatrix.scale(scale, scale)
412+
413+ val keyedItemRenderState = KeyedItemRenderState ()
414+ mc.itemModelManager.clearAndUpdate(keyedItemRenderState, preview.stack, ItemDisplayContext .GUI , world, entity, seed)
415+
416+ state.addItem(
417+ ItemGuiElementRenderState (
418+ preview.stack.item.name.toString(), itemMatrix, keyedItemRenderState, newScreenX, newScreenY, drawContext.scissorStack.peekLast()
419+ )
420+ )
421+ if (preview.hasMore) {
422+ state.addText(
423+ TextGuiElementRenderState (
424+ mc.textRenderer, buildText {
425+ literal(" +" )
426+ }.asOrderedText(), itemMatrix, newScreenX + 14 , newScreenY - 2 , - 1 , Integer .MIN_VALUE , true , false , drawContext.scissorStack.peekLast()
427+ )
428+ )
429+ }
430+ }
431+
432+ enum class Group (override val displayName : String , override val description : String ) : NamedEnum, Describable {
433+ ContentPreview (" Preview" , " Settings related to the item preview rendered on container items in inventories" ),
434+ ContainerTooltip (" Container" , " Settings related to container tooltip previews" )
435+ }
436+
337437 open class ContainerComponent (val stack : ItemStack ) : TooltipData, TooltipComponent {
338438 override fun drawItems (textRenderer : TextRenderer , x : Int , y : Int , width : Int , height : Int , context : DrawContext ) {}
339439 override fun getHeight (textRenderer : TextRenderer ): Int = 0
340440 override fun getWidth (textRenderer : TextRenderer ): Int = 0
341441 }
442+
443+ data class ContainerPreviewInfo (val stack : ItemStack ? , val hasMore : Boolean )
342444}
0 commit comments