paint and remove armor dye in painting table

This commit is contained in:
YuRaNnNzZZ 2023-10-30 15:57:39 +03:00
parent 6c9058e72d
commit 94e1ce6d8e
Signed by: YuRaNnNzZZ
GPG Key ID: 5F71738C85A6006D
5 changed files with 162 additions and 63 deletions

View File

@ -10,6 +10,7 @@ import net.minecraft.world.item.crafting.Ingredient
import ru.dbotthepony.mc.otm.core.registryName
import ru.dbotthepony.mc.otm.core.stream
import ru.dbotthepony.mc.otm.datagen.modLocation
import ru.dbotthepony.mc.otm.recipe.PainterArmorDyeRecipe
import ru.dbotthepony.mc.otm.recipe.PainterRecipe
import ru.dbotthepony.mc.otm.registry.MItems
import ru.dbotthepony.mc.otm.registry.MRegistry
@ -283,4 +284,9 @@ fun addPainterRecipes(consumer: RecipeOutput) {
striped(consumer, "stairs", MRegistry.TRITANIUM_STRIPED_STAIRS.itemsWithColor, MRegistry.TRITANIUM_STAIRS.items)
striped(consumer, "walls", MRegistry.TRITANIUM_STRIPED_WALL.itemsWithColor, MRegistry.TRITANIUM_WALL.items)
striped(consumer, "slabs", MRegistry.TRITANIUM_STRIPED_SLAB.itemsWithColor, MRegistry.TRITANIUM_SLAB.items)
for (color in DyeColor.entries) {
consumer.accept(PainterArmorDyeRecipe(mapOf(color to 1)).toFinished(modLocation("painter/armor_dye_" + color.getName().lowercase())))
}
consumer.accept(PainterArmorDyeRecipe(mapOf(null to 15)).toFinished(modLocation("painter/armor_clear_dye")))
}

View File

@ -32,6 +32,7 @@ import ru.dbotthepony.mc.otm.core.map
import ru.dbotthepony.mc.otm.core.math.RGBAColor
import ru.dbotthepony.mc.otm.core.util.CreativeMenuItemComparator
import ru.dbotthepony.mc.otm.menu.decorative.PainterMenu
import ru.dbotthepony.mc.otm.recipe.PainterRecipe
class PainterScreen(menu: PainterMenu, inventory: Inventory, title: Component) : MatteryScreen<PainterMenu>(menu, inventory, title) {
private inner class Bar(parent: EditablePanel<*>, val dye: DyeColor?) : EditablePanel<PainterScreen>(this@PainterScreen, parent, width = 5f) {
@ -130,8 +131,19 @@ class PainterScreen(menu: PainterMenu, inventory: Inventory, title: Component) :
buttons.forEach { it.remove() }
buttons.clear()
for (recipe in menu.possibleRecipes.sortedWith(CreativeMenuItemComparator.map { it.value.output.item })) {
object : LargeRectangleButtonPanel<PainterScreen>(this@PainterScreen, canvas.canvas, icon = ItemStackIcon(recipe.value.output, 14f, 14f).fixed()) {
val recipes = menu.possibleRecipes
.filter { !it.value.ingredients.isEmpty() }
.sortedWith(CreativeMenuItemComparator.map { it.value.getOutput(menu.inputContainer).item })
.plus(
menu.possibleRecipes
.filter { it.value.ingredients.isEmpty() }
.sortedBy { it.id }
)
for (recipe in recipes) {
val recipeOutput = recipe.value.getOutput(menu.inputContainer)
object : LargeRectangleButtonPanel<PainterScreen>(this@PainterScreen, canvas.canvas, icon = ItemStackIcon(recipeOutput, 14f, 14f).fixed()) {
init {
buttons.add(this)
dockRight = 1f
@ -143,7 +155,7 @@ class PainterScreen(menu: PainterMenu, inventory: Inventory, title: Component) :
set(value) {}
override fun innerRenderTooltips(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
val list = getTooltipFromItem(minecraft!!, recipe.value.output)
val list = getTooltipFromItem(minecraft!!, recipeOutput)
recipe.value.dyes.forEach {
val (dye, amount) = it

View File

@ -3,8 +3,7 @@ package ru.dbotthepony.mc.otm.menu.decorative
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.DyeColor
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.*
import net.minecraft.world.item.crafting.RecipeHolder
import net.minecraft.world.level.material.Fluids
import net.minecraftforge.common.capabilities.ForgeCapabilities
@ -17,19 +16,14 @@ import ru.dbotthepony.mc.otm.core.ISubscriptable
import ru.dbotthepony.mc.otm.core.addAll
import ru.dbotthepony.mc.otm.core.collect.SupplierMap
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.find
import ru.dbotthepony.mc.otm.core.collect.maybe
import ru.dbotthepony.mc.otm.core.ifPresentK
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.map
import ru.dbotthepony.mc.otm.core.util.CreativeMenuItemComparator
import ru.dbotthepony.mc.otm.core.util.ResourceLocationValueCodec
import ru.dbotthepony.mc.otm.isClientThread
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput
import ru.dbotthepony.mc.otm.recipe.PainterRecipe
import ru.dbotthepony.mc.otm.recipe.AbstractPainterRecipe
import ru.dbotthepony.mc.otm.registry.MMenus
import ru.dbotthepony.mc.otm.registry.MRecipes
import java.util.*
@ -48,7 +42,7 @@ class PainterMenu(
val inputContainer = MatteryContainer(::rescan, 1)
val outputContainer = MatteryContainer(1)
private var lastRecipe: RecipeHolder<PainterRecipe>? = null
private var lastRecipe: RecipeHolder<out AbstractPainterRecipe>? = null
var selectedRecipe by mSynchronizer.Field(null, ResourceLocationValueCodec.nullable).also { it.addListener { rescan() } }
val isBulk = BooleanInputWithFeedback(this, tile?.let { it::isBulk })
@ -59,6 +53,10 @@ class PainterMenu(
val inputSlot = object : MatterySlot(inputContainer, 0) {
override fun mayPlace(itemStack: ItemStack): Boolean {
if (!itemStack.isEmpty && itemStack.item is DyeableArmorItem) {
return super.mayPlace(itemStack)
}
return super.mayPlace(itemStack) && inventory.player.level().recipeManager.byType(MRecipes.PAINTER).values.any { it.value.input.test(itemStack) }
}
}
@ -126,18 +124,24 @@ class PainterMenu(
}
val listeners = ISubscriptable.Impl<Unit>()
val possibleRecipes = ArrayList<RecipeHolder<PainterRecipe>>()
val possibleRecipes = ArrayList<RecipeHolder<out AbstractPainterRecipe>>()
private fun rescan() {
possibleRecipes.clear()
possibleRecipes.addAll(inventory.player.level().recipeManager.byType(MRecipes.PAINTER).values.iterator().filter { it.value.input.test(inputContainer[0]) })
if (!inputContainer[0].isEmpty && inputContainer[0].item is DyeableArmorItem) {
possibleRecipes.addAll(inventory.player.level().recipeManager.byType(MRecipes.PAINTER_ARMOR_DYE).values)
}
listeners.accept(Unit)
if (tile !is PainterBlockEntity) return
if (inputContainer.isEmpty || selectedRecipe == null) {
outputContainer.clearContent()
} else {
val recipe = inventory.player.level().recipeManager.byType(MRecipes.PAINTER)[selectedRecipe]
// val recipe = inventory.player.level().recipeManager.byType(MRecipes.PAINTER)[selectedRecipe]
val recipe = inventory.player.level().recipeManager.byKey(selectedRecipe).get() as RecipeHolder<AbstractPainterRecipe>?
if (recipe == null || !recipe.value.canCraft(dyeStoredDirect) || !recipe.value.matches(inputContainer, inventory.player.level())) {
outputContainer.clearContent()

View File

@ -1,6 +1,5 @@
package ru.dbotthepony.mc.otm.recipe
import com.google.common.collect.ImmutableMap
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap
@ -12,8 +11,7 @@ import net.minecraft.data.recipes.FinishedRecipe
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.StringRepresentable
import net.minecraft.world.Container
import net.minecraft.world.item.DyeColor
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.*
import net.minecraft.world.item.crafting.Ingredient
import net.minecraft.world.item.crafting.Recipe
import net.minecraft.world.item.crafting.RecipeSerializer
@ -29,24 +27,15 @@ import ru.dbotthepony.mc.otm.data.PredicatedCodecList
import ru.dbotthepony.mc.otm.data.minRange
import ru.dbotthepony.mc.otm.registry.MItems
import ru.dbotthepony.mc.otm.registry.MRecipes
import java.util.EnumMap
import java.util.function.Predicate
import java.util.stream.Collector
import java.util.stream.Collectors
class PainterRecipe(
val input: Ingredient,
val output: ItemStack,
abstract class AbstractPainterRecipe(
dyes: Map<out DyeColor?, Int>
) : Recipe<Container> {
constructor(
input: Ingredient,
output: ItemStack,
dyes: Set<DyeColor?>
) : this(input, output, Object2IntArrayMap<DyeColor?>().also { map -> dyes.forEach { map[it] = 1 } })
val dyes: Object2IntMap<DyeColor?> = Object2IntMaps.unmodifiable(Object2IntArrayMap(dyes))
override fun getToastSymbol(): ItemStack = ItemStack(MItems.PAINTER)
fun canCraft(storedDyes: Map<out DyeColor?, Int>): Boolean {
if (isIncomplete) return false
if (dyes.isEmpty() || dyes.values.none { it > 0 }) return true
@ -63,6 +52,52 @@ class PainterRecipe(
return true
}
override fun canCraftInDimensions(p_43999_: Int, p_44000_: Int): Boolean = true
override fun isSpecial(): Boolean = true
open fun getOutput(container: Container): ItemStack {
if (container.isEmpty) return ItemStack.EMPTY
return container[0].copy()
}
enum class DyeColorWrapper(private val refName: String, val key: DyeColor?) : StringRepresentable {
WHITE("white", DyeColor.WHITE),
ORANGE("orange", DyeColor.ORANGE),
MAGENTA("magenta", DyeColor.MAGENTA),
LIGHT_BLUE("light_blue", DyeColor.LIGHT_BLUE),
YELLOW("yellow", DyeColor.YELLOW),
LIME("lime", DyeColor.LIME),
PINK("pink", DyeColor.PINK),
GRAY("gray", DyeColor.GRAY),
LIGHT_GRAY("light_gray", DyeColor.LIGHT_GRAY),
CYAN("cyan", DyeColor.CYAN),
PURPLE("purple", DyeColor.PURPLE),
BLUE("blue", DyeColor.BLUE),
BROWN("brown", DyeColor.BROWN),
GREEN("green", DyeColor.GREEN),
RED("red", DyeColor.RED),
BLACK("black", DyeColor.BLACK),
WATER("water", null);
override fun getSerializedName(): String {
return refName
}
}
}
class PainterRecipe(
val input: Ingredient,
val output: ItemStack,
dyes: Map<out DyeColor?, Int>
) : AbstractPainterRecipe(dyes) {
constructor(
input: Ingredient,
output: ItemStack,
dyes: Set<DyeColor?>
) : this(input, output, Object2IntArrayMap<DyeColor?>().also { map -> dyes.forEach { map[it] = 1 } })
override fun matches(p_44002_: Container, p_44003_: Level): Boolean {
if (isIncomplete) return false
return input.test(p_44002_[0])
@ -90,14 +125,6 @@ class PainterRecipe(
}
}
override fun canCraftInDimensions(p_43999_: Int, p_44000_: Int): Boolean {
return true
}
override fun isSpecial(): Boolean {
return true
}
override fun getResultItem(p_267052_: RegistryAccess): ItemStack {
return output
}
@ -110,36 +137,12 @@ class PainterRecipe(
return MRecipes.PAINTER
}
override fun getToastSymbol(): ItemStack {
return ItemStack(MItems.PAINTER)
}
fun toFinished(id: ResourceLocation): FinishedRecipe {
return SERIALIZER.toFinished(this, id)
}
private enum class DyeColorWrapper(val refName: String, val key: DyeColor?) : StringRepresentable {
WHITE("white", DyeColor.WHITE),
ORANGE("orange", DyeColor.ORANGE),
MAGENTA("magenta", DyeColor.MAGENTA),
LIGHT_BLUE("light_blue", DyeColor.LIGHT_BLUE),
YELLOW("yellow", DyeColor.YELLOW),
LIME("lime", DyeColor.LIME),
PINK("pink", DyeColor.PINK),
GRAY("gray", DyeColor.GRAY),
LIGHT_GRAY("light_gray", DyeColor.LIGHT_GRAY),
CYAN("cyan", DyeColor.CYAN),
PURPLE("purple", DyeColor.PURPLE),
BLUE("blue", DyeColor.BLUE),
BROWN("brown", DyeColor.BROWN),
GREEN("green", DyeColor.GREEN),
RED("red", DyeColor.RED),
BLACK("black", DyeColor.BLACK),
WATER("water", null);
override fun getSerializedName(): String {
return refName
}
override fun getOutput(container: Container): ItemStack {
return this.output
}
companion object {
@ -160,3 +163,75 @@ class PainterRecipe(
}
}
}
class PainterArmorDyeRecipe(
dyes: Map<out DyeColor?, Int>
) : AbstractPainterRecipe(dyes) {
constructor(
dyes: Set<DyeColor?>
) : this(Object2IntArrayMap<DyeColor?>().also { map -> dyes.forEach { map[it] = 1 } })
override fun matches(contaier: Container, level: Level): Boolean {
if (this.isIncomplete) return false
val stack = contaier[0]
return (!stack.isEmpty && stack.item is DyeableArmorItem)
}
override fun assemble(container: Container, registry: RegistryAccess): ItemStack {
var output = container[0].copy()
dyes.forEach { entry ->
if (entry.key == null) {
(output.item as DyeableLeatherItem).clearColor(output)
} else {
output = DyeableLeatherItem.dyeArmor(output.copy(), listOf(DyeItem.byColor(entry.key!!)))
}
}
return output
}
override fun getResultItem(registry: RegistryAccess): ItemStack = ItemStack.EMPTY
override fun getSerializer(): RecipeSerializer<*> = SERIALIZER
override fun getType(): RecipeType<*> = MRecipes.PAINTER_ARMOR_DYE
override fun isIncomplete(): Boolean = dyes.isEmpty()
fun toFinished(id: ResourceLocation): FinishedRecipe {
return SERIALIZER.toFinished(this, id)
}
override fun getOutput(container: Container): ItemStack {
if (container.isEmpty) return ItemStack.EMPTY
var output = container[0].copy()
dyes.forEach { entry ->
if (entry.key == null) {
(output.item as DyeableLeatherItem).clearColor(output)
} else {
output = DyeableLeatherItem.dyeArmor(output.copy(), listOf(DyeItem.byColor(entry.key!!)))
}
}
return output
}
companion object {
private val wrapperCodec = StringRepresentable.fromEnum(DyeColorWrapper::values)
val SERIALIZER = Codec2RecipeSerializer<PainterArmorDyeRecipe> { _ ->
RecordCodecBuilder.create {
it.group(
PredicatedCodecList<Map<DyeColorWrapper, Int>>(
wrapperCodec.xmap({ mapOf(it to 1) }, { it.keys.first() }) to Predicate { it.keys.size == 1 && it.values.first() == 1 },
Codec.list(wrapperCodec).xmap({ it.associateWith { 1 } }, { ArrayList(it.keys) }) to Predicate { it.values.all { it == 1 } },
Codec.unboundedMap(wrapperCodec, Codec.INT.minRange(1)) to Predicate { true }
).fieldOf("dyes").xmap({ it.mapKeys { it.key.key } }, { it.mapKeys { k -> DyeColorWrapper.entries.first { k.key == it.key } } }).forGetter(PainterArmorDyeRecipe::dyes),
).apply(it, ::PainterArmorDyeRecipe)
}
}
}
}

View File

@ -34,6 +34,7 @@ object MRecipes {
val PLATE_PRESS by register<PlatePressRecipe>("plate_press")
val PAINTER by register<PainterRecipe>("painter")
val PAINTER_ARMOR_DYE by register<PainterArmorDyeRecipe>("painter_armor_dye")
val MATTER_ENTANGLER by register<IMatterEntanglerRecipe>("matter_entangler")
val MICROWAVE by register<MicrowaveRecipe>("microwave")
@ -43,6 +44,7 @@ object MRecipes {
serializers.register("upgrade") { UpgradeRecipe.CODEC }
serializers.register("hammer_priming") { ExplosiveHammerPrimingRecipe.CODEC }
serializers.register("painter") { PainterRecipe.SERIALIZER }
serializers.register("painter_armor_dye") { PainterArmorDyeRecipe.SERIALIZER }
serializers.register("matter_entangler") { MatterEntanglerRecipe.SERIALIZER }
serializers.register("matter_entangler_energetic") { MatterEntanglerRecipe.ENERGY_SERIALIZER }
serializers.register("matter_entangler_matter") { MatterEntanglerRecipe.MATTER_SERIALIZER }