Update upgrade recipe, and merge energy container recipe onto upgrade recipe

This commit is contained in:
DBotThePony 2024-08-17 21:06:12 +07:00
parent 7f927d5f05
commit 4c0bac35c1
Signed by: DBot
GPG Key ID: DCC23B5715498507
4 changed files with 112 additions and 262 deletions

View File

@ -4,7 +4,6 @@ package ru.dbotthepony.mc.otm.datagen.recipes
import com.google.gson.JsonObject import com.google.gson.JsonObject
import net.minecraft.advancements.Advancement import net.minecraft.advancements.Advancement
import net.minecraft.advancements.Criterion import net.minecraft.advancements.Criterion
import net.minecraft.advancements.CriterionTriggerInstance
import net.minecraft.data.recipes.FinishedRecipe import net.minecraft.data.recipes.FinishedRecipe
import net.minecraft.data.recipes.RecipeCategory import net.minecraft.data.recipes.RecipeCategory
import net.minecraft.data.recipes.RecipeOutput import net.minecraft.data.recipes.RecipeOutput
@ -20,9 +19,7 @@ import ru.dbotthepony.mc.otm.core.registryName
import ru.dbotthepony.mc.otm.core.set import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.core.toJsonStrict import ru.dbotthepony.mc.otm.core.toJsonStrict
import ru.dbotthepony.mc.otm.datagen.modLocation import ru.dbotthepony.mc.otm.datagen.modLocation
import ru.dbotthepony.mc.otm.recipe.EnergyContainerRecipe
import ru.dbotthepony.mc.otm.recipe.UpgradeRecipe import ru.dbotthepony.mc.otm.recipe.UpgradeRecipe
import java.util.function.Consumer
private interface RecipeCell { private interface RecipeCell {
val value: Ingredient val value: Ingredient
@ -191,7 +188,7 @@ class MatteryRecipe(val result: ItemLike, val count: Int = 1, val category: Reci
object : FinishedRecipe by it { object : FinishedRecipe by it {
override fun serializeRecipeData(pJson: JsonObject) { override fun serializeRecipeData(pJson: JsonObject) {
pJson["parent"] = it.serializeRecipe() pJson["parent"] = it.serializeRecipe()
pJson["copyPaths"] = UpgradeRecipe.COPY_PATHS_CODEC.toJsonStrict(copyPaths) pJson["copyPaths"] = UpgradeRecipe.OPERATION_CODEC.toJsonStrict(copyPaths)
pJson["source"] = upgradeSource!!.toString() pJson["source"] = upgradeSource!!.toString()
} }

View File

@ -96,7 +96,7 @@ interface IMatteryEnergyStorage : IEnergyStorage {
val canSetBatteryLevel: Boolean get() = true val canSetBatteryLevel: Boolean get() = true
/** /**
* How much energy (estimated) is stored in this energy storage. Why estimated? Because some objects can be bottomless. * How much energy (considered to be estimated, and not ground truth) is stored in this energy storage
* *
* Implementations are free to throw [UnsupportedOperationException] if setting battery level is not supported * Implementations are free to throw [UnsupportedOperationException] if setting battery level is not supported
* due to technical complications (in this case, [canSetBatteryLevel] MUST be false) * due to technical complications (in this case, [canSetBatteryLevel] MUST be false)

View File

@ -1,83 +0,0 @@
package ru.dbotthepony.mc.otm.recipe
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import net.minecraft.core.HolderLookup
import net.minecraft.core.RegistryAccess
import net.minecraft.core.component.DataComponents
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.codec.StreamCodec
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.crafting.CraftingBookCategory
import net.minecraft.world.item.crafting.CraftingInput
import net.minecraft.world.item.crafting.RecipeSerializer
import net.minecraft.world.item.crafting.ShapedRecipe
import net.minecraft.world.item.crafting.ShapedRecipePattern
import net.minecraft.world.item.enchantment.ItemEnchantments
import net.minecraft.world.level.Level
import ru.dbotthepony.mc.otm.capability.matteryEnergy
import ru.dbotthepony.mc.otm.container.util.iterator
import ru.dbotthepony.mc.otm.core.filterNotNull
class EnergyContainerRecipe(group: String, category: CraftingBookCategory, pattern: ShapedRecipePattern, result: ItemStack, showNotification: Boolean = true) : ShapedRecipe(group, category, pattern, result, showNotification) {
constructor(parent: ShapedRecipe) : this(parent.group, parent.category(), parent.pattern, parent.result, parent.showNotification())
override fun assemble(container: CraftingInput, registryAccess: HolderLookup.Provider): ItemStack {
val itemStack = super.assemble(container, registryAccess)
val battery = container.items().stream()
.filter { !it.isEmpty }
.map { it.matteryEnergy }
.filterNotNull()
.findAny().orElse(null)
if (battery != null) {
itemStack.matteryEnergy?.batteryLevel = battery.batteryLevel
}
if (itemStack.isEnchantable) {
for (it in container.items()) {
val enchantments = it[DataComponents.ENCHANTMENTS]
if (enchantments != null) {
itemStack[DataComponents.ENCHANTMENTS] = ItemEnchantments.Mutable(itemStack.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY)).let {
for ((key, level) in enchantments.entrySet()) {
it.upgrade(key, level)
}
it.toImmutable()
}
}
}
}
return itemStack
}
override fun matches(container: CraftingInput, level: Level): Boolean {
return super.matches(container, level) && container.items().none { it.isDamaged }
}
override fun getSerializer(): RecipeSerializer<EnergyContainerRecipe> {
return Companion
}
companion object : RecipeSerializer<EnergyContainerRecipe> {
private val codec by lazy {
RecipeSerializer.SHAPED_RECIPE.codec().xmap(::EnergyContainerRecipe, { it })
}
private val streamCodec by lazy {
RecipeSerializer.SHAPED_RECIPE.streamCodec().map(::EnergyContainerRecipe, { it })
}
override fun codec(): MapCodec<EnergyContainerRecipe> {
return codec
}
override fun streamCodec(): StreamCodec<RegistryFriendlyByteBuf, EnergyContainerRecipe> {
return streamCodec
}
}
}

View File

@ -1,109 +1,59 @@
package ru.dbotthepony.mc.otm.recipe package ru.dbotthepony.mc.otm.recipe
import com.google.common.collect.ImmutableList
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.NonNullList import net.minecraft.core.HolderLookup
import net.minecraft.core.RegistryAccess import net.minecraft.core.component.DataComponentType
import net.minecraft.nbt.CompoundTag import net.minecraft.core.component.DataComponents
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.codec.ByteBufCodecs
import net.minecraft.network.codec.StreamCodec
import net.minecraft.resources.ResourceLocation import net.minecraft.resources.ResourceLocation
import net.minecraft.util.StringRepresentable import net.minecraft.util.StringRepresentable
import net.minecraft.world.inventory.CraftingContainer
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.crafting.CraftingBookCategory import net.minecraft.world.item.crafting.CraftingBookCategory
import net.minecraft.world.item.crafting.CraftingRecipe import net.minecraft.world.item.crafting.CraftingInput
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.ShapedRecipe import net.minecraft.world.item.crafting.ShapedRecipe
import net.minecraft.world.item.crafting.ShapedRecipePattern
import net.minecraft.world.item.enchantment.ItemEnchantments
import net.minecraft.world.level.Level import net.minecraft.world.level.Level
import net.minecraftforge.common.crafting.IShapedRecipe import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.container.util.stream
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.registryName import ru.dbotthepony.mc.otm.core.registryName
import ru.dbotthepony.mc.otm.core.tagNotNull
import ru.dbotthepony.mc.otm.data.SingletonCodec
import java.util.stream.Stream
class UpgradeRecipe( class UpgradeRecipe(
val parent: ShapedRecipe, group: String,
copyPaths: Stream<Op>, category: CraftingBookCategory,
val source: ResourceLocation, pattern: ShapedRecipePattern,
) : CraftingRecipe, IShapedRecipe<CraftingContainer> by parent { result: ItemStack,
constructor(parent: ShapedRecipe, copyPaths: Collection<Op>, source: ResourceLocation) : this(parent, copyPaths.stream(), source) val operations: List<Pair<ResourceLocation, Op>>,
showNotification: Boolean = true,
override fun matches(p_44002_: CraftingContainer, p_44003_: Level): Boolean { ) : ShapedRecipe(group, category, pattern, result, showNotification) {
return parent.matches(p_44002_, p_44003_) constructor(parent: ShapedRecipe, operations: List<Pair<ResourceLocation, Op>>) : this(parent.group, parent.category(), parent.pattern, parent.result, operations, parent.showNotification())
}
override fun canCraftInDimensions(p_43999_: Int, p_44000_: Int): Boolean {
return parent.canCraftInDimensions(p_43999_, p_44000_)
}
override fun getResultItem(p_267052_: RegistryAccess): ItemStack {
return parent.getResultItem(p_267052_)
}
override fun getRemainingItems(p_44004_: CraftingContainer): NonNullList<ItemStack> {
return parent.getRemainingItems(p_44004_)
}
override fun getIngredients(): NonNullList<Ingredient> {
return parent.ingredients
}
override fun isSpecial(): Boolean {
return parent.isSpecial
}
override fun showNotification(): Boolean {
return parent.showNotification()
}
override fun getGroup(): String {
return parent.group
}
override fun getToastSymbol(): ItemStack {
return parent.toastSymbol
}
override fun isIncomplete(): Boolean {
return parent.isIncomplete
}
override fun getType(): RecipeType<*> {
return parent.type
}
override fun category(): CraftingBookCategory {
return parent.category()
}
enum class OpType : StringRepresentable { enum class OpType : StringRepresentable {
DIRECT { COPY_COMPONENT {
override val codec: MapCodec<Direct> = RecordCodecBuilder.mapCodec { override val codec: MapCodec<CopyComponent<*>> = RecordCodecBuilder.mapCodec {
it.group( it.group(
Codec.STRING.fieldOf("path").forGetter(Direct::path) DataComponentType.CODEC.fieldOf("component").forGetter(CopyComponent<*>::component)
).apply(it, ::Direct) ).apply(it) { CopyComponent(it) }
} }
}, },
INDIRECT { COPY_ALL_COMPONENTS {
override val codec: MapCodec<Indirect> = RecordCodecBuilder.mapCodec { override val codec: MapCodec<CopyAllComponents> = MapCodec.unit(CopyAllComponents)
it.group(
Codec.STRING.fieldOf("source").forGetter(Indirect::pathSource),
Codec.STRING.fieldOf("destination").forGetter(Indirect::pathDestination),
).apply(it, ::Indirect)
}
}, },
ALL { COPY_ENCHANTMENTS {
override val codec: MapCodec<All> by lazy { MapCodec.unit(All) } override val codec: MapCodec<CopyEnchantments> = MapCodec.unit(CopyEnchantments)
},
COPY_ENERGY_CHARGE {
override val codec: MapCodec<CopyEnergyCharge> = MapCodec.unit(CopyEnergyCharge)
}, },
; ;
private val serName = name.lowercase()
override fun getSerializedName(): String { override fun getSerializedName(): String {
return name.lowercase() return serName
} }
abstract val codec: MapCodec<out Op> abstract val codec: MapCodec<out Op>
@ -111,134 +61,120 @@ class UpgradeRecipe(
sealed class Op { sealed class Op {
abstract val type: OpType abstract val type: OpType
abstract fun apply(source: CompoundTag, destination: CompoundTag) abstract fun apply(source: ItemStack, container: CraftingInput, registry: HolderLookup.Provider, destination: ItemStack)
open fun matches(container: CraftingInput, level: Level): Boolean {
return true
}
} }
data class Direct(val path: String) : Op() { data class CopyComponent<T>(val component: DataComponentType<T>) : Op() {
private val split = path.split('.')
override val type: OpType override val type: OpType
get() = OpType.DIRECT get() = OpType.COPY_COMPONENT
override fun apply(source: CompoundTag, destination: CompoundTag) { override fun apply(source: ItemStack, container: CraftingInput, registry: HolderLookup.Provider, destination: ItemStack) {
var a = source val existing = source[component]
var b = destination
for (i in 0 until split.size - 1) { if (existing != null) {
val value = split[i] destination[component] = existing
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() { object CopyAllComponents : Op() {
private val splitSource = pathSource.split('.')
private val splitDestination = pathDestination.split('.')
override val type: OpType override val type: OpType
get() = OpType.INDIRECT get() = OpType.COPY_ALL_COMPONENTS
override fun apply(source: CompoundTag, destination: CompoundTag) { override fun apply(source: ItemStack, container: CraftingInput, registry: HolderLookup.Provider, destination: ItemStack) {
var a = source destination.applyComponentsAndValidate(source.componentsPatch)
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()
}
} }
} }
object All : Op() { object CopyEnchantments : Op() {
override val type: OpType override val type: OpType
get() = OpType.ALL get() = OpType.COPY_ENCHANTMENTS
override fun apply(source: CompoundTag, destination: CompoundTag) { override fun apply(source: ItemStack, container: CraftingInput, registry: HolderLookup.Provider, destination: ItemStack) {
source.allKeys.forEach { val enchantments = source[DataComponents.ENCHANTMENTS]
destination[it] = source[it]!!
if (enchantments != null) {
destination[DataComponents.ENCHANTMENTS] = ItemEnchantments.Mutable(destination.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY)).let {
for ((key, level) in enchantments.entrySet()) {
it.upgrade(key, level)
}
it.toImmutable()
}
} }
} }
} }
val copyPaths: ImmutableList<Op> = copyPaths.collect(ImmutableList.toImmutableList()) object CopyEnergyCharge : Op() {
override val type: OpType
get() = OpType.COPY_ENERGY_CHARGE
override fun assemble(pInv: CraftingContainer, registryAccess: RegistryAccess): ItemStack { override fun apply(source: ItemStack, container: CraftingInput, registry: HolderLookup.Provider, destination: ItemStack) {
val result = parent.assemble(pInv, registryAccess) val batterySource = source.getCapability(MatteryCapability.ITEM_ENERGY) ?: return
val batteryDestination = destination.getCapability(MatteryCapability.ITEM_ENERGY) ?: return
batteryDestination.batteryLevel += batterySource.batteryLevel
}
}
override fun assemble(input: CraftingInput, registry: HolderLookup.Provider): ItemStack {
val result = super.assemble(input, registry)
if (result.isEmpty) { if (result.isEmpty) {
return result return result
} }
val sources = pInv.stream().filter { !it.isEmpty && it.item.registryName == source }.toList() for ((item, op) in operations) {
input.items()
if (sources.size != 1) { .filter { it.item.registryName == item }
return ItemStack.EMPTY .forEach { op.apply(it, input, registry, result) }
}
val source = sources.first()
for (op in copyPaths) {
op.apply(source.tagNotNull, result.tagNotNull)
} }
return result return result
} }
override fun getSerializer(): RecipeSerializer<UpgradeRecipe> { override fun matches(p_345040_: CraftingInput, p_44167_: Level): Boolean {
return CODEC return super.matches(p_345040_, p_44167_) && operations.all { it.second.matches(p_345040_, p_44167_) }
} }
companion object { override fun getSerializer(): RecipeSerializer<UpgradeRecipe> {
val COPY_PATHS_CODEC: Codec<List<Op>> = StringRepresentable return Companion
.fromEnum(OpType::values) }
.dispatch({ it.type }, { it.codec })
.listOf()
val CODEC = Codec2RecipeSerializer<UpgradeRecipe> { p -> companion object : RecipeSerializer<UpgradeRecipe> {
RecordCodecBuilder.create { private val operationCodec: MapCodec<Op> = StringRepresentable
.fromEnum(OpType::values)
.dispatchMap({ it.type }, { it.codec })
private val operationEntryCodec = RecordCodecBuilder.create<Pair<ResourceLocation, Op>> {
it.group( it.group(
p.wrap(ShapedRecipe.Serializer.SHAPED_RECIPE).fieldOf("parent").forGetter(UpgradeRecipe::parent), ResourceLocation.CODEC.fieldOf("item").forGetter(Pair<ResourceLocation, Op>::first),
COPY_PATHS_CODEC.fieldOf("copyPaths").forGetter(UpgradeRecipe::copyPaths), operationCodec.forGetter(Pair<ResourceLocation, Op>::second),
ResourceLocation.CODEC.fieldOf("source").forGetter(UpgradeRecipe::source) ).apply(it) { a, b -> Pair(a, b) }
}.listOf()
private val codec = RecordCodecBuilder.mapCodec {
it.group(
Serializer.CODEC.forGetter { it },
operationEntryCodec.fieldOf("operations").forGetter(UpgradeRecipe::operations)
).apply(it, ::UpgradeRecipe) ).apply(it, ::UpgradeRecipe)
} }
private val streamCodec = StreamCodec.composite(
Serializer.STREAM_CODEC, { it },
ByteBufCodecs.fromCodecWithRegistries(operationEntryCodec), { it.operations }, // quite lazy solution
::UpgradeRecipe
)
override fun codec(): MapCodec<UpgradeRecipe> {
return codec
}
override fun streamCodec(): StreamCodec<RegistryFriendlyByteBuf, UpgradeRecipe> {
return streamCodec
} }
} }
} }