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.registryName
import ru.dbotthepony.mc.otm.core.stream import ru.dbotthepony.mc.otm.core.stream
import ru.dbotthepony.mc.otm.datagen.modLocation 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.recipe.PainterRecipe
import ru.dbotthepony.mc.otm.registry.MItems import ru.dbotthepony.mc.otm.registry.MItems
import ru.dbotthepony.mc.otm.registry.MRegistry 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, "stairs", MRegistry.TRITANIUM_STRIPED_STAIRS.itemsWithColor, MRegistry.TRITANIUM_STAIRS.items)
striped(consumer, "walls", MRegistry.TRITANIUM_STRIPED_WALL.itemsWithColor, MRegistry.TRITANIUM_WALL.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) 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.math.RGBAColor
import ru.dbotthepony.mc.otm.core.util.CreativeMenuItemComparator import ru.dbotthepony.mc.otm.core.util.CreativeMenuItemComparator
import ru.dbotthepony.mc.otm.menu.decorative.PainterMenu 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) { 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) { 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.forEach { it.remove() }
buttons.clear() buttons.clear()
for (recipe in menu.possibleRecipes.sortedWith(CreativeMenuItemComparator.map { it.value.output.item })) { val recipes = menu.possibleRecipes
object : LargeRectangleButtonPanel<PainterScreen>(this@PainterScreen, canvas.canvas, icon = ItemStackIcon(recipe.value.output, 14f, 14f).fixed()) { .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 { init {
buttons.add(this) buttons.add(this)
dockRight = 1f dockRight = 1f
@ -143,7 +155,7 @@ class PainterScreen(menu: PainterMenu, inventory: Inventory, title: Component) :
set(value) {} set(value) {}
override fun innerRenderTooltips(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float): Boolean { 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 { recipe.value.dyes.forEach {
val (dye, amount) = it 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.SimpleContainer
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.DyeColor import net.minecraft.world.item.*
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.crafting.RecipeHolder import net.minecraft.world.item.crafting.RecipeHolder
import net.minecraft.world.level.material.Fluids import net.minecraft.world.level.material.Fluids
import net.minecraftforge.common.capabilities.ForgeCapabilities 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.addAll
import ru.dbotthepony.mc.otm.core.collect.SupplierMap import ru.dbotthepony.mc.otm.core.collect.SupplierMap
import ru.dbotthepony.mc.otm.core.collect.filter 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.collect.maybe
import ru.dbotthepony.mc.otm.core.ifPresentK
import ru.dbotthepony.mc.otm.core.isNotEmpty 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.core.util.ResourceLocationValueCodec
import ru.dbotthepony.mc.otm.isClientThread
import ru.dbotthepony.mc.otm.menu.MatteryMenu import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput 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.MMenus
import ru.dbotthepony.mc.otm.registry.MRecipes import ru.dbotthepony.mc.otm.registry.MRecipes
import java.util.* import java.util.*
@ -48,7 +42,7 @@ class PainterMenu(
val inputContainer = MatteryContainer(::rescan, 1) val inputContainer = MatteryContainer(::rescan, 1)
val outputContainer = MatteryContainer(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() } } var selectedRecipe by mSynchronizer.Field(null, ResourceLocationValueCodec.nullable).also { it.addListener { rescan() } }
val isBulk = BooleanInputWithFeedback(this, tile?.let { it::isBulk }) val isBulk = BooleanInputWithFeedback(this, tile?.let { it::isBulk })
@ -59,6 +53,10 @@ class PainterMenu(
val inputSlot = object : MatterySlot(inputContainer, 0) { val inputSlot = object : MatterySlot(inputContainer, 0) {
override fun mayPlace(itemStack: ItemStack): Boolean { 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) } 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 listeners = ISubscriptable.Impl<Unit>()
val possibleRecipes = ArrayList<RecipeHolder<PainterRecipe>>() val possibleRecipes = ArrayList<RecipeHolder<out AbstractPainterRecipe>>()
private fun rescan() { private fun rescan() {
possibleRecipes.clear() possibleRecipes.clear()
possibleRecipes.addAll(inventory.player.level().recipeManager.byType(MRecipes.PAINTER).values.iterator().filter { it.value.input.test(inputContainer[0]) }) 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) listeners.accept(Unit)
if (tile !is PainterBlockEntity) return if (tile !is PainterBlockEntity) return
if (inputContainer.isEmpty || selectedRecipe == null) { if (inputContainer.isEmpty || selectedRecipe == null) {
outputContainer.clearContent() outputContainer.clearContent()
} else { } 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())) { if (recipe == null || !recipe.value.canCraft(dyeStoredDirect) || !recipe.value.matches(inputContainer, inventory.player.level())) {
outputContainer.clearContent() outputContainer.clearContent()

View File

@ -1,6 +1,5 @@
package ru.dbotthepony.mc.otm.recipe package ru.dbotthepony.mc.otm.recipe
import com.google.common.collect.ImmutableMap
import com.mojang.serialization.Codec import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder import com.mojang.serialization.codecs.RecordCodecBuilder
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap 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.resources.ResourceLocation
import net.minecraft.util.StringRepresentable import net.minecraft.util.StringRepresentable
import net.minecraft.world.Container import net.minecraft.world.Container
import net.minecraft.world.item.DyeColor import net.minecraft.world.item.*
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.crafting.Ingredient import net.minecraft.world.item.crafting.Ingredient
import net.minecraft.world.item.crafting.Recipe import net.minecraft.world.item.crafting.Recipe
import net.minecraft.world.item.crafting.RecipeSerializer 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.data.minRange
import ru.dbotthepony.mc.otm.registry.MItems import ru.dbotthepony.mc.otm.registry.MItems
import ru.dbotthepony.mc.otm.registry.MRecipes import ru.dbotthepony.mc.otm.registry.MRecipes
import java.util.EnumMap
import java.util.function.Predicate import java.util.function.Predicate
import java.util.stream.Collector
import java.util.stream.Collectors
class PainterRecipe( abstract class AbstractPainterRecipe(
val input: Ingredient,
val output: ItemStack,
dyes: Map<out DyeColor?, Int> dyes: Map<out DyeColor?, Int>
) : Recipe<Container> { ) : 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)) val dyes: Object2IntMap<DyeColor?> = Object2IntMaps.unmodifiable(Object2IntArrayMap(dyes))
override fun getToastSymbol(): ItemStack = ItemStack(MItems.PAINTER)
fun canCraft(storedDyes: Map<out DyeColor?, Int>): Boolean { fun canCraft(storedDyes: Map<out DyeColor?, Int>): Boolean {
if (isIncomplete) return false if (isIncomplete) return false
if (dyes.isEmpty() || dyes.values.none { it > 0 }) return true if (dyes.isEmpty() || dyes.values.none { it > 0 }) return true
@ -63,6 +52,52 @@ class PainterRecipe(
return true 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 { override fun matches(p_44002_: Container, p_44003_: Level): Boolean {
if (isIncomplete) return false if (isIncomplete) return false
return input.test(p_44002_[0]) 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 { override fun getResultItem(p_267052_: RegistryAccess): ItemStack {
return output return output
} }
@ -110,36 +137,12 @@ class PainterRecipe(
return MRecipes.PAINTER return MRecipes.PAINTER
} }
override fun getToastSymbol(): ItemStack {
return ItemStack(MItems.PAINTER)
}
fun toFinished(id: ResourceLocation): FinishedRecipe { fun toFinished(id: ResourceLocation): FinishedRecipe {
return SERIALIZER.toFinished(this, id) return SERIALIZER.toFinished(this, id)
} }
private enum class DyeColorWrapper(val refName: String, val key: DyeColor?) : StringRepresentable { override fun getOutput(container: Container): ItemStack {
WHITE("white", DyeColor.WHITE), return this.output
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
}
} }
companion object { 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 PLATE_PRESS by register<PlatePressRecipe>("plate_press")
val PAINTER by register<PainterRecipe>("painter") 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 MATTER_ENTANGLER by register<IMatterEntanglerRecipe>("matter_entangler")
val MICROWAVE by register<MicrowaveRecipe>("microwave") val MICROWAVE by register<MicrowaveRecipe>("microwave")
@ -43,6 +44,7 @@ object MRecipes {
serializers.register("upgrade") { UpgradeRecipe.CODEC } serializers.register("upgrade") { UpgradeRecipe.CODEC }
serializers.register("hammer_priming") { ExplosiveHammerPrimingRecipe.CODEC } serializers.register("hammer_priming") { ExplosiveHammerPrimingRecipe.CODEC }
serializers.register("painter") { PainterRecipe.SERIALIZER } serializers.register("painter") { PainterRecipe.SERIALIZER }
serializers.register("painter_armor_dye") { PainterArmorDyeRecipe.SERIALIZER }
serializers.register("matter_entangler") { MatterEntanglerRecipe.SERIALIZER } serializers.register("matter_entangler") { MatterEntanglerRecipe.SERIALIZER }
serializers.register("matter_entangler_energetic") { MatterEntanglerRecipe.ENERGY_SERIALIZER } serializers.register("matter_entangler_energetic") { MatterEntanglerRecipe.ENERGY_SERIALIZER }
serializers.register("matter_entangler_matter") { MatterEntanglerRecipe.MATTER_SERIALIZER } serializers.register("matter_entangler_matter") { MatterEntanglerRecipe.MATTER_SERIALIZER }