diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cd66df7016..63a4bb5c50 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ minecraft = "1.21.1" neoForge = "21.1.219" jei = "19.32.0.358" -jade = "15.3.4+neoforge" +jade = "15.10.5+neoforge" mekanism = "10.7.19.85" parchment = "2024.11.17" modDevGradle = "2.0.140" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 37f853b1c8..4f5eb9dcc0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.5-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/channeling.json b/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/channeling.json new file mode 100644 index 0000000000..ab19f1a779 --- /dev/null +++ b/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/channeling.json @@ -0,0 +1,21 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_the_recipe": { + "conditions": { + "recipe": "anvilcraft:solid_liquid/channeling" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe" + ] + ], + "rewards": { + "recipes": [ + "anvilcraft:solid_liquid/channeling" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/disintegration.json b/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/disintegration.json new file mode 100644 index 0000000000..175bb148a7 --- /dev/null +++ b/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/disintegration.json @@ -0,0 +1,21 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_the_recipe": { + "conditions": { + "recipe": "anvilcraft:solid_liquid/disintegration" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe" + ] + ], + "rewards": { + "recipes": [ + "anvilcraft:solid_liquid/disintegration" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/felling_and_harvest_and_beheading.json b/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/felling_and_harvest_and_beheading.json new file mode 100644 index 0000000000..4e17d2438b --- /dev/null +++ b/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/felling_and_harvest_and_beheading.json @@ -0,0 +1,21 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_the_recipe": { + "conditions": { + "recipe": "anvilcraft:solid_liquid/felling_and_harvest_and_beheading" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe" + ] + ], + "rewards": { + "recipes": [ + "anvilcraft:solid_liquid/felling_and_harvest_and_beheading" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/fire_protection.json b/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/fire_protection.json new file mode 100644 index 0000000000..f688e07202 --- /dev/null +++ b/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/fire_protection.json @@ -0,0 +1,21 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_the_recipe": { + "conditions": { + "recipe": "anvilcraft:solid_liquid/fire_protection" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe" + ] + ], + "rewards": { + "recipes": [ + "anvilcraft:solid_liquid/fire_protection" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/fortune_and_looting.json b/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/fortune_and_looting.json new file mode 100644 index 0000000000..eaf9b28ed7 --- /dev/null +++ b/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/fortune_and_looting.json @@ -0,0 +1,21 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_the_recipe": { + "conditions": { + "recipe": "anvilcraft:solid_liquid/fortune_and_looting" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe" + ] + ], + "rewards": { + "recipes": [ + "anvilcraft:solid_liquid/fortune_and_looting" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/frost_walker.json b/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/frost_walker.json new file mode 100644 index 0000000000..4bd1b9e73a --- /dev/null +++ b/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/frost_walker.json @@ -0,0 +1,21 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_the_recipe": { + "conditions": { + "recipe": "anvilcraft:solid_liquid/frost_walker" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe" + ] + ], + "rewards": { + "recipes": [ + "anvilcraft:solid_liquid/frost_walker" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/mending.json b/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/mending.json new file mode 100644 index 0000000000..8077c63043 --- /dev/null +++ b/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/mending.json @@ -0,0 +1,21 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_the_recipe": { + "conditions": { + "recipe": "anvilcraft:solid_liquid/mending" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe" + ] + ], + "rewards": { + "recipes": [ + "anvilcraft:solid_liquid/mending" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/silk_touch.json b/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/silk_touch.json new file mode 100644 index 0000000000..522187c70b --- /dev/null +++ b/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/silk_touch.json @@ -0,0 +1,21 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_the_recipe": { + "conditions": { + "recipe": "anvilcraft:solid_liquid/silk_touch" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe" + ] + ], + "rewards": { + "recipes": [ + "anvilcraft:solid_liquid/silk_touch" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/smelting.json b/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/smelting.json new file mode 100644 index 0000000000..3fc6892709 --- /dev/null +++ b/src/generated/resources/data/anvilcraft/advancement/recipes/solid_liquid/smelting.json @@ -0,0 +1,21 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_the_recipe": { + "conditions": { + "recipe": "anvilcraft:solid_liquid/smelting" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe" + ] + ], + "rewards": { + "recipes": [ + "anvilcraft:solid_liquid/smelting" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/item_compress/supercapacitor.json b/src/generated/resources/data/anvilcraft/recipe/item_compress/supercapacitor.json index d067a90490..49b54366e0 100644 --- a/src/generated/resources/data/anvilcraft/recipe/item_compress/supercapacitor.json +++ b/src/generated/resources/data/anvilcraft/recipe/item_compress/supercapacitor.json @@ -55,6 +55,9 @@ "non_conflicting": [ { "type": "anvilcraft:has_cauldron", + "fluid": { + "amount": 0 + }, "offset": [ 0.0, -1.0, diff --git a/src/generated/resources/data/anvilcraft/recipe/neutron_irradiation/radiation_block_explosion.json b/src/generated/resources/data/anvilcraft/recipe/neutron_irradiation/radiation_block_explosion.json index bd4ced8667..1aebf8e635 100644 --- a/src/generated/resources/data/anvilcraft/recipe/neutron_irradiation/radiation_block_explosion.json +++ b/src/generated/resources/data/anvilcraft/recipe/neutron_irradiation/radiation_block_explosion.json @@ -29,6 +29,9 @@ "non_conflicting": [ { "type": "anvilcraft:has_cauldron", + "fluid": { + "amount": 0 + }, "offset": [ 0.0, -1.0, diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_cauldron.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_cauldron.json index 39d0d5a52e..2642702b32 100644 --- a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_cauldron.json +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_cauldron.json @@ -11,7 +11,9 @@ "items": "anvilcraft:cinerite" } ], - "produce": 1000, "results": [], - "transform": "anvilcraft:gray_cement" + "transform": { + "amount": 1000, + "id": "anvilcraft:gray_cement" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/black.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/black.json index 11a99d45d1..d15b1431f4 100644 --- a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/black.json +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/black.json @@ -1,7 +1,7 @@ { "type": "anvilcraft:solid_liquid", - "fluid": "minecraft:null", - "fluidTag": "c:cement", + "consume": 1000, + "fluid": "#c:cement", "ingredients": [ { "items": "minecraft:black_dye" @@ -9,5 +9,8 @@ ], "max_efficiency": 1, "results": [], - "transform": "anvilcraft:black_cement" + "transform": { + "amount": 1000, + "id": "anvilcraft:black_cement" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/blue.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/blue.json index 1560ce8f6f..5c6c38873c 100644 --- a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/blue.json +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/blue.json @@ -1,7 +1,7 @@ { "type": "anvilcraft:solid_liquid", - "fluid": "minecraft:null", - "fluidTag": "c:cement", + "consume": 1000, + "fluid": "#c:cement", "ingredients": [ { "items": "minecraft:blue_dye" @@ -9,5 +9,8 @@ ], "max_efficiency": 1, "results": [], - "transform": "anvilcraft:blue_cement" + "transform": { + "amount": 1000, + "id": "anvilcraft:blue_cement" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/brown.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/brown.json index 728dcbe274..9ce17a829c 100644 --- a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/brown.json +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/brown.json @@ -1,7 +1,7 @@ { "type": "anvilcraft:solid_liquid", - "fluid": "minecraft:null", - "fluidTag": "c:cement", + "consume": 1000, + "fluid": "#c:cement", "ingredients": [ { "items": "minecraft:brown_dye" @@ -9,5 +9,8 @@ ], "max_efficiency": 1, "results": [], - "transform": "anvilcraft:brown_cement" + "transform": { + "amount": 1000, + "id": "anvilcraft:brown_cement" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/cyan.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/cyan.json index 20caf0e635..e26e01e1e5 100644 --- a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/cyan.json +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/cyan.json @@ -1,7 +1,7 @@ { "type": "anvilcraft:solid_liquid", - "fluid": "minecraft:null", - "fluidTag": "c:cement", + "consume": 1000, + "fluid": "#c:cement", "ingredients": [ { "items": "minecraft:cyan_dye" @@ -9,5 +9,8 @@ ], "max_efficiency": 1, "results": [], - "transform": "anvilcraft:cyan_cement" + "transform": { + "amount": 1000, + "id": "anvilcraft:cyan_cement" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/gray.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/gray.json index 612b0058cb..f0dcd2a8c2 100644 --- a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/gray.json +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/gray.json @@ -1,7 +1,7 @@ { "type": "anvilcraft:solid_liquid", - "fluid": "minecraft:null", - "fluidTag": "c:cement", + "consume": 1000, + "fluid": "#c:cement", "ingredients": [ { "items": "minecraft:gray_dye" @@ -9,5 +9,8 @@ ], "max_efficiency": 1, "results": [], - "transform": "anvilcraft:gray_cement" + "transform": { + "amount": 1000, + "id": "anvilcraft:gray_cement" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/green.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/green.json index 2e0bd04378..4500d87bb1 100644 --- a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/green.json +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/green.json @@ -1,7 +1,7 @@ { "type": "anvilcraft:solid_liquid", - "fluid": "minecraft:null", - "fluidTag": "c:cement", + "consume": 1000, + "fluid": "#c:cement", "ingredients": [ { "items": "minecraft:green_dye" @@ -9,5 +9,8 @@ ], "max_efficiency": 1, "results": [], - "transform": "anvilcraft:green_cement" + "transform": { + "amount": 1000, + "id": "anvilcraft:green_cement" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/light_blue.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/light_blue.json index d971c33eb3..e1f73d1086 100644 --- a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/light_blue.json +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/light_blue.json @@ -1,7 +1,7 @@ { "type": "anvilcraft:solid_liquid", - "fluid": "minecraft:null", - "fluidTag": "c:cement", + "consume": 1000, + "fluid": "#c:cement", "ingredients": [ { "items": "minecraft:light_blue_dye" @@ -9,5 +9,8 @@ ], "max_efficiency": 1, "results": [], - "transform": "anvilcraft:light_blue_cement" + "transform": { + "amount": 1000, + "id": "anvilcraft:light_blue_cement" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/light_gray.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/light_gray.json index f264b74021..1501599343 100644 --- a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/light_gray.json +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/light_gray.json @@ -1,7 +1,7 @@ { "type": "anvilcraft:solid_liquid", - "fluid": "minecraft:null", - "fluidTag": "c:cement", + "consume": 1000, + "fluid": "#c:cement", "ingredients": [ { "items": "minecraft:light_gray_dye" @@ -9,5 +9,8 @@ ], "max_efficiency": 1, "results": [], - "transform": "anvilcraft:light_gray_cement" + "transform": { + "amount": 1000, + "id": "anvilcraft:light_gray_cement" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/lime.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/lime.json index c474d43ca9..3c54e45a7d 100644 --- a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/lime.json +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/lime.json @@ -1,7 +1,7 @@ { "type": "anvilcraft:solid_liquid", - "fluid": "minecraft:null", - "fluidTag": "c:cement", + "consume": 1000, + "fluid": "#c:cement", "ingredients": [ { "items": "minecraft:lime_dye" @@ -9,5 +9,8 @@ ], "max_efficiency": 1, "results": [], - "transform": "anvilcraft:lime_cement" + "transform": { + "amount": 1000, + "id": "anvilcraft:lime_cement" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/magenta.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/magenta.json index c8a3d909b0..1b380dcb9c 100644 --- a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/magenta.json +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/magenta.json @@ -1,7 +1,7 @@ { "type": "anvilcraft:solid_liquid", - "fluid": "minecraft:null", - "fluidTag": "c:cement", + "consume": 1000, + "fluid": "#c:cement", "ingredients": [ { "items": "minecraft:magenta_dye" @@ -9,5 +9,8 @@ ], "max_efficiency": 1, "results": [], - "transform": "anvilcraft:magenta_cement" + "transform": { + "amount": 1000, + "id": "anvilcraft:magenta_cement" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/orange.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/orange.json index 3f32b9331c..3fb0da8e5b 100644 --- a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/orange.json +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/orange.json @@ -1,7 +1,7 @@ { "type": "anvilcraft:solid_liquid", - "fluid": "minecraft:null", - "fluidTag": "c:cement", + "consume": 1000, + "fluid": "#c:cement", "ingredients": [ { "items": "minecraft:orange_dye" @@ -9,5 +9,8 @@ ], "max_efficiency": 1, "results": [], - "transform": "anvilcraft:orange_cement" + "transform": { + "amount": 1000, + "id": "anvilcraft:orange_cement" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/pink.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/pink.json index 28e4fa1c7e..49d0958ab3 100644 --- a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/pink.json +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/pink.json @@ -1,7 +1,7 @@ { "type": "anvilcraft:solid_liquid", - "fluid": "minecraft:null", - "fluidTag": "c:cement", + "consume": 1000, + "fluid": "#c:cement", "ingredients": [ { "items": "minecraft:pink_dye" @@ -9,5 +9,8 @@ ], "max_efficiency": 1, "results": [], - "transform": "anvilcraft:pink_cement" + "transform": { + "amount": 1000, + "id": "anvilcraft:pink_cement" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/purple.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/purple.json index 81beec3d48..57defe0d76 100644 --- a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/purple.json +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/purple.json @@ -1,7 +1,7 @@ { "type": "anvilcraft:solid_liquid", - "fluid": "minecraft:null", - "fluidTag": "c:cement", + "consume": 1000, + "fluid": "#c:cement", "ingredients": [ { "items": "minecraft:purple_dye" @@ -9,5 +9,8 @@ ], "max_efficiency": 1, "results": [], - "transform": "anvilcraft:purple_cement" + "transform": { + "amount": 1000, + "id": "anvilcraft:purple_cement" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/red.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/red.json index a45fbd780e..1535606784 100644 --- a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/red.json +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/red.json @@ -1,7 +1,7 @@ { "type": "anvilcraft:solid_liquid", - "fluid": "minecraft:null", - "fluidTag": "c:cement", + "consume": 1000, + "fluid": "#c:cement", "ingredients": [ { "items": "minecraft:red_dye" @@ -9,5 +9,8 @@ ], "max_efficiency": 1, "results": [], - "transform": "anvilcraft:red_cement" + "transform": { + "amount": 1000, + "id": "anvilcraft:red_cement" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/white.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/white.json index 78054ed80e..331707ad7d 100644 --- a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/white.json +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/white.json @@ -1,7 +1,7 @@ { "type": "anvilcraft:solid_liquid", - "fluid": "minecraft:null", - "fluidTag": "c:cement", + "consume": 1000, + "fluid": "#c:cement", "ingredients": [ { "items": "minecraft:white_dye" @@ -9,5 +9,8 @@ ], "max_efficiency": 1, "results": [], - "transform": "anvilcraft:white_cement" + "transform": { + "amount": 1000, + "id": "anvilcraft:white_cement" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/yellow.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/yellow.json index a1e72fec7a..8d6e79b72e 100644 --- a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/yellow.json +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/cement_staining/yellow.json @@ -1,7 +1,7 @@ { "type": "anvilcraft:solid_liquid", - "fluid": "minecraft:null", - "fluidTag": "c:cement", + "consume": 1000, + "fluid": "#c:cement", "ingredients": [ { "items": "minecraft:yellow_dye" @@ -9,5 +9,8 @@ ], "max_efficiency": 1, "results": [], - "transform": "anvilcraft:yellow_cement" + "transform": { + "amount": 1000, + "id": "anvilcraft:yellow_cement" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/channeling.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/channeling.json new file mode 100644 index 0000000000..f0be65c27a --- /dev/null +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/channeling.json @@ -0,0 +1,25 @@ +{ + "type": "anvilcraft:solid_liquid", + "consume": 1, + "fluid": { + "components": { + "patch": { + "!anvilcraft:liquid_enchantment": {} + } + }, + "fluids": "anvilcraft:liquid_enchantment" + }, + "ingredients": [ + { + "items": "anvilcraft:topaz" + } + ], + "results": [], + "transform": { + "amount": 1, + "components": { + "anvilcraft:liquid_enchantment": "minecraft:channeling" + }, + "id": "anvilcraft:liquid_enchantment" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/disintegration.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/disintegration.json new file mode 100644 index 0000000000..b84da11645 --- /dev/null +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/disintegration.json @@ -0,0 +1,25 @@ +{ + "type": "anvilcraft:solid_liquid", + "consume": 1, + "fluid": { + "components": { + "patch": { + "!anvilcraft:liquid_enchantment": {} + } + }, + "fluids": "anvilcraft:liquid_enchantment" + }, + "ingredients": [ + { + "items": "anvilcraft:frost_metal_ingot" + } + ], + "results": [], + "transform": { + "amount": 1, + "components": { + "anvilcraft:liquid_enchantment": "anvilcraft:disintegration" + }, + "id": "anvilcraft:liquid_enchantment" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/exp_fluid_cauldron.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/exp_fluid_cauldron.json index 6d7068221d..1753b2bdad 100644 --- a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/exp_fluid_cauldron.json +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/exp_fluid_cauldron.json @@ -1,5 +1,6 @@ { "type": "anvilcraft:solid_liquid", + "consume": 1000, "fluid": "minecraft:water", "ingredients": [ { @@ -7,5 +8,8 @@ } ], "results": [], - "transform": "anvilcraft:exp_fluid" + "transform": { + "amount": 1000, + "id": "anvilcraft:exp_fluid" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/felling_and_harvest_and_beheading.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/felling_and_harvest_and_beheading.json new file mode 100644 index 0000000000..17f779c459 --- /dev/null +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/felling_and_harvest_and_beheading.json @@ -0,0 +1,41 @@ +{ + "type": "anvilcraft:solid_liquid", + "consume": 12, + "fluid": { + "components": { + "patch": { + "!anvilcraft:liquid_enchantment": {} + } + }, + "fluids": "anvilcraft:liquid_enchantment" + }, + "ingredients": [ + { + "items": "minecraft:amethyst_block" + } + ], + "results": [], + "transform": [ + { + "amount": 4, + "components": { + "anvilcraft:liquid_enchantment": "anvilcraft:felling" + }, + "id": "anvilcraft:liquid_enchantment" + }, + { + "amount": 4, + "components": { + "anvilcraft:liquid_enchantment": "anvilcraft:harvest" + }, + "id": "anvilcraft:liquid_enchantment" + }, + { + "amount": 4, + "components": { + "anvilcraft:liquid_enchantment": "anvilcraft:beheading" + }, + "id": "anvilcraft:liquid_enchantment" + } + ] +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/fire_protection.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/fire_protection.json new file mode 100644 index 0000000000..042f15e9fa --- /dev/null +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/fire_protection.json @@ -0,0 +1,25 @@ +{ + "type": "anvilcraft:solid_liquid", + "consume": 8, + "fluid": { + "components": { + "patch": { + "!anvilcraft:liquid_enchantment": {} + } + }, + "fluids": "anvilcraft:liquid_enchantment" + }, + "ingredients": [ + { + "items": "anvilcraft:ruby" + } + ], + "results": [], + "transform": { + "amount": 8, + "components": { + "anvilcraft:liquid_enchantment": "minecraft:fire_protection" + }, + "id": "anvilcraft:liquid_enchantment" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/fortune_and_looting.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/fortune_and_looting.json new file mode 100644 index 0000000000..e03c11046f --- /dev/null +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/fortune_and_looting.json @@ -0,0 +1,34 @@ +{ + "type": "anvilcraft:solid_liquid", + "consume": 128, + "fluid": { + "components": { + "patch": { + "!anvilcraft:liquid_enchantment": {} + } + }, + "fluids": "anvilcraft:liquid_enchantment" + }, + "ingredients": [ + { + "items": "anvilcraft:transcendium_ingot" + } + ], + "results": [], + "transform": [ + { + "amount": 64, + "components": { + "anvilcraft:liquid_enchantment": "minecraft:fortune" + }, + "id": "anvilcraft:liquid_enchantment" + }, + { + "amount": 64, + "components": { + "anvilcraft:liquid_enchantment": "minecraft:looting" + }, + "id": "anvilcraft:liquid_enchantment" + } + ] +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/frost_walker.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/frost_walker.json new file mode 100644 index 0000000000..0e107f8a27 --- /dev/null +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/frost_walker.json @@ -0,0 +1,25 @@ +{ + "type": "anvilcraft:solid_liquid", + "consume": 2, + "fluid": { + "components": { + "patch": { + "!anvilcraft:liquid_enchantment": {} + } + }, + "fluids": "anvilcraft:liquid_enchantment" + }, + "ingredients": [ + { + "items": "anvilcraft:sapphire" + } + ], + "results": [], + "transform": { + "amount": 2, + "components": { + "anvilcraft:liquid_enchantment": "minecraft:frost_walker" + }, + "id": "anvilcraft:liquid_enchantment" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/liquid_enchantment.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/liquid_enchantment.json index 43bfed95c7..6ab7da564f 100644 --- a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/liquid_enchantment.json +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/liquid_enchantment.json @@ -8,7 +8,9 @@ "items": "minecraft:lapis_lazuli" } ], - "produce": 1, "results": [], - "transform": "anvilcraft:liquid_enchantment" + "transform": { + "amount": 1, + "id": "anvilcraft:liquid_enchantment" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/mending.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/mending.json new file mode 100644 index 0000000000..0abc3dfeb0 --- /dev/null +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/mending.json @@ -0,0 +1,25 @@ +{ + "type": "anvilcraft:solid_liquid", + "consume": 1, + "fluid": { + "components": { + "patch": { + "!anvilcraft:liquid_enchantment": {} + } + }, + "fluids": "anvilcraft:liquid_enchantment" + }, + "ingredients": [ + { + "items": "minecraft:emerald" + } + ], + "results": [], + "transform": { + "amount": 1, + "components": { + "anvilcraft:liquid_enchantment": "minecraft:mending" + }, + "id": "anvilcraft:liquid_enchantment" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/silk_touch.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/silk_touch.json new file mode 100644 index 0000000000..d9920df5a9 --- /dev/null +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/silk_touch.json @@ -0,0 +1,25 @@ +{ + "type": "anvilcraft:solid_liquid", + "consume": 1, + "fluid": { + "components": { + "patch": { + "!anvilcraft:liquid_enchantment": {} + } + }, + "fluids": "anvilcraft:liquid_enchantment" + }, + "ingredients": [ + { + "items": "anvilcraft:royal_steel_ingot" + } + ], + "results": [], + "transform": { + "amount": 1, + "components": { + "anvilcraft:liquid_enchantment": "minecraft:silk_touch" + }, + "id": "anvilcraft:liquid_enchantment" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/solid_liquid/smelting.json b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/smelting.json new file mode 100644 index 0000000000..88437181b1 --- /dev/null +++ b/src/generated/resources/data/anvilcraft/recipe/solid_liquid/smelting.json @@ -0,0 +1,25 @@ +{ + "type": "anvilcraft:solid_liquid", + "consume": 16, + "fluid": { + "components": { + "patch": { + "!anvilcraft:liquid_enchantment": {} + } + }, + "fluids": "anvilcraft:liquid_enchantment" + }, + "ingredients": [ + { + "items": "anvilcraft:ember_metal_ingot" + } + ], + "results": [], + "transform": { + "amount": 16, + "components": { + "anvilcraft:liquid_enchantment": "anvilcraft:smelting" + }, + "id": "anvilcraft:liquid_enchantment" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/squeezing/exp_fluid_from_sculk.json b/src/generated/resources/data/anvilcraft/recipe/squeezing/exp_fluid_from_sculk.json index 307769b5f8..5a5d2d3ce1 100644 --- a/src/generated/resources/data/anvilcraft/recipe/squeezing/exp_fluid_from_sculk.json +++ b/src/generated/resources/data/anvilcraft/recipe/squeezing/exp_fluid_from_sculk.json @@ -7,13 +7,15 @@ "inverted": true }, "chance": 0.1, - "fluid": "minecraft:null", + "fluid": [], "ingredient": { "blocks": "minecraft:sculk" }, - "produce": 250, "result": { "block": "minecraft:air" }, - "transform": "anvilcraft:exp_fluid" + "transform": { + "amount": 250, + "id": "anvilcraft:exp_fluid" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/squeezing/exp_fluid_from_sculk_use_frost_anvil.json b/src/generated/resources/data/anvilcraft/recipe/squeezing/exp_fluid_from_sculk_use_frost_anvil.json index 2b4a68f662..80bdc091eb 100644 --- a/src/generated/resources/data/anvilcraft/recipe/squeezing/exp_fluid_from_sculk_use_frost_anvil.json +++ b/src/generated/resources/data/anvilcraft/recipe/squeezing/exp_fluid_from_sculk_use_frost_anvil.json @@ -6,13 +6,15 @@ } }, "chance": 0.4, - "fluid": "minecraft:null", + "fluid": [], "ingredient": { "blocks": "minecraft:sculk" }, - "produce": 250, "result": { "block": "minecraft:air" }, - "transform": "anvilcraft:exp_fluid" + "transform": { + "amount": 250, + "id": "anvilcraft:exp_fluid" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/squeezing/lava_cauldron_from_magma_block.json b/src/generated/resources/data/anvilcraft/recipe/squeezing/lava_cauldron_from_magma_block.json index 990ca641cf..3f6bc8cabb 100644 --- a/src/generated/resources/data/anvilcraft/recipe/squeezing/lava_cauldron_from_magma_block.json +++ b/src/generated/resources/data/anvilcraft/recipe/squeezing/lava_cauldron_from_magma_block.json @@ -1,12 +1,14 @@ { "type": "anvilcraft:squeezing", - "fluid": "minecraft:null", + "fluid": [], "ingredient": { "blocks": "minecraft:magma_block" }, - "produce": 250, "result": { "block": "minecraft:netherrack" }, - "transform": "minecraft:lava" + "transform": { + "amount": 250, + "id": "minecraft:lava" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/squeezing/powder_snow_cauldron_from_snow_block.json b/src/generated/resources/data/anvilcraft/recipe/squeezing/powder_snow_cauldron_from_snow_block.json index a3bb183e28..0aba070d78 100644 --- a/src/generated/resources/data/anvilcraft/recipe/squeezing/powder_snow_cauldron_from_snow_block.json +++ b/src/generated/resources/data/anvilcraft/recipe/squeezing/powder_snow_cauldron_from_snow_block.json @@ -1,12 +1,14 @@ { "type": "anvilcraft:squeezing", - "fluid": "minecraft:null", + "fluid": [], "ingredient": { "blocks": "minecraft:snow_block" }, - "produce": 250, "result": { "block": "minecraft:ice" }, - "transform": "minecraft:powder_snow" + "transform": { + "amount": 250, + "id": "minecraft:powder_snow" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/squeezing/water_cauldron_from_moss_block.json b/src/generated/resources/data/anvilcraft/recipe/squeezing/water_cauldron_from_moss_block.json index f647b0b770..8d81cfdc30 100644 --- a/src/generated/resources/data/anvilcraft/recipe/squeezing/water_cauldron_from_moss_block.json +++ b/src/generated/resources/data/anvilcraft/recipe/squeezing/water_cauldron_from_moss_block.json @@ -1,12 +1,14 @@ { "type": "anvilcraft:squeezing", - "fluid": "minecraft:null", + "fluid": [], "ingredient": { "blocks": "minecraft:moss_block" }, - "produce": 250, "result": { "block": "minecraft:moss_carpet" }, - "transform": "minecraft:water" + "transform": { + "amount": 250, + "id": "minecraft:water" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/squeezing/water_cauldron_from_wet_sponge.json b/src/generated/resources/data/anvilcraft/recipe/squeezing/water_cauldron_from_wet_sponge.json index d040923c63..99e34ee448 100644 --- a/src/generated/resources/data/anvilcraft/recipe/squeezing/water_cauldron_from_wet_sponge.json +++ b/src/generated/resources/data/anvilcraft/recipe/squeezing/water_cauldron_from_wet_sponge.json @@ -1,12 +1,14 @@ { "type": "anvilcraft:squeezing", - "fluid": "minecraft:null", + "fluid": [], "ingredient": { "blocks": "minecraft:wet_sponge" }, - "produce": 250, "result": { "block": "minecraft:sponge" }, - "transform": "minecraft:water" + "transform": { + "amount": 250, + "id": "minecraft:water" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/super_heating/honey_cauldron_from_honey_block.json b/src/generated/resources/data/anvilcraft/recipe/super_heating/honey_cauldron_from_honey_block.json index ff6e037245..9b5149e8cb 100644 --- a/src/generated/resources/data/anvilcraft/recipe/super_heating/honey_cauldron_from_honey_block.json +++ b/src/generated/resources/data/anvilcraft/recipe/super_heating/honey_cauldron_from_honey_block.json @@ -1,11 +1,13 @@ { "type": "anvilcraft:super_heating", - "fluid": "minecraft:null", + "fluid": [], "ingredients": [ { "items": "minecraft:honey_block" } ], - "produce": 1000, - "transform": "anvilcraft:honey" + "transform": { + "amount": 1000, + "id": "anvilcraft:honey" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/super_heating/lava_from_cobblestone.json b/src/generated/resources/data/anvilcraft/recipe/super_heating/lava_from_cobblestone.json index e8bc6e8b83..a77489df83 100644 --- a/src/generated/resources/data/anvilcraft/recipe/super_heating/lava_from_cobblestone.json +++ b/src/generated/resources/data/anvilcraft/recipe/super_heating/lava_from_cobblestone.json @@ -1,6 +1,6 @@ { "type": "anvilcraft:super_heating", - "fluid": "minecraft:null", + "fluid": [], "ingredients": [ { "count": 4, @@ -10,6 +10,8 @@ "items": "anvilcraft:lime_powder" } ], - "produce": 1000, - "transform": "minecraft:lava" + "transform": { + "amount": 1000, + "id": "minecraft:lava" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/super_heating/lava_from_stone.json b/src/generated/resources/data/anvilcraft/recipe/super_heating/lava_from_stone.json index b3ed6ab9c5..910b953cd1 100644 --- a/src/generated/resources/data/anvilcraft/recipe/super_heating/lava_from_stone.json +++ b/src/generated/resources/data/anvilcraft/recipe/super_heating/lava_from_stone.json @@ -1,6 +1,6 @@ { "type": "anvilcraft:super_heating", - "fluid": "minecraft:null", + "fluid": [], "ingredients": [ { "count": 4, @@ -10,6 +10,8 @@ "items": "anvilcraft:lime_powder" } ], - "produce": 1000, - "transform": "minecraft:lava" + "transform": { + "amount": 1000, + "id": "minecraft:lava" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/super_heating/melt_gem_cauldron_from_chromatic_stone.json b/src/generated/resources/data/anvilcraft/recipe/super_heating/melt_gem_cauldron_from_chromatic_stone.json index 6b317f50d7..492ff92379 100644 --- a/src/generated/resources/data/anvilcraft/recipe/super_heating/melt_gem_cauldron_from_chromatic_stone.json +++ b/src/generated/resources/data/anvilcraft/recipe/super_heating/melt_gem_cauldron_from_chromatic_stone.json @@ -1,11 +1,13 @@ { "type": "anvilcraft:super_heating", - "fluid": "minecraft:null", + "fluid": [], "ingredients": [ { "items": "anvilcraft:chromatic_stone" } ], - "produce": 1000, - "transform": "anvilcraft:melt_gem" + "transform": { + "amount": 1000, + "id": "anvilcraft:melt_gem" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/super_heating/melt_gem_cauldron_from_gem_block.json b/src/generated/resources/data/anvilcraft/recipe/super_heating/melt_gem_cauldron_from_gem_block.json index 16e9c439a6..6eac5bfedf 100644 --- a/src/generated/resources/data/anvilcraft/recipe/super_heating/melt_gem_cauldron_from_gem_block.json +++ b/src/generated/resources/data/anvilcraft/recipe/super_heating/melt_gem_cauldron_from_gem_block.json @@ -1,11 +1,13 @@ { "type": "anvilcraft:super_heating", - "fluid": "minecraft:null", + "fluid": [], "ingredients": [ { "items": "#anvilcraft:gem_blocks" } ], - "produce": 1000, - "transform": "anvilcraft:melt_gem" + "transform": { + "amount": 1000, + "id": "anvilcraft:melt_gem" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/time_warp/mob_amber_block.json b/src/generated/resources/data/anvilcraft/recipe/time_warp/mob_amber_block.json index 1043d60658..43614f9728 100644 --- a/src/generated/resources/data/anvilcraft/recipe/time_warp/mob_amber_block.json +++ b/src/generated/resources/data/anvilcraft/recipe/time_warp/mob_amber_block.json @@ -49,6 +49,9 @@ "non_conflicting": [ { "type": "anvilcraft:has_cauldron", + "fluid": { + "amount": 0 + }, "offset": [ 0.0, -1.0, diff --git a/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_beef.json b/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_beef.json index 4da63635e7..c5369559ec 100644 --- a/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_beef.json +++ b/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_beef.json @@ -1,12 +1,14 @@ { "type": "anvilcraft:time_warp", - "fluid": "minecraft:null", + "fluid": [], "ingredients": [ { "count": 16, "items": "#c:foods/raw_beef" } ], - "produce": 250, - "transform": "anvilcraft:oil" + "transform": { + "amount": 250, + "id": "anvilcraft:oil" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_chicken.json b/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_chicken.json index bf82ed41a6..17f3f84433 100644 --- a/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_chicken.json +++ b/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_chicken.json @@ -1,12 +1,14 @@ { "type": "anvilcraft:time_warp", - "fluid": "minecraft:null", + "fluid": [], "ingredients": [ { "count": 64, "items": "#c:foods/raw_chicken" } ], - "produce": 250, - "transform": "anvilcraft:oil" + "transform": { + "amount": 250, + "id": "anvilcraft:oil" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_fish.json b/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_fish.json index 2e8ccce4e1..e73570733f 100644 --- a/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_fish.json +++ b/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_fish.json @@ -1,12 +1,14 @@ { "type": "anvilcraft:time_warp", - "fluid": "minecraft:null", + "fluid": [], "ingredients": [ { "count": 64, "items": "#c:foods/raw_fish" } ], - "produce": 250, - "transform": "anvilcraft:oil" + "transform": { + "amount": 250, + "id": "anvilcraft:oil" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_mutton.json b/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_mutton.json index 44a237c2db..f22c094aad 100644 --- a/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_mutton.json +++ b/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_mutton.json @@ -1,12 +1,14 @@ { "type": "anvilcraft:time_warp", - "fluid": "minecraft:null", + "fluid": [], "ingredients": [ { "count": 16, "items": "#c:foods/raw_mutton" } ], - "produce": 250, - "transform": "anvilcraft:oil" + "transform": { + "amount": 250, + "id": "anvilcraft:oil" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_porkchop.json b/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_porkchop.json index 1996703658..6dc08d02c0 100644 --- a/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_porkchop.json +++ b/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_porkchop.json @@ -1,12 +1,14 @@ { "type": "anvilcraft:time_warp", - "fluid": "minecraft:null", + "fluid": [], "ingredients": [ { "count": 16, "items": "#c:foods/raw_porkchop" } ], - "produce": 250, - "transform": "anvilcraft:oil" + "transform": { + "amount": 250, + "id": "anvilcraft:oil" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_rabbit.json b/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_rabbit.json index 2e4ce17b35..72c5f6738e 100644 --- a/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_rabbit.json +++ b/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_foods/raw_rabbit.json @@ -1,12 +1,14 @@ { "type": "anvilcraft:time_warp", - "fluid": "minecraft:null", + "fluid": [], "ingredients": [ { "count": 16, "items": "#c:foods/raw_rabbit" } ], - "produce": 250, - "transform": "anvilcraft:oil" + "transform": { + "amount": 250, + "id": "anvilcraft:oil" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_piglin_head.json b/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_piglin_head.json index 4974d3427b..e678b78644 100644 --- a/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_piglin_head.json +++ b/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_piglin_head.json @@ -1,11 +1,13 @@ { "type": "anvilcraft:time_warp", - "fluid": "minecraft:null", + "fluid": [], "ingredients": [ { "items": "minecraft:piglin_head" } ], - "produce": 250, - "transform": "anvilcraft:oil" + "transform": { + "amount": 250, + "id": "anvilcraft:oil" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_rotten_flesh.json b/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_rotten_flesh.json index c18a56ac20..01fab4d38d 100644 --- a/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_rotten_flesh.json +++ b/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_rotten_flesh.json @@ -1,12 +1,14 @@ { "type": "anvilcraft:time_warp", - "fluid": "minecraft:null", + "fluid": [], "ingredients": [ { "count": 64, "items": "minecraft:rotten_flesh" } ], - "produce": 250, - "transform": "anvilcraft:oil" + "transform": { + "amount": 250, + "id": "anvilcraft:oil" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_spider_eye.json b/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_spider_eye.json index f2d4c6a3a6..bd2d607796 100644 --- a/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_spider_eye.json +++ b/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_spider_eye.json @@ -1,12 +1,14 @@ { "type": "anvilcraft:time_warp", - "fluid": "minecraft:null", + "fluid": [], "ingredients": [ { "count": 64, "items": "minecraft:spider_eye" } ], - "produce": 250, - "transform": "anvilcraft:oil" + "transform": { + "amount": 250, + "id": "anvilcraft:oil" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_zombie_head.json b/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_zombie_head.json index 99c4b93681..aa2ccdffa6 100644 --- a/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_zombie_head.json +++ b/src/generated/resources/data/anvilcraft/recipe/time_warp/oil_from_zombie_head.json @@ -1,11 +1,13 @@ { "type": "anvilcraft:time_warp", - "fluid": "minecraft:null", + "fluid": [], "ingredients": [ { "items": "minecraft:zombie_head" } ], - "produce": 250, - "transform": "anvilcraft:oil" + "transform": { + "amount": 250, + "id": "anvilcraft:oil" + } } \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/time_warp/resentful_amber_block.json b/src/generated/resources/data/anvilcraft/recipe/time_warp/resentful_amber_block.json index 6b7133be1b..3f8c999662 100644 --- a/src/generated/resources/data/anvilcraft/recipe/time_warp/resentful_amber_block.json +++ b/src/generated/resources/data/anvilcraft/recipe/time_warp/resentful_amber_block.json @@ -47,6 +47,9 @@ "non_conflicting": [ { "type": "anvilcraft:has_cauldron", + "fluid": { + "amount": 0 + }, "offset": [ 0.0, -1.0, diff --git a/src/main/java/dev/dubhe/anvilcraft/api/block/ICauldron.java b/src/main/java/dev/dubhe/anvilcraft/api/block/ICauldron.java new file mode 100644 index 0000000000..e79012fa48 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/block/ICauldron.java @@ -0,0 +1,13 @@ +package dev.dubhe.anvilcraft.api.block; + +/** 炼药锅实现共有的能力。 */ +public interface ICauldron { + /** + * 检查此炼药锅是否可以处理包含多个流体输出的配方。 + * + * @return 是否支持多个流体输出 + */ + default boolean supportsMultipleFluidOutputs() { + return false; + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/api/block/IIgnitableCauldron.java b/src/main/java/dev/dubhe/anvilcraft/api/block/IIgnitableCauldron.java index 779ab7be11..481072a7ba 100644 --- a/src/main/java/dev/dubhe/anvilcraft/api/block/IIgnitableCauldron.java +++ b/src/main/java/dev/dubhe/anvilcraft/api/block/IIgnitableCauldron.java @@ -4,7 +4,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.world.level.material.Fluid; -public interface IIgnitableCauldron { +public interface IIgnitableCauldron extends ICauldron { default boolean isEmpty(BlockCache cache, BlockPos pos) { return false; } diff --git a/src/main/java/dev/dubhe/anvilcraft/api/tooltip/impl/LargeCauldronTooltipProvider.java b/src/main/java/dev/dubhe/anvilcraft/api/tooltip/impl/LargeCauldronTooltipProvider.java index b084bd711d..45fb5e6a67 100644 --- a/src/main/java/dev/dubhe/anvilcraft/api/tooltip/impl/LargeCauldronTooltipProvider.java +++ b/src/main/java/dev/dubhe/anvilcraft/api/tooltip/impl/LargeCauldronTooltipProvider.java @@ -7,10 +7,8 @@ import dev.dubhe.anvilcraft.util.CompatUtil; import dev.dubhe.anvilcraft.util.UnitUtil; import net.minecraft.ChatFormatting; -import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.entity.BlockEntity; import net.neoforged.neoforge.fluids.FluidStack; @@ -114,10 +112,9 @@ private static Set categoriesForFluid( FluidStack fluid ) { Set result = new LinkedHashSet<>(); - ResourceLocation fluidId = BuiltInRegistries.FLUID.getKey(fluid.getFluid()); for (LargeCauldronBlockEntity.RecipePreview preview : previews) { for (HasCauldron predicate : preview.fluidPredicates()) { - if (predicate.hasCheck() && predicate.matchesFluid(fluidId)) { + if (predicate.hasCheck() && predicate.matchesFluid(fluid)) { result.add(preview.categoryPath()); break; } diff --git a/src/main/java/dev/dubhe/anvilcraft/block/LargeCauldronBlock.java b/src/main/java/dev/dubhe/anvilcraft/block/LargeCauldronBlock.java index 18207f0dbc..09b3cd258e 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/LargeCauldronBlock.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/LargeCauldronBlock.java @@ -1,5 +1,6 @@ package dev.dubhe.anvilcraft.block; +import dev.dubhe.anvilcraft.api.block.ICauldron; import dev.dubhe.anvilcraft.api.block.ICauldronGeometry; import dev.dubhe.anvilcraft.api.fluid.FluidHandlerWrapper; import dev.dubhe.anvilcraft.api.hammer.IHammerRemovable; @@ -29,7 +30,6 @@ import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Mirror; -import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityTicker; @@ -53,7 +53,7 @@ public class LargeCauldronBlock extends SimpleMultiPartBlock - implements MultiPartBlockEntity, IHammerRemovable, ICauldronGeometry { + implements MultiPartBlockEntity, IHammerRemovable, ICauldron, ICauldronGeometry { public static final EnumProperty HALF = EnumProperty.create("half", Cube3x3PartHalf.class); private static final double WALL_THICKNESS = 0.25; private static final double BOTTOM_WALL_MIN_Y = 0.5; @@ -100,6 +100,11 @@ public LargeCauldronBlock getMultiBlock() { return this; } + @Override + public boolean supportsMultipleFluidOutputs() { + return true; + } + @Override public AABB getCauldronInnerArea(BlockPos pos, BlockState state) { BlockPos mainPos = this.getMainPartPos(pos, state); @@ -153,7 +158,7 @@ public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, Co @Override public boolean isLadder(BlockState state, LevelReader level, BlockPos pos, LivingEntity entity) { - return entity != null && touchesWall(state, pos, entity.getBoundingBox()); + return touchesWall(state, pos, entity.getBoundingBox()); } public static @Nullable BlockPos findClimbableWall(LevelReader level, LivingEntity entity) { @@ -209,11 +214,6 @@ protected VoxelShape getInteractionShape(BlockState state, BlockGetter level, Bl return Shapes.block(); } - @Override - protected RenderShape getRenderShape(BlockState state) { - return RenderShape.MODEL; - } - @Override protected boolean propagatesSkylightDown(BlockState state, BlockGetter level, BlockPos pos) { return true; diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/LargeCauldronBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/LargeCauldronBlockEntity.java index a6d069ba21..fd7e28a778 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/entity/LargeCauldronBlockEntity.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/LargeCauldronBlockEntity.java @@ -12,6 +12,7 @@ import dev.anvilcraft.lib.v2.yukkuri.api.vapor.VaporizationCauldron; import dev.anvilcraft.lib.v2.yukkuri.api.vapor.VaporizationManager; import dev.dubhe.anvilcraft.AnvilCraft; +import dev.dubhe.anvilcraft.api.block.ICauldron; import dev.dubhe.anvilcraft.api.event.AnvilEvent; import dev.dubhe.anvilcraft.api.fluid.FluidHandlerWrapper; import dev.dubhe.anvilcraft.api.fluid.IFluidHandlerHolder; @@ -96,7 +97,7 @@ import java.util.Set; public class LargeCauldronBlockEntity extends BlockEntity - implements IItemHandlerHolder, IItemHandlerCache, IFluidHandlerHolder, VaporizationCauldron { + implements ICauldron, IItemHandlerHolder, IItemHandlerCache, IFluidHandlerHolder, VaporizationCauldron { public static final int OUTPUT_SLOTS = 32; public static final int MAX_PROCESS_EFFICIENCY = 9; private static final int[][] INPUT_SLOT_OFFSETS = { @@ -182,8 +183,8 @@ public void setRemoved() { public static void serverTick( Level level, - BlockPos pos, - BlockState state, + @SuppressWarnings("unused") BlockPos pos, + @SuppressWarnings("unused") BlockState state, LargeCauldronBlockEntity entity ) { if (!entity.isMainPart()) return; @@ -203,7 +204,9 @@ private void absorbFluidSources(Level level) { if (fluidState.isEmpty() || !fluidState.isSource()) continue; BlockState sourceState = level.getBlockState(sourcePos); if (!(sourceState.getBlock() instanceof BucketPickup bucketPickup)) continue; - IFluidHandler source = new BucketPickupHandlerWrapper(null, bucketPickup, level, sourcePos); + // BucketPickup accepts a nullable player, but NeoForge's wrapper does not declare it. + @SuppressWarnings("DataFlowIssue") IFluidHandler source = + new BucketPickupHandlerWrapper(null, bucketPickup, level, sourcePos); FluidUtil.tryFluidTransfer( this.fluids, source, @@ -236,6 +239,11 @@ public IFluidHandler getFluidHandler() { return this.getMainPart().fluids; } + @Override + public boolean supportsMultipleFluidOutputs() { + return true; + } + public LargeCauldronInputHandler getInputHandler() { return this.getMainPart().input; } @@ -307,7 +315,7 @@ public boolean interactWithFluid(Player player, InteractionHand hand, BlockHitRe if (this.level == null) return false; LargeCauldronBlockEntity main = this.getMainPart(); double minY = main.worldPosition.getY() - 0.5; - double fraction = Math.max(0.0, Math.min(1.0, (hit.getLocation().y - minY) / 2.25)); + double fraction = Math.clamp((hit.getLocation().y - minY) / CONTENT_HEIGHT, 0.0, 1.0); int accessible = Math.max(1, (int) Math.ceil(fraction * LargeCauldronFluidHandler.TOTAL_CAPACITY)); IFluidHandler handler = main.fluids.sideAccess(accessible); if (FluidHandlerWrapper.tryInteractWithBottle(player, hand, handler, this.level, this.worldPosition)) return true; @@ -397,6 +405,7 @@ public ItemStack insertRecipeOutput(ItemStack stack) { return remainder; } + @SuppressWarnings("unused") public boolean hasInputMatching(java.util.function.Predicate predicate) { LargeCauldronInputHandler handler = this.getMainPart().input; for (int slot = 0; slot < handler.getSlots(); slot++) { @@ -482,7 +491,6 @@ public Packet getUpdatePacket() { return ClientboundBlockEntityDataPacket.create(this); } - @SuppressWarnings("checkstyle:VariableDeclarationUsageDistance") public boolean handleGiantAnvilImpact(AnvilEvent.OnLand event) { LargeCauldronBlockEntity main = this.getMainPart(); Level level = main.level; @@ -499,11 +507,11 @@ public boolean handleGiantAnvilImpact(AnvilEvent.OnLand event) { main.lastImpactGameTime = gameTime; BlockPos base = main.worldPosition.below(); - List helpers = main.findActiveHelpers(base); + List helpers = findActiveHelpers(serverLevel, base); List itemRecipePasses = helpers.isEmpty() ? List.of(RecipePass.ALL) : List.of(RecipePass.NON_COMPRESSION, RecipePass.COMPRESSION_ONLY); - List initialFluids = main.fluids.copyFluids(); + final List initialFluids = main.fluids.copyFluids(); List initialOutputSlots = new ArrayList<>(); for (int slot = 0; slot < main.output.getSlots(); slot++) { if (!main.output.getStackInSlot(slot).isEmpty()) initialOutputSlots.add(slot); @@ -578,8 +586,7 @@ public boolean handleGiantAnvilImpact(AnvilEvent.OnLand event) { serverLevel, base, centerCandidates, - event.getEntity(), - RecipePass.NON_COMPRESSION + event.getEntity() ); if (!execution.executed()) break; if (execution.damageAnvil()) event.setAnvilDamage(true); @@ -598,7 +605,7 @@ private boolean tryProcessLiquidEnchantmentRecipe(ServerLevel level, BlockPos ba while (consumed < itemBudget) { ItemStack item = this.input.getStackInSlot(slot); if (item.isEmpty()) break; - var heatingHelper = this.findActiveHeatingHelper(base, slot); + var heatingHelper = this.findActiveHeatingHelper(level, base, slot); var matched = LiquidEnchantmentCauldronRecipe.match( this.fluids.copyFluids(), item, @@ -625,11 +632,11 @@ private boolean tryProcessLiquidEnchantmentRecipe(ServerLevel level, BlockPos ba return processed; } - private Optional findActiveHeatingHelper(BlockPos base, int inputSlot) { + private Optional findActiveHeatingHelper(ServerLevel level, BlockPos base, int inputSlot) { List heaters = new ArrayList<>(); for (int slot = 0; slot < FOOTPRINT_OFFSETS.length; slot++) { BlockPos helper = positionForFootprint(base, slot).below(); - if (isActiveHeatingHelper(this.level.getBlockState(helper))) heaters.add(helper); + if (isActiveHeatingHelper(level.getBlockState(helper))) heaters.add(helper); } BlockPos inputPos = positionForInputSlot(base, inputSlot); return orderedHelpers(inputPos, heaters, base).stream().findFirst(); @@ -729,7 +736,6 @@ private RecipeExecution tryProcessItemGroup( RecipePass recipePass ) { for (BlockPos helper : candidates) { - Vec3 contextPos = new Vec3(helper.getX() + 0.5, base.getY() + 1.0, helper.getZ() + 0.5); this.processingSlot = slot; this.processingOutput = outputSource; this.processingOutputInputs.clear(); @@ -742,6 +748,7 @@ private RecipeExecution tryProcessItemGroup( } RecipeExecution execution; try { + Vec3 contextPos = new Vec3(helper.getX() + 0.5, base.getY() + 1.0, helper.getZ() + 0.5); execution = this.triggerRecipeGroup( level, contextPos, @@ -764,13 +771,12 @@ private RecipeExecution tryProcessFluidRecipe( ServerLevel level, BlockPos base, List candidates, - Entity anvil, - RecipePass recipePass + Entity anvil ) { for (BlockPos helper : candidates) { Vec3 contextPos = new Vec3(helper.getX() + 0.5, base.getY() + 1.0, helper.getZ() + 0.5); InWorldRecipeContext context = new InWorldRecipeContext(level, contextPos, anvil); - RecipeExecution execution = triggerOneRecipe(level, context, ItemStack.EMPTY, recipePass); + RecipeExecution execution = triggerOneRecipe(level, context, ItemStack.EMPTY, RecipePass.NON_COMPRESSION); if (execution.executed()) return execution; } return RecipeExecution.EMPTY; @@ -820,9 +826,9 @@ private static RecipeExecution triggerOneRecipe( InWorldRecipeManager manager = level.getRecipeManager().anvillib$getInWorldRecipeManager(); for (RecipeHolder holder : manager.recipeHolders.get(ModRecipeTriggers.ON_ANVIL_FALL_ON.get())) { InWorldRecipe recipe = holder.value(); - if (!recipePass.accepts(recipe)) continue; + if (recipePass.rejects(recipe)) continue; if (processingInput.isEmpty()) { - if (!isFluidOnlyRecipe(recipe)) continue; + if (isNotFluidOnlyRecipe(recipe)) continue; } else if (!recipeAnchoredByInput(recipe, processingInput)) { continue; } @@ -870,23 +876,23 @@ private static int highestAnchoredRecipePriority( for (RecipeHolder holder : manager.recipeHolders.get(ModRecipeTriggers.ON_ANVIL_FALL_ON.get())) { InWorldRecipe recipe = holder.value(); - if (!recipePass.accepts(recipe)) continue; + if (recipePass.rejects(recipe)) continue; if (recipeAnchoredByInput(recipe, stack)) priority = Math.max(priority, recipe.priority()); } return priority; } - private static boolean isFluidOnlyRecipe(InWorldRecipe recipe) { + private static boolean isNotFluidOnlyRecipe(InWorldRecipe recipe) { boolean hasFluid = false; for (IRecipePredicate predicate : recipe.nonConflicting()) { - if (predicate instanceof HasItemIngredient) return false; + if (predicate instanceof HasItemIngredient) return true; hasFluid |= predicate instanceof HasCauldron; } for (IRecipePredicate predicate : recipe.conflicting()) { - if (predicate instanceof HasItemIngredient) return false; + if (predicate instanceof HasItemIngredient) return true; hasFluid |= predicate instanceof HasCauldron; } - return hasFluid; + return !hasFluid; } private static boolean sameFluids(List first, List second) { @@ -925,7 +931,8 @@ public List getRecipePreviews() { LargeCauldronBlockEntity main = this.getMainPart(); if (main != this) return main.getRecipePreviews(); if (this.level == null) return List.of(); - long gameTime = this.level.getGameTime(); + Level level = this.level; + long gameTime = level.getGameTime(); if (this.recipePreviewGameTime != Long.MIN_VALUE && gameTime >= this.recipePreviewGameTime && gameTime - this.recipePreviewGameTime < 10) { @@ -933,9 +940,9 @@ public List getRecipePreviews() { } BlockPos base = this.worldPosition.below(); - List helpers = this.findActiveHelpers(base); + List helpers = findActiveHelpers(level, base); boolean itemCompressionLast = !helpers.isEmpty(); - List> recipes = this.getPreviewRecipes(itemCompressionLast); + List> recipes = getPreviewRecipes(level, itemCompressionLast); if (recipes.isEmpty()) { this.recipePreviewGameTime = gameTime; this.recipePreviewCache = List.of(); @@ -999,12 +1006,12 @@ public List getRecipePreviews() { } @SuppressWarnings("unchecked") - private List> getPreviewRecipes(boolean itemCompressionLast) { + private static List> getPreviewRecipes(Level level, boolean itemCompressionLast) { List> recipes = new ArrayList<>(); - for (RecipeHolder holder : this.level.getRecipeManager().getRecipes()) { + for (RecipeHolder holder : level.getRecipeManager().getRecipes()) { if (!(holder.value() instanceof InWorldRecipe recipe)) continue; if (!recipe.trigger().equals(ModRecipeTriggers.ON_ANVIL_FALL_ON.get())) continue; - recipes.add((RecipeHolder) (RecipeHolder) holder); + recipes.add((RecipeHolder) holder); } recipes.sort( Comparator.>comparingInt( @@ -1061,7 +1068,7 @@ private List> getPreviewRecipes(boolean itemCompress Vec3 contextPos = new Vec3(helper.getX() + 0.5, base.getY() + 1.0, helper.getZ() + 0.5); for (RecipeHolder holder : recipes) { InWorldRecipe recipe = holder.value(); - if (!isFluidOnlyRecipe(recipe)) continue; + if (isNotFluidOnlyRecipe(recipe)) continue; PreviewState state = new PreviewState( copyItemStacks(this.input), -1, @@ -1099,7 +1106,7 @@ private boolean previewCompatible( Vec3 contextPos ) { for (IRecipePredicate predicate : predicates) { - if (!this.applyPreviewPredicate(predicate, state, contextPos)) return false; + if (this.rejectsPreviewPredicate(predicate, state, contextPos)) return false; } return true; } @@ -1129,7 +1136,7 @@ private boolean previewIncompatible( for (int i = 0; i < predicates.size(); i++) { if (used[i]) continue; PreviewState branch = state.copy(); - if (!this.applyPreviewPredicate(predicates.get(i), branch, contextPos)) continue; + if (this.rejectsPreviewPredicate(predicates.get(i), branch, contextPos)) continue; used[i] = true; if (this.previewIncompatible(predicates, used, remaining - 1, branch, contextPos)) { state.copyFrom(branch); @@ -1140,7 +1147,7 @@ private boolean previewIncompatible( return false; } - private boolean applyPreviewPredicate( + private boolean rejectsPreviewPredicate( IRecipePredicate predicate, PreviewState state, Vec3 contextPos @@ -1148,34 +1155,36 @@ private boolean applyPreviewPredicate( switch (predicate) { case HasItemIngredient itemIngredient -> { int slot = state.findItem(itemIngredient); - if (slot < 0) return false; + if (slot < 0) return true; state.items.get(slot).shrink(itemIngredient.getItem().count()); state.usedItemSlots.add(slot); - return true; + return false; } case HasCauldron cauldron -> { if ((cauldron.ignited() && !this.ignited) || !this.targetsThisCauldron(cauldron, contextPos)) { - return false; + return true; } - if (!applyFluidPredicate(state.fluids, cauldron)) return false; + if (!applyFluidPredicate(state.fluids, cauldron)) return true; state.fluidPredicates.add(cauldron); - return true; + return false; } case HasBlockBase block -> { + if (this.level == null) return true; BlockPos pos = BlockPos.containing(contextPos.add(block.getOffset())); - return block.getPredicate().test(this.level, this.level.getBlockState(pos), this.level.getBlockEntity(pos)); + return !block.getPredicate().test(this.level, this.level.getBlockState(pos), this.level.getBlockEntity(pos)); } case HasAnvil anvil -> { boolean matches = anvil.anvil().testWithoutEntity(ModBlocks.GIANT_ANVIL.getDefaultState()); - return matches != anvil.inverted(); + return matches == anvil.inverted(); } default -> { } } - return false; + return true; } private boolean targetsThisCauldron(HasCauldron predicate, Vec3 contextPos) { + if (this.level == null) return false; BlockPos pos = BlockPos.containing(contextPos.add(predicate.offset())); BlockState state = this.level.getBlockState(pos); return state.getBlock() instanceof LargeCauldronBlock block @@ -1190,11 +1199,11 @@ private static String categoryPath(RecipeHolder holder) { return separator < 0 ? path : path.substring(0, separator); } - private List findActiveHelpers(BlockPos base) { + private static List findActiveHelpers(Level level, BlockPos base) { List result = new ArrayList<>(); for (int slot = 0; slot < FOOTPRINT_OFFSETS.length; slot++) { BlockPos helper = positionForFootprint(base, slot).below(); - if (isActiveRecipeHelper(this.level.getBlockState(helper))) result.add(helper); + if (isActiveRecipeHelper(level.getBlockState(helper))) result.add(helper); } return result; } @@ -1428,54 +1437,28 @@ private static void commitFluidRecipes(InWorldRecipeContext context) { private static boolean applyFluidPredicate(List fluids, HasCauldron predicate) { int source = findSourceTank(fluids, predicate); - if (predicate.fluid().equals(HasCauldron.EMPTY) && source < 0) return false; - if (predicate.hasCheck() && !predicate.fluid().equals(HasCauldron.EMPTY) && source < 0) return false; + if (predicate.hasCheck() && source < 0) return false; int sourceAmount = source < 0 ? 0 : fluids.get(source).getAmount(); if (predicate.consume() > sourceAmount) return false; - ResourceLocation sourceId = source < 0 - ? null - : BuiltInRegistries.FLUID.getKey(fluids.get(source).getFluid()); - ResourceLocation targetId = HasCauldron.isNotEmpty(predicate.transform()) - ? predicate.transform() - : sourceId != null ? sourceId : HasCauldron.isNotEmpty(predicate.fluid()) ? predicate.fluid() : null; - - if (predicate.consume() == 0 && predicate.produce() == 0) { - if (source < 0 || targetId == null || targetId.equals(sourceId)) return true; - int target = findTank(fluids, targetId); - int targetAmount = target < 0 ? 0 : fluids.get(target).getAmount(); - if (targetAmount + sourceAmount > LargeCauldronFluidHandler.TANK_CAPACITY) return false; - if (target < 0) target = findEmptyTankAfterRemoving(fluids, source); - if (target < 0) return false; - FluidStack transformed = fluids.get(target).isEmpty() - ? new FluidStack(BuiltInRegistries.FLUID.get(targetId), targetAmount + sourceAmount) - : fluids.get(target).copyWithAmount(targetAmount + sourceAmount); - fluids.set(source, FluidStack.EMPTY); - fluids.set(target, transformed); - return true; - } - if (source >= 0 && predicate.consume() > 0) { int remaining = sourceAmount - predicate.consume(); fluids.set(source, remaining == 0 ? FluidStack.EMPTY : fluids.get(source).copyWithAmount(remaining)); } - if (predicate.produce() == 0) return true; - if (targetId == null) return false; - - int target = findTank(fluids, targetId); - int targetAmount = target < 0 ? 0 : fluids.get(target).getAmount(); - if (targetAmount + predicate.produce() > LargeCauldronFluidHandler.TANK_CAPACITY) return false; - if (target < 0) target = findEmptyTank(fluids); - if (target < 0) return false; - FluidStack produced = fluids.get(target).isEmpty() - ? new FluidStack(BuiltInRegistries.FLUID.get(targetId), targetAmount + predicate.produce()) - : fluids.get(target).copyWithAmount(targetAmount + predicate.produce()); - fluids.set(target, produced); + for (FluidStack transform : predicate.transforms()) { + int target = findTank(fluids, transform); + int targetAmount = target < 0 ? 0 : fluids.get(target).getAmount(); + if (targetAmount + transform.getAmount() > LargeCauldronFluidHandler.TANK_CAPACITY) return false; + if (target < 0) target = findEmptyTank(fluids); + if (target < 0) return false; + FluidStack produced = transform.copyWithAmount(targetAmount + transform.getAmount()); + fluids.set(target, produced); + } return true; } private static int findSourceTank(List fluids, HasCauldron predicate) { - if (predicate.fluid().equals(HasCauldron.EMPTY)) { + if (predicate.requiresEmptyCauldron()) { for (FluidStack fluid : fluids) { if (!fluid.isEmpty()) return -1; } @@ -1484,15 +1467,12 @@ private static int findSourceTank(List fluids, HasCauldron predicate if (!predicate.hasCheck()) return -1; for (int i = 0; i < fluids.size(); i++) { FluidStack fluid = fluids.get(i); - if (fluid.isEmpty()) continue; - ResourceLocation id = BuiltInRegistries.FLUID.getKey(fluid.getFluid()); - if (predicate.matchesFluid(id)) return i; + if (predicate.matchesFluid(fluid)) return i; } return -1; } - private static int findTank(List fluids, ResourceLocation id) { - FluidStack target = new FluidStack(BuiltInRegistries.FLUID.get(id), 1); + private static int findTank(List fluids, FluidStack target) { for (int i = 0; i < fluids.size(); i++) { FluidStack fluid = fluids.get(i); if (FluidStack.isSameFluidSameComponents(fluid, target)) return i; @@ -1507,11 +1487,6 @@ private static int findEmptyTank(List fluids) { return -1; } - private static int findEmptyTankAfterRemoving(List fluids, int source) { - int empty = findEmptyTank(fluids); - return empty >= 0 ? empty : source; - } - private static List copyFluids(List fluids) { List result = new ArrayList<>(fluids.size()); for (FluidStack fluid : fluids) result.add(fluid.copy()); @@ -1523,12 +1498,12 @@ private enum RecipePass { NON_COMPRESSION, COMPRESSION_ONLY; - private boolean accepts(InWorldRecipe recipe) { + private boolean rejects(InWorldRecipe recipe) { boolean compression = recipe instanceof ItemCompressRecipe; return switch (this) { - case ALL -> true; - case NON_COMPRESSION -> !compression; - case COMPRESSION_ONLY -> compression; + case ALL -> false; + case NON_COMPRESSION -> compression; + case COMPRESSION_ONLY -> !compression; }; } } diff --git a/src/main/java/dev/dubhe/anvilcraft/client/markdown/recipe/anvil/MDBaseAnvilRecipeComponent.java b/src/main/java/dev/dubhe/anvilcraft/client/markdown/recipe/anvil/MDBaseAnvilRecipeComponent.java index 68ae4455f2..bb6d90dc12 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/markdown/recipe/anvil/MDBaseAnvilRecipeComponent.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/markdown/recipe/anvil/MDBaseAnvilRecipeComponent.java @@ -40,6 +40,11 @@ protected BlockState getOutputBlockState() { return Blocks.AIR.defaultBlockState(); } + protected static T getDisplayedElement(List elements) { + int index = (int) ((System.currentTimeMillis() / 1000) % elements.size()); + return elements.get(index); + } + @Override protected void renderRecipe(MDRenderContext context, float mouseX, float mouseY) { GuiGraphics g = context.graphics(); diff --git a/src/main/java/dev/dubhe/anvilcraft/client/markdown/recipe/anvil/MDFastCookingRecipeComponent.java b/src/main/java/dev/dubhe/anvilcraft/client/markdown/recipe/anvil/MDFastCookingRecipeComponent.java index 349dcdc924..f2ffc406d1 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/markdown/recipe/anvil/MDFastCookingRecipeComponent.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/markdown/recipe/anvil/MDFastCookingRecipeComponent.java @@ -2,6 +2,7 @@ import dev.anvilcraft.lib.v2.util.predicate.ChanceItemStack; import dev.anvilcraft.lib.v2.util.predicate.ItemIngredientPredicate; +import dev.dubhe.anvilcraft.recipe.anvil.predicate.block.HasCauldron; import dev.dubhe.anvilcraft.recipe.anvil.wrap.FastCookingRecipe; import dev.dubhe.anvilcraft.util.CauldronUtil; import lombok.Getter; @@ -26,7 +27,7 @@ public MDFastCookingRecipeComponent(FastCookingRecipe recipe, boolean enableAlig ingredients = recipe.getInputItems(); resultItems = recipe.getResultItems(); inputBlockStates = List.of( - CauldronUtil.fullState(recipe.getHasCauldron().getFluidCauldron()), + CauldronUtil.fullState(HasCauldron.getDefaultCauldron(recipe.getHasCauldron().fluid())), Blocks.CAMPFIRE.defaultBlockState() ); } diff --git a/src/main/java/dev/dubhe/anvilcraft/client/markdown/recipe/anvil/MDSolidLiquidRecipeComponent.java b/src/main/java/dev/dubhe/anvilcraft/client/markdown/recipe/anvil/MDSolidLiquidRecipeComponent.java index 5c36603dd5..24d308a6ed 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/markdown/recipe/anvil/MDSolidLiquidRecipeComponent.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/markdown/recipe/anvil/MDSolidLiquidRecipeComponent.java @@ -2,11 +2,13 @@ import dev.anvilcraft.lib.v2.util.predicate.ChanceItemStack; import dev.anvilcraft.lib.v2.util.predicate.ItemIngredientPredicate; +import dev.dubhe.anvilcraft.recipe.anvil.predicate.block.HasCauldron; import dev.dubhe.anvilcraft.recipe.anvil.wrap.SolidLiquidRecipe; import dev.dubhe.anvilcraft.util.CauldronUtil; import lombok.Getter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.fluids.FluidStack; import java.util.List; @@ -41,12 +43,15 @@ protected BlockState getOutputBlockState() { } public static BlockState getInputCauldron(SolidLiquidRecipe recipe) { - Block material = recipe.getHasCauldron().getFluidCauldron(); + Block material = HasCauldron.getDefaultCauldron(recipe.getHasCauldron().fluid()); return CauldronUtil.fullState(material); } static BlockState getResultCauldron(SolidLiquidRecipe recipe) { - Block result = recipe.getHasCauldron().getTransformCauldron(); + List transforms = recipe.getHasCauldron().transforms(); + Block result = transforms.isEmpty() + ? HasCauldron.getDefaultCauldron(recipe.getHasCauldron().fluid()) + : HasCauldron.getDefaultCauldron(getDisplayedElement(transforms).getFluid()); if (recipe.isConsumeFluid()) { return CauldronUtil.getStateFromContentAndLevel(result, CauldronUtil.maxLevel(result) - 1); } else if (recipe.isProduceFluid()) { diff --git a/src/main/java/dev/dubhe/anvilcraft/client/markdown/recipe/anvil/MDSuperHeatingRecipeComponent.java b/src/main/java/dev/dubhe/anvilcraft/client/markdown/recipe/anvil/MDSuperHeatingRecipeComponent.java index 2c9894d673..e65ba3cec3 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/markdown/recipe/anvil/MDSuperHeatingRecipeComponent.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/markdown/recipe/anvil/MDSuperHeatingRecipeComponent.java @@ -4,11 +4,13 @@ import dev.anvilcraft.lib.v2.util.predicate.ItemIngredientPredicate; import dev.dubhe.anvilcraft.block.HeaterBlock; import dev.dubhe.anvilcraft.init.block.ModBlocks; +import dev.dubhe.anvilcraft.recipe.anvil.predicate.block.HasCauldron; import dev.dubhe.anvilcraft.recipe.anvil.wrap.SuperHeatingRecipe; import dev.dubhe.anvilcraft.util.CauldronUtil; import lombok.Getter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.fluids.FluidStack; import java.util.List; @@ -43,12 +45,15 @@ protected BlockState getOutputBlockState() { } public static BlockState getInputCauldron(SuperHeatingRecipe recipe) { - Block material = recipe.getHasCauldron().getFluidCauldron(); + Block material = HasCauldron.getDefaultCauldron(recipe.getHasCauldron().fluid()); return CauldronUtil.fullState(material); } public static BlockState getResultCauldron(SuperHeatingRecipe recipe) { - Block result = recipe.getHasCauldron().getTransformCauldron(); + List transforms = recipe.getHasCauldron().transforms(); + Block result = transforms.isEmpty() + ? HasCauldron.getDefaultCauldron(recipe.getHasCauldron().fluid()) + : HasCauldron.getDefaultCauldron(getDisplayedElement(transforms).getFluid()); if (recipe.isConsumeFluid()) { return CauldronUtil.getStateFromContentAndLevel(result, CauldronUtil.maxLevel(result) - 1); } else if (recipe.isProduceFluid()) { diff --git a/src/main/java/dev/dubhe/anvilcraft/client/markdown/recipe/anvil/MDTimeWarpRecipeComponent.java b/src/main/java/dev/dubhe/anvilcraft/client/markdown/recipe/anvil/MDTimeWarpRecipeComponent.java index 6aeecb1d85..71690137ac 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/markdown/recipe/anvil/MDTimeWarpRecipeComponent.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/markdown/recipe/anvil/MDTimeWarpRecipeComponent.java @@ -4,6 +4,7 @@ import dev.anvilcraft.lib.v2.util.predicate.ItemIngredientPredicate; import dev.anvilcraft.resource.ageratum.client.feat.markdown.MDRenderContext; import dev.dubhe.anvilcraft.init.block.ModBlocks; +import dev.dubhe.anvilcraft.recipe.anvil.predicate.block.HasCauldron; import dev.dubhe.anvilcraft.recipe.anvil.wrap.TimeWarpRecipe; import dev.dubhe.anvilcraft.util.AgeratumUtil; import dev.dubhe.anvilcraft.util.CauldronUtil; @@ -12,6 +13,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.fluids.FluidStack; import java.util.List; @@ -57,26 +59,30 @@ protected void renderRecipe(MDRenderContext context, float mouseX, float mouseY) Component text = Component.translatable( "gui.anvilcraft.category.time_warp.consume_fluid", recipe.getHasCauldron().consume(), - recipe.getHasCauldron().getFluidCauldron().getName() + HasCauldron.getDefaultCauldron(recipe.getHasCauldron().fluid()).getName() ); AgeratumUtil.renderText(graphics, text, INFO_X, INFO_Y); } else if (recipe.isProduceFluid()) { + FluidStack transform = getDisplayedElement(recipe.getHasCauldron().transforms()); Component text = Component.translatable( "gui.anvilcraft.category.time_warp.produce_fluid", - recipe.getHasCauldron().produce(), - recipe.getHasCauldron().getTransformCauldron().getName() + transform.getAmount(), + HasCauldron.getDefaultCauldron(transform.getFluid()).getName() ); AgeratumUtil.renderText(graphics, text, INFO_X, INFO_Y); } } public static BlockState getInputCauldron(TimeWarpRecipe recipe) { - Block material = recipe.getHasCauldron().getFluidCauldron(); + Block material = HasCauldron.getDefaultCauldron(recipe.getHasCauldron().fluid()); return CauldronUtil.fullState(material); } public static BlockState getResultCauldron(TimeWarpRecipe recipe) { - Block result = recipe.getHasCauldron().getTransformCauldron(); + List transforms = recipe.getHasCauldron().transforms(); + Block result = transforms.isEmpty() + ? HasCauldron.getDefaultCauldron(recipe.getHasCauldron().fluid()) + : HasCauldron.getDefaultCauldron(getDisplayedElement(transforms).getFluid()); if (recipe.isConsumeFluid()) { return CauldronUtil.getStateFromContentAndLevel(result, CauldronUtil.maxLevel(result) - 1); } else if (recipe.isProduceFluid()) { diff --git a/src/main/java/dev/dubhe/anvilcraft/data/recipe/ConcreteRecipeLoader.java b/src/main/java/dev/dubhe/anvilcraft/data/recipe/ConcreteRecipeLoader.java index 422485f101..59be046518 100644 --- a/src/main/java/dev/dubhe/anvilcraft/data/recipe/ConcreteRecipeLoader.java +++ b/src/main/java/dev/dubhe/anvilcraft/data/recipe/ConcreteRecipeLoader.java @@ -8,11 +8,13 @@ import dev.dubhe.anvilcraft.init.block.ModBlocks; import dev.dubhe.anvilcraft.recipe.anvil.wrap.SolidLiquidRecipe; import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.ItemTags; import net.minecraft.tags.TagKey; import net.minecraft.world.item.Item; import net.minecraft.world.item.Items; +import net.minecraft.world.level.material.Fluid; import net.neoforged.neoforge.common.Tags; import java.util.List; @@ -69,12 +71,16 @@ private static void initConcrete(RegistrumRecipeProvider provider) { * 生成水泥染色配方(铁砧加工),每种染料对应将任意颜色水泥炼药锅/鱼缸中的水泥染成对应颜色 */ private static void initCementStaining(RegistrumRecipeProvider provider) { - ResourceLocation cementTag = ResourceLocation.fromNamespaceAndPath("c", "cement"); + TagKey cementTag = TagKey.create( + Registries.FLUID, + ResourceLocation.fromNamespaceAndPath("c", "cement") + ); for (Color color : Color.values()) { ResourceLocation targetCement = AnvilCraft.of("%s_cement".formatted(color.getSerializedName())); SolidLiquidRecipe.builder() - .fluidTag(cementTag) - .transform(targetCement) + .cauldron(cementTag) + .consume(1000) + .transform(BuiltInRegistries.FLUID.get(targetCement), 1000) .requires(color.dyeItem()) .maxEfficiency(1) .save(provider, AnvilCraft.of("solid_liquid/cement_staining/%s".formatted(color.getSerializedName()))); diff --git a/src/main/java/dev/dubhe/anvilcraft/data/recipe/SolidLiquidRecipeLoader.java b/src/main/java/dev/dubhe/anvilcraft/data/recipe/SolidLiquidRecipeLoader.java index 9321149f27..adc09093af 100644 --- a/src/main/java/dev/dubhe/anvilcraft/data/recipe/SolidLiquidRecipeLoader.java +++ b/src/main/java/dev/dubhe/anvilcraft/data/recipe/SolidLiquidRecipeLoader.java @@ -5,18 +5,25 @@ import dev.dubhe.anvilcraft.block.state.Color; import dev.dubhe.anvilcraft.init.block.ModBlocks; import dev.dubhe.anvilcraft.init.block.ModFluids; +import dev.dubhe.anvilcraft.init.enchantment.ModEnchantments; +import dev.dubhe.anvilcraft.init.item.ModComponents; import dev.dubhe.anvilcraft.init.item.ModFoodItems; import dev.dubhe.anvilcraft.init.item.ModItemTags; import dev.dubhe.anvilcraft.init.item.ModItems; import dev.dubhe.anvilcraft.recipe.anvil.wrap.SolidLiquidRecipe; +import dev.dubhe.anvilcraft.util.FluidStackPredicate; import dev.dubhe.anvilcraft.util.VanillaConstants; +import net.minecraft.resources.ResourceKey; import net.minecraft.tags.TagKey; import net.minecraft.world.item.Item; import net.minecraft.world.item.Items; +import net.minecraft.world.item.enchantment.Enchantment; +import net.minecraft.world.item.enchantment.Enchantments; import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.neoforged.neoforge.common.NeoForgeMod; +import net.neoforged.neoforge.fluids.FluidStack; public class SolidLiquidRecipeLoader { public static void init(RegistrumRecipeProvider provider) { @@ -45,8 +52,7 @@ public static void init(RegistrumRecipeProvider provider) { SolidLiquidRecipe.builder() .cauldron(Blocks.WATER_CAULDRON) .consume(1000) - .transform(ModBlocks.CEMENT_CAULDRONS.get(Color.GRAY).get()) - .produce(1000) + .transform(ModBlocks.CEMENT_CAULDRONS.get(Color.GRAY).get(), 1000) .requires(ModItems.LIME_POWDER, 4) .requires(ModBlocks.CINERITE) .save(provider, AnvilCraft.of("solid_liquid/cement_cauldron")); @@ -65,36 +71,53 @@ public static void init(RegistrumRecipeProvider provider) { .save(provider); SolidLiquidRecipe.builder() - .cauldron(ModFluids.EXP_FLUID.getId()) + .cauldron(ModFluids.EXP_FLUID.get()) .consume(1000) .result(ModItems.EXP_GEM) .save(provider); SolidLiquidRecipe.builder() - .cauldron(ModFluids.EXP_FLUID.getId()) + .cauldron(ModFluids.EXP_FLUID.get()) .consume(2000) - .transform(ModFluids.LIQUID_ENCHANTMENT.getId()) - .produce(1) + .transform(ModFluids.LIQUID_ENCHANTMENT.get(), 1) .requires(Items.LAPIS_LAZULI, 3) .save(provider, AnvilCraft.of("solid_liquid/liquid_enchantment")); SolidLiquidRecipe.builder() - .cauldron(NeoForgeMod.MILK.getId()) + .cauldron(NeoForgeMod.MILK.get()) .consume(1000) .result(ModFoodItems.CREAM, 4) .save(provider, AnvilCraft.of("solid_liquid/cream_from_milk")); SolidLiquidRecipe.builder() - .cauldron(ModFluids.HONEY.getId()) + .cauldron(ModFluids.HONEY.get()) .consume(1000) .result(Items.HONEY_BLOCK) .save(provider, AnvilCraft.of("solid_liquid/honey_block")); SolidLiquidRecipe.builder() .cauldron(Blocks.WATER_CAULDRON) - .transform(ModBlocks.EXP_FLUID_CAULDRON.get()) + .consume(1000) + .transform(ModBlocks.EXP_FLUID_CAULDRON.get(), 1000) .requires(ModItems.EXP_GEM) .save(provider, AnvilCraft.of("solid_liquid/exp_fluid_cauldron")); + + SolidLiquidRecipeLoader.liquidEnchantment(provider, ModItems.ROYAL_STEEL_INGOT, 1, Enchantments.SILK_TOUCH); + SolidLiquidRecipeLoader.liquidEnchantment(provider, ModItems.FROST_METAL_INGOT, 1, ModEnchantments.DISINTEGRATION_KEY); + SolidLiquidRecipeLoader.liquidEnchantment(provider, ModItems.EMBER_METAL_INGOT, 16, ModEnchantments.SMELTING_KEY); + SolidLiquidRecipeLoader.liquidEnchantment(provider, ModItems.TRANSCENDIUM_INGOT, 128, Enchantments.FORTUNE, Enchantments.LOOTING); + SolidLiquidRecipeLoader.liquidEnchantment(provider, Items.EMERALD, 1, Enchantments.MENDING); + SolidLiquidRecipeLoader.liquidEnchantment(provider, ModItems.RUBY, 8, Enchantments.FIRE_PROTECTION); + SolidLiquidRecipeLoader.liquidEnchantment(provider, ModItems.SAPPHIRE, 2, Enchantments.FROST_WALKER); + SolidLiquidRecipeLoader.liquidEnchantment(provider, ModItems.TOPAZ, 1, Enchantments.CHANNELING); + SolidLiquidRecipeLoader.liquidEnchantment( + provider, + Items.AMETHYST_BLOCK, + 12, + ModEnchantments.FELLING_KEY, + ModEnchantments.HARVEST_KEY, + ModEnchantments.BEHEADING_KEY + ); } private static void solidLiquid(RegistrumRecipeProvider provider, ItemLike input, ItemLike result, int consume) { @@ -125,4 +148,33 @@ private static void solidLiquid(RegistrumRecipeProvider provider, TagKey i SolidLiquidRecipeLoader.solidLiquid(provider, input, result, 0); } + @SafeVarargs + @SuppressWarnings("SameParameterValue") + private static void liquidEnchantment( + RegistrumRecipeProvider provider, + ItemLike input, + int amount, + ResourceKey... enchantments + ) { + SolidLiquidRecipe.Builder builder = SolidLiquidRecipe.builder() + .cauldron( + FluidStackPredicate.builder() + .fluid(ModFluids.LIQUID_ENCHANTMENT) + .component(b -> b.expectNull(ModComponents.LIQUID_ENCHANTMENT)) + .build() + ) + .consume(amount) + .requires(input); + int each = amount / enchantments.length; + StringBuilder idBuilder = new StringBuilder(); + for (ResourceKey enchantment : enchantments) { + FluidStack stack = new FluidStack(ModFluids.LIQUID_ENCHANTMENT.get(), each); + stack.set(ModComponents.LIQUID_ENCHANTMENT, enchantment); + builder.transform(stack); + idBuilder.append(enchantment.location().getPath()); + idBuilder.append("_and_"); + } + String id = idBuilder.substring(0, idBuilder.length() - 5); + builder.save(provider, AnvilCraft.of("solid_liquid/" + id)); + } } diff --git a/src/main/java/dev/dubhe/anvilcraft/data/recipe/SqueezingRecipeLoader.java b/src/main/java/dev/dubhe/anvilcraft/data/recipe/SqueezingRecipeLoader.java index 284a692d7e..71d1c63bd2 100644 --- a/src/main/java/dev/dubhe/anvilcraft/data/recipe/SqueezingRecipeLoader.java +++ b/src/main/java/dev/dubhe/anvilcraft/data/recipe/SqueezingRecipeLoader.java @@ -19,8 +19,7 @@ public static void init(RegistrumRecipeProvider provider) { SqueezingRecipe.builder() .requires(Blocks.SCULK) .result(Blocks.AIR) - .transform(ModBlocks.EXP_FLUID_CAULDRON.get()) - .produce(250) + .transform(ModBlocks.EXP_FLUID_CAULDRON.get(), 250) .chance(0.1f) .noFrostAnvil() .save(provider, AnvilCraft.of("squeezing/exp_fluid_from_sculk")); @@ -28,8 +27,7 @@ public static void init(RegistrumRecipeProvider provider) { SqueezingRecipe.builder() .requires(Blocks.SCULK) .result(Blocks.AIR) - .transform(ModBlocks.EXP_FLUID_CAULDRON.get()) - .produce(250) + .transform(ModBlocks.EXP_FLUID_CAULDRON.get(), 250) .chance(0.4f) .frostAnvil() .save(provider, AnvilCraft.of("squeezing/exp_fluid_from_sculk_use_frost_anvil")); @@ -39,8 +37,7 @@ public static void squeezing(RegistrumRecipeProvider provider, Block requires, B SqueezingRecipe.builder() .requires(requires) .result(result) - .transform(cauldron) - .produce(produce) + .transform(cauldron, produce) .save(provider, AnvilCraft.of("squeezing/%s_from_%s".formatted(getName(cauldron), getName(requires)))); } } diff --git a/src/main/java/dev/dubhe/anvilcraft/data/recipe/SuperHeatingRecipeLoader.java b/src/main/java/dev/dubhe/anvilcraft/data/recipe/SuperHeatingRecipeLoader.java index f686270d0b..e436741c1d 100644 --- a/src/main/java/dev/dubhe/anvilcraft/data/recipe/SuperHeatingRecipeLoader.java +++ b/src/main/java/dev/dubhe/anvilcraft/data/recipe/SuperHeatingRecipeLoader.java @@ -18,14 +18,12 @@ public class SuperHeatingRecipeLoader { public static void init(RegistrumRecipeProvider provider) { SuperHeatingRecipe.builder() - .transform(Blocks.LAVA_CAULDRON) - .produce(1000) + .transform(Blocks.LAVA_CAULDRON, 1000) .requires(Items.COBBLESTONE, 4) .requires(ModItems.LIME_POWDER) .save(provider, AnvilCraft.of("super_heating/lava_from_cobblestone")); SuperHeatingRecipe.builder() - .transform(Blocks.LAVA_CAULDRON) - .produce(1000) + .transform(Blocks.LAVA_CAULDRON, 1000) .requires(Tags.Items.STONES, 4) .requires(ModItems.LIME_POWDER) .save(provider, AnvilCraft.of("super_heating/lava_from_stone")); @@ -120,20 +118,17 @@ public static void init(RegistrumRecipeProvider provider) { .save(provider); SuperHeatingRecipe.builder() - .transform(ModBlocks.MELT_GEM_CAULDRON.get()) - .produce(1000) + .transform(ModBlocks.MELT_GEM_CAULDRON.get(), 1000) .requires(ModItemTags.GEM_BLOCKS) .save(provider, AnvilCraft.of("super_heating/melt_gem_cauldron_from_gem_block")); SuperHeatingRecipe.builder() - .transform(ModBlocks.MELT_GEM_CAULDRON.get()) - .produce(1000) + .transform(ModBlocks.MELT_GEM_CAULDRON.get(), 1000) .requires(ModBlocks.CHROMATIC_STONE) .save(provider, AnvilCraft.of("super_heating/melt_gem_cauldron_from_chromatic_stone")); // Honey Block to Honey Cauldron SuperHeatingRecipe.builder() - .transform(ModBlocks.HONEY_CAULDRON.get()) - .produce(1000) + .transform(ModBlocks.HONEY_CAULDRON.get(), 1000) .requires(Items.HONEY_BLOCK) .save(provider, AnvilCraft.of("super_heating/honey_cauldron_from_honey_block")); diff --git a/src/main/java/dev/dubhe/anvilcraft/data/recipe/TimeWarpRecipeLoader.java b/src/main/java/dev/dubhe/anvilcraft/data/recipe/TimeWarpRecipeLoader.java index 1ba5ceaa58..d55f883726 100644 --- a/src/main/java/dev/dubhe/anvilcraft/data/recipe/TimeWarpRecipeLoader.java +++ b/src/main/java/dev/dubhe/anvilcraft/data/recipe/TimeWarpRecipeLoader.java @@ -299,8 +299,7 @@ private static void timeWarp( private static void timeWarpToOilCauldron(RegistrumRecipeProvider provider, ItemLike input, int inputCount) { TimeWarpRecipe.builder() .requires(input, inputCount) - .transform(ModBlocks.OIL_CAULDRON.get()) - .produce(250) + .transform(ModBlocks.OIL_CAULDRON.get(), 250) .save( provider, AnvilCraft.of("time_warp/oil_from_" + BuiltInRegistries.ITEM.getKey(input.asItem()).getPath()) @@ -311,8 +310,7 @@ private static void timeWarpToOilCauldron(RegistrumRecipeProvider provider, Item private static void timeWarpToOilCauldron(RegistrumRecipeProvider provider, TagKey input, int inputCount) { TimeWarpRecipe.builder() .requires(input, inputCount) - .transform(ModBlocks.OIL_CAULDRON.get()) - .produce(250) + .transform(ModBlocks.OIL_CAULDRON.get(), 250) .save( provider, AnvilCraft.of("time_warp/oil_from_" + input.location().getPath()) diff --git a/src/main/java/dev/dubhe/anvilcraft/init/item/ModComponents.java b/src/main/java/dev/dubhe/anvilcraft/init/item/ModComponents.java index 1be863f5f1..ca9ddf5bbf 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/item/ModComponents.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/item/ModComponents.java @@ -20,11 +20,11 @@ import dev.dubhe.anvilcraft.item.property.component.StructureData; import dev.dubhe.anvilcraft.item.property.component.StructureDiskData; import dev.dubhe.anvilcraft.item.property.component.amulet.IAmulet; -import net.minecraft.core.Holder; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.registries.Registries; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; +import net.minecraft.resources.ResourceKey; import net.minecraft.world.item.component.ChargedProjectiles; import net.minecraft.world.item.enchantment.Enchantment; import net.minecraft.world.item.enchantment.ItemEnchantments; @@ -145,9 +145,9 @@ public class ModComponents { b -> b.persistent(ItemEnchantments.CODEC).networkSynchronized(ItemEnchantments.STREAM_CODEC) ); - public static final DataComponentType> LIQUID_ENCHANTMENT = register( + public static final DataComponentType> LIQUID_ENCHANTMENT = register( "liquid_enchantment", - b -> b.persistent(Enchantment.CODEC).networkSynchronized(Enchantment.STREAM_CODEC) + b -> b.persistent(ResourceKey.codec(Registries.ENCHANTMENT)).networkSynchronized(ResourceKey.streamCodec(Registries.ENCHANTMENT)) ); public static final DataComponentType CAN_TAKE_OUT_AMMO = register( diff --git a/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/FluidMixingCategory.java b/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/FluidMixingCategory.java index 40fa94edae..36a4e2228d 100644 --- a/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/FluidMixingCategory.java +++ b/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/FluidMixingCategory.java @@ -13,13 +13,13 @@ import dev.dubhe.anvilcraft.integration.jei.AnvilCraftJeiPlugin; import dev.dubhe.anvilcraft.integration.jei.recipe.ComplexFluidJeiRecipe; import dev.dubhe.anvilcraft.integration.jei.recipe.LiquidEnchantmentJeiRecipeUtil; +import dev.dubhe.anvilcraft.integration.jei.util.JeiFluidUtil; import dev.dubhe.anvilcraft.integration.jei.util.JeiItemUtil; import dev.dubhe.anvilcraft.integration.jei.util.JeiRecipeUtil; import dev.dubhe.anvilcraft.integration.jei.util.JeiRenderHelper; import dev.dubhe.anvilcraft.recipe.FluidMixingRecipe; import dev.dubhe.anvilcraft.recipe.anvil.wrap.SolidLiquidRecipe; import mezz.jei.api.gui.ITickTimer; -import mezz.jei.api.gui.builder.IIngredientAcceptor; import mezz.jei.api.gui.builder.IRecipeLayoutBuilder; import mezz.jei.api.gui.builder.IRecipeSlotBuilder; import mezz.jei.api.gui.drawable.IDrawable; @@ -34,12 +34,9 @@ import net.minecraft.ChatFormatting; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.network.chat.Component; -import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; import net.minecraft.world.item.crafting.RecipeHolder; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.material.Fluid; import net.neoforged.neoforge.fluids.FluidStack; import net.neoforged.neoforge.fluids.crafting.SizedFluidIngredient; import org.jetbrains.annotations.Nullable; @@ -119,24 +116,20 @@ public void setRecipe( private static void setFluidMixingRecipe(IRecipeLayoutBuilder builder, FluidMixingRecipe recipe) { List ingredients = recipe.getFluidIngredients(); - IIngredientAcceptor bucketIngredients = builder.addInvisibleIngredients(RecipeIngredientRole.INPUT); for (int index = 0; index < ingredients.size(); index++) { SizedFluidIngredient ingredient = ingredients.get(index); SlotPosition position = inputPosition(ingredients.size(), index); - IRecipeSlotBuilder recipeSlot = builder.addSlot( + JeiFluidUtil.addFluidSlot( + builder, RecipeIngredientRole.INPUT, position.x() + 1, - position.y() + 1 - ).setFluidRenderer(ingredient.amount(), false, 16, 16); - for (FluidStack fluid : ingredient.getFluids()) { - recipeSlot.addFluidStack( - fluid.getFluid(), - ingredient.amount(), - fluid.getComponentsPatch() - ); - Item bucket = fluid.getFluid().getBucket(); - if (bucket != Items.AIR) bucketIngredients.addItemStack(new ItemStack(bucket)); - } + position.y() + 1, + 16, + 16, + ingredient.amount(), + false, + List.of(ingredient.getFluids()) + ); } List itemResults = recipe.getItemResults(); @@ -147,15 +140,20 @@ private static void setFluidMixingRecipe(IRecipeLayoutBuilder builder, FluidMixi builder.addSlot(RecipeIngredientRole.OUTPUT, position.x() + 1, position.y() + 1) .addItemStack(itemResults.get(index).copy()); } - IIngredientAcceptor outputBuckets = builder.addInvisibleIngredients(RecipeIngredientRole.OUTPUT); for (int index = 0; index < fluidResults.size(); index++) { FluidStack fluid = fluidResults.get(index); SlotPosition position = fluidOutputPosition(fluidResults.size(), index, splitOutputColumns); - builder.addSlot(RecipeIngredientRole.OUTPUT, position.x() + 1, position.y() + 1) - .setFluidRenderer(fluid.getAmount(), true, 16, 16) - .addFluidStack(fluid.getFluid(), fluid.getAmount(), fluid.getComponentsPatch()); - Item bucket = fluid.getFluid().getBucket(); - if (bucket != Items.AIR) outputBuckets.addItemStack(new ItemStack(bucket)); + JeiFluidUtil.addFluidSlot( + builder, + RecipeIngredientRole.OUTPUT, + position.x() + 1, + position.y() + 1, + 16, + 16, + fluid.getAmount(), + true, + List.of(fluid) + ); } } @@ -186,10 +184,10 @@ private static void setComplexRecipe(IRecipeLayoutBuilder builder, ComplexFluidJ } if (recipe.isHeaterRequired()) { builder.addSlot( - RecipeIngredientRole.CATALYST, - HEATER_POSITION.x() + 1, - HEATER_POSITION.y() + 1 - ).addItemStacks(List.of(ModBlocks.HEATER.asStack(), ModBlocks.BURNING_HEATER.asStack())) + RecipeIngredientRole.CATALYST, + HEATER_POSITION.x() + 1, + HEATER_POSITION.y() + 1 + ).addItemStacks(List.of(ModBlocks.HEATER.asStack(), ModBlocks.BURNING_HEATER.asStack())) .addRichTooltipCallback((slotView, tooltip) -> tooltip.add(HEATER_ACTIVE)); } @@ -231,20 +229,17 @@ private static IRecipeSlotBuilder addFluidSlot( boolean showCapacity ) { int amount = fluids.getFirst().getAmount(); - IRecipeSlotBuilder slot = builder.addSlot(role, position.x() + 1, position.y() + 1) - .setFluidRenderer(amount, showCapacity, 16, 16); - for (FluidStack fluid : fluids) { - slot.addFluidStack(fluid.getFluid(), fluid.getAmount(), fluid.getComponentsPatch()); - } - List buckets = fluids.stream() - .map(FluidStack::getFluid) - .map(Fluid::getBucket) - .filter(bucket -> bucket != Items.AIR) - .distinct() - .map(ItemStack::new) - .toList(); - if (!buckets.isEmpty()) builder.addInvisibleIngredients(role).addItemStacks(buckets); - return slot; + return JeiFluidUtil.addFluidSlot( + builder, + role, + position.x() + 1, + position.y() + 1, + 16, + 16, + amount, + showCapacity, + fluids + ); } @Override @@ -319,8 +314,8 @@ private static SlotPosition inputPosition(int count, int index) { if (count == 2) return new SlotPosition(6 + index * 19, 23); if (count == 3) { return index == 0 - ? new SlotPosition(15, 14) - : new SlotPosition(6 + (index - 1) * 19, 33); + ? new SlotPosition(15, 14) + : new SlotPosition(6 + (index - 1) * 19, 33); } return new SlotPosition(6 + index % 2 * 19, 14 + index / 2 * 19); } diff --git a/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/anvil/SqueezingCategory.java b/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/anvil/SqueezingCategory.java index 7506c2e53b..0fc91000c6 100644 --- a/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/anvil/SqueezingCategory.java +++ b/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/anvil/SqueezingCategory.java @@ -10,7 +10,7 @@ import dev.dubhe.anvilcraft.integration.jei.util.JeiFluidUtil; import dev.dubhe.anvilcraft.integration.jei.util.JeiRecipeUtil; import dev.dubhe.anvilcraft.integration.jei.util.JeiRenderHelper; -import dev.dubhe.anvilcraft.recipe.anvil.predicate.block.HasCauldron; +import dev.dubhe.anvilcraft.integration.jei.util.JeiSlotUtil; import dev.dubhe.anvilcraft.recipe.anvil.wrap.SqueezingRecipe; import dev.dubhe.anvilcraft.recipe.component.HasCauldronSimple; import dev.dubhe.anvilcraft.util.TooltipUtil; @@ -116,7 +116,7 @@ public void setRecipe( builder.addInvisibleIngredients(RecipeIngredientRole.OUTPUT) .addItemStack(output.stack().copyWithCount(output.getMaxCount())); } - JeiFluidUtil.addOutputSlot(builder, OUTPUT_FLUID, FLUID_X, FLUID_Y, 16, 16, recipe.getHasCauldron() + JeiFluidUtil.addOutputSlots(builder, OUTPUT_FLUID, FLUID_X, FLUID_Y, 16, 16, recipe.getHasCauldron() ); } @@ -184,9 +184,15 @@ public void draw( arrowDefault.draw(guiGraphics, 73, 28); HasCauldronSimple cauldronFluid = recipe.getHasCauldron(); - if (HasCauldron.isNotEmpty(cauldronFluid.transform())) { + if (!cauldronFluid.transforms().isEmpty()) { IDrawable fluidSlot = cauldronFluid.chance() < 1.0f ? slotProbability : slotDefault; - fluidSlot.draw(guiGraphics, FLUID_X - 1, FLUID_Y - 1); + JeiSlotUtil.drawSlots( + guiGraphics, + fluidSlot, + cauldronFluid.transforms().size(), + FLUID_X - 1, + FLUID_Y - 1 + ); } List result = recipe.getResultBlocks(); diff --git a/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/anvil/liquid/AbstractLiquidCategory.java b/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/anvil/liquid/AbstractLiquidCategory.java index 9bbdf3d585..aea5208ed9 100644 --- a/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/anvil/liquid/AbstractLiquidCategory.java +++ b/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/anvil/liquid/AbstractLiquidCategory.java @@ -1,13 +1,11 @@ package dev.dubhe.anvilcraft.integration.jei.category.anvil.liquid; import dev.dubhe.anvilcraft.client.support.RenderSupport; -import dev.dubhe.anvilcraft.integration.jei.AnvilCraftJeiPlugin; import dev.dubhe.anvilcraft.integration.jei.util.JeiFluidUtil; import dev.dubhe.anvilcraft.integration.jei.util.JeiItemUtil; import dev.dubhe.anvilcraft.integration.jei.util.JeiRecipeUtil; import dev.dubhe.anvilcraft.integration.jei.util.JeiRenderHelper; import dev.dubhe.anvilcraft.integration.jei.util.JeiSlotUtil; -import dev.dubhe.anvilcraft.recipe.anvil.predicate.block.HasCauldron; import dev.dubhe.anvilcraft.recipe.anvil.wrap.AbstractProcessRecipe; import dev.dubhe.anvilcraft.recipe.component.HasCauldronSimple; import mezz.jei.api.gui.ITickTimer; @@ -17,9 +15,7 @@ import mezz.jei.api.gui.widgets.IRecipeExtrasBuilder; import mezz.jei.api.helpers.IGuiHelper; import mezz.jei.api.recipe.IFocusGroup; -import mezz.jei.api.recipe.RecipeType; import mezz.jei.api.recipe.category.IRecipeCategory; -import mezz.jei.api.registration.IRecipeCatalystRegistration; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.network.chat.Component; @@ -83,8 +79,8 @@ public void setRecipe( HasCauldronSimple cauldron = recipe.getHasCauldron(); final boolean hasInputItems = !recipe.getInputItems().isEmpty(); final boolean hasOutputItems = !recipe.getResultItems().isEmpty(); - final boolean hasInputFluid = cauldron.fluidTag() != null || HasCauldron.isNotEmpty(cauldron.fluid()); - final boolean hasOutputFluid = HasCauldron.isNotEmpty(cauldron.transform()); + final boolean hasInputFluid = cauldron.hasFluid(); + final boolean hasOutputFluid = !cauldron.transforms().isEmpty(); final boolean inputMixed = hasInputItems && hasInputFluid; final boolean outputMixed = hasOutputItems && hasOutputFluid; @@ -115,9 +111,9 @@ public void setRecipe( } if (hasOutputFluid) { if (outputMixed) { - JeiFluidUtil.addFluidOutputSlot(builder, OUTPUT_FLUID, 16, 16, cauldron); + JeiFluidUtil.addFluidOutputSlots(builder, OUTPUT_FLUID, 16, 16, cauldron); } else { - JeiFluidUtil.addDefaultOutputSlot(builder, OUTPUT_FLUID, 16, 16, cauldron); + JeiFluidUtil.addDefaultOutputSlots(builder, OUTPUT_FLUID, 16, 16, cauldron); } } } @@ -150,8 +146,8 @@ public void draw( final boolean hasInputItems = !recipe.getInputItems().isEmpty(); final boolean hasOutputItems = !recipe.getResultItems().isEmpty(); - final boolean hasInputFluid = cauldron.fluidTag() != null || HasCauldron.isNotEmpty(cauldron.fluid()); - final boolean hasOutputFluid = HasCauldron.isNotEmpty(cauldron.transform()); + final boolean hasInputFluid = cauldron.hasFluid(); + final boolean hasOutputFluid = !cauldron.transforms().isEmpty(); final boolean inputMixed = hasInputItems && hasInputFluid; final boolean outputMixed = hasOutputItems && hasOutputFluid; @@ -184,10 +180,11 @@ public void draw( } // 输出流体 if (hasOutputFluid) { + IDrawable fluidSlot = cauldron.chance() < 1.0f ? slotProbability : slotDefault; if (outputMixed) { - JeiSlotUtil.drawFluidOutputSlots(guiGraphics, slotDefault, 1); + JeiSlotUtil.drawFluidOutputSlots(guiGraphics, fluidSlot, cauldron.transforms().size()); } else { - JeiSlotUtil.drawDefaultOutputSlots(guiGraphics, slotDefault, 1); + JeiSlotUtil.drawDefaultOutputSlots(guiGraphics, fluidSlot, cauldron.transforms().size()); } } diff --git a/src/main/java/dev/dubhe/anvilcraft/integration/jei/recipe/ComplexFluidJeiRecipe.java b/src/main/java/dev/dubhe/anvilcraft/integration/jei/recipe/ComplexFluidJeiRecipe.java index 5cc7c2b5db..da074bbc51 100644 --- a/src/main/java/dev/dubhe/anvilcraft/integration/jei/recipe/ComplexFluidJeiRecipe.java +++ b/src/main/java/dev/dubhe/anvilcraft/integration/jei/recipe/ComplexFluidJeiRecipe.java @@ -6,34 +6,32 @@ import dev.dubhe.anvilcraft.init.block.ModFluids; import dev.dubhe.anvilcraft.init.item.ModItemTags; import dev.dubhe.anvilcraft.init.item.ModItems; +import dev.dubhe.anvilcraft.integration.jei.util.JeiFluidUtil; import dev.dubhe.anvilcraft.recipe.FluidMixingRecipe; -import dev.dubhe.anvilcraft.recipe.anvil.predicate.block.HasCauldron; import dev.dubhe.anvilcraft.recipe.anvil.wrap.SolidLiquidRecipe; import dev.dubhe.anvilcraft.recipe.component.HasCauldronSimple; -import net.minecraft.core.Holder; -import net.minecraft.core.HolderSet; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; +import lombok.Getter; +import net.minecraft.resources.ResourceKey; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.enchantment.Enchantment; -import net.minecraft.world.level.material.Fluid; import net.neoforged.neoforge.fluids.FluidStack; import net.neoforged.neoforge.fluids.FluidType; import net.neoforged.neoforge.fluids.crafting.SizedFluidIngredient; -import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; -/** JEI 专用的复杂流体反应配方,用于展示同时包含物品和流体的大锅反应。 */ +/** + * JEI 专用的复杂流体反应配方,用于展示同时包含物品和流体的大锅反应。 + */ public final class ComplexFluidJeiRecipe extends FluidMixingRecipe { + @Getter private final List inputItems; private final List resultItems; private final List> displayFluidInputs; private final List> displayFluidResults; + @Getter private final boolean heaterRequired; private final boolean linkFluidVariants; @@ -59,35 +57,35 @@ private ComplexFluidJeiRecipe( this.linkFluidVariants = linkFluidVariants; } + @SuppressWarnings("BooleanMethodIsAlwaysInverted") public static boolean isComplex(SolidLiquidRecipe recipe) { HasCauldronSimple cauldron = recipe.getHasCauldron(); - return cauldron.consume() > FluidType.BUCKET_VOLUME + return cauldron.transforms().size() > 1 + || cauldron.consume() > FluidType.BUCKET_VOLUME || cauldron.produce() > FluidType.BUCKET_VOLUME; } public static ComplexFluidJeiRecipe fromSolidLiquid(SolidLiquidRecipe recipe) { HasCauldronSimple cauldron = recipe.getHasCauldron(); - List inputs = createFluidStacks( + List inputs = JeiFluidUtil.getDisplayFluids( cauldron.fluid(), - cauldron.fluidTag(), displayAmount(cauldron.consume()) ); - List results = createFluidStacks( - cauldron.transform(), - null, - displayAmount(cauldron.produce()) - ); + List> results = cauldron.transforms().stream() + .map(fluid -> JeiFluidUtil.getDisplayFluids(fluid, displayAmount(fluid.getAmount()))) + .filter(group -> !group.isEmpty()) + .toList(); return new ComplexFluidJeiRecipe( recipe.getInputItems(), recipe.getResultItems(), asGroup(inputs), - asGroup(results), + results, false, false ); } - public static ComplexFluidJeiRecipe assimilation(List> enchantments) { + public static ComplexFluidJeiRecipe assimilation(List> enchantments) { List enchantedInputs = LiquidEnchantmentJeiRecipeUtil.createFluidStacks(enchantments, 8); List enchantedResults = LiquidEnchantmentJeiRecipeUtil.createFluidStacks(enchantments, 9); return new ComplexFluidJeiRecipe( @@ -103,7 +101,7 @@ public static ComplexFluidJeiRecipe assimilation(List> enchantments) { + public static ComplexFluidJeiRecipe cleanse(List> enchantments) { return new ComplexFluidJeiRecipe( List.of(ItemIngredientPredicate.of(ModItemTags.SILVER_NUGGETS).build()), List.of(), @@ -114,7 +112,7 @@ public static ComplexFluidJeiRecipe cleanse(List> ); } - public static ComplexFluidJeiRecipe curseGoldIngot(List> curses) { + public static ComplexFluidJeiRecipe curseGoldIngot(List> curses) { return new ComplexFluidJeiRecipe( List.of(ItemIngredientPredicate.of(Items.GOLD_INGOT).withCount(16).build()), List.of(ChanceItemStack.of(ModItems.CURSED_GOLD_INGOT, 16)), @@ -125,7 +123,7 @@ public static ComplexFluidJeiRecipe curseGoldIngot(List> curses) { + public static ComplexFluidJeiRecipe curseGoldBlock(List> curses) { return new ComplexFluidJeiRecipe( List.of(ItemIngredientPredicate.of(Items.GOLD_BLOCK).withCount(16).build()), List.of(ChanceItemStack.of(ModBlocks.CURSED_GOLD_BLOCK, 16)), @@ -136,10 +134,6 @@ public static ComplexFluidJeiRecipe curseGoldBlock(List getInputItems() { - return this.inputItems; - } - public List getDisplayItemResults() { return this.resultItems; } @@ -160,10 +154,6 @@ public int getDisplayFluidResultCount() { return this.displayFluidResults.size(); } - public boolean isHeaterRequired() { - return this.heaterRequired; - } - public boolean shouldLinkFluidVariants() { return this.linkFluidVariants; } @@ -203,28 +193,4 @@ private static int displayAmount(int amount) { return amount > 0 ? amount : FluidType.BUCKET_VOLUME; } - private static List createFluidStacks( - ResourceLocation fluidId, - @Nullable ResourceLocation fluidTag, - int amount - ) { - if (fluidTag != null) { - TagKey tag = TagKey.create(Registries.FLUID, fluidTag); - return BuiltInRegistries.FLUID.getTag(tag) - .stream() - .flatMap(HolderSet.ListBacked::stream) - .map(Holder::value) - .filter(fluid -> fluid.defaultFluidState().isSource()) - .distinct() - .map(fluid -> new FluidStack(fluid, amount)) - .toList(); - } - if (!HasCauldron.isNotEmpty(fluidId)) return List.of(); - return BuiltInRegistries.FLUID.getHolder(fluidId) - .stream() - .map(Holder::value) - .filter(fluid -> fluid.defaultFluidState().isSource()) - .map(fluid -> new FluidStack(fluid, amount)) - .toList(); - } } diff --git a/src/main/java/dev/dubhe/anvilcraft/integration/jei/recipe/LiquidEnchantmentJeiRecipeUtil.java b/src/main/java/dev/dubhe/anvilcraft/integration/jei/recipe/LiquidEnchantmentJeiRecipeUtil.java index 1214e39625..0bec75b4e6 100644 --- a/src/main/java/dev/dubhe/anvilcraft/integration/jei/recipe/LiquidEnchantmentJeiRecipeUtil.java +++ b/src/main/java/dev/dubhe/anvilcraft/integration/jei/recipe/LiquidEnchantmentJeiRecipeUtil.java @@ -5,6 +5,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.core.Holder; import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; import net.minecraft.tags.EnchantmentTags; import net.minecraft.world.item.enchantment.Enchantment; import net.neoforged.neoforge.fluids.FluidStack; @@ -12,28 +13,28 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Objects; public final class LiquidEnchantmentJeiRecipeUtil { private LiquidEnchantmentJeiRecipeUtil() { } - public static List> getEnchantments(boolean cursesOnly) { + public static List> getEnchantments(boolean cursesOnly) { var connection = Minecraft.getInstance().getConnection(); if (connection == null) return List.of(); return connection.registryAccess() .lookupOrThrow(Registries.ENCHANTMENT) .listElements() .filter(enchantment -> !cursesOnly || enchantment.is(EnchantmentTags.CURSE)) - .sorted(Comparator.comparing(enchantment -> enchantment.key().location().toString())) + .map(Holder.Reference::getKey) + .map(Objects::requireNonNull) + .sorted(Comparator.comparing(key -> key.location().toString())) .toList(); } - public static List createFluidStacks( - List> enchantments, - int amount - ) { + public static List createFluidStacks(List> enchantments, int amount) { List fluids = new ArrayList<>(enchantments.size()); - for (Holder enchantment : enchantments) { + for (ResourceKey enchantment : enchantments) { FluidStack fluid = new FluidStack(ModFluids.LIQUID_ENCHANTMENT.get(), amount); fluid.set(ModComponents.LIQUID_ENCHANTMENT, enchantment); fluids.add(fluid); diff --git a/src/main/java/dev/dubhe/anvilcraft/integration/jei/util/JeiFluidUtil.java b/src/main/java/dev/dubhe/anvilcraft/integration/jei/util/JeiFluidUtil.java index 9767fd1e45..93f8a45870 100644 --- a/src/main/java/dev/dubhe/anvilcraft/integration/jei/util/JeiFluidUtil.java +++ b/src/main/java/dev/dubhe/anvilcraft/integration/jei/util/JeiFluidUtil.java @@ -1,23 +1,20 @@ package dev.dubhe.anvilcraft.integration.jei.util; -import dev.dubhe.anvilcraft.recipe.anvil.predicate.block.HasCauldron; import dev.dubhe.anvilcraft.recipe.component.HasCauldronSimple; +import dev.dubhe.anvilcraft.util.DataComponentPredicate; +import dev.dubhe.anvilcraft.util.FluidStackPredicate; import mezz.jei.api.gui.builder.IRecipeLayoutBuilder; import mezz.jei.api.gui.builder.IRecipeSlotBuilder; import mezz.jei.api.gui.widgets.IRecipeExtrasBuilder; import mezz.jei.api.recipe.RecipeIngredientRole; -import net.minecraft.core.Holder; import net.minecraft.core.HolderSet; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; +import net.minecraft.core.component.DataComponentPatch; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.storage.loot.providers.number.ConstantValue; +import net.neoforged.neoforge.fluids.FluidStack; import net.neoforged.neoforge.fluids.FluidType; -import org.jetbrains.annotations.Nullable; import java.util.List; @@ -55,25 +52,25 @@ public static void addInputSlot( ) { addSlot( builder, RecipeIngredientRole.INPUT, name, x, y, width, height, - getFluids(cauldron.fluid(), cauldron.fluidTag()), cauldron.consume(), 1.0f + getDisplayFluids(cauldron.fluid(), cauldron.consume()), false, 1.0f ); } /** * 存在物品时流体位置向下偏移 */ - public static void addFluidOutputSlot(IRecipeLayoutBuilder builder, String name, int width, int height, HasCauldronSimple cauldron) { - addOutputSlot(builder, name, JeiSlotUtil.OUTPUT_X, JeiSlotUtil.FLUID_Y, width, height, cauldron); + public static void addFluidOutputSlots(IRecipeLayoutBuilder builder, String name, int width, int height, HasCauldronSimple cauldron) { + addOutputSlots(builder, name, JeiSlotUtil.OUTPUT_X, JeiSlotUtil.FLUID_Y, width, height, cauldron); } /** * 默认的居中位置 */ - public static void addDefaultOutputSlot(IRecipeLayoutBuilder builder, String name, int width, int height, HasCauldronSimple cauldron) { - addOutputSlot(builder, name, JeiSlotUtil.OUTPUT_X, JeiSlotUtil.DEFAULT_Y, width, height, cauldron); + public static void addDefaultOutputSlots(IRecipeLayoutBuilder builder, String name, int width, int height, HasCauldronSimple cauldron) { + addOutputSlots(builder, name, JeiSlotUtil.OUTPUT_X, JeiSlotUtil.DEFAULT_Y, width, height, cauldron); } - public static void addOutputSlot( + public static void addOutputSlots( IRecipeLayoutBuilder builder, String name, int x, @@ -82,10 +79,27 @@ public static void addOutputSlot( int height, HasCauldronSimple cauldron ) { - addSlot( - builder, RecipeIngredientRole.OUTPUT, name, x, y, width, height, - getFluids(cauldron.transform(), null), cauldron.produce(), cauldron.chance() - ); + List transforms = cauldron.transforms(); + if (transforms.isEmpty()) return; + int cols = (int) Math.ceil(Math.sqrt(transforms.size())); + int rows = Math.ceilDiv(transforms.size(), cols); + int startX = x - (cols - 1) * JeiSlotUtil.OFFSET / 2; + int startY = y - (rows - 1) * JeiSlotUtil.OFFSET / 2; + for (int index = 0; index < transforms.size(); index++) { + FluidStack transform = transforms.get(index); + addSlot( + builder, + RecipeIngredientRole.OUTPUT, + transforms.size() == 1 ? name : name + "/" + index, + startX + index % cols * JeiSlotUtil.OFFSET, + startY + index / cols * JeiSlotUtil.OFFSET, + width, + height, + getDisplayFluids(transform, transform.getAmount()), + true, + cauldron.chance() + ); + } } private static void addSlot( @@ -96,29 +110,41 @@ private static void addSlot( int y, int width, int height, - List fluids, - int amount, + List fluids, + boolean showCapacity, float chance ) { if (fluids.isEmpty()) return; - long displayAmount = amount > 0 ? amount : FluidType.BUCKET_VOLUME; - IRecipeSlotBuilder slot = builder.addSlot(role, x, y) - .setSlotName(SLOT_PREFIX + name) - .setFluidRenderer(displayAmount, false, width, height); - fluids.forEach(fluid -> slot.addFluidStack(fluid, displayAmount)); + IRecipeSlotBuilder slot = addFluidSlot( + builder, role, x, y, width, height, fluids.getFirst().getAmount(), showCapacity, fluids + ) + .setSlotName(SLOT_PREFIX + name); if (chance < 1.0f) { slot.addRichTooltipCallback((slotView, tooltip) -> - tooltip.addAll(JeiRecipeUtil.getTooltips(ConstantValue.exactly(chance)))); + tooltip.addAll(JeiRecipeUtil.getTooltips(ConstantValue.exactly(chance)))); } - addBucketIngredients(builder, role, fluids); } - private static void addBucketIngredients( + public static IRecipeSlotBuilder addFluidSlot( IRecipeLayoutBuilder builder, RecipeIngredientRole role, - List fluids + int x, + int y, + int width, + int height, + long capacity, + boolean showCapacity, + List fluids ) { + IRecipeSlotBuilder slot = builder.addSlot(role, x, y) + .setFluidRenderer(capacity, showCapacity, width, height); + fluids.forEach(fluid -> slot.addFluidStack( + fluid.getFluid(), + fluid.getAmount(), + fluid.getComponentsPatch() + )); List buckets = fluids.stream() + .map(FluidStack::getFluid) .map(Fluid::getBucket) .filter(bucket -> bucket != Items.AIR) .distinct() @@ -127,27 +153,31 @@ private static void addBucketIngredients( if (!buckets.isEmpty()) { builder.addInvisibleIngredients(role).addItemStacks(buckets); } + return slot; } - private static List getFluids(ResourceLocation fluidId, @Nullable ResourceLocation fluidTag) { - if (fluidTag != null) { - TagKey tag = TagKey.create(Registries.FLUID, fluidTag); - return BuiltInRegistries.FLUID.getTag(tag) - .stream() - .flatMap(HolderSet.ListBacked::stream) - .map(Holder::value) - .filter(fluid -> fluid.defaultFluidState().isSource()) - .distinct() - .toList(); - } - if (!HasCauldron.isNotEmpty(fluidId)) return List.of(); - return BuiltInRegistries.FLUID.getHolder(fluidId) - .stream() - .map(Holder::value) - .filter(fluid -> fluid.defaultFluidState().isSource()) + public static List getDisplayFluids(FluidStackPredicate predicate, int amount) { + int displayAmount = amount > 0 ? amount : FluidType.BUCKET_VOLUME; + DataComponentPatch components = predicate.isNegate() + ? DataComponentPatch.EMPTY + : predicate.component() + .filter(component -> !component.isNegate()) + .map(DataComponentPredicate::patch) + .orElse(DataComponentPatch.EMPTY); + return predicate.fluids().stream() + .flatMap(HolderSet::stream) + .filter(holder -> holder.value().defaultFluidState().isSource()) + .distinct() + .map(holder -> new FluidStack(holder, displayAmount, components)) .toList(); } + public static List getDisplayFluids(FluidStack fluid, int amount) { + if (!fluid.getFluid().defaultFluidState().isSource()) return List.of(); + int displayAmount = amount > 0 ? amount : FluidType.BUCKET_VOLUME; + return List.of(fluid.copyWithAmount(displayAmount)); + } + public static void suppressHoverOverlays(IRecipeExtrasBuilder builder) { JeiBlockIngredientUtil.suppressHoverOverlays(builder); } diff --git a/src/main/java/dev/dubhe/anvilcraft/inventory/TranscendenceGrindstoneMenu.java b/src/main/java/dev/dubhe/anvilcraft/inventory/TranscendenceGrindstoneMenu.java index 9ea2e4cec3..92b1525b6e 100644 --- a/src/main/java/dev/dubhe/anvilcraft/inventory/TranscendenceGrindstoneMenu.java +++ b/src/main/java/dev/dubhe/anvilcraft/inventory/TranscendenceGrindstoneMenu.java @@ -236,6 +236,7 @@ public boolean isGoldMode() { return this.getMode() == Mode.GOLD; } + @SuppressWarnings("BooleanMethodIsAlwaysInverted") public boolean isTransferMode() { Mode mode = this.getMode(); return mode == Mode.BOOK || mode == Mode.ITEM; @@ -362,7 +363,7 @@ private ItemStack createTankResult() { if (amount <= 0 || amount > capacity - totalAmount) return ItemStack.EMPTY; FluidStack fluid = new FluidStack(ModFluids.LIQUID_ENCHANTMENT.get(), (int) amount); - fluid.set(ModComponents.LIQUID_ENCHANTMENT, data.enchantment()); + fluid.set(ModComponents.LIQUID_ENCHANTMENT, data.enchantment().getKey()); fluids.add(fluid); totalAmount += amount; } diff --git a/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/builder/ExtendInWorldRecipeBuilder.java b/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/builder/ExtendInWorldRecipeBuilder.java index c53aec4471..197088ee7b 100644 --- a/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/builder/ExtendInWorldRecipeBuilder.java +++ b/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/builder/ExtendInWorldRecipeBuilder.java @@ -9,7 +9,6 @@ import dev.dubhe.anvilcraft.recipe.anvil.predicate.block.HasAnvil; import dev.dubhe.anvilcraft.recipe.anvil.predicate.block.HasCauldron; import lombok.EqualsAndHashCode; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; import net.minecraft.world.phys.Vec3; @@ -119,77 +118,6 @@ public ExtendInWorldRecipeBuilder hasCauldron(double x, double y, double z) { return this.with(HasCauldron.builder().empty().offset(new Vec3(x, y, z)).build()); } - /** - * 添加炼药锅谓词 - * - * @param fluid 液体ID - * @return 当前构建器实例 - */ - public ExtendInWorldRecipeBuilder hasCauldron(ResourceLocation fluid) { - return this.with(HasCauldron.builder().fluid(fluid).offset(this.offset).build()); - } - - /** - * 添加炼药锅谓词 - * - * @param offset 偏移向量 - * @param fluid 液体ID - * @return 当前构建器实例 - */ - public ExtendInWorldRecipeBuilder hasCauldron(Vec3 offset, ResourceLocation fluid) { - return this.with(HasCauldron.builder().fluid(fluid).offset(offset).build()); - } - - /** - * 添加炼药锅谓词 - * - * @param x X轴偏移量 - * @param y Y轴偏移量 - * @param z Z轴偏移量 - * @param fluid 液体ID - * @return 当前构建器实例 - */ - public ExtendInWorldRecipeBuilder hasCauldron(double x, double y, double z, ResourceLocation fluid) { - return this.with(HasCauldron.builder().fluid(fluid).offset(new Vec3(x, y, z)).build()); - } - - /** - * 添加炼药锅谓词 - * - * @param fluid 液体ID - * @param consume 消耗量 - * @return 当前构建器实例 - */ - public ExtendInWorldRecipeBuilder hasCauldron(ResourceLocation fluid, int consume) { - return this.with(HasCauldron.builder().fluid(fluid).offset(this.offset).consume(consume).build()); - } - - /** - * 添加炼药锅谓词 - * - * @param offset 偏移向量 - * @param fluid 液体ID - * @param consume 消耗量 - * @return 当前构建器实例 - */ - public ExtendInWorldRecipeBuilder hasCauldron(Vec3 offset, ResourceLocation fluid, int consume) { - return this.with(HasCauldron.builder().fluid(fluid).offset(offset).consume(consume).build()); - } - - /** - * 添加炼药锅谓词 - * - * @param x X轴偏移量 - * @param y Y轴偏移量 - * @param z Z轴偏移量 - * @param fluid 液体ID - * @param consume 消耗量 - * @return 当前构建器实例 - */ - public ExtendInWorldRecipeBuilder hasCauldron(double x, double y, double z, ResourceLocation fluid, int consume) { - return this.with(HasCauldron.builder().fluid(fluid).offset(new Vec3(x, y, z)).consume(consume).build()); - } - /** * 添加炼药锅谓词 * diff --git a/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/predicate/block/HasCauldron.java b/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/predicate/block/HasCauldron.java index 04fcde611a..72914b5540 100644 --- a/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/predicate/block/HasCauldron.java +++ b/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/predicate/block/HasCauldron.java @@ -1,5 +1,6 @@ package dev.dubhe.anvilcraft.recipe.anvil.predicate.block; +import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; @@ -8,6 +9,7 @@ import dev.anvilcraft.lib.v2.recipe.predicate.IRecipePredicate; import dev.anvilcraft.lib.v2.recipe.util.InWorldRecipeContext; import dev.anvilcraft.lib.v2.util.MathUtil; +import dev.dubhe.anvilcraft.api.block.ICauldron; import dev.dubhe.anvilcraft.api.block.IIgnitableCauldron; import dev.dubhe.anvilcraft.api.entity.IEntityCauldron; import dev.dubhe.anvilcraft.api.fluid.IFluidHandlerHolder; @@ -17,10 +19,12 @@ import dev.dubhe.anvilcraft.recipe.anvil.util.WrapUtils; import dev.dubhe.anvilcraft.util.CauldronUtil; import dev.dubhe.anvilcraft.util.CompatUtil; +import dev.dubhe.anvilcraft.util.FluidStackPredicate; +import net.minecraft.advancements.critereon.MinMaxBounds; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.core.registries.Registries; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; @@ -38,7 +42,10 @@ import net.neoforged.neoforge.fluids.FluidStack; import net.neoforged.neoforge.fluids.capability.IFluidHandler; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; +import java.util.function.Function; import javax.annotation.Nullable; /** @@ -46,46 +53,41 @@ * *

用于检查指定位置是否存在特定炼药锅的谓词条件,并在配方完成后处理炼药锅中的流体

* - * @param fluid 流体ID + * @param offset 偏移量 + * @param fluid 流体谓词 * @param consume 消耗量 - * @param transform 转换后的流体ID - * @param produce 产生量 + * @param transforms 转换后的流体栈列表,每个栈的数量为产生量 * @param chance 转换成功的概率 * @param ignited 是否需要点燃 - * @param fluidTag 流体标签ID,当非null时使用标签匹配而非精确匹配 */ public record HasCauldron( Vec3 offset, - ResourceLocation fluid, + FluidStackPredicate fluid, int consume, - ResourceLocation transform, - int produce, + List transforms, float chance, - boolean ignited, - @Nullable ResourceLocation fluidTag + boolean ignited ) implements IRecipePredicate { - /** - * 空炼药锅标识 - */ - public static final ResourceLocation EMPTY = ResourceLocation.withDefaultNamespace("empty"); - - /** - * 空转换标识 - */ - public static final ResourceLocation NULL = ResourceLocation.withDefaultNamespace("null"); + private static final Codec> TRANSFORMS_CODEC = Codec + .either(FluidStack.CODEC, FluidStack.CODEC.listOf()) + .xmap( + either -> either.map(List::of, Function.identity()), + transforms -> transforms.size() == 1 ? Either.left(transforms.getFirst()) : Either.right(transforms) + ); + private static final FluidStackPredicate EMPTY_PREDICATE = FluidStackPredicate.builder().amount(0).build(); /** * 构造一个炼药锅条件谓词 * * @param offset 偏移量 - * @param fluid 流体ID + * @param fluid 流体谓词 * @param consume 消耗量 - * @param transform 转换后的流体ID - * @param produce 产生量 + * @param transforms 转换后的流体栈列表 * @param chance 转换成功的概率 * @param ignited 是否需要点燃 */ public HasCauldron { + transforms = transforms.stream().filter(fluidStack -> !fluidStack.isEmpty()).toList(); } /** @@ -95,7 +97,7 @@ public record HasCauldron( * @return HasCauldron实例 */ public static HasCauldron empty(Vec3 offset) { - return new HasCauldron(offset, EMPTY, 0, NULL, 0, 1.0f, false, null); + return new HasCauldron(offset, EMPTY_PREDICATE, 0, List.of(), 1.0f, false); } @Override @@ -113,22 +115,29 @@ public boolean test(InWorldRecipeContext context) { * 7. 流体可以相互替代使用 */ - // 消耗/产生为负 否决 - if (this.consume() < 0 || this.produce() < 0) return false; + // 消耗为负 否决 + if (this.consume() < 0) return false; // 概率不在0-1之间 否决 if (this.chance() < 0 || this.chance() > 1) return false; - // 转换为空且产生流体 否决 - if (!HasCauldron.isNotEmpty(this.transform()) && this.produce() > 0) return false; + // 消耗量比允许的现有量大 否决 + if (this.fluid().amount().flatMap(MinMaxBounds.Ints::max).map(max -> this.consume() > max).orElse(false)) return false; // 不是锅 否决 BlockPos pos = BlockPos.containing(context.getPos().add(this.offset())); BlockCache cache = context.computeIfAbsent(BlockCache.BLOCK_CACHE); IEntityCauldron entityCauldron = findEntityCauldron(context, pos); - if (!cache.getBlockState(pos).is(BlockTags.CAULDRONS) && entityCauldron == null) return false; + BlockState state = cache.getBlockState(pos); + if (!state.is(BlockTags.CAULDRONS) && entityCauldron == null) return false; + if (this.hasMultipleFluidOutputs() && !HasCauldron.supportsMultipleFluidOutputs(cache, pos)) return false; if (cache.getBlockEntity(pos) instanceof LargeCauldronBlockEntity cauldron) { - if (this.consume() > LargeCauldronFluidHandler.TANK_CAPACITY - || this.produce() > LargeCauldronFluidHandler.TANK_CAPACITY) return false; + if ( + this.consume() > LargeCauldronFluidHandler.TANK_CAPACITY + || this.transforms().stream() + .anyMatch(transform -> transform.getAmount() > LargeCauldronFluidHandler.TANK_CAPACITY) + ) { + return false; + } return cauldron.testFluidRecipe(context, this); } @@ -137,7 +146,7 @@ public boolean test(InWorldRecipeContext context) { if (this.consume() > capacity || this.produce() > capacity) return false; // 锅中流体检查不通过 否决 - ResourceLocation curFluid = HasCauldron.getCurFluid(cache, pos, entityCauldron); + FluidStack curFluid = HasCauldron.getCurFluidStack(cache, pos, entityCauldron); if (this.hasCheck() && !this.matchesFluid(curFluid)) return false; // 如果锅必须为可点燃锅 @@ -162,8 +171,12 @@ public boolean test(InWorldRecipeContext context) { if (afterConsume + this.produce() > capacity) return false; // 锅中有流体 且 转换有效 且 前后流体类型不同 且 锅中流体没有消耗完 否决 - // 例外:consume==0 && produce==0 表示仅交换流体类型(如水泥染色),允许通过 - if (cur > 0 && HasCauldron.isNotEmpty(this.transform()) && !curFluid.equals(this.transform()) && afterConsume != 0) { + if ( + cur > 0 + && !this.transforms().isEmpty() + && !FluidStack.isSameFluidSameComponents(curFluid, this.transforms().getFirst()) + && afterConsume != 0 + ) { return false; } @@ -180,17 +193,16 @@ public void accept(InWorldRecipeContext context) { return; } if (context.getLevel().getRandom().nextFloat() > this.chance()) return; - if (this.fluid().equals(EMPTY) && !HasCauldron.isNotEmpty(this.transform())) return; + if (this.consume() == 0 && this.transforms().isEmpty()) return; IEntityCauldron entityCauldron = findEntityCauldron(context, pos); + FluidStack curFluid = HasCauldron.getCurFluidStack(cache, pos, entityCauldron); double cur = HasCauldron.getCur(cache, pos, entityCauldron); double afterConsume = cur - this.consume(); double amount = afterConsume + this.produce(); - ResourceLocation newFluid = this.transform(); - if (!HasCauldron.isNotEmpty(newFluid)) newFluid = this.fluid(); - if (!HasCauldron.isNotEmpty(newFluid)) return; - if (amount > 0 && HasCauldron.isNotEmpty(newFluid)) { + FluidStack newFluid = this.transforms().isEmpty() ? curFluid : this.transforms().getFirst(); + if (amount > 0 && !newFluid.isEmpty()) { HasCauldron.applyFluid(cache, pos, newFluid, amount, this.ignited, entityCauldron); } else { HasCauldron.applyEmpty(cache, pos, entityCauldron); @@ -232,28 +244,41 @@ public static Builder builder() { return new Builder(); } - public static boolean isNotEmpty(ResourceLocation fluid) { - return !fluid.equals(HasCauldron.NULL) && !fluid.equals(HasCauldron.EMPTY); + public int produce() { + return this.transforms.stream().mapToInt(FluidStack::getAmount).sum(); + } + + public boolean hasMultipleFluidOutputs() { + return this.transforms.size() > 1; } public boolean hasCheck() { - return !this.fluid().equals(HasCauldron.NULL) || this.fluidTag() != null; + return this.fluid().fluids().isPresent() + || this.fluid().component().map(predicate -> !predicate.patch().isEmpty() || predicate.isNegate()).orElse(false) + || this.fluid().amount().isPresent() + || this.fluid().isNegate(); + } + + public boolean requiresEmptyCauldron() { + return this.fluid().equals(EMPTY_PREDICATE); } /** * 检查当前流体是否匹配条件 * - * @param curFluid 当前流体ID + * @param curFluid 当前流体栈 * @return 是否匹配 */ - public boolean matchesFluid(ResourceLocation curFluid) { - if (this.fluidTag() != null) { - TagKey tagKey = TagKey.create(Registries.FLUID, this.fluidTag()); - return BuiltInRegistries.FLUID.getHolder(curFluid) - .map(holder -> holder.is(tagKey)) - .orElse(false); - } - return this.fluid().equals(curFluid); + public boolean matchesFluid(FluidStack curFluid) { + return this.fluid().test(curFluid); + } + + private static boolean supportsMultipleFluidOutputs(BlockCache cache, BlockPos pos) { + if (cache.getBlockEntity(pos) instanceof ICauldron cauldron) { + return cauldron.supportsMultipleFluidOutputs(); + } + return cache.getBlockState(pos).getBlock() instanceof ICauldron cauldron + && cauldron.supportsMultipleFluidOutputs(); } public static double getCapacity(BlockCache cache, BlockPos pos) { @@ -265,38 +290,17 @@ private static double getCapacity(BlockCache cache, BlockPos pos, @Nullable IEnt return handler == null ? 1000 : handler.getTankCapacity(0); } - /** - * 获取流体对应的炼药锅方块 - * - * @return 炼药锅方块 - */ - public static ResourceLocation getCurFluid(BlockCache cache, BlockPos pos) { - return getCurFluid(cache, pos, null); - } - - @SuppressWarnings("deprecation") - private static ResourceLocation getCurFluid( + private static FluidStack getCurFluidStack( BlockCache cache, BlockPos pos, @Nullable IEntityCauldron entityCauldron ) { IFluidHandler handler = getFluidHandler(cache, pos, entityCauldron); - if (handler != null) { - FluidStack stack = handler.getFluidInTank(0); - return stack.isEmpty() ? EMPTY : BuiltInRegistries.FLUID.getKey(stack.getFluid()); - } - return cache.getBlockState(pos).getBlock() instanceof IIgnitableCauldron cauldron - ? cauldron.getFluid(cache, pos).builtInRegistryHolder().key().location() - : WrapUtils.cauldron2Fluid(cache.getBlockState(pos).getBlock()); - } - - /** - * 获取流体对应的炼药锅方块 - * - * @return 炼药锅方块 - */ - public static double getCur(BlockCache cache, BlockPos pos) { - return getCur(cache, pos, null); + if (handler != null) return handler.getFluidInTank(0); + Fluid fluid = cache.getBlockState(pos).getBlock() instanceof IIgnitableCauldron cauldron + ? cauldron.getFluid(cache, pos) + : BuiltInRegistries.FLUID.get(WrapUtils.cauldron2Fluid(cache.getBlockState(pos).getBlock())); + return new FluidStack(fluid, (int) Math.round(getCur(cache, pos))); } private static double getCur(BlockCache cache, BlockPos pos, @Nullable IEntityCauldron entityCauldron) { @@ -314,8 +318,8 @@ private static double getCur(BlockCache cache, BlockPos pos, @Nullable IEntityCa return value.map(layer -> (double) layer / finalProperty.max * 1000.0).orElse(1000.0); } - public static void applyEmpty(BlockCache cache, BlockPos pos) { - applyEmpty(cache, pos, null); + private static double getCur(BlockCache cache, BlockPos pos) { + return getCur(cache, pos, null); } private static void applyEmpty(BlockCache cache, BlockPos pos, @Nullable IEntityCauldron entityCauldron) { @@ -327,14 +331,10 @@ private static void applyEmpty(BlockCache cache, BlockPos pos, @Nullable IEntity } } - public static void applyFluid(BlockCache cache, BlockPos pos, ResourceLocation fluid, double mb, boolean ignited) { - applyFluid(cache, pos, fluid, mb, ignited, null); - } - private static void applyFluid( BlockCache cache, BlockPos pos, - ResourceLocation fluid, + FluidStack fluid, double mb, boolean ignited, @Nullable IEntityCauldron entityCauldron @@ -346,20 +346,20 @@ private static void applyFluid( IFluidHandler handler = getFluidHandler(cache, pos, entityCauldron); if (handler != null) { FluidStack fluidInTank = handler.getFluidInTank(0); - Fluid target = BuiltInRegistries.FLUID.get(fluid); - if (fluidInTank.is(target)) { + if (FluidStack.isSameFluidSameComponents(fluidInTank, fluid)) { int diff = (int) Math.round(mb) - fluidInTank.getAmount(); if (diff < 0) { handler.drain(-diff, IFluidHandler.FluidAction.EXECUTE); } else { - handler.fill(fluidInTank.copyWithAmount(diff), IFluidHandler.FluidAction.EXECUTE); + handler.fill(fluid.copyWithAmount(diff), IFluidHandler.FluidAction.EXECUTE); } } else { handler.drain(Integer.MAX_VALUE, IFluidHandler.FluidAction.EXECUTE); - handler.fill(new FluidStack(target, (int) Math.round(mb)), IFluidHandler.FluidAction.EXECUTE); + handler.fill(fluid.copyWithAmount((int) Math.round(mb)), IFluidHandler.FluidAction.EXECUTE); } } else { - BlockState cauldron = HasCauldron.getDefaultCauldron(fluid).defaultBlockState(); + BlockState cauldron = HasCauldron.getDefaultCauldron(fluid.getFluid()) + .defaultBlockState(); IntegerProperty property = CauldronUtil.LEVEL_4; if (cauldron.getOptionalValue(property).isEmpty()) property = CauldronUtil.LEVEL_3; if (cauldron.getOptionalValue(property).isEmpty()) property = null; @@ -410,14 +410,14 @@ private static void applyFluid( /** * 根据流体ID获取默认的炼药锅方块 * - * @param fluid 流体ID + * @param fluid 流体 * @return 炼药锅方块 */ - public static Block getDefaultCauldron(ResourceLocation fluid) { - if (fluid.equals(HasCauldron.EMPTY) || fluid.equals(HasCauldron.NULL)) return Blocks.CAULDRON; - if (CompatUtil.F2C_TRANSFORM.containsKey(fluid)) return CompatUtil.F2C_TRANSFORM.get(fluid).get(); - String namespace = fluid.getNamespace(); - String path = fluid.getPath(); + public static Block getDefaultCauldron(Fluid fluid) { + ResourceLocation fluidId = BuiltInRegistries.FLUID.getKey(fluid); + if (CompatUtil.F2C_TRANSFORM.containsKey(fluidId)) return CompatUtil.F2C_TRANSFORM.get(fluidId).get(); + String namespace = fluidId.getNamespace(); + String path = fluidId.getPath(); ResourceLocation cauldron = ResourceLocation.fromNamespaceAndPath(namespace, "%s_cauldron".formatted(path)); Holder.Reference reference = BuiltInRegistries.BLOCK.getHolder(cauldron).orElse(null); Block block = Blocks.WATER_CAULDRON; @@ -425,6 +425,15 @@ public static Block getDefaultCauldron(ResourceLocation fluid) { return block; } + public static Block getDefaultCauldron(FluidStackPredicate fluid) { + return fluid.fluids().stream() + .flatMap(HolderSet::stream) + .map(Holder::value) + .findFirst() + .map(HasCauldron::getDefaultCauldron) + .orElse(Blocks.CAULDRON); + } + @Override public Type getType() { return ModRecipePredicateTypes.HAS_CAULDRON.get(); @@ -438,63 +447,35 @@ public static class Type implements IRecipePredicate.Type { * 编解码器 */ public final MapCodec codec = RecordCodecBuilder.mapCodec(instance -> instance.group( - Vec3.CODEC - .fieldOf("offset") - .forGetter(HasCauldron::offset), - ResourceLocation.CODEC - .optionalFieldOf("fluid", EMPTY) + Vec3.CODEC.fieldOf("offset").forGetter(HasCauldron::offset), + FluidStackPredicate.CODEC.optionalFieldOf("fluid", FluidStackPredicate.ANY) .forGetter(HasCauldron::fluid), - Codec.INT - .optionalFieldOf("consume", 0) - .forGetter(HasCauldron::consume), - ResourceLocation.CODEC - .optionalFieldOf("transform", NULL) - .forGetter(HasCauldron::transform), - Codec.INT - .optionalFieldOf("produce", 0) - .forGetter(HasCauldron::produce), - Codec.FLOAT - .optionalFieldOf("chance", 1.0f) - .forGetter(HasCauldron::chance), - Codec.BOOL - .optionalFieldOf("ignited", false) - .forGetter(HasCauldron::ignited), - ResourceLocation.CODEC - .optionalFieldOf("fluidTag") - .forGetter(h -> Optional.ofNullable(h.fluidTag())) - ).apply(instance, (offset, fluid, consume, transform, produce, chance, ignited, fluidTag) -> - new HasCauldron(offset, fluid, consume, transform, produce, chance, ignited, fluidTag.orElse(null))) + Codec.INT.optionalFieldOf("consume", 0).forGetter(HasCauldron::consume), + TRANSFORMS_CODEC.optionalFieldOf("transform", List.of()) + .forGetter(HasCauldron::transforms), + Codec.FLOAT.optionalFieldOf("chance", 1.0f).forGetter(HasCauldron::chance), + Codec.BOOL.optionalFieldOf("ignited", false).forGetter(HasCauldron::ignited) + ).apply(instance, HasCauldron::new) ); /** * 流编解码器 */ - public final StreamCodec mapCodec = new StreamCodec<>() { - @Override - public HasCauldron decode(RegistryFriendlyByteBuf buf) { - Vec3 offset = StreamCodecUtil.VEC3.decode(buf); - ResourceLocation fluid = ResourceLocation.STREAM_CODEC.decode(buf); - int consume = ByteBufCodecs.INT.decode(buf); - ResourceLocation transform = ResourceLocation.STREAM_CODEC.decode(buf); - int produce = ByteBufCodecs.INT.decode(buf); - float chance = ByteBufCodecs.FLOAT.decode(buf); - boolean ignited = ByteBufCodecs.BOOL.decode(buf); - ResourceLocation fluidTag = ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC).decode(buf).orElse(null); - return new HasCauldron(offset, fluid, consume, transform, produce, chance, ignited, fluidTag); - } - - @Override - public void encode(RegistryFriendlyByteBuf buf, HasCauldron h) { - StreamCodecUtil.VEC3.encode(buf, h.offset()); - ResourceLocation.STREAM_CODEC.encode(buf, h.fluid()); - ByteBufCodecs.INT.encode(buf, h.consume()); - ResourceLocation.STREAM_CODEC.encode(buf, h.transform()); - ByteBufCodecs.INT.encode(buf, h.produce()); - ByteBufCodecs.FLOAT.encode(buf, h.chance()); - ByteBufCodecs.BOOL.encode(buf, h.ignited()); - ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC).encode(buf, Optional.ofNullable(h.fluidTag())); - } - }; + public final StreamCodec mapCodec = StreamCodec.composite( + StreamCodecUtil.VEC3, + HasCauldron::offset, + FluidStackPredicate.STREAM_CODEC, + HasCauldron::fluid, + ByteBufCodecs.INT, + HasCauldron::consume, + FluidStack.STREAM_CODEC.apply(ByteBufCodecs.list()), + HasCauldron::transforms, + ByteBufCodecs.FLOAT, + HasCauldron::chance, + ByteBufCodecs.BOOL, + HasCauldron::ignited, + HasCauldron::new + ); @Override public MapCodec codec() { @@ -512,13 +493,11 @@ public StreamCodec streamCodec() { */ public static class Builder { private Vec3 offset = Vec3.ZERO; - private ResourceLocation fluid = HasCauldron.EMPTY; + private FluidStackPredicate fluid = FluidStackPredicate.ANY; private int consume = 0; - private ResourceLocation transform = HasCauldron.NULL; - private int produce = 0; - private float chance = 1; + private final List transforms = new ArrayList<>(); + private float chance = 1.00F; private boolean ignited = false; - private @Nullable ResourceLocation fluidTag = null; /** * 设置偏移量 @@ -587,7 +566,7 @@ public Builder above() { * @return 构建器实例 */ public Builder empty() { - this.fluid = HasCauldron.EMPTY; + this.fluid = EMPTY_PREDICATE; return this; } @@ -597,11 +576,26 @@ public Builder empty() { * @param fluid 流体ID * @return 构建器实例 */ - public Builder fluid(ResourceLocation fluid) { + public Builder fluid(FluidStackPredicate fluid) { this.fluid = fluid; return this; } + public Builder fluid(Fluid fluid) { + this.fluid = FluidStackPredicate.builder().fluid(fluid).build(); + return this; + } + + public Builder fluid(Holder fluid) { + this.fluid = FluidStackPredicate.builder().fluid(fluid).build(); + return this; + } + + public Builder fluid(TagKey fluid) { + this.fluid = FluidStackPredicate.builder().fluid(fluid).build(); + return this; + } + /** * 设置炼药锅方块 * @@ -609,19 +603,25 @@ public Builder fluid(ResourceLocation fluid) { * @return 构建器实例 */ public Builder cauldron(Block cauldron) { - this.fluid = WrapUtils.cauldron2Fluid(cauldron); + if (cauldron == Blocks.CAULDRON) return this.empty(); + return this.fluid(BuiltInRegistries.FLUID.get(WrapUtils.cauldron2Fluid(cauldron))); + } + + public Builder transform(Fluid transform, int produce) { + return this.transform(new FluidStack(transform, produce)); + } + + public Builder transform(Holder transform, int produce) { + return this.transform(new FluidStack(transform, produce)); + } + + public Builder transform(FluidStack transform) { + this.transforms.add(transform); return this; } - /** - * 设置转换后的流体ID - * - * @param transform 转换后的流体ID - * @return 构建器实例 - */ - public Builder transform(ResourceLocation transform) { - this.transform = transform; - if (!HasCauldron.isNotEmpty(this.fluid)) this.fluid = HasCauldron.NULL; + public Builder transforms(List transforms) { + this.transforms.addAll(transforms); return this; } @@ -636,17 +636,6 @@ public Builder consume(int consume) { return this; } - /** - * 设置产生指定单位流体 - * - * @param produce 产生量 - * @return 构建器实例 - */ - public Builder produce(int produce) { - this.produce = produce; - return this; - } - /** * 设置转换成功的概率 * @@ -663,23 +652,12 @@ public Builder chance(float chance) { * * @return 构建器实例 */ + @SuppressWarnings("unused") public Builder ignite() { this.ignited = true; return this; } - /** - * 设置流体标签ID,用于标签匹配 - * - * @param fluidTag 流体标签ID - * @return 构建器实例 - */ - public Builder fluidTag(ResourceLocation fluidTag) { - this.fluidTag = fluidTag; - if (!HasCauldron.isNotEmpty(this.fluid)) this.fluid = HasCauldron.NULL; - return this; - } - /** * 构建HasCauldron实例 * @@ -690,11 +668,9 @@ public HasCauldron build() { this.offset, this.fluid, this.consume, - this.transform, - this.produce, + this.transforms, this.chance, - this.ignited, - this.fluidTag + this.ignited ); } } diff --git a/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/util/WrapUtils.java b/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/util/WrapUtils.java index bb0ff51317..60f1afeb25 100644 --- a/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/util/WrapUtils.java +++ b/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/util/WrapUtils.java @@ -20,12 +20,14 @@ import java.util.ArrayList; import java.util.List; +import javax.annotation.Nullable; /** * 包装工具类 * *

提供各种配方相关对象的创建和转换工具方法

*/ +@SuppressWarnings("unused") public class WrapUtils { /** * 根据方块状态谓词列表创建HasBlock谓词列表 @@ -131,7 +133,8 @@ public static List> getOutcomes( * @param result ChanceBlockState * @return 物品 */ - public static Item getItem(ChanceBlockState result) { + public static Item getItem(@Nullable ChanceBlockState result) { + if (result == null) return Items.ANVIL; BlockState state = result.state(); if (state.isEmpty() || state.isAir()) return Items.ANVIL; Item item = state.getBlock().asItem(); @@ -186,4 +189,4 @@ public static ResourceLocation cauldron2Fluid(Block cauldron) { path = path.endsWith("_cauldron") ? path.substring(0, path.length() - 9) : "empty"; return ResourceLocation.fromNamespaceAndPath(namespace, path); } -} \ No newline at end of file +} diff --git a/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/AbstractProcessRecipe.java b/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/AbstractProcessRecipe.java index e339e262e6..0a5ef5bc02 100644 --- a/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/AbstractProcessRecipe.java +++ b/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/AbstractProcessRecipe.java @@ -20,7 +20,6 @@ import dev.dubhe.anvilcraft.recipe.anvil.builder.AbstractRecipeBuilder; import dev.dubhe.anvilcraft.recipe.anvil.outcome.ProduceHeat; import dev.dubhe.anvilcraft.recipe.anvil.predicate.block.HasAnvil; -import dev.dubhe.anvilcraft.recipe.anvil.predicate.block.HasCauldron; import dev.dubhe.anvilcraft.recipe.component.HasCauldronSimple; import lombok.Getter; import net.minecraft.core.Vec3i; @@ -47,6 +46,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import javax.annotation.Nullable; /** * 抽象处理配方类 @@ -155,7 +155,7 @@ public ChanceBlockState getFirstResultBlock() { * @return 炼药锅条件 */ public HasCauldronSimple getHasCauldron() { - return this.property.getHasCauldron(); + return Objects.requireNonNull(this.property.getHasCauldron()); } /** @@ -172,7 +172,7 @@ public HasAnvil getHasAnvil() { * * @return 产热信息 */ - public ProduceHeat getProduceHeat() { + public @Nullable ProduceHeat getProduceHeat() { return this.property.getProduceHeat(); } @@ -468,7 +468,7 @@ public static class Property { /** * 输入物品列表 */ - private List inputItems = null; + private @Nullable List inputItems = null; /** Optional functions applied to each matched input item. */ private final Map>> inputItemFunctions = new HashMap<>(); @@ -481,7 +481,7 @@ public static class Property { /** * 结果物品列表 */ - private List resultItems = null; + private @Nullable List resultItems = null; /** Optional functions applied to each spawned item result. */ private final Map>> resultItemFunctions = new HashMap<>(); @@ -496,7 +496,7 @@ public static class Property { /** * 输入方块列表 */ - private List inputBlocks = null; + private @Nullable List inputBlocks = null; /** * 方块输出偏移量 @@ -506,7 +506,7 @@ public static class Property { /** * 结果方块列表 */ - private List resultBlocks = null; + private @Nullable List resultBlocks = null; /** * 炼药锅偏移量 @@ -516,7 +516,7 @@ public static class Property { /** * 炼药锅条件 */ - private HasCauldronSimple hasCauldron = null; + private @Nullable HasCauldronSimple hasCauldron = null; /** * 铁砧条件 @@ -526,12 +526,12 @@ public static class Property { /** * 产热信息 */ - private ProduceHeat produceHeat = null; + private @Nullable ProduceHeat produceHeat = null; /** * 优先级 */ - private Integer priority = null; + private @Nullable Integer priority = null; /** * 额外结果列表 @@ -544,16 +544,19 @@ public static class Property { * @param outcome 额外结果 * @return 属性实例 */ + @SuppressWarnings("UnusedReturnValue") public Property addOutcome(IRecipeOutcome outcome) { this.extraOutcomes.add(outcome); return this; } + @SuppressWarnings("UnusedReturnValue") public Property addInputItemFunction(int index, IPredicateFunction function) { this.inputItemFunctions.computeIfAbsent(index, ignored -> new ArrayList<>()).add(function); return this; } + @SuppressWarnings("UnusedReturnValue") public Property addResultItemFunction( int index, IOutcomeFunction function @@ -797,14 +800,14 @@ private int getPriority() { + (this.inputBlocks == null ? 0 : this.inputBlocks.size() * 100) + (this.resultBlocks == null ? 0 : this.resultBlocks.size()) + this.getHasCauldronPriority() - + (this.hasAnvil != null ? 1 : 0); + + (this.hasAnvil != HasAnvil.DEFAULT ? 1 : 0); } private int getHasCauldronPriority() { if (this.hasCauldron == null) return 0; int priority = 1; - if (HasCauldron.isNotEmpty(this.hasCauldron.fluid())) priority++; - if (HasCauldron.isNotEmpty(this.hasCauldron.transform())) priority++; + if (this.hasCauldron.hasFluid()) priority++; + if (!this.hasCauldron.transforms().isEmpty()) priority++; return priority; } diff --git a/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/FastCookingRecipe.java b/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/FastCookingRecipe.java index 708fdb26a8..db04de8ac8 100644 --- a/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/FastCookingRecipe.java +++ b/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/FastCookingRecipe.java @@ -6,21 +6,23 @@ import dev.anvilcraft.lib.v2.util.predicate.ChanceItemStack; import dev.anvilcraft.lib.v2.util.predicate.ItemIngredientPredicate; import dev.dubhe.anvilcraft.init.recipe.ModRecipeTypes; -import dev.dubhe.anvilcraft.recipe.anvil.predicate.block.HasCauldron; import dev.dubhe.anvilcraft.recipe.anvil.util.WrapUtils; import dev.dubhe.anvilcraft.recipe.component.HasCauldronSimple; import lombok.Getter; +import net.minecraft.core.Holder; import net.minecraft.core.Vec3i; +import net.minecraft.core.registries.BuiltInRegistries; 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.world.item.crafting.RecipeSerializer; import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.CampfireBlock; +import net.minecraft.world.level.material.Fluid; import net.minecraft.world.phys.Vec3; +import net.neoforged.neoforge.fluids.FluidStack; import java.util.List; @@ -65,14 +67,16 @@ public static Builder builder() { return new Builder(); } + @SuppressWarnings("unused") public boolean isConsumeFluid() { HasCauldronSimple hasCauldron = this.getHasCauldron(); - return HasCauldron.isNotEmpty(hasCauldron.fluid()) && hasCauldron.consume() > 0; + return hasCauldron.hasFluid() && hasCauldron.consume() > 0; } + @SuppressWarnings("unused") public boolean isProduceFluid() { HasCauldronSimple hasCauldron = this.getHasCauldron(); - return HasCauldron.isNotEmpty(hasCauldron.transform()) && hasCauldron.produce() > 0; + return !hasCauldron.transforms().isEmpty(); } public static class Serializer implements RecipeSerializer { @@ -111,28 +115,41 @@ public StreamCodec streamCodec() { public static class Builder extends SimpleAbstractBuilder { private final HasCauldronSimple.Builder hasCauldron = HasCauldronSimple.empty(); - public Builder cauldron(ResourceLocation fluid) { + public Builder cauldron(Fluid fluid) { + this.hasCauldron.fluid(fluid); + return this; + } + + public Builder cauldron(Holder fluid) { this.hasCauldron.fluid(fluid); return this; } public Builder cauldron(Block cauldron) { - this.hasCauldron.fluid(WrapUtils.cauldron2Fluid(cauldron)); + return this.cauldron(BuiltInRegistries.FLUID.get(WrapUtils.cauldron2Fluid(cauldron))); + } + + public Builder transform(Fluid fluid, int produce) { + this.hasCauldron.transform(fluid, produce); return this; } - public Builder transform(ResourceLocation fluid) { - this.hasCauldron.transform(fluid); + public Builder transform(Holder fluid, int produce) { + this.hasCauldron.transform(fluid, produce); return this; } - public Builder consume(int amount) { - this.hasCauldron.consume(amount); + public Builder transform(Block cauldron, int produce) { + return this.transform(BuiltInRegistries.FLUID.get(WrapUtils.cauldron2Fluid(cauldron)), produce); + } + + public Builder transform(FluidStack fluid) { + this.hasCauldron.transform(fluid); return this; } - public Builder produce(int amount) { - this.hasCauldron.produce(amount); + public Builder consume(int amount) { + this.hasCauldron.consume(amount); return this; } diff --git a/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/NeutronIrradiationRecipe.java b/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/NeutronIrradiationRecipe.java index bcd45c82ee..a091ae94f1 100644 --- a/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/NeutronIrradiationRecipe.java +++ b/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/NeutronIrradiationRecipe.java @@ -10,7 +10,9 @@ import dev.dubhe.anvilcraft.recipe.anvil.util.WrapUtils; import dev.dubhe.anvilcraft.recipe.component.HasCauldronSimple; import lombok.Getter; +import net.minecraft.core.Holder; import net.minecraft.core.Vec3i; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; @@ -18,7 +20,9 @@ import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.material.Fluid; import net.minecraft.world.phys.Vec3; +import net.neoforged.neoforge.fluids.FluidStack; import java.util.List; @@ -101,33 +105,41 @@ public StreamCodec streamCode public static class Builder extends SimpleAbstractBuilder { HasCauldronSimple.Builder hasCauldron = HasCauldronSimple.empty(); - public Builder fluid(ResourceLocation fluid) { + public Builder fluid(Fluid fluid) { this.hasCauldron.fluid(fluid); return this; } - public Builder fluid(Block cauldron) { - this.fluid(WrapUtils.cauldron2Fluid(cauldron)); + public Builder fluid(Holder fluid) { + this.hasCauldron.fluid(fluid); return this; } - public Builder transform(ResourceLocation transform) { - this.hasCauldron.transform(transform); + public Builder fluid(Block cauldron) { + return this.fluid(BuiltInRegistries.FLUID.get(WrapUtils.cauldron2Fluid(cauldron))); + } + + public Builder transform(Fluid transform, int produce) { + this.hasCauldron.transform(transform, produce); return this; } - public Builder transform(Block cauldron) { - this.transform(WrapUtils.cauldron2Fluid(cauldron)); + public Builder transform(Holder transform, int produce) { + this.hasCauldron.transform(transform, produce); return this; } - public Builder consume(int consume) { - this.hasCauldron.consume(consume); + public Builder transform(Block cauldron, int produce) { + return this.transform(BuiltInRegistries.FLUID.get(WrapUtils.cauldron2Fluid(cauldron)), produce); + } + + public Builder transform(FluidStack transform) { + this.hasCauldron.transform(transform); return this; } - public Builder produce(int produce) { - this.hasCauldron.produce(produce); + public Builder consume(int consume) { + this.hasCauldron.consume(consume); return this; } @@ -150,4 +162,4 @@ protected Builder getThis() { return this; } } -} \ No newline at end of file +} diff --git a/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/SolidLiquidRecipe.java b/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/SolidLiquidRecipe.java index 50ea928a2f..d39d2289ec 100644 --- a/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/SolidLiquidRecipe.java +++ b/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/SolidLiquidRecipe.java @@ -6,27 +6,32 @@ import dev.anvilcraft.lib.v2.util.predicate.ChanceItemStack; import dev.anvilcraft.lib.v2.util.predicate.ItemIngredientPredicate; import dev.dubhe.anvilcraft.init.recipe.ModRecipeTypes; -import dev.dubhe.anvilcraft.recipe.anvil.predicate.block.HasCauldron; import dev.dubhe.anvilcraft.recipe.anvil.util.WrapUtils; import dev.dubhe.anvilcraft.recipe.component.HasCauldronSimple; +import dev.dubhe.anvilcraft.util.FluidStackPredicate; import lombok.Getter; +import net.minecraft.core.Holder; import net.minecraft.core.Vec3i; import net.minecraft.core.registries.BuiltInRegistries; 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.tags.TagKey; import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.Fluids; import net.minecraft.world.phys.Vec3; +import net.neoforged.neoforge.fluids.FluidStack; import java.util.List; /** An anvil-processing recipe between item ingredients and cauldron fluid. */ @Getter public class SolidLiquidRecipe extends AbstractProcessRecipe { + @SuppressWarnings("unused") public SolidLiquidRecipe( List itemIngredients, List results, @@ -70,16 +75,19 @@ public static Builder builder() { public boolean isConsumeFluid() { HasCauldronSimple hasCauldron = this.getHasCauldron(); - return HasCauldron.isNotEmpty(hasCauldron.fluid()) && this.getHasCauldron().consume() > 0; + return hasCauldron.hasFluid() && hasCauldron.consume() > 0; } public boolean isProduceFluid() { HasCauldronSimple hasCauldron = this.getHasCauldron(); - return HasCauldron.isNotEmpty(hasCauldron.transform()) && this.getHasCauldron().produce() > 0; + return !hasCauldron.transforms().isEmpty(); } + @SuppressWarnings("unused") public boolean isFromWater() { - return this.getHasCauldron().fluid().equals(BuiltInRegistries.FLUID.getKey(Fluids.WATER)); + return this.getHasCauldron().fluid().fluids() + .map(fluids -> fluids.stream().anyMatch(holder -> holder.value() == Fluids.WATER)) + .orElse(false); } public static class Serializer implements RecipeSerializer { @@ -124,40 +132,52 @@ public static class Builder extends SimpleAbstractBuilder fluid) { + this.hasCauldron.fluid(fluid); return this; } - public Builder transform(ResourceLocation transform) { - this.hasCauldron.transform(transform); + public Builder cauldron(FluidStackPredicate fluid) { + this.hasCauldron.fluid(fluid); return this; } - public Builder transform(Block transform) { - this.hasCauldron.transform(WrapUtils.cauldron2Fluid(transform)); + public Builder cauldron(TagKey fluid) { + this.hasCauldron.fluid(fluid); return this; } - public Builder produce(int produce) { - if (produce <= 0) return this; - this.hasCauldron.produce(produce); + public Builder cauldron(Block cauldron) { + return this.cauldron(BuiltInRegistries.FLUID.get(WrapUtils.cauldron2Fluid(cauldron))); + } + + public Builder transform(Fluid transform, int produce) { + this.hasCauldron.transform(transform, produce); return this; } - public Builder consume(int consume) { - if (consume <= 0) return this; - this.hasCauldron.consume(consume); + public Builder transform(Holder transform, int produce) { + this.hasCauldron.transform(transform, produce); + return this; + } + + public Builder transform(Block transform, int produce) { + return this.transform(BuiltInRegistries.FLUID.get(WrapUtils.cauldron2Fluid(transform)), produce); + } + + public Builder transform(FluidStack transform) { + this.hasCauldron.transform(transform); return this; } - public Builder fluidTag(ResourceLocation fluidTag) { - this.hasCauldron.fluidTag(fluidTag); + public Builder consume(int consume) { + if (consume <= 0) return this; + this.hasCauldron.consume(consume); return this; } @@ -174,10 +194,10 @@ protected SolidLiquidRecipe of(List itemIngredients, Li @Override public void validate(ResourceLocation id) { HasCauldronSimple cauldron = this.hasCauldron.build(); - if (!HasCauldron.isNotEmpty(cauldron.fluid()) && cauldron.fluidTag() == null) { + if (!cauldron.hasFluid()) { throw new IllegalArgumentException("Recipe fluid must not be empty, RecipeId: " + id); } - if (this.results.isEmpty() && !HasCauldron.isNotEmpty(cauldron.transform())) { + if (this.results.isEmpty() && cauldron.transforms().isEmpty()) { throw new IllegalArgumentException("Recipe must have an item or fluid result, RecipeId: " + id); } } diff --git a/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/SqueezingRecipe.java b/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/SqueezingRecipe.java index ec36b4eda8..611f70e469 100644 --- a/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/SqueezingRecipe.java +++ b/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/SqueezingRecipe.java @@ -7,11 +7,12 @@ import dev.dubhe.anvilcraft.init.recipe.ModRecipeTypes; import dev.dubhe.anvilcraft.recipe.anvil.builder.AbstractRecipeBuilder; import dev.dubhe.anvilcraft.recipe.anvil.predicate.block.HasAnvil; -import dev.dubhe.anvilcraft.recipe.anvil.predicate.block.HasCauldron; import dev.dubhe.anvilcraft.recipe.anvil.util.WrapUtils; import dev.dubhe.anvilcraft.recipe.component.HasCauldronSimple; import lombok.Getter; +import net.minecraft.core.Holder; import net.minecraft.core.Vec3i; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.resources.ResourceLocation; @@ -20,8 +21,11 @@ import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.material.Fluid; +import net.neoforged.neoforge.fluids.FluidStack; import java.util.function.Consumer; +import javax.annotation.Nullable; /** * 压榨配方类 @@ -80,9 +84,10 @@ public static Builder builder() { * * @return 如果产生流体返回true,否则返回false */ + @SuppressWarnings("unused") public boolean isProduceFluid() { HasCauldronSimple hasCauldron = this.getHasCauldron(); - return HasCauldron.isNotEmpty(hasCauldron.transform()) && this.getHasCauldron().produce() > 0; + return !hasCauldron.transforms().isEmpty(); } /** @@ -140,12 +145,12 @@ public static class Builder extends AbstractRecipeBuilder { /** * 原料列表 */ - private BlockStatePredicate ingredient = null; + private @Nullable BlockStatePredicate ingredient; /** * 结果列表 */ - private ChanceBlockState result = null; + private @Nullable ChanceBlockState result; /** * 炼药锅条件构建器 @@ -226,7 +231,12 @@ public Builder result(Block result) { * @param fluid 流体ID * @return 构建器实例 */ - public Builder cauldron(ResourceLocation fluid) { + public Builder cauldron(Fluid fluid) { + this.hasCauldron.fluid(fluid); + return this; + } + + public Builder cauldron(Holder fluid) { this.hasCauldron.fluid(fluid); return this; } @@ -238,8 +248,7 @@ public Builder cauldron(ResourceLocation fluid) { * @return 构建器实例 */ public Builder cauldron(Block cauldron) { - this.cauldron(WrapUtils.cauldron2Fluid(cauldron)); - return this; + return this.cauldron(BuiltInRegistries.FLUID.get(WrapUtils.cauldron2Fluid(cauldron))); } /** @@ -248,8 +257,8 @@ public Builder cauldron(Block cauldron) { * @param transform 转换后的流体ID * @return 构建器实例 */ - public Builder transform(ResourceLocation transform) { - this.hasCauldron.transform(transform); + public Builder transform(Fluid transform, int produce) { + this.hasCauldron.transform(transform, produce); return this; } @@ -259,20 +268,17 @@ public Builder transform(ResourceLocation transform) { * @param transform 转换后的炼药锅方块 * @return 构建器实例 */ - public Builder transform(Block transform) { - this.hasCauldron.transform(WrapUtils.cauldron2Fluid(transform)); + public Builder transform(Holder transform, int produce) { + this.hasCauldron.transform(transform, produce); return this; } - /** - * 设置是否产生流体 - * - * @param produce 是否产生流体 - * @return 构建器实例 - */ - public Builder produce(int produce) { - if (produce <= 0) return this; - this.hasCauldron.produce(produce); + public Builder transform(Block transform, int produce) { + return this.transform(BuiltInRegistries.FLUID.get(WrapUtils.cauldron2Fluid(transform)), produce); + } + + public Builder transform(FluidStack transform) { + this.hasCauldron.transform(transform); return this; } @@ -305,6 +311,7 @@ public Builder chance(float chance) { * * @return 构建器实例 */ + @SuppressWarnings("unused") public Builder ignite() { this.hasCauldron.ignite(); return this; @@ -367,7 +374,10 @@ public Builder noFrostAnvil() { @Override public SqueezingRecipe buildRecipe() { - return new SqueezingRecipe(this.ingredient, this.result, hasCauldron.build(), this.hasAnvil); + if (this.ingredient == null || this.result == null) { + throw new IllegalStateException("Squeezing recipe requires an ingredient and a result"); + } + return new SqueezingRecipe(this.ingredient, this.result, this.hasCauldron.build(), this.hasAnvil); } @Override @@ -384,4 +394,4 @@ public Item getResult() { return WrapUtils.getItem(this.result); } } -} \ No newline at end of file +} diff --git a/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/SuperHeatingRecipe.java b/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/SuperHeatingRecipe.java index a0ca150bc4..b9d63c8e07 100644 --- a/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/SuperHeatingRecipe.java +++ b/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/SuperHeatingRecipe.java @@ -17,24 +17,29 @@ import dev.dubhe.anvilcraft.init.recipe.ModRecipeOutcomeTypes; import dev.dubhe.anvilcraft.init.recipe.ModRecipeTypes; import dev.dubhe.anvilcraft.recipe.anvil.outcome.RoyalPreferenceOutcome; -import dev.dubhe.anvilcraft.recipe.anvil.predicate.block.HasCauldron; import dev.dubhe.anvilcraft.recipe.anvil.util.WrapUtils; import dev.dubhe.anvilcraft.recipe.component.HasCauldronSimple; +import dev.dubhe.anvilcraft.util.FluidStackPredicate; import lombok.Getter; import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; import net.minecraft.core.HolderLookup; import net.minecraft.core.Vec3i; +import net.minecraft.core.registries.BuiltInRegistries; 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.server.level.ServerLevel; +import net.minecraft.tags.TagKey; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Fluid; import net.minecraft.world.phys.Vec3; +import net.neoforged.neoforge.fluids.FluidStack; import java.util.List; @@ -126,7 +131,7 @@ public static Builder builder() { */ public boolean isConsumeFluid() { HasCauldronSimple hasCauldron = this.getHasCauldron(); - return HasCauldron.isNotEmpty(hasCauldron.fluid()) && this.getHasCauldron().consume() > 0; + return hasCauldron.hasFluid() && hasCauldron.consume() > 0; } /** @@ -136,7 +141,7 @@ public boolean isConsumeFluid() { */ public boolean isProduceFluid() { HasCauldronSimple hasCauldron = this.getHasCauldron(); - return HasCauldron.isNotEmpty(hasCauldron.transform()) && this.getHasCauldron().produce() > 0; + return !hasCauldron.transforms().isEmpty(); } /** @@ -193,10 +198,25 @@ public static class Builder extends SimpleAbstractBuilder fluid) { + this.hasCauldron.fluid(fluid); + return this; + } + + public Builder fluid(FluidStackPredicate fluid) { + this.hasCauldron.fluid(fluid); + return this; + } + + public Builder fluid(TagKey fluid) { this.hasCauldron.fluid(fluid); return this; } @@ -208,8 +228,7 @@ public Builder fluid(ResourceLocation fluid) { * @return 构建器实例 */ public Builder fluid(Block cauldron) { - this.fluid(WrapUtils.cauldron2Fluid(cauldron)); - return this; + return this.fluid(BuiltInRegistries.FLUID.get(WrapUtils.cauldron2Fluid(cauldron))); } /** @@ -218,19 +237,28 @@ public Builder fluid(Block cauldron) { * @param transform 转换后的流体ID * @return 构建器实例 */ - public Builder transform(ResourceLocation transform) { - this.hasCauldron.transform(transform); + public Builder transform(Fluid transform, int produce) { + this.hasCauldron.transform(transform, produce); return this; } /** - * 设置转换后的炼药锅方块 + * 设置转换后的流体 * - * @param cauldron 转换后的炼药锅方块 + * @param transform 转换后的流体 * @return 构建器实例 */ - public Builder transform(Block cauldron) { - this.transform(WrapUtils.cauldron2Fluid(cauldron)); + public Builder transform(Holder transform, int produce) { + this.hasCauldron.transform(transform, produce); + return this; + } + + public Builder transform(Block cauldron, int produce) { + return this.transform(BuiltInRegistries.FLUID.get(WrapUtils.cauldron2Fluid(cauldron)), produce); + } + + public Builder transform(FluidStack transform) { + this.hasCauldron.transform(transform); return this; } @@ -245,17 +273,6 @@ public Builder consume(int consume) { return this; } - /** - * 设置产生量 - * - * @param produce 产量 - * @return 构建器实例 - */ - public Builder produce(int produce) { - this.hasCauldron.produce(produce); - return this; - } - @Override protected SuperHeatingRecipe of(List itemIngredients, List results) { return new SuperHeatingRecipe(itemIngredients, results, this.hasCauldron.build()); diff --git a/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/TimeWarpRecipe.java b/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/TimeWarpRecipe.java index e3b019d9e5..6eae9a751c 100644 --- a/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/TimeWarpRecipe.java +++ b/src/main/java/dev/dubhe/anvilcraft/recipe/anvil/wrap/TimeWarpRecipe.java @@ -10,12 +10,13 @@ import dev.dubhe.anvilcraft.init.block.ModBlocks; import dev.dubhe.anvilcraft.init.recipe.ModRecipeTypes; import dev.dubhe.anvilcraft.recipe.anvil.outcome.ProduceHeat; -import dev.dubhe.anvilcraft.recipe.anvil.predicate.block.HasCauldron; import dev.dubhe.anvilcraft.recipe.anvil.util.Distance; import dev.dubhe.anvilcraft.recipe.anvil.util.WrapUtils; import dev.dubhe.anvilcraft.recipe.component.HasCauldronSimple; import lombok.Getter; +import net.minecraft.core.Holder; import net.minecraft.core.Vec3i; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; @@ -23,7 +24,9 @@ import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.material.Fluid; import net.minecraft.world.phys.Vec3; +import net.neoforged.neoforge.fluids.FluidStack; import java.util.List; @@ -94,7 +97,7 @@ public static Builder builder() { */ public boolean isConsumeFluid() { HasCauldronSimple hasCauldron = this.getHasCauldron(); - return HasCauldron.isNotEmpty(hasCauldron.fluid()) && this.getHasCauldron().consume() > 0; + return hasCauldron.hasFluid() && hasCauldron.consume() > 0; } /** @@ -104,7 +107,7 @@ public boolean isConsumeFluid() { */ public boolean isProduceFluid() { HasCauldronSimple hasCauldron = this.getHasCauldron(); - return HasCauldron.isNotEmpty(hasCauldron.transform()) && this.getHasCauldron().produce() > 0; + return !hasCauldron.transforms().isEmpty(); } /** @@ -156,6 +159,7 @@ public StreamCodec streamCodec() { /** * 时移配方构建器 */ + @SuppressWarnings("unused") public static class Builder extends SimpleAbstractBuilder { /** * 炼药锅条件构建器 @@ -341,7 +345,12 @@ public Builder distanceChebyshev() { * @param fluid 流体ID * @return 构建器实例 */ - public Builder fluid(ResourceLocation fluid) { + public Builder fluid(Fluid fluid) { + this.hasCauldron.fluid(fluid); + return this; + } + + public Builder fluid(Holder fluid) { this.hasCauldron.fluid(fluid); return this; } @@ -353,8 +362,7 @@ public Builder fluid(ResourceLocation fluid) { * @return 构建器实例 */ public Builder fluid(Block cauldron) { - this.fluid(WrapUtils.cauldron2Fluid(cauldron)); - return this; + return this.fluid(BuiltInRegistries.FLUID.get(WrapUtils.cauldron2Fluid(cauldron))); } /** @@ -363,19 +371,28 @@ public Builder fluid(Block cauldron) { * @param transform 转换后的流体ID * @return 构建器实例 */ - public Builder transform(ResourceLocation transform) { - this.hasCauldron.transform(transform); + public Builder transform(Fluid transform, int produce) { + this.hasCauldron.transform(transform, produce); return this; } /** - * 设置转换后的炼药锅方块 + * 设置转换后的流体 * - * @param cauldron 转换后的炼药锅方块 + * @param transform 转换后的流体 * @return 构建器实例 */ - public Builder transform(Block cauldron) { - this.transform(WrapUtils.cauldron2Fluid(cauldron)); + public Builder transform(Holder transform, int produce) { + this.hasCauldron.transform(transform, produce); + return this; + } + + public Builder transform(Block cauldron, int produce) { + return this.transform(BuiltInRegistries.FLUID.get(WrapUtils.cauldron2Fluid(cauldron)), produce); + } + + public Builder transform(FluidStack transform) { + this.hasCauldron.transform(transform); return this; } @@ -390,17 +407,6 @@ public Builder consume(int consume) { return this; } - /** - * 设置产生量 - * - * @param produce 产量 - * @return 构建器实例 - */ - public Builder produce(int produce) { - this.hasCauldron.produce(produce); - return this; - } - /** * 设置需要点燃锅 * @@ -419,9 +425,7 @@ protected TimeWarpRecipe of(List itemIngredients, List< @Override public void validate(ResourceLocation id) { HasCauldronSimple hasCauldronSimple = this.hasCauldron.build(); - if (itemIngredients.isEmpty() - && (hasCauldronSimple.fluid().equals(HasCauldron.EMPTY) - || hasCauldronSimple.fluid().equals(HasCauldron.NULL))) { + if (itemIngredients.isEmpty() && !hasCauldronSimple.hasFluid()) { throw new IllegalArgumentException("Recipe input must not be empty, RecipeId: " + id); } } @@ -436,4 +440,4 @@ protected Builder getThis() { return this; } } -} \ No newline at end of file +} diff --git a/src/main/java/dev/dubhe/anvilcraft/recipe/component/HasCauldronSimple.java b/src/main/java/dev/dubhe/anvilcraft/recipe/component/HasCauldronSimple.java index 1f19ca43ef..328197d8e7 100644 --- a/src/main/java/dev/dubhe/anvilcraft/recipe/component/HasCauldronSimple.java +++ b/src/main/java/dev/dubhe/anvilcraft/recipe/component/HasCauldronSimple.java @@ -1,83 +1,81 @@ package dev.dubhe.anvilcraft.recipe.component; +import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import dev.anvilcraft.lib.v2.util.MathUtil; import dev.dubhe.anvilcraft.recipe.anvil.predicate.block.HasCauldron; +import dev.dubhe.anvilcraft.util.FluidStackPredicate; +import net.minecraft.core.Holder; 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.world.level.block.Block; +import net.minecraft.tags.TagKey; +import net.minecraft.world.level.material.Fluid; import net.minecraft.world.phys.Vec3; -import org.jetbrains.annotations.Nullable; +import net.neoforged.neoforge.fluids.FluidStack; -import java.util.Optional; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; /** * 简单的炼药锅条件 * - *

该类用于定义配方中对炼药锅的要求,包括所需流体、消耗量和转换后的流体

+ *

该类用于定义配方中对炼药锅的要求,包括流体谓词、消耗量和转换后的流体栈

* - * @param fluid 流体ID + * @param fluid 流体谓词 * @param consume 消耗量 - * @param transform 转换后的流体ID - * @param produce 产生量 + * @param transforms 转换后的流体栈列表,每个栈的数量为产生量 * @param chance 转换成功的概率 * @param ignited 是否需要点燃 - * @param fluidTag 流体标签ID,当非null时使用标签匹配而非精确匹配 */ public record HasCauldronSimple( - ResourceLocation fluid, + FluidStackPredicate fluid, int consume, - ResourceLocation transform, - int produce, + List transforms, float chance, - boolean ignited, - @Nullable ResourceLocation fluidTag + boolean ignited ) { - /** - * 构造一个简单的炼药锅条件 - * - * @param fluid 流体ID - * @param consume 消耗量 - * @param transform 转换后的流体ID - * @param produce 产生量 - * @param chance 转换成功的概率 - * @param ignited 是否需要点燃 - */ + private static final Codec> TRANSFORMS_CODEC = Codec + .either(FluidStack.CODEC, FluidStack.CODEC.listOf()) + .xmap( + either -> either.map(List::of, Function.identity()), + transforms -> transforms.size() == 1 ? Either.left(transforms.getFirst()) : Either.right(transforms) + ); + private static final FluidStackPredicate EMPTY_PREDICATE = FluidStackPredicate.builder().amount(0).build(); + public HasCauldronSimple { + transforms = transforms.stream().filter(fluidStack -> !fluidStack.isEmpty()).toList(); } /** * HasCauldronSimple的编解码器 */ - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( - instance -> instance.group( - ResourceLocation.CODEC - .optionalFieldOf("fluid", HasCauldron.EMPTY) - .forGetter(HasCauldronSimple::fluid), - Codec.INT - .optionalFieldOf("consume", 0) - .forGetter(HasCauldronSimple::consume), - ResourceLocation.CODEC - .optionalFieldOf("transform", HasCauldron.NULL) - .forGetter(HasCauldronSimple::transform), - Codec.INT - .optionalFieldOf("produce", 0) - .forGetter(HasCauldronSimple::produce), - Codec.FLOAT - .optionalFieldOf("chance", 1.0f) - .forGetter(HasCauldronSimple::chance), - Codec.BOOL - .optionalFieldOf("ignited", false) - .forGetter(HasCauldronSimple::ignited), - ResourceLocation.CODEC - .optionalFieldOf("fluidTag") - .forGetter(h -> Optional.ofNullable(h.fluidTag())) - ).apply(instance, (fluid, consume, transform, produce, chance, ignited, fluidTag) -> - new HasCauldronSimple(fluid, consume, transform, produce, chance, ignited, fluidTag.orElse(null))) + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + FluidStackPredicate.CODEC.optionalFieldOf("fluid", EMPTY_PREDICATE).forGetter(HasCauldronSimple::fluid), + Codec.INT.optionalFieldOf("consume", 0).forGetter(HasCauldronSimple::consume), + TRANSFORMS_CODEC.optionalFieldOf("transform", List.of()).forGetter(HasCauldronSimple::transforms), + Codec.FLOAT.optionalFieldOf("chance", 1.0f).forGetter(HasCauldronSimple::chance), + Codec.BOOL.optionalFieldOf("ignited", false).forGetter(HasCauldronSimple::ignited) + ).apply(instance, HasCauldronSimple::new)); + + /** + * HasCauldronSimple的网络流编解码器 + */ + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + FluidStackPredicate.STREAM_CODEC, + HasCauldronSimple::fluid, + ByteBufCodecs.INT, + HasCauldronSimple::consume, + FluidStack.STREAM_CODEC.apply(ByteBufCodecs.list()), + HasCauldronSimple::transforms, + ByteBufCodecs.FLOAT, + HasCauldronSimple::chance, + ByteBufCodecs.BOOL, + HasCauldronSimple::ignited, + HasCauldronSimple::new ); /** @@ -87,54 +85,21 @@ public record HasCauldronSimple( * @return HasCauldron谓词 */ public HasCauldron toHasCauldron(Vec3 offset) { - return new HasCauldron(offset, this.fluid, this.consume, this.transform, this.produce, this.chance, this.ignited, this.fluidTag); + return new HasCauldron(offset, this.fluid, this.consume, this.transforms, this.chance, this.ignited); } - /** - * 获取流体对应的炼药锅方块 - * - * @return 炼药锅方块 - */ - public Block getFluidCauldron() { - return HasCauldron.getDefaultCauldron(this.fluid); + public boolean hasFluid() { + return this.fluid.fluids().isPresent(); } - /** - * 获取转换后的炼药锅方块 - * - * @return 炼药锅方块 - */ - public Block getTransformCauldron() { - return HasCauldron.getDefaultCauldron(this.transform); + @SuppressWarnings("unused") + public boolean requiresEmptyCauldron() { + return this.fluid.equals(EMPTY_PREDICATE); } - /** - * HasCauldronSimple的网络流编解码器 - */ - public static final StreamCodec STREAM_CODEC = new StreamCodec<>() { - @Override - public HasCauldronSimple decode(RegistryFriendlyByteBuf buf) { - ResourceLocation fluid = ResourceLocation.STREAM_CODEC.decode(buf); - int consume = ByteBufCodecs.INT.decode(buf); - ResourceLocation transform = ResourceLocation.STREAM_CODEC.decode(buf); - int produce = ByteBufCodecs.INT.decode(buf); - float chance = ByteBufCodecs.FLOAT.decode(buf); - boolean ignited = ByteBufCodecs.BOOL.decode(buf); - ResourceLocation fluidTag = ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC).decode(buf).orElse(null); - return new HasCauldronSimple(fluid, consume, transform, produce, chance, ignited, fluidTag); - } - - @Override - public void encode(RegistryFriendlyByteBuf buf, HasCauldronSimple h) { - ResourceLocation.STREAM_CODEC.encode(buf, h.fluid()); - ByteBufCodecs.INT.encode(buf, h.consume()); - ResourceLocation.STREAM_CODEC.encode(buf, h.transform()); - ByteBufCodecs.INT.encode(buf, h.produce()); - ByteBufCodecs.FLOAT.encode(buf, h.chance()); - ByteBufCodecs.BOOL.encode(buf, h.ignited()); - ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC).encode(buf, Optional.ofNullable(h.fluidTag())); - } - }; + public int produce() { + return this.transforms.stream().mapToInt(FluidStack::getAmount).sum(); + } /** * 创建一个空的构建器 @@ -145,13 +110,19 @@ public static Builder empty() { return Builder.empty(); } - /** - * 创建一个指定流体的构建器 - * - * @param fluid 流体ID - * @return 构建器实例 - */ - public static Builder fluid(ResourceLocation fluid) { + public static Builder fluid(Fluid fluid) { + return Builder.of(fluid); + } + + public static Builder fluid(Holder fluid) { + return Builder.of(fluid); + } + + public static Builder fluid(FluidStackPredicate fluid) { + return Builder.of(fluid); + } + + public static Builder fluid(TagKey fluid) { return Builder.of(fluid); } @@ -159,13 +130,11 @@ public static Builder fluid(ResourceLocation fluid) { * 构建器类,用于构建HasCauldronSimple实例 */ public static class Builder { - private ResourceLocation fluid = HasCauldron.EMPTY; + private FluidStackPredicate fluid = EMPTY_PREDICATE; private int consume = 0; - private ResourceLocation transform = HasCauldron.NULL; - private int produce = 0; - private float chance = 1f; + private final List transforms = new ArrayList<>(); + private float chance = 1.0f; private boolean ignited = false; - private ResourceLocation fluidTag = null; /** * 创建一个空的构建器 @@ -176,38 +145,55 @@ public static Builder empty() { return new Builder(); } - /** - * 创建一个指定流体的构建器 - * - * @param fluid 流体ID - * @return 构建器实例 - */ - public static Builder of(ResourceLocation fluid) { - Builder builder = new Builder(); - builder.fluid = fluid; - return builder; + public static Builder of(Fluid fluid) { + return new Builder().fluid(fluid); } - /** - * 设置流体ID - * - * @param fluid 流体ID - * @return 构建器实例 - */ - public Builder fluid(ResourceLocation fluid) { + public static Builder of(Holder fluid) { + return new Builder().fluid(fluid); + } + + public static Builder of(FluidStackPredicate fluid) { + return new Builder().fluid(fluid); + } + + public static Builder of(TagKey fluid) { + return new Builder().fluid(fluid); + } + + public Builder fluid(FluidStackPredicate fluid) { this.fluid = fluid; return this; } - /** - * 设置转换后的流体ID - * - * @param transform 转换后的流体ID - * @return 构建器实例 - */ - public Builder transform(ResourceLocation transform) { - this.transform = transform; - if (!HasCauldron.isNotEmpty(this.fluid)) this.fluid = HasCauldron.NULL; + public Builder fluid(Fluid fluid) { + return this.fluid(FluidStackPredicate.builder().fluid(fluid).build()); + } + + public Builder fluid(Holder fluid) { + return this.fluid(FluidStackPredicate.builder().fluid(fluid).build()); + } + + public Builder fluid(TagKey fluid) { + return this.fluid(FluidStackPredicate.builder().fluid(fluid).build()); + } + + public Builder transform(Fluid transform, int produce) { + return this.transform(new FluidStack(transform, produce)); + } + + public Builder transform(Holder transform, int produce) { + return this.transform(new FluidStack(transform, produce)); + } + + public Builder transform(FluidStack transform) { + this.transforms.add(transform); + if (this.requiresEmptyCauldron()) this.fluid = FluidStackPredicate.ANY; + return this; + } + + public Builder transforms(List transforms) { + transforms.forEach(this::transform); return this; } @@ -222,17 +208,6 @@ public Builder consume(int consume) { return this; } - /** - * 设置产生量 - * - * @param produce 产生量 - * @return 构建器实例 - */ - public Builder produce(int produce) { - this.produce = produce; - return this; - } - /** * 设置转换成功的概率 * @@ -254,25 +229,23 @@ public Builder ignite() { return this; } - /** - * 设置流体标签ID,用于标签匹配 - * - * @param fluidTag 流体标签ID - * @return 构建器实例 - */ - public Builder fluidTag(ResourceLocation fluidTag) { - this.fluidTag = fluidTag; - if (!HasCauldron.isNotEmpty(this.fluid)) this.fluid = HasCauldron.NULL; - return this; - } - /** * 构建HasCauldronSimple实例 * * @return HasCauldronSimple实例 */ public HasCauldronSimple build() { - return new HasCauldronSimple(this.fluid, this.consume, this.transform, this.produce, this.chance, this.ignited, this.fluidTag); + return new HasCauldronSimple( + this.fluid, + this.consume, + this.transforms, + this.chance, + this.ignited + ); + } + + private boolean requiresEmptyCauldron() { + return this.fluid.equals(EMPTY_PREDICATE); } } -} \ No newline at end of file +} diff --git a/src/main/java/dev/dubhe/anvilcraft/util/DataComponentPredicate.java b/src/main/java/dev/dubhe/anvilcraft/util/DataComponentPredicate.java new file mode 100644 index 0000000000..ab4d68ba21 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/util/DataComponentPredicate.java @@ -0,0 +1,89 @@ +package dev.dubhe.anvilcraft.util; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.component.DataComponentMap; +import net.minecraft.core.component.DataComponentPatch; +import net.minecraft.core.component.DataComponentType; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; + +import java.util.Map; +import java.util.Optional; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +public record DataComponentPredicate(DataComponentPatch patch, boolean isNegate) implements Predicate { + public static final Codec CODEC = RecordCodecBuilder.create(ins -> ins.group( + DataComponentPatch.CODEC + .optionalFieldOf("patch", DataComponentPatch.EMPTY) + .forGetter(DataComponentPredicate::patch), + Codec.BOOL + .optionalFieldOf("isNegate", false) + .forGetter(DataComponentPredicate::isNegate) + ).apply(ins, DataComponentPredicate::new)); + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + DataComponentPatch.STREAM_CODEC, + DataComponentPredicate::patch, + ByteBufCodecs.BOOL, + DataComponentPredicate::isNegate, + DataComponentPredicate::new + ); + + public static Builder builder() { + return new Builder(); + } + + @Override + public boolean test(DataComponentMap components) { + for (Map.Entry, Optional> entry : this.patch.entrySet()) { + DataComponentType type = entry.getKey(); + Object value = components.get(type); + Optional expect = entry.getValue(); + if ( + expect.isEmpty() && value != null + || expect.isPresent() && !expect.get().equals(value) + ) { + return this.isNegate; + } + } + return !this.isNegate; + } + + @Override + public Predicate negate() { + return new DataComponentPredicate(this.patch, !this.isNegate); + } + + public static class Builder { + private final DataComponentPatch.Builder patch = DataComponentPatch.builder(); + private boolean isNegate = false; + + @SuppressWarnings("unused") + public Builder expect(DataComponentType component, @Nullable T value) { + if (value != null) { + this.patch.set(component, value); + } else { + this.patch.remove(component); + } + return this; + } + + @SuppressWarnings("UnusedReturnValue") + public Builder expectNull(DataComponentType component) { + this.patch.remove(component); + return this; + } + + @SuppressWarnings("unused") + public Builder negate() { + this.isNegate = true; + return this; + } + + public DataComponentPredicate build() { + return new DataComponentPredicate(this.patch.build(), this.isNegate); + } + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/util/FluidStackPredicate.java b/src/main/java/dev/dubhe/anvilcraft/util/FluidStackPredicate.java new file mode 100644 index 0000000000..10fe62179f --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/util/FluidStackPredicate.java @@ -0,0 +1,167 @@ +package dev.dubhe.anvilcraft.util; + +import com.google.common.collect.Lists; +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import dev.anvilcraft.lib.v2.codec.StreamCodecUtil; +import net.minecraft.advancements.critereon.MinMaxBounds; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.core.RegistryCodecs; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.tags.TagKey; +import net.minecraft.world.level.material.Fluid; +import net.neoforged.neoforge.fluids.FluidStack; + +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +public record FluidStackPredicate( + Optional> fluids, + Optional component, + Optional amount, + boolean isNegate +) implements Predicate { + public static final Codec INLINE_CODEC = RegistryCodecs.homogeneousList(Registries.FLUID).xmap( + fluids -> new FluidStackPredicate( + fluids.size() == 0 ? Optional.empty() : Optional.of(fluids), + Optional.empty(), + Optional.empty(), + false + ), + predicate -> predicate.fluids().orElse(HolderSet.empty()) + ); + public static final Codec FULL_CODEC = RecordCodecBuilder.create(instance -> instance.group( + RegistryCodecs.homogeneousList(Registries.FLUID).optionalFieldOf("fluids").forGetter(FluidStackPredicate::fluids), + DataComponentPredicate.CODEC.optionalFieldOf("components").forGetter(FluidStackPredicate::component), + MinMaxBounds.Ints.CODEC.optionalFieldOf("amount").forGetter(FluidStackPredicate::amount), + Codec.BOOL.optionalFieldOf("negate", false).forGetter(FluidStackPredicate::isNegate) + ).apply(instance, FluidStackPredicate::new)); + public static final Codec CODEC = Codec.either(INLINE_CODEC, FULL_CODEC).xmap( + either -> either.map(Function.identity(), Function.identity()), + predicate -> predicate.component().isEmpty() && predicate.amount().isEmpty() && !predicate.isNegate() + ? Either.left(predicate) + : Either.right(predicate) + ); + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.optional(ByteBufCodecs.holderSet(Registries.FLUID)), + FluidStackPredicate::fluids, + ByteBufCodecs.optional(DataComponentPredicate.STREAM_CODEC), + FluidStackPredicate::component, + ByteBufCodecs.optional(StreamCodecUtil.MIN_MAX_BOUNDS_INTS), + FluidStackPredicate::amount, + ByteBufCodecs.BOOL, + FluidStackPredicate::isNegate, + FluidStackPredicate::new + ); + public static final FluidStackPredicate ANY = FluidStackPredicate.builder().build(); + + public static Builder builder() { + return new Builder(); + } + + @Override + public boolean test(FluidStack stack) { + boolean falseValue = this.isNegate; + if (this.fluids.isPresent() && !stack.is(this.fluids.get())) { + return falseValue; + } + if (this.component.isPresent() && !this.component.get().test(stack.getComponents())) { + return falseValue; + } + if (this.amount.isPresent() && !this.amount.get().matches(stack.getAmount())) { + return falseValue; + } + return !falseValue; // trueValue + } + + @Override + public FluidStackPredicate negate() { + return new FluidStackPredicate(this.fluids, this.component, this.amount, !this.isNegate); + } + + public static class Builder { + private @Nullable HolderSet fluids; + private @Nullable DataComponentPredicate.Builder component; + private @Nullable MinMaxBounds.Ints amount; + private boolean isNegate = false; + + @SuppressWarnings("deprecation") + public Builder fluid(Fluid fluid) { + this.fluids = HolderSet.direct(fluid.builtInRegistryHolder()); + return this; + } + + @SuppressWarnings("deprecation") + public Builder fluid(Fluid... fluids) { + this.fluids = HolderSet.direct(Lists.transform(List.of(fluids), Fluid::builtInRegistryHolder)); + return this; + } + + public Builder fluid(Holder fluid) { + this.fluids = HolderSet.direct(fluid); + return this; + } + + @SafeVarargs + public final Builder fluid(Holder... fluids) { + this.fluids = HolderSet.direct(fluids); + return this; + } + + public Builder fluid(TagKey fluids) { + this.fluids = BuiltInRegistries.FLUID.getOrCreateTag(fluids); + return this; + } + + public Builder component(Consumer component) { + this.component = DataComponentPredicate.builder(); + component.accept(this.component); + return this; + } + + public Builder min(int min) { + this.amount = MinMaxBounds.Ints.atLeast(min); + return this; + } + + public Builder max(int max) { + this.amount = MinMaxBounds.Ints.atMost(max); + return this; + } + + public Builder amount(int min, int max) { + this.amount = MinMaxBounds.Ints.between(min, max); + return this; + } + + public Builder amount(int exactly) { + this.amount = MinMaxBounds.Ints.exactly(exactly); + return this; + } + + @SuppressWarnings("unused") + public Builder negate() { + this.isNegate = true; + return this; + } + + public FluidStackPredicate build() { + return new FluidStackPredicate( + Optional.ofNullable(this.fluids), + this.component == null ? Optional.empty() : Optional.of(this.component.build()), + Optional.ofNullable(this.amount), + this.isNegate + ); + } + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/util/LiquidEnchantmentClientFluidTypeExtension.java b/src/main/java/dev/dubhe/anvilcraft/util/LiquidEnchantmentClientFluidTypeExtension.java index 0662deacc2..4956ea6f5c 100644 --- a/src/main/java/dev/dubhe/anvilcraft/util/LiquidEnchantmentClientFluidTypeExtension.java +++ b/src/main/java/dev/dubhe/anvilcraft/util/LiquidEnchantmentClientFluidTypeExtension.java @@ -10,8 +10,8 @@ public class LiquidEnchantmentClientFluidTypeExtension extends ModClientFluidTypeExtensionImpl { private static final int[] BASE_LAYER = {0xFFFFFFFF}; - private static final int ENCHANTMENT_ALPHA = 0x70; - private static final int CURSE_LAYER = 0x60FF0000; + private static final int LAYER_ALPHA = 0x80; + private static final int CURSE_LAYER = (LAYER_ALPHA << 24) | 0x00FF0000; public LiquidEnchantmentClientFluidTypeExtension(ResourceLocation texture) { super(texture, texture); @@ -21,7 +21,7 @@ public LiquidEnchantmentClientFluidTypeExtension(ResourceLocation texture) { public int[] getLayerColors(FluidStack stack) { Optional> enchantment = LiquidEnchantmentUtil.getEnchantment(stack); if (enchantment.isEmpty()) return BASE_LAYER; - int enchantmentLayer = (ENCHANTMENT_ALPHA << 24) | LiquidEnchantmentUtil.getColor(enchantment.get()); + int enchantmentLayer = (LAYER_ALPHA << 24) | LiquidEnchantmentUtil.getColor(enchantment.get()); return enchantment.get().is(EnchantmentTags.CURSE) ? new int[]{0xFFFFFFFF, enchantmentLayer, CURSE_LAYER} : new int[]{0xFFFFFFFF, enchantmentLayer}; @@ -31,16 +31,53 @@ public int[] getLayerColors(FluidStack stack) { public int getTintColor(FluidStack stack) { int result = 0xFFFFFFFF; int[] layers = this.getLayerColors(stack); - for (int i = 1; i < layers.length; i++) result = blend(result, layers[i]); + for (int i = 1; i < layers.length; i++) { + result = layers.length == 3 && i == 2 + ? overlay(result, layers[i]) + : softLight(result, layers[i]); + } return result; } - private static int blend(int background, int foreground) { + private static int softLight(int background, int foreground) { + return blendByMode(background, foreground, false); + } + + private static int overlay(int background, int foreground) { + return blendByMode(background, foreground, true); + } + + private static int blendByMode(int background, int foreground, boolean useOverlay) { int alpha = foreground >>> 24; - int inverseAlpha = 255 - alpha; - int red = ((foreground >> 16 & 0xFF) * alpha + (background >> 16 & 0xFF) * inverseAlpha) / 255; - int green = ((foreground >> 8 & 0xFF) * alpha + (background >> 8 & 0xFF) * inverseAlpha) / 255; - int blue = ((foreground & 0xFF) * alpha + (background & 0xFF) * inverseAlpha) / 255; + if (alpha == 0) return background; + int red = blendChannel(background >> 16 & 0xFF, foreground >> 16 & 0xFF, alpha, useOverlay); + int green = blendChannel(background >> 8 & 0xFF, foreground >> 8 & 0xFF, alpha, useOverlay); + int blue = blendChannel(background & 0xFF, foreground & 0xFF, alpha, useOverlay); return 0xFF000000 | (red << 16) | (green << 8) | blue; } + + private static int blendChannel(int background, int foreground, int alpha, boolean useOverlay) { + int result; + if (useOverlay) { + result = background < 128 + ? 2 * background * foreground / 255 + : 255 - 2 * (255 - background) * (255 - foreground) / 255; + } else { + // Tint composition starts from white, so the layer color must be the soft-light base. + result = softLightChannel(foreground, background); + } + return (result * alpha + background * (255 - alpha)) / 255; + } + + private static int softLightChannel(int background, int foreground) { + float base = background / 255.0f; + float blend = foreground / 255.0f; + if (blend <= 0.5f) { + return Math.round((base - (1.0f - 2.0f * blend) * base * (1.0f - base)) * 255.0f); + } + float softenedBase = base <= 0.25f + ? ((16.0f * base - 12.0f) * base + 4.0f) * base + : (float) Math.sqrt(base); + return Math.round((base + (2.0f * blend - 1.0f) * (softenedBase - base)) * 255.0f); + } } diff --git a/src/main/java/dev/dubhe/anvilcraft/util/LiquidEnchantmentUtil.java b/src/main/java/dev/dubhe/anvilcraft/util/LiquidEnchantmentUtil.java index af39565b2c..92ad8415b4 100644 --- a/src/main/java/dev/dubhe/anvilcraft/util/LiquidEnchantmentUtil.java +++ b/src/main/java/dev/dubhe/anvilcraft/util/LiquidEnchantmentUtil.java @@ -3,12 +3,15 @@ import dev.dubhe.anvilcraft.init.block.ModFluids; import dev.dubhe.anvilcraft.init.item.ModComponents; import net.minecraft.core.Holder; +import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.EnchantmentTags; import net.minecraft.world.item.enchantment.Enchantment; +import net.neoforged.neoforge.common.CommonHooks; import net.neoforged.neoforge.fluids.FluidStack; import java.nio.charset.StandardCharsets; +import java.util.Objects; import java.util.Optional; public final class LiquidEnchantmentUtil { @@ -20,7 +23,8 @@ private LiquidEnchantmentUtil() { public static Optional> getEnchantment(FluidStack stack) { if (!stack.is(ModFluids.LIQUID_ENCHANTMENT.get())) return Optional.empty(); - return Optional.ofNullable(stack.get(ModComponents.LIQUID_ENCHANTMENT)); + return Optional.ofNullable(stack.get(ModComponents.LIQUID_ENCHANTMENT)) + .flatMap(key -> Objects.requireNonNull(CommonHooks.resolveLookup(Registries.ENCHANTMENT)).get(key)); } public static boolean isBlank(FluidStack stack) {