Update upgrade recipe, and merge energy container recipe onto upgrade recipe
This commit is contained in:
parent
7f927d5f05
commit
4c0bac35c1
@ -4,7 +4,6 @@ package ru.dbotthepony.mc.otm.datagen.recipes
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.advancements.Advancement
|
||||
import net.minecraft.advancements.Criterion
|
||||
import net.minecraft.advancements.CriterionTriggerInstance
|
||||
import net.minecraft.data.recipes.FinishedRecipe
|
||||
import net.minecraft.data.recipes.RecipeCategory
|
||||
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.toJsonStrict
|
||||
import ru.dbotthepony.mc.otm.datagen.modLocation
|
||||
import ru.dbotthepony.mc.otm.recipe.EnergyContainerRecipe
|
||||
import ru.dbotthepony.mc.otm.recipe.UpgradeRecipe
|
||||
import java.util.function.Consumer
|
||||
|
||||
private interface RecipeCell {
|
||||
val value: Ingredient
|
||||
@ -191,7 +188,7 @@ class MatteryRecipe(val result: ItemLike, val count: Int = 1, val category: Reci
|
||||
object : FinishedRecipe by it {
|
||||
override fun serializeRecipeData(pJson: JsonObject) {
|
||||
pJson["parent"] = it.serializeRecipe()
|
||||
pJson["copyPaths"] = UpgradeRecipe.COPY_PATHS_CODEC.toJsonStrict(copyPaths)
|
||||
pJson["copyPaths"] = UpgradeRecipe.OPERATION_CODEC.toJsonStrict(copyPaths)
|
||||
pJson["source"] = upgradeSource!!.toString()
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,7 @@ interface IMatteryEnergyStorage : IEnergyStorage {
|
||||
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
|
||||
* due to technical complications (in this case, [canSetBatteryLevel] MUST be false)
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -1,109 +1,59 @@
|
||||
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.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.NonNullList
|
||||
import net.minecraft.core.RegistryAccess
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.core.component.DataComponentType
|
||||
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.util.StringRepresentable
|
||||
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.CraftingRecipe
|
||||
import net.minecraft.world.item.crafting.Ingredient
|
||||
import net.minecraft.world.item.crafting.CraftingInput
|
||||
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.ShapedRecipePattern
|
||||
import net.minecraft.world.item.enchantment.ItemEnchantments
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraftforge.common.crafting.IShapedRecipe
|
||||
import ru.dbotthepony.mc.otm.container.util.stream
|
||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||
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(
|
||||
val parent: ShapedRecipe,
|
||||
copyPaths: Stream<Op>,
|
||||
val source: ResourceLocation,
|
||||
) : CraftingRecipe, IShapedRecipe<CraftingContainer> by parent {
|
||||
constructor(parent: ShapedRecipe, copyPaths: Collection<Op>, source: ResourceLocation) : this(parent, copyPaths.stream(), source)
|
||||
|
||||
override fun matches(p_44002_: CraftingContainer, p_44003_: Level): Boolean {
|
||||
return parent.matches(p_44002_, p_44003_)
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
group: String,
|
||||
category: CraftingBookCategory,
|
||||
pattern: ShapedRecipePattern,
|
||||
result: ItemStack,
|
||||
val operations: List<Pair<ResourceLocation, Op>>,
|
||||
showNotification: Boolean = true,
|
||||
) : ShapedRecipe(group, category, pattern, result, showNotification) {
|
||||
constructor(parent: ShapedRecipe, operations: List<Pair<ResourceLocation, Op>>) : this(parent.group, parent.category(), parent.pattern, parent.result, operations, parent.showNotification())
|
||||
|
||||
enum class OpType : StringRepresentable {
|
||||
DIRECT {
|
||||
override val codec: MapCodec<Direct> = RecordCodecBuilder.mapCodec {
|
||||
COPY_COMPONENT {
|
||||
override val codec: MapCodec<CopyComponent<*>> = RecordCodecBuilder.mapCodec {
|
||||
it.group(
|
||||
Codec.STRING.fieldOf("path").forGetter(Direct::path)
|
||||
).apply(it, ::Direct)
|
||||
DataComponentType.CODEC.fieldOf("component").forGetter(CopyComponent<*>::component)
|
||||
).apply(it) { CopyComponent(it) }
|
||||
}
|
||||
},
|
||||
INDIRECT {
|
||||
override val codec: MapCodec<Indirect> = RecordCodecBuilder.mapCodec {
|
||||
it.group(
|
||||
Codec.STRING.fieldOf("source").forGetter(Indirect::pathSource),
|
||||
Codec.STRING.fieldOf("destination").forGetter(Indirect::pathDestination),
|
||||
).apply(it, ::Indirect)
|
||||
}
|
||||
COPY_ALL_COMPONENTS {
|
||||
override val codec: MapCodec<CopyAllComponents> = MapCodec.unit(CopyAllComponents)
|
||||
},
|
||||
ALL {
|
||||
override val codec: MapCodec<All> by lazy { MapCodec.unit(All) }
|
||||
COPY_ENCHANTMENTS {
|
||||
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 {
|
||||
return name.lowercase()
|
||||
return serName
|
||||
}
|
||||
|
||||
abstract val codec: MapCodec<out Op>
|
||||
@ -111,134 +61,120 @@ class UpgradeRecipe(
|
||||
|
||||
sealed class Op {
|
||||
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() {
|
||||
private val split = path.split('.')
|
||||
data class CopyComponent<T>(val component: DataComponentType<T>) : Op() {
|
||||
override val type: OpType
|
||||
get() = OpType.DIRECT
|
||||
get() = OpType.COPY_COMPONENT
|
||||
|
||||
override fun apply(source: CompoundTag, destination: CompoundTag) {
|
||||
var a = source
|
||||
var b = destination
|
||||
override fun apply(source: ItemStack, container: CraftingInput, registry: HolderLookup.Provider, destination: ItemStack) {
|
||||
val existing = source[component]
|
||||
|
||||
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()
|
||||
if (existing != null) {
|
||||
destination[component] = existing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Indirect(val pathSource: String, val pathDestination: String) : Op() {
|
||||
private val splitSource = pathSource.split('.')
|
||||
private val splitDestination = pathDestination.split('.')
|
||||
|
||||
object CopyAllComponents : Op() {
|
||||
override val type: OpType
|
||||
get() = OpType.INDIRECT
|
||||
get() = OpType.COPY_ALL_COMPONENTS
|
||||
|
||||
override fun apply(source: CompoundTag, destination: CompoundTag) {
|
||||
var a = source
|
||||
var b = destination
|
||||
override fun apply(source: ItemStack, container: CraftingInput, registry: HolderLookup.Provider, destination: ItemStack) {
|
||||
destination.applyComponentsAndValidate(source.componentsPatch)
|
||||
}
|
||||
}
|
||||
|
||||
for (i in 0 until splitSource.size - 1) {
|
||||
val value = splitSource[i]
|
||||
object CopyEnchantments : Op() {
|
||||
override val type: OpType
|
||||
get() = OpType.COPY_ENCHANTMENTS
|
||||
|
||||
if (value !in a) {
|
||||
a[value] = CompoundTag()
|
||||
override fun apply(source: ItemStack, container: CraftingInput, registry: HolderLookup.Provider, destination: ItemStack) {
|
||||
val enchantments = source[DataComponents.ENCHANTMENTS]
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
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 CopyEnergyCharge : Op() {
|
||||
override val type: OpType
|
||||
get() = OpType.ALL
|
||||
get() = OpType.COPY_ENERGY_CHARGE
|
||||
|
||||
override fun apply(source: CompoundTag, destination: CompoundTag) {
|
||||
source.allKeys.forEach {
|
||||
destination[it] = source[it]!!
|
||||
}
|
||||
override fun apply(source: ItemStack, container: CraftingInput, registry: HolderLookup.Provider, destination: ItemStack) {
|
||||
val batterySource = source.getCapability(MatteryCapability.ITEM_ENERGY) ?: return
|
||||
val batteryDestination = destination.getCapability(MatteryCapability.ITEM_ENERGY) ?: return
|
||||
batteryDestination.batteryLevel += batterySource.batteryLevel
|
||||
}
|
||||
}
|
||||
|
||||
val copyPaths: ImmutableList<Op> = copyPaths.collect(ImmutableList.toImmutableList())
|
||||
|
||||
override fun assemble(pInv: CraftingContainer, registryAccess: RegistryAccess): ItemStack {
|
||||
val result = parent.assemble(pInv, registryAccess)
|
||||
override fun assemble(input: CraftingInput, registry: HolderLookup.Provider): ItemStack {
|
||||
val result = super.assemble(input, registry)
|
||||
|
||||
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)
|
||||
for ((item, op) in operations) {
|
||||
input.items()
|
||||
.filter { it.item.registryName == item }
|
||||
.forEach { op.apply(it, input, registry, result) }
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
override fun getSerializer(): RecipeSerializer<UpgradeRecipe> {
|
||||
return CODEC
|
||||
override fun matches(p_345040_: CraftingInput, p_44167_: Level): Boolean {
|
||||
return super.matches(p_345040_, p_44167_) && operations.all { it.second.matches(p_345040_, p_44167_) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
val COPY_PATHS_CODEC: Codec<List<Op>> = StringRepresentable
|
||||
.fromEnum(OpType::values)
|
||||
.dispatch({ it.type }, { it.codec })
|
||||
.listOf()
|
||||
override fun getSerializer(): RecipeSerializer<UpgradeRecipe> {
|
||||
return Companion
|
||||
}
|
||||
|
||||
val CODEC = Codec2RecipeSerializer<UpgradeRecipe> { p ->
|
||||
RecordCodecBuilder.create {
|
||||
it.group(
|
||||
p.wrap(ShapedRecipe.Serializer.SHAPED_RECIPE).fieldOf("parent").forGetter(UpgradeRecipe::parent),
|
||||
COPY_PATHS_CODEC.fieldOf("copyPaths").forGetter(UpgradeRecipe::copyPaths),
|
||||
ResourceLocation.CODEC.fieldOf("source").forGetter(UpgradeRecipe::source)
|
||||
).apply(it, ::UpgradeRecipe)
|
||||
}
|
||||
companion object : RecipeSerializer<UpgradeRecipe> {
|
||||
private val operationCodec: MapCodec<Op> = StringRepresentable
|
||||
.fromEnum(OpType::values)
|
||||
.dispatchMap({ it.type }, { it.codec })
|
||||
|
||||
private val operationEntryCodec = RecordCodecBuilder.create<Pair<ResourceLocation, Op>> {
|
||||
it.group(
|
||||
ResourceLocation.CODEC.fieldOf("item").forGetter(Pair<ResourceLocation, Op>::first),
|
||||
operationCodec.forGetter(Pair<ResourceLocation, Op>::second),
|
||||
).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)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user