Skip to content

Commit

Permalink
feat: recipe generator and strainer filtering fluid recipe
Browse files Browse the repository at this point in the history
  • Loading branch information
gloridifice committed May 29, 2024
1 parent f14b101 commit a35967e
Show file tree
Hide file tree
Showing 9 changed files with 315 additions and 3 deletions.
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
plugins {
id 'fabric-loom' version '1.5-SNAPSHOT'
id 'maven-publish'
id "org.jetbrains.kotlin.jvm" version "1.9.22"
id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.23'
id "org.jetbrains.kotlin.jvm" version "2.0.0"
id 'org.jetbrains.kotlin.plugin.serialization' version '2.0.0'

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import xyz.koiro.watersource.datagen.ModChineseLangGenerator
import xyz.koiro.watersource.datagen.ModEnglishLangGenerator
import xyz.koiro.watersource.datagen.ModModelGenerator
import xyz.koiro.watersource.datagen.ModItemTagGenerator
import xyz.koiro.watersource.datagen.ModRecipeGenerator

object WaterSourceDataGenerator : DataGeneratorEntrypoint {
override fun onInitializeDataGenerator(fabricDataGenerator: FabricDataGenerator) {
Expand All @@ -16,5 +17,6 @@ object WaterSourceDataGenerator : DataGeneratorEntrypoint {
pack.addProvider(::ModChineseLangGenerator)
pack.addProvider(::ModEnglishLangGenerator)
pack.addProvider(::ModItemTagGenerator)
pack.addProvider(::ModRecipeGenerator)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package xyz.koiro.watersource.datagen

import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider
import net.minecraft.data.server.recipe.RecipeJsonProvider
import net.minecraft.fluid.Fluid
import net.minecraft.fluid.Fluids
import net.minecraft.recipe.Ingredient
import net.minecraft.util.Identifier
import xyz.koiro.watersource.WaterSource
import xyz.koiro.watersource.datagen.recipe.StrainerFilteringFluidRecipeJsonBuilder
import xyz.koiro.watersource.world.fluid.ModFluids
import xyz.koiro.watersource.world.tag.ModTags
import java.util.function.Consumer

class ModRecipeGenerator(output: FabricDataOutput?) : FabricRecipeProvider(output) {
override fun generate(exporter: Consumer<RecipeJsonProvider>?) {
genSFFRecipe(
exporter,
WaterSource.identifier("water_to_purified_water"),
Fluids.WATER,
ModFluids.PURIFIED_WATER,
Ingredient.fromTag(ModTags.Item.PURIFICATION_STRAINER)
)
}

private fun genSFFRecipe(
exporter: Consumer<RecipeJsonProvider>?,
id: Identifier,
inFluid: Fluid,
outFluid: Fluid,
strainer: Ingredient
) {
StrainerFilteringFluidRecipeJsonBuilder(id, outFluid, inFluid, strainer).offerTo(exporter, id)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package xyz.koiro.watersource.datagen.recipe

import com.google.gson.JsonObject
import net.minecraft.advancement.Advancement
import net.minecraft.advancement.AdvancementRewards
import net.minecraft.advancement.CriterionMerger
import net.minecraft.advancement.criterion.CriterionConditions
import net.minecraft.advancement.criterion.RecipeUnlockedCriterion
import net.minecraft.data.server.recipe.CraftingRecipeJsonBuilder
import net.minecraft.data.server.recipe.RecipeJsonProvider
import net.minecraft.fluid.Fluid
import net.minecraft.item.Item
import net.minecraft.item.Items
import net.minecraft.recipe.Ingredient
import net.minecraft.recipe.RecipeSerializer
import net.minecraft.util.Identifier
import xyz.koiro.watersource.identifier
import xyz.koiro.watersource.world.recipe.ModRecipes
import java.util.function.Consumer

class StrainerFilteringFluidRecipeJsonBuilder(
val id: Identifier,
val inFluid: Fluid,
val outFluid: Fluid,
val strainer: Ingredient
) : CraftingRecipeJsonBuilder {
var advancementBuilder: Advancement.Builder? = null
var advancementId: Identifier? = null
var group: String? = null

fun advancement(advancementId: Identifier, advancementBuilder: Advancement.Builder): CraftingRecipeJsonBuilder {
this.advancementId = advancementId
this.advancementBuilder = advancementBuilder
return this
}

override fun criterion(name: String?, conditions: CriterionConditions?): CraftingRecipeJsonBuilder {
this.advancementBuilder?.criterion(name, conditions)
return this
}

override fun group(group: String?): CraftingRecipeJsonBuilder {
this.group = group
return this
}

override fun getOutputItem(): Item {
return Items.APPLE
}

override fun offerTo(exporter: Consumer<RecipeJsonProvider>?, recipeId: Identifier?) {
advancementBuilder?.parent(CraftingRecipeJsonBuilder.ROOT)
?.criterion("has_the_recipe", RecipeUnlockedCriterion.create(recipeId))
?.rewards(AdvancementRewards.Builder.recipe(recipeId))?.criteriaMerger(CriterionMerger.OR)
exporter?.accept(
Provider(
id,
inFluid.identifier().toString(),
outFluid.identifier().toString(),
strainer,
advancementBuilder,
advancementId,
group,
)
)
}

class Provider(
val id: Identifier,
val inFluidId: String,
val outFluidId: String,
val strainer: Ingredient,
val advancementBuilder: Advancement.Builder?,
val advancementId: Identifier?,
val group: String?,
) : RecipeJsonProvider {
override fun serialize(json: JsonObject) {
if (!group.isNullOrBlank()) json.addProperty("group", group)
json.addProperty("fluidIn", inFluidId)
json.addProperty("fluidOut", outFluidId)
json.add("strainer", strainer.toJson())
}

override fun getRecipeId(): Identifier = id

override fun getSerializer(): RecipeSerializer<*> = ModRecipes.STRAINER_FILTERING_FLUID_SERIALIZER

override fun toAdvancementJson(): JsonObject? = this.advancementBuilder?.toJson()

override fun getAdvancementId(): Identifier? = this.advancementId
}
}
6 changes: 6 additions & 0 deletions src/main/kotlin/xyz/koiro/watersource/world/item/ModItems.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ object ModItems {
Strainer(FabricItemSettings().maxCount(1).maxDamage(8))
)

@AutoGenItemData(enLang = "Waste Strainer", cnLang = "旧滤网")
val WASTE_STRAINER = registerItem(
"waste_strainer",
Item(FabricItemSettings())
)

//todo test
@AutoGenItemData(enLang = "Leather Water Bag", cnLang = "皮水袋")
val LEATHER_WATER_BAG = registerItem(
Expand Down
17 changes: 17 additions & 0 deletions src/main/kotlin/xyz/koiro/watersource/world/item/Strainer.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
package xyz.koiro.watersource.world.item

import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import xyz.koiro.watersource.WSConfig
import kotlin.math.ceil

class Strainer(settings: Settings?) : Item(settings) {
fun calCostDamage(strainerStack: ItemStack, volume: Long): Int {
return ceil(volume.toDouble() / WSConfig.UNIT_DRINK_VOLUME.toDouble()).toInt()
}

fun useStrainer(strainerStack: ItemStack, volume: Long): ItemStack {
val dmg = calCostDamage(strainerStack, volume)
val remainedStrainer = if (strainerStack.damage + dmg >= strainerStack.maxDamage) {
ItemStack(ModItems.WASTE_STRAINER)
} else {
val strainerCopy = strainerStack.copy()
strainerCopy.damage += dmg
strainerCopy
}
return remainedStrainer
}
}
19 changes: 19 additions & 0 deletions src/main/kotlin/xyz/koiro/watersource/world/recipe/ModRecipes.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
package xyz.koiro.watersource.world.recipe

import net.minecraft.recipe.RecipeSerializer
import net.minecraft.recipe.RecipeType
import net.minecraft.registry.Registries
import net.minecraft.registry.Registry
import net.minecraft.util.Identifier
import xyz.koiro.watersource.WaterSource

object ModRecipes {

fun initialize(){
}

val STRAINER_FILTERING_FLUID_TYPE = registerRecipeType("strainer_filtering_fluid_type", StrainerFilteringFluidRecipe.Type())
val STRAINER_FILTERING_FLUID_SERIALIZER = registerRecipeSerializer("strainer_filtering_fluid_serializer", StrainerFilteringFluidRecipe.Serializer())

private fun registerRecipeType(registryName: String, recipeType: RecipeType<*>): RecipeType<*> {
return Registry.register(Registries.RECIPE_TYPE, Identifier(WaterSource.MODID, registryName), recipeType)
}

private fun registerRecipeSerializer(registryName: String, recipeType: RecipeSerializer<*>): RecipeSerializer<*> {
return Registry.register(Registries.RECIPE_SERIALIZER, Identifier(WaterSource.MODID, registryName), recipeType)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package xyz.koiro.watersource.world.recipe

import com.google.gson.JsonObject
import net.minecraft.fluid.Fluid
import net.minecraft.inventory.RecipeInputInventory
import net.minecraft.item.ItemStack
import net.minecraft.network.PacketByteBuf
import net.minecraft.recipe.Ingredient
import net.minecraft.recipe.RecipeSerializer
import net.minecraft.recipe.RecipeType
import net.minecraft.recipe.SpecialCraftingRecipe
import net.minecraft.recipe.book.CraftingRecipeCategory
import net.minecraft.registry.DynamicRegistryManager
import net.minecraft.registry.Registries
import net.minecraft.util.Identifier
import net.minecraft.util.collection.DefaultedList
import net.minecraft.world.World
import xyz.koiro.watersource.api.getOrCreateFluidStorageData
import xyz.koiro.watersource.api.modifyFluidStorage
import xyz.koiro.watersource.identifier
import xyz.koiro.watersource.world.item.Strainer

class StrainerFilteringFluidRecipe(
val inFluid: Fluid, val outFluid: Fluid,
val id: Identifier, val strainer: Ingredient
) : SpecialCraftingRecipe(id, CraftingRecipeCategory.MISC) {
fun getInputAndStrainer(inventory: RecipeInputInventory): Ctx? {
if (inventory.size() == 2) {
var strainerStack: ItemStack? = null
var inputStack: ItemStack? = null
var sIndex = 0
var iIndex = 0

for (i in 0..<inventory.size()) {
val stack = inventory.getStack(i)
if (this.strainer.test(strainerStack)) {
strainerStack = stack
sIndex = i
}
if (stack.getOrCreateFluidStorageData()?.fluid == inFluid) {
inputStack = stack
iIndex = i
}
}
if (inputStack != null && strainerStack != null)
return Ctx(strainerStack, inputStack, sIndex, iIndex)
}
return null;
}

override fun matches(inventory: RecipeInputInventory, world: World): Boolean {
getInputAndStrainer(inventory)?.let { ctx ->
val input = ctx.input
val strainer = ctx.strainer

input.getOrCreateFluidStorageData()?.let { fluidStorageData ->
(strainer.item as? Strainer)?.calCostDamage(strainer, fluidStorageData.amount)?.let { dmg ->
return strainer.damage + dmg < strainer.maxDamage
}
}
}
return false
}

data class Ctx(val strainer: ItemStack, val input: ItemStack, val strainerIndex: Int, val inputIndex: Int)

override fun craft(inventory: RecipeInputInventory, registryManager: DynamicRegistryManager): ItemStack? {
getInputAndStrainer(inventory)?.let { ctx ->
val input = ctx.input

val output = input.copy()
output.modifyFluidStorage { _, fluidStorageData ->
fluidStorageData.copy(fluid = outFluid)
}
return output
}
return ItemStack.EMPTY
}

override fun getRemainder(inventory: RecipeInputInventory): DefaultedList<ItemStack> {
val defaultedList = super.getRemainder(inventory)
getInputAndStrainer(inventory)?.let { ctx ->
ctx.input.getOrCreateFluidStorageData()?.let { fluidStorageData ->
val remained = (ctx.strainer.item as? Strainer)?.useStrainer(
strainerStack = ctx.strainer,
volume = fluidStorageData.amount
) ?: ItemStack.EMPTY
defaultedList[ctx.strainerIndex] = remained
}
}
return defaultedList
}

override fun fits(width: Int, height: Int): Boolean {
return true
}

override fun getOutput(registryManager: DynamicRegistryManager?): ItemStack {
return ItemStack.EMPTY
}

override fun getId(): Identifier = id

override fun getSerializer(): RecipeSerializer<*> {
return ModRecipes.STRAINER_FILTERING_FLUID_SERIALIZER
}

override fun getType(): RecipeType<*> {
return ModRecipes.STRAINER_FILTERING_FLUID_TYPE
}

class Type: RecipeType<StrainerFilteringFluidRecipe>

class Serializer : RecipeSerializer<StrainerFilteringFluidRecipe> {
override fun read(id: Identifier, json: JsonObject): StrainerFilteringFluidRecipe {
val fI = Registries.FLUID.get(Identifier.tryParse(json.get("fluidIn").asString))
val fO = Registries.FLUID.get(Identifier.tryParse(json.get("fluidOut").asString))
val strainer = Ingredient.fromJson(json.get("strainer"))
return StrainerFilteringFluidRecipe(fI, fO, id, strainer)
}

override fun read(id: Identifier, buf: PacketByteBuf): StrainerFilteringFluidRecipe {
val fI = Registries.FLUID.get(Identifier.tryParse(buf.readString()))
val fO = Registries.FLUID.get(Identifier.tryParse(buf.readString()))
val strainer = Ingredient.fromPacket(buf)
return StrainerFilteringFluidRecipe(fI, fO, id, strainer)
}

override fun write(buf: PacketByteBuf, recipe: StrainerFilteringFluidRecipe) {
buf.writeString(recipe.inFluid.identifier().toString())
buf.writeString(recipe.outFluid.identifier().toString())
recipe.strainer.write(buf)
}
}
}
7 changes: 6 additions & 1 deletion src/main/kotlin/xyz/koiro/watersource/world/tag/ModTags.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package xyz.koiro.watersource.world.tag

import net.minecraft.item.Item
import net.minecraft.registry.RegistryKeys
import net.minecraft.registry.tag.TagKey
import xyz.koiro.watersource.WaterSource

object ModTags {
object Item{

val PURIFICATION_STRAINER = TagKey.of(RegistryKeys.ITEM, WaterSource.identifier("purification_strainer"))!!;
}

object Block{
Expand Down

0 comments on commit a35967e

Please sign in to comment.