Update matter entangler recipe, replace ingredient matrix with helpers over shaped recipe pattern
This commit is contained in:
parent
e80947d8d7
commit
7cb6949e27
@ -1,113 +0,0 @@
|
|||||||
package ru.dbotthepony.mc.otm.data
|
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Pair
|
|
||||||
import com.mojang.serialization.Codec
|
|
||||||
import com.mojang.serialization.DataResult
|
|
||||||
import com.mojang.serialization.DynamicOps
|
|
||||||
import com.mojang.serialization.JsonOps
|
|
||||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
|
||||||
import net.minecraft.world.item.crafting.Ingredient
|
|
||||||
import ru.dbotthepony.mc.otm.core.collect.allEqual
|
|
||||||
import ru.dbotthepony.mc.otm.core.collect.map
|
|
||||||
import ru.dbotthepony.mc.otm.core.collect.toList
|
|
||||||
import ru.dbotthepony.mc.otm.core.collect.toStream
|
|
||||||
import ru.dbotthepony.mc.otm.core.stream
|
|
||||||
import ru.dbotthepony.mc.otm.recipe.IIngredientMatrix
|
|
||||||
import ru.dbotthepony.mc.otm.recipe.IngredientMatrix
|
|
||||||
import java.util.function.Supplier
|
|
||||||
|
|
||||||
class IngredientMatrixCodec(ingredientCodec: Codec<Ingredient>) : Codec<IIngredientMatrix> {
|
|
||||||
private val ingredientList = Codec.list(ingredientCodec)
|
|
||||||
private val doubleIngredientList = Codec.list(ingredientList)
|
|
||||||
|
|
||||||
override fun <T : Any> encode(input: IIngredientMatrix, ops: DynamicOps<T>, prefix: T): DataResult<T> {
|
|
||||||
return doubleIngredientList.encode(
|
|
||||||
(0 until input.height).iterator().map { row ->
|
|
||||||
(0 until input.width).iterator().map { column ->
|
|
||||||
input[column, row]
|
|
||||||
}.toList()
|
|
||||||
}.toList(),
|
|
||||||
ops,
|
|
||||||
prefix
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private data class Handwritten(val pattern: List<String>, val key: Map<Char, Ingredient>)
|
|
||||||
|
|
||||||
private val handwrittenCodec = RecordCodecBuilder.create<Handwritten> {
|
|
||||||
it.group(
|
|
||||||
Codec.list(Codec.STRING)
|
|
||||||
.flatXmap(
|
|
||||||
{ DataResult.success(it) },
|
|
||||||
{ if (it.iterator().map { it.length }.allEqual()) DataResult.success(it) else DataResult.error { "One or more of patten strings differ in length" } }
|
|
||||||
)
|
|
||||||
.fieldOf("pattern").forGetter(Handwritten::pattern),
|
|
||||||
Codec.unboundedMap(
|
|
||||||
Codec.STRING
|
|
||||||
.flatXmap(
|
|
||||||
{ if (it.length == 1) DataResult.success(it[0]) else DataResult.error { "Ingredient key must be exactly 1 symbol in length, '$it' is invalid" } },
|
|
||||||
{ DataResult.success(it.toString()) }
|
|
||||||
), ingredientCodec).fieldOf("key").forGetter(Handwritten::key)
|
|
||||||
).apply(it, ::Handwritten)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<IIngredientMatrix, T>> {
|
|
||||||
return ops.getList(input).mapOrElse(
|
|
||||||
{
|
|
||||||
val lines = ArrayList<DataResult<List<Ingredient>>>()
|
|
||||||
|
|
||||||
it.accept {
|
|
||||||
lines.add(ingredientList.decode(ops, it).map { it.first })
|
|
||||||
}
|
|
||||||
|
|
||||||
val errors = ArrayList<Supplier<String>>()
|
|
||||||
val ingredients = ArrayList<List<Ingredient>>()
|
|
||||||
|
|
||||||
lines.withIndex().forEach {
|
|
||||||
val (line, result) = it
|
|
||||||
result.mapOrElse({ ingredients.add(it) }, { errors.add { "Line $line: ${it.message()}" } })
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errors.isNotEmpty()) {
|
|
||||||
DataResult.error { "Failed to decode ingredient matrix: ${errors.joinToString { it.get() }}" }
|
|
||||||
} else if (ingredients.isEmpty()) {
|
|
||||||
DataResult.error { "Ingredient list is empty" }
|
|
||||||
} else if (!ingredients.iterator().map { it.size }.allEqual()) {
|
|
||||||
DataResult.error { "Ingredient list is not a matrix (one or multiple of rows are mismatched size)" }
|
|
||||||
} else {
|
|
||||||
val result = IngredientMatrix(ingredients.first().size, ingredients.size)
|
|
||||||
|
|
||||||
for ((row, columns) in ingredients.withIndex()) {
|
|
||||||
for ((column, ingredient) in columns.withIndex()) {
|
|
||||||
result[column, row] = ingredient
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DataResult.success(Pair(result, ops.empty()))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ err1 ->
|
|
||||||
handwrittenCodec.decode(ops, input).mapOrElse(
|
|
||||||
{
|
|
||||||
DataResult.success(it)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
DataResult.error { "Failed to decode ingredients as list: ${err1.message()} and as pattern/dictionary: ${it.message()}" }
|
|
||||||
}
|
|
||||||
).flatMap {
|
|
||||||
val handwritten = it.first
|
|
||||||
val result = IngredientMatrix(handwritten.pattern.first().length, handwritten.pattern.size)
|
|
||||||
|
|
||||||
for ((row, pattern) in handwritten.pattern.withIndex()) {
|
|
||||||
for ((column, symbol) in pattern.withIndex()) {
|
|
||||||
val ingredient = if (symbol == ' ') handwritten.key[symbol] ?: Ingredient.EMPTY else handwritten.key[symbol] ?: return@flatMap DataResult.error { "Unknown ingredient with index '$symbol'" }
|
|
||||||
result[column, row] = ingredient
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DataResult.success(Pair(result, ops.empty()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,6 +13,7 @@ import net.minecraft.network.protocol.common.custom.CustomPacketPayload
|
|||||||
import net.neoforged.neoforge.network.connection.ConnectionType
|
import net.neoforged.neoforge.network.connection.ConnectionType
|
||||||
import net.neoforged.neoforge.network.handling.IPayloadContext
|
import net.neoforged.neoforge.network.handling.IPayloadContext
|
||||||
import net.neoforged.neoforge.network.registration.PayloadRegistrar
|
import net.neoforged.neoforge.network.registration.PayloadRegistrar
|
||||||
|
import java.util.Optional
|
||||||
import kotlin.reflect.KFunction1
|
import kotlin.reflect.KFunction1
|
||||||
|
|
||||||
fun <T : CustomPacketPayload> PayloadRegistrar.playToClient(
|
fun <T : CustomPacketPayload> PayloadRegistrar.playToClient(
|
||||||
@ -74,6 +75,7 @@ fun FriendlyByteBuf.readByteList(): ByteArrayList {
|
|||||||
|
|
||||||
fun <S : ByteBuf, V> StreamCodec<S, V>.wrap(): MatteryStreamCodec<S, V> = MatteryStreamCodec.Wrapper(this)
|
fun <S : ByteBuf, V> StreamCodec<S, V>.wrap(): MatteryStreamCodec<S, V> = MatteryStreamCodec.Wrapper(this)
|
||||||
fun <S : ByteBuf, V> MatteryStreamCodec<S, V>.nullable(): MatteryStreamCodec<S, V?> = MatteryStreamCodec.Nullable(this)
|
fun <S : ByteBuf, V> MatteryStreamCodec<S, V>.nullable(): MatteryStreamCodec<S, V?> = MatteryStreamCodec.Nullable(this)
|
||||||
|
fun <S : ByteBuf, V : Any> MatteryStreamCodec<S, V>.optional(): MatteryStreamCodec<S, Optional<V>> = MatteryStreamCodec.Optional(this)
|
||||||
|
|
||||||
fun <V> StreamCodec<in RegistryFriendlyByteBuf, V>.encode(registry: RegistryAccess, value: V): ByteArrayList {
|
fun <V> StreamCodec<in RegistryFriendlyByteBuf, V>.encode(registry: RegistryAccess, value: V): ByteArrayList {
|
||||||
return encodePayload(registry) {
|
return encodePayload(registry) {
|
||||||
|
@ -52,6 +52,31 @@ interface MatteryStreamCodec<S : ByteBuf, V> : StreamCodec<S, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Optional<S : ByteBuf, V : Any>(val parent: MatteryStreamCodec<S, V>) : MatteryStreamCodec<S, java.util.Optional<V>> {
|
||||||
|
override fun decode(stream: S): java.util.Optional<V> {
|
||||||
|
return if (!stream.readBoolean()) java.util.Optional.empty() else java.util.Optional.of(parent.decode(stream))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun encode(stream: S, value: java.util.Optional<V>) {
|
||||||
|
if (value.isEmpty)
|
||||||
|
stream.writeBoolean(false)
|
||||||
|
else {
|
||||||
|
stream.writeBoolean(true)
|
||||||
|
parent.encode(stream, value.get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun copy(value: java.util.Optional<V>): java.util.Optional<V> {
|
||||||
|
return value.map(parent::copy)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun compare(a: java.util.Optional<V>, b: java.util.Optional<V>): Boolean {
|
||||||
|
if (a.isEmpty && b.isEmpty) return true
|
||||||
|
if (a.isEmpty || b.isEmpty) return false
|
||||||
|
return parent.compare(a.get(), b.get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class Collection<S : FriendlyByteBuf, E, C : MutableCollection<E>>(val elementCodec: MatteryStreamCodec<in S, E>, val collectionFactory: (Int) -> C) : MatteryStreamCodec<S, C> {
|
class Collection<S : FriendlyByteBuf, E, C : MutableCollection<E>>(val elementCodec: MatteryStreamCodec<in S, E>, val collectionFactory: (Int) -> C) : MatteryStreamCodec<S, C> {
|
||||||
override fun decode(stream: S): C {
|
override fun decode(stream: S): C {
|
||||||
val size = stream.readVarInt()
|
val size = stream.readVarInt()
|
||||||
|
@ -3,17 +3,14 @@ package ru.dbotthepony.mc.otm.network
|
|||||||
import io.netty.buffer.ByteBuf
|
import io.netty.buffer.ByteBuf
|
||||||
import net.minecraft.core.UUIDUtil
|
import net.minecraft.core.UUIDUtil
|
||||||
import net.minecraft.network.FriendlyByteBuf
|
import net.minecraft.network.FriendlyByteBuf
|
||||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
|
||||||
import net.minecraft.network.codec.ByteBufCodecs
|
import net.minecraft.network.codec.ByteBufCodecs
|
||||||
import net.minecraft.network.codec.StreamCodec
|
import net.minecraft.network.codec.StreamCodec
|
||||||
import net.minecraft.resources.ResourceLocation
|
import net.minecraft.resources.ResourceLocation
|
||||||
import net.minecraft.util.valueproviders.FloatProvider
|
|
||||||
import ru.dbotthepony.kommons.math.RGBAColor
|
import ru.dbotthepony.kommons.math.RGBAColor
|
||||||
import ru.dbotthepony.mc.otm.core.math.readDecimal
|
import ru.dbotthepony.mc.otm.core.math.readDecimal
|
||||||
import ru.dbotthepony.mc.otm.core.math.writeDecimal
|
import ru.dbotthepony.mc.otm.core.math.writeDecimal
|
||||||
import ru.dbotthepony.mc.otm.core.readItemType
|
import ru.dbotthepony.mc.otm.core.readItemType
|
||||||
import ru.dbotthepony.mc.otm.core.writeItemType
|
import ru.dbotthepony.mc.otm.core.writeItemType
|
||||||
import kotlin.reflect.KMutableProperty1
|
|
||||||
|
|
||||||
object StreamCodecs {
|
object StreamCodecs {
|
||||||
val NOTHING: MatteryStreamCodec<ByteBuf, Nothing?> = StreamCodec.of<ByteBuf, Nothing?>({ _, _ -> }, { null }).wrap()
|
val NOTHING: MatteryStreamCodec<ByteBuf, Nothing?> = StreamCodec.of<ByteBuf, Nothing?>({ _, _ -> }, { null }).wrap()
|
||||||
@ -38,4 +35,38 @@ object StreamCodecs {
|
|||||||
val ITEM_TYPE = StreamCodec.of(FriendlyByteBuf::writeItemType, FriendlyByteBuf::readItemType).wrap()
|
val ITEM_TYPE = StreamCodec.of(FriendlyByteBuf::writeItemType, FriendlyByteBuf::readItemType).wrap()
|
||||||
val ITEM_TYPE_NULLABLE = ITEM_TYPE.nullable()
|
val ITEM_TYPE_NULLABLE = ITEM_TYPE.nullable()
|
||||||
val DECIMAL = StreamCodec.of(FriendlyByteBuf::writeDecimal, FriendlyByteBuf::readDecimal).wrap()
|
val DECIMAL = StreamCodec.of(FriendlyByteBuf::writeDecimal, FriendlyByteBuf::readDecimal).wrap()
|
||||||
|
|
||||||
|
fun <S : ByteBuf, T, T0, T1, T2, T3, T4, T5, T6> composite(
|
||||||
|
c0: StreamCodec<in S, T0>, g0: (T) -> T0,
|
||||||
|
c1: StreamCodec<in S, T1>, g1: (T) -> T1,
|
||||||
|
c2: StreamCodec<in S, T2>, g2: (T) -> T2,
|
||||||
|
c3: StreamCodec<in S, T3>, g3: (T) -> T3,
|
||||||
|
c4: StreamCodec<in S, T4>, g4: (T) -> T4,
|
||||||
|
c5: StreamCodec<in S, T5>, g5: (T) -> T5,
|
||||||
|
c6: StreamCodec<in S, T6>, g6: (T) -> T6,
|
||||||
|
factory: (T0, T1, T2, T3, T4, T5, T6) -> T
|
||||||
|
): StreamCodec<S, T> {
|
||||||
|
return object : StreamCodec<S, T> {
|
||||||
|
override fun decode(stream: S): T {
|
||||||
|
val v0 = c0.decode(stream)
|
||||||
|
val v1 = c1.decode(stream)
|
||||||
|
val v2 = c2.decode(stream)
|
||||||
|
val v3 = c3.decode(stream)
|
||||||
|
val v4 = c4.decode(stream)
|
||||||
|
val v5 = c5.decode(stream)
|
||||||
|
val v6 = c6.decode(stream)
|
||||||
|
return factory(v0, v1, v2, v3, v4, v5, v6)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun encode(stream: S, value: T) {
|
||||||
|
c0.encode(stream, g0(value))
|
||||||
|
c1.encode(stream, g1(value))
|
||||||
|
c2.encode(stream, g2(value))
|
||||||
|
c3.encode(stream, g3(value))
|
||||||
|
c4.encode(stream, g4(value))
|
||||||
|
c5.encode(stream, g5(value))
|
||||||
|
c6.encode(stream, g6(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
78
src/main/kotlin/ru/dbotthepony/mc/otm/recipe/Ext.kt
Normal file
78
src/main/kotlin/ru/dbotthepony/mc/otm/recipe/Ext.kt
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.recipe
|
||||||
|
|
||||||
|
import net.minecraft.world.item.crafting.CraftingInput
|
||||||
|
import net.minecraft.world.item.crafting.Ingredient
|
||||||
|
import net.minecraft.world.item.crafting.ShapedRecipePattern
|
||||||
|
import ru.dbotthepony.mc.otm.core.get
|
||||||
|
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
||||||
|
|
||||||
|
val ShapedRecipePattern.isIncomplete: Boolean
|
||||||
|
get() = ingredients().any { it.items.size == 1 && it.hasNoItems() }
|
||||||
|
|
||||||
|
val ShapedRecipePattern.width: Int get() = width()
|
||||||
|
val ShapedRecipePattern.height: Int get() = height()
|
||||||
|
|
||||||
|
operator fun ShapedRecipePattern.get(column: Int, row: Int): Ingredient {
|
||||||
|
return ingredients()[column + row * width]
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun ShapedRecipePattern.get(column: Int, row: Int, flop: Boolean): Ingredient {
|
||||||
|
return if (flop)
|
||||||
|
get(width - column - 1, row)
|
||||||
|
else
|
||||||
|
get(column, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ShapedRecipePattern.preemptiveTest(t: CraftingInput, fromColumn: Int, fromRow: Int, flop: Boolean): Boolean {
|
||||||
|
if (t.width() - fromColumn < width || t.height() - fromRow < height)
|
||||||
|
return false
|
||||||
|
|
||||||
|
for (column in 0 until width) {
|
||||||
|
for (row in 0 until height) {
|
||||||
|
val item = t[fromColumn + column, fromRow + row, flop]
|
||||||
|
val ingredient = this[column, row, flop]
|
||||||
|
|
||||||
|
if (!ingredient.test(item) && item.isNotEmpty) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ShapedRecipePattern.preemptiveTest(t: CraftingInput): Boolean {
|
||||||
|
if (t.width() < width || t.height() < height)
|
||||||
|
return false
|
||||||
|
|
||||||
|
for (column in 0 .. t.width() - width)
|
||||||
|
for (row in 0 .. t.height() - height)
|
||||||
|
if (preemptiveTest(t, column, row, false) || preemptiveTest(t, column, row, true))
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ShapedRecipePattern.test(t: CraftingInput, fromColumn: Int, fromRow: Int, flop: Boolean): Boolean {
|
||||||
|
if (t.width() - fromColumn < width || t.height() - fromRow < height)
|
||||||
|
return false
|
||||||
|
|
||||||
|
for (column in 0 until width)
|
||||||
|
for (row in 0 until height)
|
||||||
|
if (!this[column, row, flop].test(t[fromColumn + column, fromRow + row, flop]))
|
||||||
|
return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ShapedRecipePattern.test(t: CraftingInput): Boolean {
|
||||||
|
if (t.width() < width || t.height() < height)
|
||||||
|
return false
|
||||||
|
|
||||||
|
for (column in 0 .. t.width() - width)
|
||||||
|
for (row in 0 .. t.height() - height)
|
||||||
|
if (test(t, column, row, false) || test(t, column, row, true))
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
@ -1,10 +1,14 @@
|
|||||||
package ru.dbotthepony.mc.otm.recipe
|
package ru.dbotthepony.mc.otm.recipe
|
||||||
|
|
||||||
|
import net.minecraft.core.HolderLookup
|
||||||
import net.minecraft.core.NonNullList
|
import net.minecraft.core.NonNullList
|
||||||
import net.minecraft.world.item.ItemStack
|
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.RecipeInput
|
import net.minecraft.world.item.crafting.RecipeInput
|
||||||
|
import net.minecraft.world.item.crafting.RecipeSerializer
|
||||||
|
import net.minecraft.world.item.crafting.RecipeType
|
||||||
|
import net.minecraft.world.level.Level
|
||||||
|
|
||||||
// passthrough all default methods to fix Kotlin bug related to implementation delegation not properly working on Java interfaces
|
// passthrough all default methods to fix Kotlin bug related to implementation delegation not properly working on Java interfaces
|
||||||
// https://youtrack.jetbrains.com/issue/KT-55080/Change-the-behavior-of-inheritance-delegation-to-delegates-implementing-Java-interfaces-with-default-methods
|
// https://youtrack.jetbrains.com/issue/KT-55080/Change-the-behavior-of-inheritance-delegation-to-delegates-implementing-Java-interfaces-with-default-methods
|
||||||
|
@ -1,162 +0,0 @@
|
|||||||
package ru.dbotthepony.mc.otm.recipe
|
|
||||||
|
|
||||||
import net.minecraft.core.NonNullList
|
|
||||||
import net.minecraft.world.item.crafting.CraftingInput
|
|
||||||
import net.minecraft.world.item.crafting.Ingredient
|
|
||||||
import ru.dbotthepony.mc.otm.core.collect.allEqual
|
|
||||||
import ru.dbotthepony.mc.otm.core.collect.any
|
|
||||||
import ru.dbotthepony.mc.otm.core.collect.flatMap
|
|
||||||
import ru.dbotthepony.mc.otm.core.collect.map
|
|
||||||
import ru.dbotthepony.mc.otm.core.get
|
|
||||||
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
|
||||||
import ru.dbotthepony.mc.otm.core.util.InvalidableLazy
|
|
||||||
import java.util.function.Predicate
|
|
||||||
|
|
||||||
interface IIngredientMatrix : Predicate<CraftingInput>, Iterable<Ingredient> {
|
|
||||||
val width: Int
|
|
||||||
val height: Int
|
|
||||||
val isEmpty: Boolean
|
|
||||||
get() = width == 0 || height == 0
|
|
||||||
|
|
||||||
val isIncomplete: Boolean
|
|
||||||
get() = iterator().any { it.hasNoItems() }
|
|
||||||
|
|
||||||
operator fun get(column: Int, row: Int): Ingredient
|
|
||||||
|
|
||||||
operator fun get(column: Int, row: Int, flop: Boolean): Ingredient {
|
|
||||||
return if (flop)
|
|
||||||
get(width - column - 1, row)
|
|
||||||
else
|
|
||||||
get(column, row)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun iterator(): Iterator<Ingredient> {
|
|
||||||
return (0 until width).iterator().flatMap { x ->
|
|
||||||
(0 until height).iterator().map { y ->
|
|
||||||
get(x, y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val ingredients: NonNullList<Ingredient> get() {
|
|
||||||
val result = NonNullList.createWithCapacity<Ingredient>(width * height)
|
|
||||||
|
|
||||||
for (x in 0 until width) {
|
|
||||||
for (y in 0 until height) {
|
|
||||||
result.add(get(x, y))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
fun test(t: CraftingInput, fromColumn: Int, fromRow: Int, flop: Boolean): Boolean {
|
|
||||||
if (t.width() - fromColumn < width || t.height() - fromRow < height)
|
|
||||||
return false
|
|
||||||
|
|
||||||
for (column in 0 until width)
|
|
||||||
for (row in 0 until height)
|
|
||||||
if (!this[column, row, flop].test(t[fromColumn + column, fromRow + row, flop]))
|
|
||||||
return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun preemptiveTest(t: CraftingInput, fromColumn: Int, fromRow: Int, flop: Boolean): Boolean {
|
|
||||||
if (t.width() - fromColumn < width || t.height() - fromRow < height)
|
|
||||||
return false
|
|
||||||
|
|
||||||
for (column in 0 until width) {
|
|
||||||
for (row in 0 until height) {
|
|
||||||
val item = t[fromColumn + column, fromRow + row, flop]
|
|
||||||
val ingredient = this[column, row, flop]
|
|
||||||
|
|
||||||
if (!ingredient.test(item) && item.isNotEmpty) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun test(t: CraftingInput): Boolean {
|
|
||||||
if (t.width() < width || t.height() < height)
|
|
||||||
return false
|
|
||||||
|
|
||||||
for (column in 0 .. t.width() - width)
|
|
||||||
for (row in 0 .. t.height() - height)
|
|
||||||
if (test(t, column, row, false) || test(t, column, row, true))
|
|
||||||
return true
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun preemptiveTest(t: CraftingInput): Boolean {
|
|
||||||
if (t.width() < width || t.height() < height)
|
|
||||||
return false
|
|
||||||
|
|
||||||
for (column in 0 .. t.width() - width)
|
|
||||||
for (row in 0 .. t.height() - height)
|
|
||||||
if (preemptiveTest(t, column, row, false) || preemptiveTest(t, column, row, true))
|
|
||||||
return true
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object : IIngredientMatrix {
|
|
||||||
override val width: Int
|
|
||||||
get() = 0
|
|
||||||
override val height: Int
|
|
||||||
get() = 0
|
|
||||||
|
|
||||||
override fun get(column: Int, row: Int): Ingredient {
|
|
||||||
return Ingredient.EMPTY
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class IngredientMatrix(override val width: Int, override val height: Int) : IIngredientMatrix {
|
|
||||||
private val data = Array(width * height) { Ingredient.EMPTY }
|
|
||||||
|
|
||||||
private val lazy = InvalidableLazy.Impl {
|
|
||||||
super.isIncomplete
|
|
||||||
}
|
|
||||||
|
|
||||||
override val isIncomplete by lazy
|
|
||||||
|
|
||||||
override fun iterator(): Iterator<Ingredient> {
|
|
||||||
return data.iterator()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun get(column: Int, row: Int): Ingredient {
|
|
||||||
require(column in 0 until width) { "Column out of bounds: $column (matrix width: $width)" }
|
|
||||||
require(row in 0 until height) { "Row out of bounds: $row > (matrix height: $height)" }
|
|
||||||
return data[column + row * width]
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun set(column: Int, row: Int, value: Ingredient) {
|
|
||||||
require(column in 0 until width) { "Column out of bounds: $column (matrix width: $width)" }
|
|
||||||
require(row in 0 until height) { "Row out of bounds: $row > (matrix height: $height)" }
|
|
||||||
data[column + row * width] = value
|
|
||||||
lazy.invalidate()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun of(vararg values: Collection<Ingredient>): IngredientMatrix {
|
|
||||||
if (!values.iterator().map { it.size }.allEqual()) {
|
|
||||||
throw IllegalArgumentException("One or more rows have different number of columns than the rest")
|
|
||||||
}
|
|
||||||
|
|
||||||
val result = IngredientMatrix(values.first().size, values.size)
|
|
||||||
|
|
||||||
for ((y, l) in values.withIndex()) {
|
|
||||||
for ((x, i) in l.withIndex()) {
|
|
||||||
result[x, y] = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +1,31 @@
|
|||||||
package ru.dbotthepony.mc.otm.recipe
|
package ru.dbotthepony.mc.otm.recipe
|
||||||
|
|
||||||
import com.mojang.serialization.Codec
|
import com.mojang.serialization.Codec
|
||||||
|
import com.mojang.serialization.MapCodec
|
||||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||||
import net.minecraft.core.HolderLookup
|
import net.minecraft.core.HolderLookup
|
||||||
import net.minecraft.core.NonNullList
|
import net.minecraft.core.NonNullList
|
||||||
import net.minecraft.core.RegistryAccess
|
|
||||||
import net.minecraft.core.UUIDUtil
|
import net.minecraft.core.UUIDUtil
|
||||||
import net.minecraft.resources.ResourceLocation
|
import net.minecraft.core.component.DataComponentType
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
|
import net.minecraft.network.codec.StreamCodec
|
||||||
import net.minecraft.world.item.ItemStack
|
import net.minecraft.world.item.ItemStack
|
||||||
import net.minecraft.world.item.crafting.CraftingInput
|
import net.minecraft.world.item.crafting.CraftingInput
|
||||||
import net.minecraft.world.item.crafting.Ingredient
|
import net.minecraft.world.item.crafting.Ingredient
|
||||||
import net.minecraft.world.item.crafting.RecipeSerializer
|
import net.minecraft.world.item.crafting.RecipeSerializer
|
||||||
import net.minecraft.world.item.crafting.RecipeType
|
import net.minecraft.world.item.crafting.RecipeType
|
||||||
|
import net.minecraft.world.item.crafting.ShapedRecipePattern
|
||||||
import net.minecraft.world.level.Level
|
import net.minecraft.world.level.Level
|
||||||
|
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||||
import ru.dbotthepony.mc.otm.capability.matteryEnergy
|
import ru.dbotthepony.mc.otm.capability.matteryEnergy
|
||||||
import ru.dbotthepony.mc.otm.core.collect.filterNotNull
|
import ru.dbotthepony.mc.otm.core.collect.filterNotNull
|
||||||
import ru.dbotthepony.mc.otm.core.collect.map
|
import ru.dbotthepony.mc.otm.core.collect.map
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
|
||||||
import ru.dbotthepony.mc.otm.data.DecimalCodec
|
import ru.dbotthepony.mc.otm.data.DecimalCodec
|
||||||
import ru.dbotthepony.mc.otm.data.IngredientMatrixCodec
|
|
||||||
import ru.dbotthepony.mc.otm.data.minRange
|
import ru.dbotthepony.mc.otm.data.minRange
|
||||||
|
import ru.dbotthepony.mc.otm.network.StreamCodecs
|
||||||
|
import ru.dbotthepony.mc.otm.network.optional
|
||||||
|
import ru.dbotthepony.mc.otm.network.wrap
|
||||||
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.Optional
|
import java.util.Optional
|
||||||
@ -30,7 +35,7 @@ import kotlin.jvm.optionals.getOrElse
|
|||||||
interface IMatterEntanglerRecipe : IMatteryRecipe<CraftingInput> {
|
interface IMatterEntanglerRecipe : IMatteryRecipe<CraftingInput> {
|
||||||
val matter: Decimal
|
val matter: Decimal
|
||||||
val ticks: Double
|
val ticks: Double
|
||||||
val ingredients: IIngredientMatrix
|
val ingredients: ShapedRecipePattern
|
||||||
val result: ItemStack
|
val result: ItemStack
|
||||||
val experience: Float
|
val experience: Float
|
||||||
|
|
||||||
@ -38,12 +43,12 @@ interface IMatterEntanglerRecipe : IMatteryRecipe<CraftingInput> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open class MatterEntanglerRecipe(
|
open class MatterEntanglerRecipe(
|
||||||
override val ingredients: IIngredientMatrix,
|
override val ingredients: ShapedRecipePattern,
|
||||||
override val matter: Decimal,
|
override val matter: Decimal,
|
||||||
override val ticks: Double,
|
override val ticks: Double,
|
||||||
override val result: ItemStack,
|
override val result: ItemStack,
|
||||||
override val experience: Float = 0f,
|
override val experience: Float = 0f,
|
||||||
val uuidKey: String = "uuid",
|
val uuidKey: Optional<DataComponentType<UUID>> = Optional.empty(),
|
||||||
val fixedUuid: Optional<UUID> = Optional.empty()
|
val fixedUuid: Optional<UUID> = Optional.empty()
|
||||||
) : IMatterEntanglerRecipe {
|
) : IMatterEntanglerRecipe {
|
||||||
override fun matches(container: CraftingInput, level: Level): Boolean {
|
override fun matches(container: CraftingInput, level: Level): Boolean {
|
||||||
@ -58,7 +63,9 @@ open class MatterEntanglerRecipe(
|
|||||||
|
|
||||||
override fun assemble(container: CraftingInput, registry: HolderLookup.Provider): ItemStack {
|
override fun assemble(container: CraftingInput, registry: HolderLookup.Provider): ItemStack {
|
||||||
return result.copy().also {
|
return result.copy().also {
|
||||||
it.tagNotNull[uuidKey] = fixedUuid.getOrElse { UUID.randomUUID() }
|
uuidKey.ifPresent { id ->
|
||||||
|
it[id] = fixedUuid.getOrElse { UUID.randomUUID() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,12 +73,12 @@ open class MatterEntanglerRecipe(
|
|||||||
return width >= ingredients.width && height >= ingredients.height
|
return width >= ingredients.width && height >= ingredients.height
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getResultItem(registry: RegistryAccess): ItemStack {
|
override fun getResultItem(registry: HolderLookup.Provider): ItemStack {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSerializer(): RecipeSerializer<*> {
|
override fun getSerializer(): RecipeSerializer<*> {
|
||||||
return SERIALIZER
|
return Companion
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getType(): RecipeType<*> {
|
override fun getType(): RecipeType<*> {
|
||||||
@ -79,7 +86,7 @@ open class MatterEntanglerRecipe(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getIngredients(): NonNullList<Ingredient> {
|
override fun getIngredients(): NonNullList<Ingredient> {
|
||||||
return ingredients.ingredients
|
return ingredients.ingredients()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isIncomplete(): Boolean {
|
override fun isIncomplete(): Boolean {
|
||||||
@ -94,10 +101,6 @@ open class MatterEntanglerRecipe(
|
|||||||
return ItemStack(MItems.MATTER_ENTANGLER)
|
return ItemStack(MItems.MATTER_ENTANGLER)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toFinished(id: ResourceLocation): FinishedRecipe {
|
|
||||||
return SERIALIZER.toFinished(this, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun energetic() = Energy(this)
|
fun energetic() = Energy(this)
|
||||||
fun matter() = Matter(this)
|
fun matter() = Matter(this)
|
||||||
|
|
||||||
@ -110,49 +113,81 @@ open class MatterEntanglerRecipe(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toFinished(id: ResourceLocation): FinishedRecipe {
|
|
||||||
return ENERGY_SERIALIZER.toFinished(this, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getSerializer(): RecipeSerializer<*> {
|
override fun getSerializer(): RecipeSerializer<*> {
|
||||||
return ENERGY_SERIALIZER
|
return EnergySerializer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open class Matter(val parent: MatterEntanglerRecipe) : IMatterEntanglerRecipe by parent {
|
open class Matter(val parent: MatterEntanglerRecipe) : IMatterEntanglerRecipe by parent {
|
||||||
override fun assemble(container: CraftingInput, registry: HolderLookup.Provider): ItemStack {
|
override fun assemble(container: CraftingInput, registry: HolderLookup.Provider): ItemStack {
|
||||||
return parent.assemble(container, registry).also { result ->
|
return parent.assemble(container, registry).also { result ->
|
||||||
container.items().iterator().map { it.matter }.filterNotNull().forEach {
|
container.items().iterator().map { it.getCapability(MatteryCapability.MATTER_ITEM) }.filterNotNull().forEach {
|
||||||
result.matter!!.storedMatter += it.storedMatter
|
result.getCapability(MatteryCapability.MATTER_ITEM)!!.storedMatter += it.storedMatter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toFinished(id: ResourceLocation): FinishedRecipe {
|
|
||||||
return MATTER_SERIALIZER.toFinished(this, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getSerializer(): RecipeSerializer<*> {
|
override fun getSerializer(): RecipeSerializer<*> {
|
||||||
return MATTER_SERIALIZER
|
return MatterSerializer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
private object EnergySerializer : RecipeSerializer<Energy> {
|
||||||
val SERIALIZER = Codec2RecipeSerializer<MatterEntanglerRecipe> { context ->
|
private val energyCodec: MapCodec<Energy> = codec.xmap(::Energy, Energy::parent)
|
||||||
RecordCodecBuilder.create {
|
private val energyStreamCodec: StreamCodec<RegistryFriendlyByteBuf, Energy> = streamCodec.map(::Energy, Energy::parent)
|
||||||
it.group(
|
|
||||||
IngredientMatrixCodec(context.ingredients).fieldOf("ingredients").forGetter(MatterEntanglerRecipe::ingredients),
|
override fun codec(): MapCodec<Energy> {
|
||||||
DecimalCodec.minRange(Decimal.ZERO).fieldOf("matter").forGetter(MatterEntanglerRecipe::matter),
|
return energyCodec
|
||||||
Codec.DOUBLE.minRange(0.0).fieldOf("ticks").forGetter(MatterEntanglerRecipe::ticks),
|
|
||||||
ItemStack.CODEC.fieldOf("result").forGetter(MatterEntanglerRecipe::result),
|
|
||||||
Codec.FLOAT.minRange(0f).optionalFieldOf("experience", 0f).forGetter(MatterEntanglerRecipe::experience),
|
|
||||||
Codec.STRING.optionalFieldOf("uuidKey", "uuid").forGetter(MatterEntanglerRecipe::uuidKey),
|
|
||||||
UUIDUtil.STRING_CODEC.optionalFieldOf("fixedUuid").forGetter(MatterEntanglerRecipe::fixedUuid)
|
|
||||||
).apply(it, ::MatterEntanglerRecipe)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val ENERGY_SERIALIZER = SERIALIZER.xmap(::Energy, Energy::parent)
|
override fun streamCodec(): StreamCodec<RegistryFriendlyByteBuf, Energy> {
|
||||||
val MATTER_SERIALIZER = SERIALIZER.xmap(::Matter, Matter::parent)
|
return energyStreamCodec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private object MatterSerializer : RecipeSerializer<Matter> {
|
||||||
|
private val matterCodec: MapCodec<Matter> = codec.xmap(::Matter, Matter::parent)
|
||||||
|
private val matterStreamCodec: StreamCodec<RegistryFriendlyByteBuf, Matter> = streamCodec.map(::Matter, Matter::parent)
|
||||||
|
|
||||||
|
override fun codec(): MapCodec<Matter> {
|
||||||
|
return matterCodec
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun streamCodec(): StreamCodec<RegistryFriendlyByteBuf, Matter> {
|
||||||
|
return matterStreamCodec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object : RecipeSerializer<MatterEntanglerRecipe> {
|
||||||
|
val codec: MapCodec<MatterEntanglerRecipe> = RecordCodecBuilder.mapCodec {
|
||||||
|
it.group(
|
||||||
|
ShapedRecipePattern.MAP_CODEC.fieldOf("ingredients").forGetter(MatterEntanglerRecipe::ingredients),
|
||||||
|
DecimalCodec.minRange(Decimal.ZERO).fieldOf("matter").forGetter(MatterEntanglerRecipe::matter),
|
||||||
|
Codec.DOUBLE.minRange(0.0).fieldOf("ticks").forGetter(MatterEntanglerRecipe::ticks),
|
||||||
|
ItemStack.CODEC.fieldOf("result").forGetter(MatterEntanglerRecipe::result),
|
||||||
|
Codec.FLOAT.minRange(0f).optionalFieldOf("experience", 0f).forGetter(MatterEntanglerRecipe::experience),
|
||||||
|
(DataComponentType.CODEC as Codec<DataComponentType<UUID>>).optionalFieldOf("uuidKey").forGetter(MatterEntanglerRecipe::uuidKey),
|
||||||
|
UUIDUtil.STRING_CODEC.optionalFieldOf("fixedUuid").forGetter(MatterEntanglerRecipe::fixedUuid)
|
||||||
|
).apply(it, ::MatterEntanglerRecipe)
|
||||||
|
}
|
||||||
|
|
||||||
|
val streamCodec: StreamCodec<RegistryFriendlyByteBuf, MatterEntanglerRecipe> = StreamCodecs.composite(
|
||||||
|
ShapedRecipePattern.STREAM_CODEC, MatterEntanglerRecipe::ingredients,
|
||||||
|
StreamCodecs.DECIMAL, { it.matter },
|
||||||
|
StreamCodecs.DOUBLE, MatterEntanglerRecipe::ticks,
|
||||||
|
ItemStack.STREAM_CODEC, MatterEntanglerRecipe::result,
|
||||||
|
StreamCodecs.FLOAT, MatterEntanglerRecipe::experience,
|
||||||
|
DataComponentType.STREAM_CODEC.wrap().optional() as StreamCodec<RegistryFriendlyByteBuf, Optional<DataComponentType<UUID>>>, MatterEntanglerRecipe::uuidKey,
|
||||||
|
UUIDUtil.STREAM_CODEC.wrap().optional(), MatterEntanglerRecipe::fixedUuid,
|
||||||
|
::MatterEntanglerRecipe
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun codec(): MapCodec<MatterEntanglerRecipe> {
|
||||||
|
return codec
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun streamCodec(): StreamCodec<RegistryFriendlyByteBuf, MatterEntanglerRecipe> {
|
||||||
|
return streamCodec
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user