Painter table water ingredient, semi-realistic color mixing
This commit is contained in:
parent
474a19a30b
commit
636afe639e
@ -445,7 +445,7 @@ fun addCraftingTableRecipes(consumer: RecipeOutput) {
|
||||
.build(consumer)
|
||||
|
||||
MatteryRecipe(MItems.PAINTER, category = machinesCategory)
|
||||
.row(Items.BRUSH, Items.BUCKET, Items.WATER_BUCKET)
|
||||
.row(Items.BRUSH, Items.BUCKET, Items.BUCKET)
|
||||
.row(MItemTags.IRON_PLATES, Items.BUCKET, MItemTags.IRON_PLATES)
|
||||
.row(MItemTags.IRON_PLATES, MItemTags.CRAFTING_TABLES, MItemTags.IRON_PLATES)
|
||||
.unlockedBy(Tags.Items.DYES)
|
||||
|
@ -50,7 +50,7 @@ private fun cleaning(consumer: RecipeOutput, to: Item, from: Map<out DyeColor?,
|
||||
consumer.accept(PainterRecipe(
|
||||
Ingredient.of(from.entries.stream().filter { it.key != null }.map { ItemStack(it.value) }),
|
||||
ItemStack(to),
|
||||
setOf()
|
||||
mapOf(null to 15)
|
||||
).toFinished(modLocation("painter/cleaning/" + to.recipeName)))
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,11 @@
|
||||
package ru.dbotthepony.mc.otm.block.entity.decorative
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.mojang.datafixers.util.Either
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
@ -10,25 +14,31 @@ import net.minecraft.world.inventory.AbstractContainerMenu
|
||||
import net.minecraft.world.item.DyeColor
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
import net.minecraftforge.common.capabilities.ForgeCapabilities
|
||||
import net.minecraftforge.fluids.FluidStack
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler
|
||||
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
|
||||
import ru.dbotthepony.mc.otm.container.HandlerFilter
|
||||
import ru.dbotthepony.mc.otm.container.MatteryContainer
|
||||
import ru.dbotthepony.mc.otm.core.ifPresentK
|
||||
import ru.dbotthepony.mc.otm.core.immutableList
|
||||
import ru.dbotthepony.mc.otm.core.immutableMap
|
||||
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
||||
import ru.dbotthepony.mc.otm.core.nbt.mapPresent
|
||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
||||
import ru.dbotthepony.mc.otm.menu.decorative.PainterMenu
|
||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
||||
import java.util.*
|
||||
|
||||
class PainterBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.PAINTER, blockPos, blockState) {
|
||||
class PainterBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.PAINTER, blockPos, blockState), IFluidHandler {
|
||||
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
|
||||
return PainterMenu(containerID, inventory, this)
|
||||
}
|
||||
|
||||
val dyeInput = MatteryContainer(this::markDirtyFast, 1)
|
||||
private val dyeStored = EnumMap<DyeColor, Int>(DyeColor::class.java)
|
||||
val dyeStoredView: Map<DyeColor, Int> = Collections.unmodifiableMap(dyeStored)
|
||||
// null - water
|
||||
val dyeStored = Object2IntArrayMap<DyeColor?>()
|
||||
|
||||
var isBulk = false
|
||||
set(value) {
|
||||
@ -36,19 +46,63 @@ class PainterBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDe
|
||||
markDirtyFast()
|
||||
}
|
||||
|
||||
override fun getTanks(): Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
override fun getFluidInTank(tank: Int): FluidStack {
|
||||
if (tank != 1) return FluidStack.EMPTY
|
||||
if (waterStored() == 0) return FluidStack.EMPTY
|
||||
return FluidStack(Fluids.WATER, waterStored())
|
||||
}
|
||||
|
||||
override fun getTankCapacity(tank: Int): Int {
|
||||
if (tank == 1) return MAX_WATER_STORAGE
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun isFluidValid(tank: Int, stack: FluidStack): Boolean {
|
||||
return tank == 0 && stack.fluid == Fluids.WATER
|
||||
}
|
||||
|
||||
override fun fill(resource: FluidStack, action: IFluidHandler.FluidAction): Int {
|
||||
if (resource.isEmpty || resource.fluid != Fluids.WATER || waterStored() >= MAX_WATER_STORAGE) return 0
|
||||
val diff = (waterStored() + resource.amount).coerceAtMost(MAX_WATER_STORAGE) - waterStored()
|
||||
if (action.simulate() || diff == 0) return diff
|
||||
dyeStored[null] = waterStored() + diff
|
||||
return diff
|
||||
}
|
||||
|
||||
override fun drain(resource: FluidStack, action: IFluidHandler.FluidAction): FluidStack {
|
||||
return FluidStack.EMPTY
|
||||
}
|
||||
|
||||
override fun drain(maxDrain: Int, action: IFluidHandler.FluidAction): FluidStack {
|
||||
return FluidStack.EMPTY
|
||||
}
|
||||
|
||||
init {
|
||||
addDroppableContainer(dyeInput)
|
||||
savetables.stateful(dyeInput, INVENTORY_KEY)
|
||||
savetables.bool(::isBulk)
|
||||
exposeGlobally(ForgeCapabilities.FLUID_HANDLER, this)
|
||||
}
|
||||
|
||||
fun takeDyes(dyes: Map<DyeColor, Int>) {
|
||||
fun takeDyes(dyes: Map<out DyeColor?, Int>) {
|
||||
for ((dye, amount) in dyes) {
|
||||
for (i in 0 until amount) {
|
||||
mixer(dye).mix(dyeStored)
|
||||
if (dye == null) {
|
||||
for (i in 0 until amount) {
|
||||
if (waterStored() >= 1) {
|
||||
dyeStored[dye] = waterStored() - 1
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i in 0 until amount) {
|
||||
mixer(dye)?.mix(dyeStored)
|
||||
|
||||
if (dyeStored(dye) > 0) {
|
||||
dyeStored[dye] = dyeStored(dye) - 1
|
||||
if (dyeStored(dye) > 0) {
|
||||
dyeStored[dye] = dyeStored(dye) - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -58,6 +112,16 @@ class PainterBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDe
|
||||
|
||||
val config = ConfigurableItemHandler(input = dyeInput.handler(object : HandlerFilter {
|
||||
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
|
||||
if (waterStored() < MAX_WATER_STORAGE) {
|
||||
stack.getCapability(ForgeCapabilities.FLUID_HANDLER_ITEM).ifPresentK {
|
||||
val drain = it.drain(FluidStack(Fluids.WATER, MAX_WATER_STORAGE - waterStored()), IFluidHandler.FluidAction.SIMULATE)
|
||||
|
||||
if (drain.isNotEmpty) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val dye = DyeColor.entries.firstOrNull { stack.`is`(it.tag) } ?: return false
|
||||
return dyeStored(dye) + HUE_PER_ITEM <= MAX_STORAGE
|
||||
}
|
||||
@ -75,14 +139,12 @@ class PainterBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDe
|
||||
}
|
||||
}))
|
||||
|
||||
fun dyeStored(dye: DyeColor): Int {
|
||||
return dyeStored[dye]!!
|
||||
fun waterStored(): Int {
|
||||
return dyeStored.getInt(null)
|
||||
}
|
||||
|
||||
init {
|
||||
for (dye in DyeColor.entries) {
|
||||
dyeStored[dye] = 0
|
||||
}
|
||||
fun dyeStored(dye: DyeColor?): Int {
|
||||
return dyeStored.getInt(dye)
|
||||
}
|
||||
|
||||
override fun saveShared(nbt: CompoundTag) {
|
||||
@ -90,7 +152,7 @@ class PainterBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDe
|
||||
|
||||
nbt["dyes"] = CompoundTag().also {
|
||||
for ((k, v) in dyeStored) {
|
||||
it[k.getName()] = v
|
||||
it[k?.getName() ?: "water"] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -98,13 +160,14 @@ class PainterBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDe
|
||||
override fun load(nbt: CompoundTag) {
|
||||
super.load(nbt)
|
||||
|
||||
for (dye in DyeColor.entries) {
|
||||
dyeStored[dye] = 0
|
||||
}
|
||||
dyeStored.clear()
|
||||
|
||||
nbt.mapPresent("dyes") { it: CompoundTag ->
|
||||
for (k in it.allKeys) {
|
||||
dyeStored[DyeColor.entries.firstOrNull { it.getName() == k } ?: continue] = it.getInt(k)
|
||||
if (k == "water")
|
||||
dyeStored[null] = it.getInt("water")
|
||||
else
|
||||
dyeStored[DyeColor.entries.firstOrNull { it.getName() == k } ?: continue] = it.getInt(k)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -113,6 +176,17 @@ class PainterBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDe
|
||||
super.tick()
|
||||
|
||||
for (slot in dyeInput.slotIterator()) {
|
||||
if (waterStored() < MAX_WATER_STORAGE) {
|
||||
slot.item.getCapability(ForgeCapabilities.FLUID_HANDLER_ITEM).ifPresentK {
|
||||
val drain = it.drain(FluidStack(Fluids.WATER, MAX_WATER_STORAGE - waterStored()), IFluidHandler.FluidAction.EXECUTE)
|
||||
|
||||
if (drain.isNotEmpty) {
|
||||
dyeStored[null] = waterStored() + drain.amount
|
||||
slot.item = it.container
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val dye = DyeColor.entries.firstOrNull { slot.item.`is`(it.tag) } ?: continue
|
||||
val stored = dyeStored(dye)
|
||||
|
||||
@ -124,20 +198,20 @@ class PainterBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDe
|
||||
}
|
||||
}
|
||||
|
||||
class Mixer(val color: DyeColor, vararg mixing: ImmutableList<DyeColor>) : Map.Entry<DyeColor, Mixer> {
|
||||
val mixing: ImmutableList<ImmutableList<DyeColor>> = ImmutableList.copyOf(mixing)
|
||||
class Mixer(val color: DyeColor, vararg mixing: ImmutableList<Optional<DyeColor>>) : Map.Entry<DyeColor, Mixer> {
|
||||
val mixing: ImmutableList<ImmutableList<Optional<DyeColor>>> = ImmutableList.copyOf(mixing)
|
||||
|
||||
override val key: DyeColor
|
||||
get() = color
|
||||
override val value: Mixer
|
||||
get() = this
|
||||
|
||||
private fun mix(input: MutableMap<DyeColor, Int>, seen: MutableSet<DyeColor>, stack: MutableSet<DyeColor>) {
|
||||
if (input[color]!! > 0 || color in seen || color in stack) return
|
||||
private fun mix(input: Object2IntMap<DyeColor?>, seen: MutableSet<DyeColor>, stack: MutableSet<DyeColor>) {
|
||||
if (input.getInt(color) > 0 || color in seen || color in stack) return
|
||||
stack.add(color)
|
||||
|
||||
for (ingredients in mixing) {
|
||||
val copy = EnumMap(input)
|
||||
val copy = Object2IntArrayMap(input)
|
||||
var i = 0
|
||||
val volumes = Int2IntArrayMap()
|
||||
|
||||
@ -146,8 +220,11 @@ class PainterBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDe
|
||||
for (i2 in ingredients.indices)
|
||||
volumes[i2] = 1
|
||||
|
||||
while (!ingredients.all { copy[it]!! > 0 } && i++ < 20) {
|
||||
ingredients.withIndex().forEach { for (t in 0 until volumes[it.index]) mixer(it.value).mix(copy, seen, stack) }
|
||||
while (!ingredients.all { copy.getInt(it.orElse(null)) > 0 } && i++ < 20) {
|
||||
ingredients.withIndex().forEach {
|
||||
for (t in 0 until volumes[it.index])
|
||||
it.value.ifPresent { mixer(it)?.mix(copy, seen, stack) }
|
||||
}
|
||||
|
||||
var increase = i
|
||||
val iter = ingredients.indices.iterator()
|
||||
@ -157,8 +234,8 @@ class PainterBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDe
|
||||
}
|
||||
}
|
||||
|
||||
if (ingredients.all { copy[it]!! > 0 }) {
|
||||
ingredients.forEach { copy[it] = copy[it]!! - 1 }
|
||||
if (ingredients.all { copy.getInt(it.orElse(null)) > 0 }) {
|
||||
ingredients.forEach { copy[it.orElse(null)] = copy.getInt(it.orElse(null)) - 1 }
|
||||
copy[color] = ingredients.size
|
||||
|
||||
input.putAll(copy)
|
||||
@ -171,29 +248,38 @@ class PainterBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDe
|
||||
seen.add(color)
|
||||
}
|
||||
|
||||
fun mix(input: MutableMap<DyeColor, Int>) {
|
||||
fun mix(input: Object2IntMap<DyeColor?>) {
|
||||
mix(input, EnumSet.noneOf(DyeColor::class.java), EnumSet.noneOf(DyeColor::class.java))
|
||||
}
|
||||
|
||||
fun isAvailable(input: Map<DyeColor, Int>): Boolean {
|
||||
if (input[color]!! > 0) return true
|
||||
return EnumMap(input).also(::mix)[color]!! > 0
|
||||
fun isAvailable(input: Object2IntMap<DyeColor?>): Boolean {
|
||||
if (input.getInt(color) > 0) return true
|
||||
return Object2IntArrayMap(input).also(::mix).getInt(color) > 0
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun mixer(color: DyeColor): Mixer {
|
||||
return MIXING[color]!!
|
||||
fun mixer(color: DyeColor): Mixer? {
|
||||
return MIXING[color]
|
||||
}
|
||||
|
||||
val MIXING = immutableMap {
|
||||
put(Mixer(DyeColor.BLACK, immutableList(DyeColor.CYAN, DyeColor.MAGENTA, DyeColor.YELLOW)))
|
||||
put(Mixer(DyeColor.RED, immutableList(DyeColor.MAGENTA, DyeColor.YELLOW)))
|
||||
put(Mixer(DyeColor.GREEN, immutableList(DyeColor.CYAN, DyeColor.YELLOW)))
|
||||
put(Mixer(DyeColor.BLUE, immutableList(DyeColor.CYAN, DyeColor.MAGENTA)))
|
||||
val MIXING = immutableMap<DyeColor, Mixer> {
|
||||
put(Mixer(DyeColor.BLACK, immutableList(Optional.of(DyeColor.CYAN), Optional.of(DyeColor.MAGENTA), Optional.of(DyeColor.YELLOW))))
|
||||
put(Mixer(DyeColor.RED, immutableList(Optional.of(DyeColor.MAGENTA), Optional.of(DyeColor.YELLOW))))
|
||||
put(Mixer(DyeColor.GREEN, immutableList(Optional.of(DyeColor.CYAN), Optional.of(DyeColor.YELLOW))))
|
||||
put(Mixer(DyeColor.BLUE, immutableList(Optional.of(DyeColor.CYAN), Optional.of(DyeColor.MAGENTA))))
|
||||
put(Mixer(DyeColor.LIGHT_BLUE, immutableList(Optional.of(DyeColor.WHITE), Optional.of(DyeColor.BLUE))))
|
||||
put(Mixer(DyeColor.LIME, immutableList(Optional.of(DyeColor.WHITE), Optional.of(DyeColor.GREEN))))
|
||||
put(Mixer(DyeColor.ORANGE, immutableList(Optional.of(DyeColor.WHITE), Optional.of(DyeColor.RED), Optional.empty())))
|
||||
put(Mixer(DyeColor.PURPLE, immutableList(Optional.of(DyeColor.CYAN), Optional.of(DyeColor.MAGENTA), Optional.empty())))
|
||||
put(Mixer(DyeColor.BROWN, immutableList(Optional.of(DyeColor.YELLOW), Optional.of(DyeColor.MAGENTA), Optional.of(DyeColor.BLACK), Optional.empty())))
|
||||
put(Mixer(DyeColor.PINK, immutableList(Optional.of(DyeColor.YELLOW), Optional.of(DyeColor.MAGENTA), Optional.of(DyeColor.WHITE), Optional.empty())))
|
||||
put(Mixer(DyeColor.GRAY, immutableList(Optional.of(DyeColor.WHITE), Optional.of(DyeColor.BLACK), Optional.empty())))
|
||||
put(Mixer(DyeColor.LIGHT_GRAY, immutableList(Optional.of(DyeColor.WHITE), Optional.of(DyeColor.WHITE), Optional.of(DyeColor.BLACK), Optional.empty())))
|
||||
}
|
||||
|
||||
const val MAX_STORAGE = 256
|
||||
const val HUE_PER_ITEM = 32
|
||||
const val MAX_WATER_STORAGE = 4_000
|
||||
}
|
||||
}
|
||||
|
@ -34,11 +34,16 @@ import ru.dbotthepony.mc.otm.core.util.CreativeMenuItemComparator
|
||||
import ru.dbotthepony.mc.otm.menu.decorative.PainterMenu
|
||||
|
||||
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) {
|
||||
init {
|
||||
dock = Dock.RIGHT
|
||||
dockLeft = 1f
|
||||
tooltips.add(TranslatableComponent("item.minecraft.${dye.getName()}_dye"))
|
||||
|
||||
if (dye == null)
|
||||
tooltips.add(TranslatableComponent("block.minecraft.water"))
|
||||
else
|
||||
tooltips.add(TranslatableComponent("item.minecraft.${dye.getName() ?: "water"}_dye"))
|
||||
|
||||
tooltips.add(TextComponent(""))
|
||||
tooltips.add(TextComponent(""))
|
||||
}
|
||||
@ -46,10 +51,10 @@ class PainterScreen(menu: PainterMenu, inventory: Inventory, title: Component) :
|
||||
override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
|
||||
graphics.renderRect(0f, 0f, width, height, color = RGBAColor.DARK_GRAY)
|
||||
|
||||
val color = RGBAColor.rgb(dye.textColor)
|
||||
val color = RGBAColor.rgb(dye?.textColor ?: DyeColor.LIGHT_BLUE.textColor)
|
||||
graphics.renderRect(0f, 0f, width, height, color = color.copy(alpha = 0.4f))
|
||||
|
||||
val multiplier = menu.dyeStoredDirect[dye]!!.toFloat() / PainterBlockEntity.MAX_STORAGE.toFloat()
|
||||
val multiplier = menu.dyeStoredDirect[dye]!!.toFloat() / (if (dye == null) PainterBlockEntity.MAX_WATER_STORAGE.toFloat() else PainterBlockEntity.MAX_STORAGE.toFloat())
|
||||
graphics.renderRect(0f, height * (1f - multiplier), width, height * multiplier, color = color)
|
||||
|
||||
tooltips[tooltips.size - 1] = TextComponent("${menu.dyeStoredDirect[dye]} (${(multiplier * 100f).toInt()}%)")
|
||||
@ -68,10 +73,12 @@ class PainterScreen(menu: PainterMenu, inventory: Inventory, title: Component) :
|
||||
it.dockTop = 4f
|
||||
}
|
||||
|
||||
EditablePanel(this, frame, width = 6f * DyeColor.entries.size / 2f).also {
|
||||
EditablePanel(this, frame, width = 6f * (DyeColor.entries.size / 2f + 1f)).also {
|
||||
it.dock = Dock.RIGHT
|
||||
it.dockLeft = 4f
|
||||
|
||||
Bar(it, null).childrenOrder = -1000
|
||||
|
||||
EditablePanel(this, it, height = 46f).also {
|
||||
it.dock = Dock.TOP
|
||||
|
||||
@ -141,10 +148,17 @@ class PainterScreen(menu: PainterMenu, inventory: Inventory, title: Component) :
|
||||
recipe.value.dyes.forEach {
|
||||
val (dye, amount) = it
|
||||
|
||||
if (amount == 1)
|
||||
list.add(TranslatableComponent("otm.gui.needs", TranslatableComponent("item.minecraft.${dye.getName()}_dye")))
|
||||
else if (amount > 1)
|
||||
list.add(TranslatableComponent("otm.gui.needs_x", TranslatableComponent("item.minecraft.${dye.getName()}_dye"), amount))
|
||||
if (dye == null) {
|
||||
if (amount == 1)
|
||||
list.add(TranslatableComponent("otm.gui.needs", TranslatableComponent("block.minecraft.water")))
|
||||
else if (amount > 1)
|
||||
list.add(TranslatableComponent("otm.gui.needs_x", TranslatableComponent("block.minecraft.water"), amount))
|
||||
} else {
|
||||
if (amount == 1)
|
||||
list.add(TranslatableComponent("otm.gui.needs", TranslatableComponent("item.minecraft.${dye.getName() ?: "water"}_dye")))
|
||||
else if (amount > 1)
|
||||
list.add(TranslatableComponent("otm.gui.needs_x", TranslatableComponent("item.minecraft.${dye.getName() ?: "water"}_dye"), amount))
|
||||
}
|
||||
}
|
||||
|
||||
graphics.renderComponentTooltip(font, list, mouseX.toInt(), mouseY.toInt())
|
||||
|
@ -11,6 +11,7 @@ import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.item.crafting.Ingredient
|
||||
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
||||
import ru.dbotthepony.mc.otm.client.render.ItemStackIcon
|
||||
@ -55,7 +56,7 @@ object PainterRecipeCategory : IRecipeCategory<PainterRecipe>, IDrawable {
|
||||
|
||||
for ((dye, count) in recipe.dyes) {
|
||||
builder.addSlot(RecipeIngredientRole.CATALYST, 1 + x * 18, 1 + y * 18)
|
||||
.addIngredients(VanillaTypes.ITEM_STACK, Ingredient.of(dye.tag).items.map { it.copyWithCount(count) })
|
||||
.addIngredients(VanillaTypes.ITEM_STACK, dye?.let { Ingredient.of(it.tag).items.map { it.copyWithCount(count) } } ?: listOf(ItemStack(Items.WATER_BUCKET, count)))
|
||||
|
||||
y++
|
||||
|
||||
|
@ -6,6 +6,10 @@ import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.DyeColor
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.crafting.RecipeHolder
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
import net.minecraftforge.common.capabilities.ForgeCapabilities
|
||||
import net.minecraftforge.fluids.FluidStack
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler
|
||||
import ru.dbotthepony.mc.otm.block.entity.decorative.PainterBlockEntity
|
||||
import ru.dbotthepony.mc.otm.capability.matteryPlayer
|
||||
import ru.dbotthepony.mc.otm.container.MatteryContainer
|
||||
@ -15,6 +19,7 @@ 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
|
||||
@ -34,7 +39,7 @@ import kotlin.collections.ArrayList
|
||||
class PainterMenu(
|
||||
containerId: Int, inventory: Inventory, tile: PainterBlockEntity? = null
|
||||
) : MatteryMenu(MMenus.PAINTER, containerId, inventory, tile) {
|
||||
val dyeStored = DyeColor.entries.associateWith { dye ->
|
||||
val dyeStored = (DyeColor.entries.toMutableList<DyeColor?>().also { it.add(0, null) }).associateWith { dye ->
|
||||
mSynchronizer.ComputedIntField({ tile?.dyeStored(dye) ?: 0 }).also { it.addListener(IntConsumer { rescan() }) }
|
||||
}
|
||||
|
||||
@ -95,7 +100,11 @@ class PainterMenu(
|
||||
|
||||
val dyeSlot = object : MatterySlot(tile?.dyeInput ?: SimpleContainer(1), 0) {
|
||||
override fun mayPlace(itemStack: ItemStack): Boolean {
|
||||
return super.mayPlace(itemStack) && (DyeColor.getColor(itemStack)?.let { dyeStoredDirect[it]!! + PainterBlockEntity.HUE_PER_ITEM <= PainterBlockEntity.MAX_STORAGE } ?: false)
|
||||
return super.mayPlace(itemStack) && ((
|
||||
itemStack.getCapability(ForgeCapabilities.FLUID_HANDLER_ITEM).resolve().map {
|
||||
dyeStoredDirect[null]!! < PainterBlockEntity.MAX_WATER_STORAGE && it.drain(FluidStack(Fluids.WATER, PainterBlockEntity.MAX_WATER_STORAGE - dyeStoredDirect[null]!!), IFluidHandler.FluidAction.SIMULATE).isNotEmpty
|
||||
}.orElse(false)
|
||||
) || (DyeColor.getColor(itemStack)?.let { dyeStoredDirect[it]!! + PainterBlockEntity.HUE_PER_ITEM <= PainterBlockEntity.MAX_STORAGE } ?: false))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,16 @@
|
||||
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
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMaps
|
||||
import net.minecraft.core.NonNullList
|
||||
import net.minecraft.core.RegistryAccess
|
||||
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
|
||||
@ -26,28 +31,32 @@ 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,
|
||||
val dyes: Map<DyeColor, Int>
|
||||
dyes: Map<out DyeColor?, Int>
|
||||
) : Recipe<Container> {
|
||||
constructor(
|
||||
input: Ingredient,
|
||||
output: ItemStack,
|
||||
dyes: Set<DyeColor>
|
||||
) : this(input, output, dyes.associateWith { 1 })
|
||||
dyes: Set<DyeColor?>
|
||||
) : this(input, output, Object2IntArrayMap<DyeColor?>().also { map -> dyes.forEach { map[it] = 1 } })
|
||||
|
||||
fun canCraft(storedDyes: Map<DyeColor, Int>): Boolean {
|
||||
val dyes: Object2IntMap<DyeColor?> = Object2IntMaps.unmodifiable(Object2IntArrayMap(dyes))
|
||||
|
||||
fun canCraft(storedDyes: Map<out DyeColor?, Int>): Boolean {
|
||||
if (isIncomplete) return false
|
||||
if (dyes.isEmpty() || dyes.values.none { it > 0 }) return true
|
||||
val copy = EnumMap(storedDyes)
|
||||
val copy = Object2IntArrayMap(storedDyes)
|
||||
|
||||
for ((dye, amount) in dyes) {
|
||||
for (i in 0 until amount) {
|
||||
PainterBlockEntity.mixer(dye).mix(copy)
|
||||
if (copy[dye]!! <= 0) return false
|
||||
copy[dye] = copy[dye]!! - 1
|
||||
if (dye != null) PainterBlockEntity.mixer(dye)?.mix(copy)
|
||||
if (copy.getInt(dye) <= 0) return false
|
||||
copy[dye] = copy.getInt(dye) - 1
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,17 +118,43 @@ class PainterRecipe(
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val wrapperCodec = StringRepresentable.fromEnum(DyeColorWrapper::values)
|
||||
|
||||
val SERIALIZER = Codec2RecipeSerializer<PainterRecipe> { context ->
|
||||
RecordCodecBuilder.create {
|
||||
it.group(
|
||||
context.ingredients.fieldOf("input").forGetter(PainterRecipe::input),
|
||||
ItemStack.CODEC.fieldOf("output").forGetter(PainterRecipe::output),
|
||||
PredicatedCodecList<Map<DyeColor, Int>>(
|
||||
DyeColor.CODEC.xmap({ mapOf(it to 1) }, { it.keys.first() }) to Predicate { it.keys.size == 1 && it.values.first() == 1 },
|
||||
Codec.list(DyeColor.CODEC).xmap({ it.associateWith { 1 } }, { ArrayList(it.keys) }) to Predicate { it.values.all { it == 1 } },
|
||||
Codec.unboundedMap(DyeColor.CODEC, Codec.INT.minRange(1)) to Predicate { true }
|
||||
).fieldOf("dyes").forGetter(PainterRecipe::dyes),
|
||||
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(PainterRecipe::dyes),
|
||||
).apply(it, ::PainterRecipe)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user