Item repairer recipe

This commit is contained in:
DBotThePony 2023-03-24 23:28:06 +07:00
parent d1978d7e04
commit 66140d33a2
Signed by: DBot
GPG Key ID: DCC23B5715498507
6 changed files with 324 additions and 22 deletions

View File

@ -8,12 +8,14 @@ import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.crafting.Ingredient
import net.minecraftforge.common.Tags
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
import ru.dbotthepony.mc.otm.registry.MBlocks
import ru.dbotthepony.mc.otm.registry.MItemTags
import ru.dbotthepony.mc.otm.registry.MItems
import ru.dbotthepony.mc.otm.registry.MRegistry
import ru.dbotthepony.mc.otm.core.registryName
import ru.dbotthepony.mc.otm.datagen.modLocation
import ru.dbotthepony.mc.otm.recipe.UpgradeRecipe
import java.util.function.Consumer
fun addCraftingTableRecipes(consumer: Consumer<FinishedRecipe>) {
@ -295,4 +297,15 @@ fun addCraftingTableRecipes(consumer: Consumer<FinishedRecipe>) {
.rowB(Tags.Items.RODS_WOODEN)
.rowB(Tags.Items.RODS_WOODEN)
.build(consumer)
MatteryRecipe(MItems.ITEM_REPAIRER, category = machinesCategory)
.setUpgradeSource(MItems.MATTER_REPLICATOR)
.addUpgradeOps(
UpgradeRecipe.Indirect("BlockEntityTag.${MatteryBlockEntity.ENERGY_KEY}", "BlockEntityTag.energy"),
UpgradeRecipe.Indirect("BlockEntityTag.${MatteryBlockEntity.MATTER_STORAGE_KEY}", "BlockEntityTag.matter"),
)
.row(MItemTags.ADVANCED_CIRCUIT, Tags.Items.GEMS_EMERALD, MItemTags.ADVANCED_CIRCUIT)
.row(MItems.ELECTRIC_PARTS, MItems.MATTER_REPLICATOR, MItems.ELECTRIC_PARTS)
.row(MItems.ELECTROMAGNET, MItems.ELECTROMAGNET, MItems.ELECTROMAGNET)
.build(consumer)
}

View File

@ -1,13 +1,11 @@
package ru.dbotthepony.mc.otm.datagen.recipes
import com.google.common.collect.ImmutableList
import com.google.gson.JsonObject
import net.minecraft.advancements.CriterionTriggerInstance
import net.minecraft.data.recipes.FinishedRecipe
import net.minecraft.data.recipes.RecipeBuilder
import net.minecraft.data.recipes.RecipeCategory
import net.minecraft.data.recipes.ShapedRecipeBuilder
import net.minecraft.data.recipes.ShapelessRecipeBuilder
import net.minecraft.resources.ResourceLocation
import net.minecraft.tags.TagKey
import net.minecraft.world.item.Item
@ -15,8 +13,11 @@ import net.minecraft.world.item.crafting.Ingredient
import net.minecraft.world.item.crafting.RecipeSerializer
import net.minecraft.world.level.ItemLike
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.core.collect.JsonArrayCollector
import ru.dbotthepony.mc.otm.core.registryName
import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.recipe.EnergyContainerRecipe
import ru.dbotthepony.mc.otm.recipe.UpgradeRecipe
import java.util.function.Consumer
private interface RecipeCell {
@ -116,7 +117,7 @@ class MatteryRecipe(val result: ItemLike, val count: Int = 1, val category: Reci
}
}
fun build(consumer: Consumer<FinishedRecipe>, name: String? = null) {
private fun buildRegular(): ShapedRecipeBuilder {
if (index == 0) {
throw NoSuchElementException("No recipe rows were defined")
}
@ -140,15 +141,62 @@ class MatteryRecipe(val result: ItemLike, val count: Int = 1, val category: Reci
builder.unlockedBy(a, b)
}
return builder
}
private var source: ResourceLocation? = null
private val copyPaths = ArrayList<UpgradeRecipe.Op>()
fun setUpgradeSource(source: ResourceLocation): MatteryRecipe {
this.source = source
return this
}
fun setUpgradeSource(source: ItemLike): MatteryRecipe {
this.source = source.asItem().registryName!!
return this
}
fun addUpgradeOps(vararg operations: UpgradeRecipe.Op): MatteryRecipe {
for (op in operations) copyPaths.add(op)
return this
}
private fun filter(consumer: Consumer<FinishedRecipe>): Consumer<FinishedRecipe> {
if (source != null) {
check(copyPaths.isNotEmpty()) { "Defined upgrade recipe without nbt migration operations" }
return Consumer {
consumer.accept(object : FinishedRecipe by it {
override fun serializeRecipeData(pJson: JsonObject) {
it.serializeRecipeData(pJson)
pJson["copyPaths"] = copyPaths.stream().map { it.serialize() }.collect(JsonArrayCollector)
pJson["source"] = source!!.toString()
}
override fun getType(): RecipeSerializer<*> {
return UpgradeRecipe.Companion
}
})
}
}
return consumer
}
fun build(consumer: Consumer<FinishedRecipe>, name: String? = null) {
val builder = buildRegular()
if (name != null) {
builder.save(consumer, ResourceLocation(OverdriveThatMatters.MOD_ID,
builder.save(filter(consumer), ResourceLocation(OverdriveThatMatters.MOD_ID,
if (result.asItem().registryName!!.namespace == OverdriveThatMatters.MOD_ID)
"${result.asItem().registryName!!.path}_$name"
else
"${result.asItem().registryName!!.namespace}_${result.asItem().registryName!!.path}_$name"
))
} else {
builder.save(consumer)
builder.save(filter(consumer))
}
}

View File

@ -11,6 +11,7 @@ import ru.dbotthepony.mc.otm.OverdriveThatMatters;
import ru.dbotthepony.mc.otm.recipe.EnergyContainerRecipe;
import ru.dbotthepony.mc.otm.recipe.PlatePressRecipe;
import ru.dbotthepony.mc.otm.recipe.PlatePressRecipeFactory;
import ru.dbotthepony.mc.otm.recipe.UpgradeRecipe;
public class MRecipes {
public static class MatteryRecipeType<T extends Recipe<?>> implements RecipeType<T> {
@ -28,6 +29,7 @@ public class MRecipes {
public static final MatteryRecipeType<PlatePressRecipe> PLATE_PRESS = new MatteryRecipeType<>(OverdriveThatMatters.loc(MNames.PLATE_PRESS));
public static final MatteryRecipeType<PlatePressRecipe> ENERGY_CONTAINER = new MatteryRecipeType<>(OverdriveThatMatters.loc("energy_container"));
public static final MatteryRecipeType<PlatePressRecipe> UPGRADE = new MatteryRecipeType<>(OverdriveThatMatters.loc("upgrade"));
private static final DeferredRegister<RecipeSerializer<?>> serializerRegistry = DeferredRegister.create(ForgeRegistries.RECIPE_SERIALIZERS, OverdriveThatMatters.MOD_ID);
private static final DeferredRegister<RecipeType<?>> typeRegistry = DeferredRegister.create(ForgeRegistries.RECIPE_TYPES, OverdriveThatMatters.MOD_ID);
@ -35,8 +37,11 @@ public class MRecipes {
static {
serializerRegistry.register(MNames.PLATE_PRESS, () -> PlatePressRecipeFactory.INSTANCE);
serializerRegistry.register(ENERGY_CONTAINER.name.getPath(), () -> EnergyContainerRecipe.Companion);
serializerRegistry.register(UPGRADE.name.getPath(), () -> UpgradeRecipe.Companion);
typeRegistry.register(MNames.PLATE_PRESS, () -> PLATE_PRESS);
typeRegistry.register(ENERGY_CONTAINER.name.getPath(), () -> ENERGY_CONTAINER);
typeRegistry.register(UPGRADE.name.getPath(), () -> UPGRADE);
}
public static void register(IEventBus bus) {

View File

@ -0,0 +1,31 @@
package ru.dbotthepony.mc.otm.core.collect
import com.google.gson.JsonArray
import com.google.gson.JsonElement
import java.util.function.BiConsumer
import java.util.function.BinaryOperator
import java.util.function.Function
import java.util.function.Supplier
import java.util.stream.Collector
object JsonArrayCollector : Collector<JsonElement, JsonArray, JsonArray> {
override fun supplier(): Supplier<JsonArray> {
return Supplier { JsonArray() }
}
override fun accumulator(): BiConsumer<JsonArray, JsonElement> {
return BiConsumer { t, u -> t.add(u) }
}
override fun combiner(): BinaryOperator<JsonArray> {
return BinaryOperator { t, u -> t.addAll(u); t }
}
override fun finisher(): Function<JsonArray, JsonArray> {
return Function.identity()
}
override fun characteristics(): Set<Collector.Characteristics> {
return setOf(Collector.Characteristics.IDENTITY_FINISH)
}
}

View File

@ -17,14 +17,14 @@ import ru.dbotthepony.mc.otm.container.stream
import ru.dbotthepony.mc.otm.core.filterNotNull
class EnergyContainerRecipe(
p_44153_: ResourceLocation,
p_44154_: String,
id: ResourceLocation,
group: String,
category: CraftingBookCategory,
p_44155_: Int,
p_44156_: Int,
p_44157_: NonNullList<Ingredient>,
p_44158_: ItemStack,
) : ShapedRecipe(p_44153_, p_44154_, category, p_44155_, p_44156_, p_44157_, p_44158_) {
width: Int,
height: Int,
ingredients: NonNullList<Ingredient>,
result: ItemStack,
) : ShapedRecipe(id, group, category, width, height, ingredients, result) {
constructor(parent: ShapedRecipe) : this(parent.id, parent.group, parent.category(), parent.width, parent.height, parent.ingredients, parent.resultItem)
override fun assemble(container: CraftingContainer): ItemStack {
@ -53,25 +53,25 @@ class EnergyContainerRecipe(
return itemStack
}
override fun matches(p_44002_: CraftingContainer, p_44003_: Level): Boolean {
return super.matches(p_44002_, p_44003_) && !p_44002_.stream().anyMatch { it.isDamaged }
override fun matches(container: CraftingContainer, level: Level): Boolean {
return super.matches(container, level) && !container.stream().anyMatch { it.isDamaged }
}
override fun getSerializer(): RecipeSerializer<*> {
override fun getSerializer(): RecipeSerializer<EnergyContainerRecipe> {
return Companion
}
companion object : RecipeSerializer<EnergyContainerRecipe> {
override fun fromJson(p_44103_: ResourceLocation, p_44104_: JsonObject): EnergyContainerRecipe {
return EnergyContainerRecipe(Serializer.SHAPED_RECIPE.fromJson(p_44103_, p_44104_))
override fun fromJson(id: ResourceLocation, data: JsonObject): EnergyContainerRecipe {
return EnergyContainerRecipe(Serializer.SHAPED_RECIPE.fromJson(id, data))
}
override fun fromNetwork(p_44105_: ResourceLocation, p_44106_: FriendlyByteBuf): EnergyContainerRecipe? {
return Serializer.SHAPED_RECIPE.fromNetwork(p_44105_, p_44106_)?.let(::EnergyContainerRecipe)
override fun fromNetwork(id: ResourceLocation, data: FriendlyByteBuf): EnergyContainerRecipe? {
return Serializer.SHAPED_RECIPE.fromNetwork(id, data)?.let(::EnergyContainerRecipe)
}
override fun toNetwork(p_44101_: FriendlyByteBuf, p_44102_: EnergyContainerRecipe) {
Serializer.SHAPED_RECIPE.toNetwork(p_44101_, p_44102_)
override fun toNetwork(buff: FriendlyByteBuf, value: EnergyContainerRecipe) {
Serializer.SHAPED_RECIPE.toNetwork(buff, value)
}
}
}

View File

@ -0,0 +1,205 @@
package ru.dbotthepony.mc.otm.recipe
import com.google.common.collect.ImmutableList
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import net.minecraft.core.NonNullList
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.GsonHelper
import net.minecraft.world.inventory.CraftingContainer
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.crafting.CraftingBookCategory
import net.minecraft.world.item.crafting.Ingredient
import net.minecraft.world.item.crafting.RecipeSerializer
import net.minecraft.world.item.crafting.ShapedRecipe
import ru.dbotthepony.mc.otm.container.stream
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.registryName
import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.core.tagNotNull
import ru.dbotthepony.mc.otm.core.util.readJson
import ru.dbotthepony.mc.otm.core.util.writeJson
import ru.dbotthepony.mc.otm.data.stream
import java.util.stream.Stream
class UpgradeRecipe(
id: ResourceLocation,
group: String,
category: CraftingBookCategory,
width: Int,
height: Int,
ingredients: NonNullList<Ingredient>,
result: ItemStack,
copyPaths: Stream<Op>,
val source: ResourceLocation,
) : ShapedRecipe(id, group, category, width, height, ingredients, result) {
constructor(parent: ShapedRecipe, copyPaths: Stream<Op>, source: ResourceLocation) : this(parent.id, parent.group, parent.category(), parent.width, parent.height, parent.ingredients, parent.resultItem, copyPaths, source)
enum class OpType {
DIRECT {
override fun deserialize(obj: JsonObject): Op {
return Direct(GsonHelper.getAsString(obj, "path"))
}
}, INDIRECT {
override fun deserialize(obj: JsonObject): Op {
return Indirect(GsonHelper.getAsString(obj, "source"), GsonHelper.getAsString(obj, "destination"))
}
};
abstract fun deserialize(obj: JsonObject): Op
}
interface Op {
val type: OpType
fun apply(source: CompoundTag, destination: CompoundTag)
fun serialize(json: JsonObject)
fun serialize(): JsonObject {
return JsonObject().also {
it["type"] = JsonPrimitive(type.name)
serialize(it)
}
}
}
data class Direct(val path: String) : Op {
private val split = path.split('.')
override val type: OpType
get() = OpType.DIRECT
override fun serialize(json: JsonObject) {
json["path"] = JsonPrimitive("path")
}
override fun apply(source: CompoundTag, destination: CompoundTag) {
var a = source
var b = destination
for (i in 0 until split.size - 1) {
val value = split[i]
if (value !in a) {
a[value] = CompoundTag()
}
if (value !in b) {
b[value] = CompoundTag()
}
a = a[value] as CompoundTag
b = b[value] as CompoundTag
}
val value = split.last()
if (value in a) {
b[value] = a[value]!!.copy()
}
}
}
data class Indirect(val pathSource: String, val pathDestination: String) : Op {
private val splitSource = pathSource.split('.')
private val splitDestination = pathDestination.split('.')
override val type: OpType
get() = OpType.INDIRECT
override fun serialize(json: JsonObject) {
json["source"] = JsonPrimitive(pathSource)
json["destination"] = JsonPrimitive(pathDestination)
}
override fun apply(source: CompoundTag, destination: CompoundTag) {
var a = source
var b = destination
for (i in 0 until splitSource.size - 1) {
val value = splitSource[i]
if (value !in a) {
a[value] = CompoundTag()
}
a = a[value] as CompoundTag
}
for (i in 0 until splitDestination.size - 1) {
val value = splitDestination[i]
if (value !in b) {
b[value] = CompoundTag()
}
b = b[value] as CompoundTag
}
val valueA = splitSource.last()
val valueB = splitDestination.last()
if (valueA in a) {
b[valueB] = a[valueA]!!.copy()
}
}
}
val copyPaths: ImmutableList<Op> = copyPaths.collect(ImmutableList.toImmutableList())
override fun assemble(pInv: CraftingContainer): ItemStack {
val result = super.assemble(pInv)
if (result.isEmpty) {
return result
}
val sources = pInv.stream().filter { !it.isEmpty && it.item.registryName == source }.toList()
if (sources.size != 1) {
return ItemStack.EMPTY
}
val source = sources.first()
for (op in copyPaths) {
op.apply(source.tagNotNull, result.tagNotNull)
}
return result
}
override fun getSerializer(): RecipeSerializer<UpgradeRecipe> {
return Companion
}
companion object : RecipeSerializer<UpgradeRecipe> {
fun deserializeOp(json: JsonObject): Op {
return OpType.valueOf(GsonHelper.getAsString(json, "type")).deserialize(json)
}
override fun fromJson(id: ResourceLocation, data: JsonObject): UpgradeRecipe {
return UpgradeRecipe(
Serializer.SHAPED_RECIPE.fromJson(id, data),
GsonHelper.getAsJsonArray(data, "copyPaths").stream().map { deserializeOp(it as JsonObject) },
ResourceLocation(GsonHelper.getAsString(data, "source"))
)
}
override fun fromNetwork(id: ResourceLocation, buff: FriendlyByteBuf): UpgradeRecipe? {
val recipe = Serializer.SHAPED_RECIPE.fromNetwork(id, buff) ?: return null
return UpgradeRecipe(
recipe,
buff.readCollection({ ArrayList<Op>(it) }, { deserializeOp(it.readJson() as JsonObject) }).stream(),
buff.readResourceLocation()
)
}
override fun toNetwork(buff: FriendlyByteBuf, value: UpgradeRecipe) {
Serializer.SHAPED_RECIPE.toNetwork(buff, value)
buff.writeCollection(value.copyPaths) { it, v -> it.writeJson(v.serialize()) }
buff.writeResourceLocation(value.source)
}
}
}