diff --git a/src/generated/resources/assets/poopsky/blockstates/breeding_box.json b/src/generated/resources/assets/poopsky/blockstates/breeding_box.json new file mode 100644 index 00000000..42ac1e67 --- /dev/null +++ b/src/generated/resources/assets/poopsky/blockstates/breeding_box.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "poopsky:block/breeding_box" + } + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/blockstates/fly_nest.json b/src/generated/resources/assets/poopsky/blockstates/fly_nest.json new file mode 100644 index 00000000..cb6624b6 --- /dev/null +++ b/src/generated/resources/assets/poopsky/blockstates/fly_nest.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "poopsky:block/fly_nest" + } + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/block/breeding_box.json b/src/generated/resources/assets/poopsky/models/block/breeding_box.json new file mode 100644 index 00000000..a0c9ee5d --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/block/breeding_box.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "texture": "minecraft:block/bee_nest_side" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/block/fly_nest.json b/src/generated/resources/assets/poopsky/models/block/fly_nest.json new file mode 100644 index 00000000..c37577df --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/block/fly_nest.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "texture": "minecraft:block/beehive_side" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/item/breeding_box.json b/src/generated/resources/assets/poopsky/models/item/breeding_box.json new file mode 100644 index 00000000..f79c4c48 --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/item/breeding_box.json @@ -0,0 +1,3 @@ +{ + "parent": "poopsky:block/breeding_box" +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/item/fly.json b/src/generated/resources/assets/poopsky/models/item/fly.json new file mode 100644 index 00000000..68113d4d --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/item/fly.json @@ -0,0 +1,110 @@ +{ + "parent": "minecraft:item/generated", + "overrides": [ + { + "model": "poopsky:item/fly_brown", + "predicate": { + "poopsky:fly_type": 7.0 + } + }, + { + "model": "poopsky:item/fly_cyan", + "predicate": { + "poopsky:fly_type": 13.0 + } + }, + { + "model": "poopsky:item/fly_light_gray", + "predicate": { + "poopsky:fly_type": 9.0 + } + }, + { + "model": "poopsky:item/fly_light_blue", + "predicate": { + "poopsky:fly_type": 10.0 + } + }, + { + "model": "poopsky:item/fly_red", + "predicate": { + "poopsky:fly_type": 6.0 + } + }, + { + "model": "poopsky:item/fly_pink", + "predicate": { + "poopsky:fly_type": 14.0 + } + }, + { + "model": "poopsky:item/fly_purple", + "predicate": { + "poopsky:fly_type": 16.0 + } + }, + { + "model": "poopsky:item/fly_gray", + "predicate": { + "poopsky:fly_type": 8.0 + } + }, + { + "model": "poopsky:item/fly_blue", + "predicate": { + "poopsky:fly_type": 5.0 + } + }, + { + "model": "poopsky:item/fly_white", + "predicate": { + "poopsky:fly_type": 1.0 + } + }, + { + "model": "poopsky:item/fly_lime", + "predicate": { + "poopsky:fly_type": 11.0 + } + }, + { + "model": "poopsky:item/fly_magenta", + "predicate": { + "poopsky:fly_type": 12.0 + } + }, + { + "model": "poopsky:item/fly_normal", + "predicate": { + "poopsky:fly_type": 0.0 + } + }, + { + "model": "poopsky:item/fly_orange", + "predicate": { + "poopsky:fly_type": 15.0 + } + }, + { + "model": "poopsky:item/fly_black", + "predicate": { + "poopsky:fly_type": 2.0 + } + }, + { + "model": "poopsky:item/fly_yellow", + "predicate": { + "poopsky:fly_type": 4.0 + } + }, + { + "model": "poopsky:item/fly_green", + "predicate": { + "poopsky:fly_type": 3.0 + } + } + ], + "textures": { + "layer0": "minecraft:item/bone_meal" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/item/fly_black.json b/src/generated/resources/assets/poopsky/models/item/fly_black.json new file mode 100644 index 00000000..0502b405 --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/item/fly_black.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/black_dye" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/item/fly_blue.json b/src/generated/resources/assets/poopsky/models/item/fly_blue.json new file mode 100644 index 00000000..4235b598 --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/item/fly_blue.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/blue_dye" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/item/fly_brown.json b/src/generated/resources/assets/poopsky/models/item/fly_brown.json new file mode 100644 index 00000000..d9cb87fb --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/item/fly_brown.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/brown_dye" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/item/fly_catcher.json b/src/generated/resources/assets/poopsky/models/item/fly_catcher.json new file mode 100644 index 00000000..120911e8 --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/item/fly_catcher.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "poopsky:item/fly_catcher" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/item/fly_cyan.json b/src/generated/resources/assets/poopsky/models/item/fly_cyan.json new file mode 100644 index 00000000..634aa6ec --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/item/fly_cyan.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/cyan_dye" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/item/fly_gray.json b/src/generated/resources/assets/poopsky/models/item/fly_gray.json new file mode 100644 index 00000000..f3c30106 --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/item/fly_gray.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/gray_dye" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/item/fly_green.json b/src/generated/resources/assets/poopsky/models/item/fly_green.json new file mode 100644 index 00000000..2ded932e --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/item/fly_green.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/green_dye" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/item/fly_light_blue.json b/src/generated/resources/assets/poopsky/models/item/fly_light_blue.json new file mode 100644 index 00000000..297407da --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/item/fly_light_blue.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/light_blue_dye" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/item/fly_light_gray.json b/src/generated/resources/assets/poopsky/models/item/fly_light_gray.json new file mode 100644 index 00000000..40a44acc --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/item/fly_light_gray.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/light_gray_dye" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/item/fly_lime.json b/src/generated/resources/assets/poopsky/models/item/fly_lime.json new file mode 100644 index 00000000..36ae6c82 --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/item/fly_lime.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/lime_dye" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/item/fly_magenta.json b/src/generated/resources/assets/poopsky/models/item/fly_magenta.json new file mode 100644 index 00000000..f1ebae5b --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/item/fly_magenta.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/magenta_dye" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/item/fly_nest.json b/src/generated/resources/assets/poopsky/models/item/fly_nest.json new file mode 100644 index 00000000..370561a6 --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/item/fly_nest.json @@ -0,0 +1,3 @@ +{ + "parent": "poopsky:block/fly_nest" +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/item/fly_normal.json b/src/generated/resources/assets/poopsky/models/item/fly_normal.json new file mode 100644 index 00000000..60f7c5f0 --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/item/fly_normal.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/bone_meal" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/item/fly_orange.json b/src/generated/resources/assets/poopsky/models/item/fly_orange.json new file mode 100644 index 00000000..4c5e5e9b --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/item/fly_orange.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/orange_dye" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/item/fly_pink.json b/src/generated/resources/assets/poopsky/models/item/fly_pink.json new file mode 100644 index 00000000..bf230ebc --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/item/fly_pink.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/pink_dye" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/item/fly_purple.json b/src/generated/resources/assets/poopsky/models/item/fly_purple.json new file mode 100644 index 00000000..a4082d10 --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/item/fly_purple.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/purple_dye" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/item/fly_red.json b/src/generated/resources/assets/poopsky/models/item/fly_red.json new file mode 100644 index 00000000..77765d36 --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/item/fly_red.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/red_dye" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/item/fly_spawn_egg.json b/src/generated/resources/assets/poopsky/models/item/fly_spawn_egg.json new file mode 100644 index 00000000..d1aaa9d6 --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/item/fly_spawn_egg.json @@ -0,0 +1,3 @@ +{ + "parent": "minecraft:item/template_spawn_egg" +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/item/fly_white.json b/src/generated/resources/assets/poopsky/models/item/fly_white.json new file mode 100644 index 00000000..68b02c07 --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/item/fly_white.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/white_dye" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/poopsky/models/item/fly_yellow.json b/src/generated/resources/assets/poopsky/models/item/fly_yellow.json new file mode 100644 index 00000000..14d6bb6a --- /dev/null +++ b/src/generated/resources/assets/poopsky/models/item/fly_yellow.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/yellow_dye" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/green_plus_blue.json b/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/green_plus_blue.json new file mode 100644 index 00000000..65a8d771 --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/green_plus_blue.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:breeding_box/green_plus_blue" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:breeding_box/green_plus_blue" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/purple_plus_pink.json b/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/purple_plus_pink.json new file mode 100644 index 00000000..c69a8f44 --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/purple_plus_pink.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:breeding_box/purple_plus_pink" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:breeding_box/purple_plus_pink" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/red_plus_blue.json b/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/red_plus_blue.json new file mode 100644 index 00000000..ade54aba --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/red_plus_blue.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:breeding_box/red_plus_blue" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:breeding_box/red_plus_blue" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/red_plus_yellow.json b/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/red_plus_yellow.json new file mode 100644 index 00000000..b3d517ca --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/red_plus_yellow.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:breeding_box/red_plus_yellow" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:breeding_box/red_plus_yellow" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/white_plus_black.json b/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/white_plus_black.json new file mode 100644 index 00000000..021bdc44 --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/white_plus_black.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:breeding_box/white_plus_black" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:breeding_box/white_plus_black" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/white_plus_blue.json b/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/white_plus_blue.json new file mode 100644 index 00000000..0d3bfe6e --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/white_plus_blue.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:breeding_box/white_plus_blue" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:breeding_box/white_plus_blue" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/white_plus_gray.json b/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/white_plus_gray.json new file mode 100644 index 00000000..69d3516d --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/white_plus_gray.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:breeding_box/white_plus_gray" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:breeding_box/white_plus_gray" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/white_plus_green.json b/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/white_plus_green.json new file mode 100644 index 00000000..432f9a25 --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/white_plus_green.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:breeding_box/white_plus_green" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:breeding_box/white_plus_green" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/white_plus_red.json b/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/white_plus_red.json new file mode 100644 index 00000000..3b061cbe --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/breeding_box/white_plus_red.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:breeding_box/white_plus_red" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:breeding_box/white_plus_red" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/black.json b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/black.json new file mode 100644 index 00000000..ae2bd0b8 --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/black.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:fly_nest/black" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:fly_nest/black" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/blue.json b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/blue.json new file mode 100644 index 00000000..bf5ed8ce --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/blue.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:fly_nest/blue" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:fly_nest/blue" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/brown.json b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/brown.json new file mode 100644 index 00000000..be6409f7 --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/brown.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:fly_nest/brown" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:fly_nest/brown" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/cyan.json b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/cyan.json new file mode 100644 index 00000000..3d2ecdf9 --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/cyan.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:fly_nest/cyan" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:fly_nest/cyan" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/gray.json b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/gray.json new file mode 100644 index 00000000..99b81cfa --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/gray.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:fly_nest/gray" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:fly_nest/gray" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/green.json b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/green.json new file mode 100644 index 00000000..4b6b2a14 --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/green.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:fly_nest/green" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:fly_nest/green" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/light_blue.json b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/light_blue.json new file mode 100644 index 00000000..f95dfdec --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/light_blue.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:fly_nest/light_blue" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:fly_nest/light_blue" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/light_gray.json b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/light_gray.json new file mode 100644 index 00000000..52db3339 --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/light_gray.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:fly_nest/light_gray" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:fly_nest/light_gray" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/lime.json b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/lime.json new file mode 100644 index 00000000..a4706400 --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/lime.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:fly_nest/lime" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:fly_nest/lime" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/magenta.json b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/magenta.json new file mode 100644 index 00000000..f1bac7a1 --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/magenta.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:fly_nest/magenta" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:fly_nest/magenta" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/normal.json b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/normal.json new file mode 100644 index 00000000..a47832f8 --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/normal.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:fly_nest/normal" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:fly_nest/normal" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/orange.json b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/orange.json new file mode 100644 index 00000000..c90172d2 --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/orange.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:fly_nest/orange" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:fly_nest/orange" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/pink.json b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/pink.json new file mode 100644 index 00000000..bf7df524 --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/pink.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:fly_nest/pink" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:fly_nest/pink" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/purple.json b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/purple.json new file mode 100644 index 00000000..ea1a5a6e --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/purple.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:fly_nest/purple" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:fly_nest/purple" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/red.json b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/red.json new file mode 100644 index 00000000..06a2d244 --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/red.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:fly_nest/red" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:fly_nest/red" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/white.json b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/white.json new file mode 100644 index 00000000..7b55252e --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/white.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:fly_nest/white" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:fly_nest/white" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/yellow.json b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/yellow.json new file mode 100644 index 00000000..c7b60dfa --- /dev/null +++ b/src/generated/resources/data/poopsky/advancement/recipes/fly_nest/yellow.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_fly_nest": { + "conditions": { + "items": [ + { + "items": "poopsky:fly_nest" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "poopsky:fly_nest/yellow" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fly_nest" + ] + ], + "rewards": { + "recipes": [ + "poopsky:fly_nest/yellow" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/loot_table/blocks/breeding_box.json b/src/generated/resources/data/poopsky/loot_table/blocks/breeding_box.json new file mode 100644 index 00000000..75ed5ee4 --- /dev/null +++ b/src/generated/resources/data/poopsky/loot_table/blocks/breeding_box.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "poopsky:breeding_box" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "poopsky:blocks/breeding_box" +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/loot_table/blocks/fly_nest.json b/src/generated/resources/data/poopsky/loot_table/blocks/fly_nest.json new file mode 100644 index 00000000..64fa4c22 --- /dev/null +++ b/src/generated/resources/data/poopsky/loot_table/blocks/fly_nest.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "poopsky:fly_nest" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "poopsky:blocks/fly_nest" +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/breeding_box/green_plus_blue.json b/src/generated/resources/data/poopsky/recipe/breeding_box/green_plus_blue.json new file mode 100644 index 00000000..9a6dfecb --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/breeding_box/green_plus_blue.json @@ -0,0 +1,7 @@ +{ + "type": "poopsky:breeding_box", + "chance": 0.2, + "parent1": "green", + "parent2": "blue", + "result": "cyan" +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/breeding_box/purple_plus_pink.json b/src/generated/resources/data/poopsky/recipe/breeding_box/purple_plus_pink.json new file mode 100644 index 00000000..80689bc7 --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/breeding_box/purple_plus_pink.json @@ -0,0 +1,7 @@ +{ + "type": "poopsky:breeding_box", + "chance": 0.2, + "parent1": "purple", + "parent2": "pink", + "result": "magenta" +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/breeding_box/red_plus_blue.json b/src/generated/resources/data/poopsky/recipe/breeding_box/red_plus_blue.json new file mode 100644 index 00000000..d600d2ec --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/breeding_box/red_plus_blue.json @@ -0,0 +1,7 @@ +{ + "type": "poopsky:breeding_box", + "chance": 0.2, + "parent1": "red", + "parent2": "blue", + "result": "purple" +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/breeding_box/red_plus_yellow.json b/src/generated/resources/data/poopsky/recipe/breeding_box/red_plus_yellow.json new file mode 100644 index 00000000..f8921abb --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/breeding_box/red_plus_yellow.json @@ -0,0 +1,7 @@ +{ + "type": "poopsky:breeding_box", + "chance": 0.2, + "parent1": "red", + "parent2": "yellow", + "result": "orange" +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/breeding_box/white_plus_black.json b/src/generated/resources/data/poopsky/recipe/breeding_box/white_plus_black.json new file mode 100644 index 00000000..ea4c4aab --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/breeding_box/white_plus_black.json @@ -0,0 +1,7 @@ +{ + "type": "poopsky:breeding_box", + "chance": 0.2, + "parent1": "white", + "parent2": "black", + "result": "gray" +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/breeding_box/white_plus_blue.json b/src/generated/resources/data/poopsky/recipe/breeding_box/white_plus_blue.json new file mode 100644 index 00000000..d56e8c43 --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/breeding_box/white_plus_blue.json @@ -0,0 +1,7 @@ +{ + "type": "poopsky:breeding_box", + "chance": 0.2, + "parent1": "white", + "parent2": "blue", + "result": "light_blue" +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/breeding_box/white_plus_gray.json b/src/generated/resources/data/poopsky/recipe/breeding_box/white_plus_gray.json new file mode 100644 index 00000000..7b7b3990 --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/breeding_box/white_plus_gray.json @@ -0,0 +1,7 @@ +{ + "type": "poopsky:breeding_box", + "chance": 0.2, + "parent1": "white", + "parent2": "gray", + "result": "light_gray" +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/breeding_box/white_plus_green.json b/src/generated/resources/data/poopsky/recipe/breeding_box/white_plus_green.json new file mode 100644 index 00000000..8742c423 --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/breeding_box/white_plus_green.json @@ -0,0 +1,7 @@ +{ + "type": "poopsky:breeding_box", + "chance": 0.2, + "parent1": "white", + "parent2": "green", + "result": "lime" +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/breeding_box/white_plus_red.json b/src/generated/resources/data/poopsky/recipe/breeding_box/white_plus_red.json new file mode 100644 index 00000000..d4e109c4 --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/breeding_box/white_plus_red.json @@ -0,0 +1,7 @@ +{ + "type": "poopsky:breeding_box", + "chance": 0.2, + "parent1": "white", + "parent2": "red", + "result": "pink" +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/fly_nest/black.json b/src/generated/resources/data/poopsky/recipe/fly_nest/black.json new file mode 100644 index 00000000..38859c50 --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/fly_nest/black.json @@ -0,0 +1,8 @@ +{ + "type": "poopsky:fly_nest", + "fly_type": "black", + "result": { + "count": 1, + "id": "minecraft:coal" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/fly_nest/blue.json b/src/generated/resources/data/poopsky/recipe/fly_nest/blue.json new file mode 100644 index 00000000..4c14a16a --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/fly_nest/blue.json @@ -0,0 +1,8 @@ +{ + "type": "poopsky:fly_nest", + "fly_type": "blue", + "result": { + "count": 1, + "id": "minecraft:lapis_lazuli" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/fly_nest/brown.json b/src/generated/resources/data/poopsky/recipe/fly_nest/brown.json new file mode 100644 index 00000000..41a65935 --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/fly_nest/brown.json @@ -0,0 +1,8 @@ +{ + "type": "poopsky:fly_nest", + "fly_type": "brown", + "result": { + "count": 1, + "id": "minecraft:cocoa_beans" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/fly_nest/cyan.json b/src/generated/resources/data/poopsky/recipe/fly_nest/cyan.json new file mode 100644 index 00000000..1a7f239d --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/fly_nest/cyan.json @@ -0,0 +1,8 @@ +{ + "type": "poopsky:fly_nest", + "fly_type": "cyan", + "result": { + "count": 1, + "id": "minecraft:prismarine_crystals" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/fly_nest/gray.json b/src/generated/resources/data/poopsky/recipe/fly_nest/gray.json new file mode 100644 index 00000000..6029d999 --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/fly_nest/gray.json @@ -0,0 +1,8 @@ +{ + "type": "poopsky:fly_nest", + "fly_type": "gray", + "result": { + "count": 1, + "id": "minecraft:gray_dye" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/fly_nest/green.json b/src/generated/resources/data/poopsky/recipe/fly_nest/green.json new file mode 100644 index 00000000..c3c0566f --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/fly_nest/green.json @@ -0,0 +1,8 @@ +{ + "type": "poopsky:fly_nest", + "fly_type": "green", + "result": { + "count": 1, + "id": "minecraft:cactus" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/fly_nest/light_blue.json b/src/generated/resources/data/poopsky/recipe/fly_nest/light_blue.json new file mode 100644 index 00000000..36d99e5a --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/fly_nest/light_blue.json @@ -0,0 +1,8 @@ +{ + "type": "poopsky:fly_nest", + "fly_type": "light_blue", + "result": { + "count": 1, + "id": "minecraft:light_blue_dye" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/fly_nest/light_gray.json b/src/generated/resources/data/poopsky/recipe/fly_nest/light_gray.json new file mode 100644 index 00000000..3d524e99 --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/fly_nest/light_gray.json @@ -0,0 +1,8 @@ +{ + "type": "poopsky:fly_nest", + "fly_type": "light_gray", + "result": { + "count": 1, + "id": "minecraft:light_gray_dye" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/fly_nest/lime.json b/src/generated/resources/data/poopsky/recipe/fly_nest/lime.json new file mode 100644 index 00000000..9075c3df --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/fly_nest/lime.json @@ -0,0 +1,8 @@ +{ + "type": "poopsky:fly_nest", + "fly_type": "lime", + "result": { + "count": 1, + "id": "minecraft:sea_pickle" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/fly_nest/magenta.json b/src/generated/resources/data/poopsky/recipe/fly_nest/magenta.json new file mode 100644 index 00000000..6c4a7983 --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/fly_nest/magenta.json @@ -0,0 +1,8 @@ +{ + "type": "poopsky:fly_nest", + "fly_type": "magenta", + "result": { + "count": 1, + "id": "minecraft:magenta_dye" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/fly_nest/normal.json b/src/generated/resources/data/poopsky/recipe/fly_nest/normal.json new file mode 100644 index 00000000..ece1de9f --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/fly_nest/normal.json @@ -0,0 +1,8 @@ +{ + "type": "poopsky:fly_nest", + "fly_type": "normal", + "result": { + "count": 1, + "id": "poopsky:maggots_seeds" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/fly_nest/orange.json b/src/generated/resources/data/poopsky/recipe/fly_nest/orange.json new file mode 100644 index 00000000..e7660a61 --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/fly_nest/orange.json @@ -0,0 +1,8 @@ +{ + "type": "poopsky:fly_nest", + "fly_type": "orange", + "result": { + "count": 1, + "id": "minecraft:orange_dye" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/fly_nest/pink.json b/src/generated/resources/data/poopsky/recipe/fly_nest/pink.json new file mode 100644 index 00000000..2347254b --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/fly_nest/pink.json @@ -0,0 +1,8 @@ +{ + "type": "poopsky:fly_nest", + "fly_type": "pink", + "result": { + "count": 1, + "id": "minecraft:pink_dye" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/fly_nest/purple.json b/src/generated/resources/data/poopsky/recipe/fly_nest/purple.json new file mode 100644 index 00000000..14f04baf --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/fly_nest/purple.json @@ -0,0 +1,8 @@ +{ + "type": "poopsky:fly_nest", + "fly_type": "purple", + "result": { + "count": 1, + "id": "minecraft:amethyst_shard" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/fly_nest/red.json b/src/generated/resources/data/poopsky/recipe/fly_nest/red.json new file mode 100644 index 00000000..7b87d7e7 --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/fly_nest/red.json @@ -0,0 +1,8 @@ +{ + "type": "poopsky:fly_nest", + "fly_type": "red", + "result": { + "count": 1, + "id": "minecraft:redstone" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/fly_nest/white.json b/src/generated/resources/data/poopsky/recipe/fly_nest/white.json new file mode 100644 index 00000000..d2fdce75 --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/fly_nest/white.json @@ -0,0 +1,8 @@ +{ + "type": "poopsky:fly_nest", + "fly_type": "white", + "result": { + "count": 1, + "id": "minecraft:bone_meal" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/poopsky/recipe/fly_nest/yellow.json b/src/generated/resources/data/poopsky/recipe/fly_nest/yellow.json new file mode 100644 index 00000000..7c9d96ed --- /dev/null +++ b/src/generated/resources/data/poopsky/recipe/fly_nest/yellow.json @@ -0,0 +1,8 @@ +{ + "type": "poopsky:fly_nest", + "fly_type": "yellow", + "result": { + "count": 1, + "id": "minecraft:yellow_dye" + } +} \ No newline at end of file diff --git a/src/main/java/com/altnoir/poopsky/PSItemGroups.java b/src/main/java/com/altnoir/poopsky/PSItemGroups.java index 61dc1ed0..da1a55a7 100644 --- a/src/main/java/com/altnoir/poopsky/PSItemGroups.java +++ b/src/main/java/com/altnoir/poopsky/PSItemGroups.java @@ -3,6 +3,8 @@ import com.altnoir.poopsky.block.PSBlocks; import com.altnoir.poopsky.block.AllToiletBlocks; import com.altnoir.poopsky.item.PSItems; +import com.altnoir.poopsky.item.p.FlyItem; +import com.altnoir.poopsky.init.PFlyTypes; import net.minecraft.core.registries.Registries; import net.minecraft.network.chat.Component; import net.minecraft.world.item.BlockItem; @@ -27,6 +29,7 @@ public class PSItemGroups { PSItems.ITEMS.getEntries().stream() .map(DeferredHolder::get) .filter(item -> !(item instanceof BlockItem)) + .filter(item -> !(item instanceof FlyItem)) .forEach(output::accept); Set skip = Set.of( @@ -43,6 +46,10 @@ public class PSItemGroups { .filter(block -> !skip.contains(block)) .forEach(output::accept); + for (var type : PFlyTypes.getAll().values()) { + output.accept(FlyItem.withType(type)); + } + AllToiletBlocks.BLOCKS.getEntries().stream() .map(DeferredHolder::get) .forEach(output::accept); diff --git a/src/main/java/com/altnoir/poopsky/PoopSky.java b/src/main/java/com/altnoir/poopsky/PoopSky.java index 1574a008..660ed277 100644 --- a/src/main/java/com/altnoir/poopsky/PoopSky.java +++ b/src/main/java/com/altnoir/poopsky/PoopSky.java @@ -67,6 +67,9 @@ public PoopSky(IEventBus modEventBus, ModContainer modContainer) { PFluids.FLUIDS.register(modEventBus); PFluidTypes.FLUID_TYPES.register(modEventBus); + + PMenuTypes.register(modEventBus); + modContainer.registerConfig(ModConfig.Type.COMMON, Config.SPEC); } diff --git a/src/main/java/com/altnoir/poopsky/PoopSkyClient.java b/src/main/java/com/altnoir/poopsky/PoopSkyClient.java index 397c1c0e..d81f7144 100644 --- a/src/main/java/com/altnoir/poopsky/PoopSkyClient.java +++ b/src/main/java/com/altnoir/poopsky/PoopSkyClient.java @@ -2,27 +2,30 @@ import com.altnoir.poopsky.block.PSBlocks; import com.altnoir.poopsky.block.abs.AbstractCompooperBlock; -import com.altnoir.poopsky.init.PEntityType; +import com.altnoir.poopsky.init.*; import com.altnoir.poopsky.entity.renderer.*; import com.altnoir.poopsky.event.PSClientGameEvents; import com.altnoir.poopsky.event.PSClientModEvents; import com.altnoir.poopsky.event.PSKeyBoardInput; -import com.altnoir.poopsky.init.PFluidTypes; -import com.altnoir.poopsky.init.PParticles; -import com.altnoir.poopsky.init.PRecipes; +import com.altnoir.poopsky.inventory.BreedingBoxScreen; +import com.altnoir.poopsky.inventory.FlyNestScreen; +import com.altnoir.poopsky.item.PSItems; import com.altnoir.poopsky.misc.particle.PoopParticle; import net.minecraft.client.renderer.BiomeColors; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.RecipeBookCategories; +import net.minecraft.client.renderer.item.ItemProperties; import net.minecraft.resources.ResourceLocation; import net.neoforged.api.distmarker.Dist; import net.neoforged.bus.api.IEventBus; import net.neoforged.fml.ModContainer; import net.neoforged.fml.common.Mod; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; import net.neoforged.neoforge.client.event.EntityRenderersEvent; import net.neoforged.neoforge.client.event.RegisterColorHandlersEvent; import net.neoforged.neoforge.client.event.RegisterNamedRenderTypesEvent; import net.neoforged.neoforge.client.event.RegisterParticleProvidersEvent; +import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent; import net.neoforged.neoforge.client.event.RegisterRecipeBookCategoriesEvent; import net.neoforged.neoforge.client.extensions.common.IClientFluidTypeExtensions; import net.neoforged.neoforge.client.extensions.common.RegisterClientExtensionsEvent; @@ -43,6 +46,7 @@ public PoopSkyClient(IEventBus modEventBus, ModContainer modContainer) { public static void registerMod(IEventBus modEventBus) { modEventBus.addListener(PSClientModEvents::registerLayers); + modEventBus.addListener(ClientModEvents::registerItemProperties); modEventBus.addListener(PSClientModEvents::registerBlockEntityRenderers); modEventBus.addListener(PSKeyBoardInput::onRegisterKeyMappings); modEventBus.addListener(ClientModEvents::registerRenderTypes); @@ -53,6 +57,7 @@ public static void registerMod(IEventBus modEventBus) { modEventBus.addListener(ClientModEvents::onRegisterItemColors); modEventBus.addListener(ClientModEvents::onRegisterBlockRenderBuffers); modEventBus.addListener(ClientModEvents::registerClientExtensions); + modEventBus.addListener(ClientModEvents::registerMenuScreens); } public static void registerGame(IEventBus modEventBus) { @@ -61,6 +66,13 @@ public static void registerGame(IEventBus modEventBus) { } public static class ClientModEvents { + public static void registerItemProperties(FMLClientSetupEvent event) { + event.enqueueWork(() -> { + ItemProperties.register(PSItems.FLY.get(), PoopSky.loc("fly_type"), + (stack, level, entity, seed) -> (float) PFlyTypes.getIndex(PFlyTypes.byId(stack.get(PComponents.FLY_TYPE.get())))); + }); + } + public static void registerRenderTypes(RegisterNamedRenderTypesEvent event) { event.register(PoopSky.loc("poop_empty_log"), RenderType.cutout(), RenderType.entityCutout(PSBlocks.POOP_EMPTY_LOG.getId())); } @@ -74,8 +86,15 @@ public static void registerEntityRenderers(EntityRenderersEvent.RegisterRenderer event.registerEntityRenderer(PEntityType.POOP_TNT.get(), PoopTntRenderer::new); } + public static void registerMenuScreens(RegisterMenuScreensEvent event) { + event.register(PMenuTypes.FLY_NEST.get(), FlyNestScreen::new); + event.register(PMenuTypes.BREEDING_BOX.get(), BreedingBoxScreen::new); + } + public static void registerRecipeBookCategories(RegisterRecipeBookCategoriesEvent event) { event.registerRecipeCategoryFinder(PRecipes.SIEVE_TYPE.get(), recipe -> RecipeBookCategories.UNKNOWN); + event.registerRecipeCategoryFinder(PRecipes.FLY_NEST_TYPE.get(), recipe -> RecipeBookCategories.UNKNOWN); + event.registerRecipeCategoryFinder(PRecipes.BREEDING_BOX_TYPE.get(), recipe -> RecipeBookCategories.UNKNOWN); } public static void registerParticleProviders(RegisterParticleProvidersEvent event) { @@ -123,4 +142,4 @@ public ResourceLocation getOverlayTexture() { }, PFluidTypes.POOP_FLUID_TYPE.get()); } } -} +} \ No newline at end of file diff --git a/src/main/java/com/altnoir/poopsky/block/PSBlocks.java b/src/main/java/com/altnoir/poopsky/block/PSBlocks.java index e79ce169..bf1b9e47 100644 --- a/src/main/java/com/altnoir/poopsky/block/PSBlocks.java +++ b/src/main/java/com/altnoir/poopsky/block/PSBlocks.java @@ -558,6 +558,26 @@ public class PSBlocks { ) ); + // ——— 苍蝇系统 ——— + public static final DeferredBlock FLY_NEST = registerDefaultBlock("fly_nest", + () -> new FlyNestBlock(BlockBehaviour.Properties.of() + .mapColor(MapColor.COLOR_BROWN) + .strength(0.5F) + .sound(SoundType.WOOD) + .noOcclusion() + ) + ); + + public static final DeferredBlock BREEDING_BOX = registerDefaultBlock("breeding_box", + () -> new BreedingBoxBlock(BlockBehaviour.Properties.of() + .mapColor(MapColor.COLOR_BROWN) + .strength(1.0F) + .sound(SoundType.WOOD) + .noOcclusion() + ) + ); + + private static BlockBehaviour.Properties poopCakeProperties() { return BlockBehaviour.Properties.of() .forceSolidOn() @@ -633,3 +653,4 @@ public static void register(IEventBus eventBus) { BLOCKS.register(eventBus); } } + diff --git a/src/main/java/com/altnoir/poopsky/block/entity/BreedingBoxBlockEntity.java b/src/main/java/com/altnoir/poopsky/block/entity/BreedingBoxBlockEntity.java new file mode 100644 index 00000000..76d7f4b9 --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/block/entity/BreedingBoxBlockEntity.java @@ -0,0 +1,309 @@ +package com.altnoir.poopsky.block.entity; + +import com.altnoir.poopsky.init.PFlyRecipes; +import com.altnoir.poopsky.init.PFlyTypes; +import com.altnoir.poopsky.init.PBlockEntityType; +import com.altnoir.poopsky.tag.PSBlockTags; +import com.altnoir.poopsky.item.p.FlyItem; +import com.altnoir.poopsky.tag.PSItemTags; +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.world.Container; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.SimpleContainer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.items.IItemHandler; +import net.neoforged.neoforge.items.ItemStackHandler; +import net.neoforged.neoforge.items.wrapper.RangedWrapper; +import org.jetbrains.annotations.Nullable; + +public class BreedingBoxBlockEntity extends BlockEntity implements MenuProvider { + public static final int SLOT_FECES = 0; + public static final int SLOT_FLY_1 = 1; + public static final int SLOT_FLY_2 = 2; + public static final int SLOT_OUTPUT_1 = 3; + public static final int SLOT_OUTPUT_2 = 4; + public static final int SLOT_OUTPUT_3 = 5; + public static final int TOTAL_SLOTS = 6; + + private static final int BASE_TICK_INTERVAL = 400; // 基础繁殖间隔(tick) + private static final int MAX_ENVIRONMENT_BONUS = 10; + + private int progress = 0; + + private final ItemStackHandler itemHandler = new ItemStackHandler(TOTAL_SLOTS) { + @Override + protected void onContentsChanged(int slot) { + setChanged(); + syncToClient(); + } + + @Override + public boolean isItemValid(int slot, ItemStack stack) { + if (slot == SLOT_FECES) return stack.is(PSItemTags.POOPS); + if (slot == SLOT_FLY_1 || slot == SLOT_FLY_2) return FlyItem.isFlyItem(stack); + return false; + } + + @Override + public int getSlotLimit(int slot) { + return 64; + } + }; + + // 自动化:上面/侧面 = 输入(粪便 + 苍蝇) + private final IItemHandler topSideHandler = new RangedWrapper(itemHandler, SLOT_FECES, SLOT_FLY_2 + 1) { + @Override + public boolean isItemValid(int slot, ItemStack stack) { + return super.isItemValid(slot, stack); + } + + @Override + public ItemStack extractItem(int slot, int amount, boolean simulate) { + return ItemStack.EMPTY; + } + }; + + // 自动化:下面 = 输出 + private final IItemHandler bottomHandler = new RangedWrapper(itemHandler, SLOT_OUTPUT_1, TOTAL_SLOTS) { + @Override + public boolean isItemValid(int slot, ItemStack stack) { + return false; + } + + @Override + public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) { + return stack; + } + }; + + public BreedingBoxBlockEntity(BlockPos pos, BlockState blockState) { + super(PBlockEntityType.BREEDING_BOX.get(), pos, blockState); + } + + public static void tick(Level level, BlockPos pos, BlockState state, BreedingBoxBlockEntity be) { + if (level.isClientSide) return; + + ItemStack fly1 = be.itemHandler.getStackInSlot(SLOT_FLY_1); + ItemStack fly2 = be.itemHandler.getStackInSlot(SLOT_FLY_2); + ItemStack feces = be.itemHandler.getStackInSlot(SLOT_FECES); + + if (fly1.isEmpty() || fly2.isEmpty() || feces.isEmpty()) { + be.progress = 0; + return; + } + + if (!FlyItem.isFlyItem(fly1) || !FlyItem.isFlyItem(fly2)) { + be.progress = 0; + return; + } + + // 所有输出槽满则停止 + if (be.areOutputsFull()) return; + + // 环境加速:附近的粪便块越多,繁殖越快 + int envBonus = be.getEnvironmentBonus(level, pos); + int currentInterval = Math.max(40, BASE_TICK_INTERVAL - envBonus * 30); + be.progress++; + + if (be.progress >= currentInterval) { + be.breed(); + be.progress = 0; + } + + be.setChanged(); + } + + private int getEnvironmentBonus(Level level, BlockPos pos) { + int bonus = 0; + for (BlockPos checkPos : BlockPos.betweenClosed(pos.offset(-1, -1, -1), pos.offset(1, 1, 1))) { + if (checkPos.equals(pos)) continue; + BlockState state = level.getBlockState(checkPos); + if (state.is(PSBlockTags.POOP_BLOCKS)) { + bonus++; + if (bonus >= MAX_ENVIRONMENT_BONUS) return bonus; + } + } + return bonus; + } + + private boolean areOutputsFull() { + for (int i = SLOT_OUTPUT_1; i <= SLOT_OUTPUT_3; i++) { + ItemStack stack = itemHandler.getStackInSlot(i); + if (stack.getCount() < stack.getMaxStackSize()) return false; + } + return true; + } + + private void breed() { + // 消耗一个粪便 + ItemStack feces = itemHandler.getStackInSlot(SLOT_FECES); + feces.shrink(1); + if (feces.isEmpty()) { + itemHandler.setStackInSlot(SLOT_FECES, ItemStack.EMPTY); + } + + ItemStack fly1 = itemHandler.getStackInSlot(SLOT_FLY_1); + ItemStack fly2 = itemHandler.getStackInSlot(SLOT_FLY_2); + + PFlyTypes.FlyType type1 = FlyItem.getFlyType(fly1); + PFlyTypes.FlyType type2 = FlyItem.getFlyType(fly2); + + // 变异判定 + PFlyRecipes.MutationResult result = PFlyRecipes.tryMutate(level, type1, type2); + + // 产卵数量:基础1个 + 环境加成 + int envBonus = getEnvironmentBonus(level, worldPosition); + int count = Math.max(1, 1 + envBonus / 3); + + ItemStack flyProduct = FlyItem.withType(result.result()); + flyProduct.setCount(count); + + // 尝试放入输出槽 + for (int i = SLOT_OUTPUT_1; i <= SLOT_OUTPUT_3; i++) { + ItemStack remainder = tryInsert(i, flyProduct.copy()); + if (remainder.isEmpty()) { + flyProduct = ItemStack.EMPTY; + break; + } + flyProduct = remainder; + } + } + + private ItemStack tryInsert(int slot, ItemStack stack) { + ItemStack current = itemHandler.getStackInSlot(slot); + if (current.isEmpty()) { + itemHandler.setStackInSlot(slot, stack.copy()); + return ItemStack.EMPTY; + } + if (ItemStack.isSameItemSameComponents(current, stack)) { + int space = current.getMaxStackSize() - current.getCount(); + int toAdd = Math.min(space, stack.getCount()); + if (toAdd > 0) { + current.grow(toAdd); + itemHandler.setStackInSlot(slot, current); + stack.shrink(toAdd); + } + } + return stack; + } + + @Override + public Component getDisplayName() { + return Component.translatable("container.poopsky.breeding_box"); + } + + @Override + public @Nullable AbstractContainerMenu createMenu(int id, Inventory playerInventory, Player player) { + return new com.altnoir.poopsky.inventory.BreedingBoxMenu(id, playerInventory, createContainerProxy()); + } + + private Container createContainerProxy() { + return new SimpleContainer(TOTAL_SLOTS) { + @Override + public ItemStack getItem(int slot) { + return itemHandler.getStackInSlot(slot); + } + + @Override + public void setItem(int slot, ItemStack stack) { + itemHandler.setStackInSlot(slot, stack); + } + + @Override + public ItemStack removeItem(int slot, int amount) { + return itemHandler.extractItem(slot, amount, false); + } + + @Override + public ItemStack removeItemNoUpdate(int slot) { + ItemStack stack = itemHandler.getStackInSlot(slot); + itemHandler.setStackInSlot(slot, ItemStack.EMPTY); + return stack; + } + + @Override + public boolean canPlaceItem(int slot, ItemStack stack) { + return itemHandler.isItemValid(slot, stack); + } + + @Override + public void setChanged() { + BreedingBoxBlockEntity.this.setChanged(); + } + + @Override + public boolean stillValid(Player player) { + return Container.stillValidBlockEntity(BreedingBoxBlockEntity.this, player); + } + + @Override + public int getContainerSize() { + return TOTAL_SLOTS; + } + + @Override + public boolean isEmpty() { + for (int i = 0; i < TOTAL_SLOTS; i++) { + if (!itemHandler.getStackInSlot(i).isEmpty()) return false; + } + return true; + } + }; + } + + public IItemHandler getItemHandler() { + return itemHandler; + } + + public IItemHandler getTopSideHandler() { + return topSideHandler; + } + + public IItemHandler getBottomHandler() { + return bottomHandler; + } + + @Override + protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.saveAdditional(tag, registries); + tag.put("inventory", itemHandler.serializeNBT(registries)); + tag.putInt("progress", progress); + } + + @Override + protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.loadAdditional(tag, registries); + itemHandler.deserializeNBT(registries, tag.getCompound("inventory")); + progress = tag.getInt("progress"); + } + + @Override + public CompoundTag getUpdateTag(HolderLookup.Provider registries) { + CompoundTag tag = new CompoundTag(); + saveAdditional(tag, registries); + return tag; + } + + @Override + public @Nullable ClientboundBlockEntityDataPacket getUpdatePacket() { + return ClientboundBlockEntityDataPacket.create(this); + } + + private void syncToClient() { + if (level != null && !level.isClientSide) { + BlockState state = getBlockState(); + level.sendBlockUpdated(worldPosition, state, state, Block.UPDATE_ALL); + } + } +} diff --git a/src/main/java/com/altnoir/poopsky/block/entity/FlyNestBlockEntity.java b/src/main/java/com/altnoir/poopsky/block/entity/FlyNestBlockEntity.java new file mode 100644 index 00000000..5c859a65 --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/block/entity/FlyNestBlockEntity.java @@ -0,0 +1,282 @@ +package com.altnoir.poopsky.block.entity; + +import com.altnoir.poopsky.init.PFlyRecipes; +import com.altnoir.poopsky.init.PFlyTypes; +import com.altnoir.poopsky.init.PBlockEntityType; +import com.altnoir.poopsky.tag.PSBlockTags; +import com.altnoir.poopsky.item.p.FlyItem; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.world.Container; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.SimpleContainer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.items.IItemHandler; +import net.neoforged.neoforge.items.ItemStackHandler; +import net.neoforged.neoforge.items.wrapper.RangedWrapper; +import org.jetbrains.annotations.Nullable; + +public class FlyNestBlockEntity extends BlockEntity implements MenuProvider { + public static final int SLOT_INPUT = 0; + public static final int SLOT_OUTPUT_1 = 1; + public static final int SLOT_OUTPUT_2 = 2; + public static final int SLOT_OUTPUT_3 = 3; + public static final int SLOT_OUTPUT_4 = 4; + public static final int TOTAL_SLOTS = 5; + + private static final int BASE_TICK_INTERVAL = 200; // 基础生产间隔(tick),环境加速会让这个值减小 + private static final int MAX_ENVIRONMENT_BONUS = 10; // 最大环境加速(附近粪便块数量上限) + private static final int BASE_PRODUCT_COUNT = 1; // 一次产出数量(受环境加速影响) + private static final int MAX_PRODUCT_COUNT = 4; + + private int progress = 0; + + private final ItemStackHandler itemHandler = new ItemStackHandler(TOTAL_SLOTS) { + @Override + protected void onContentsChanged(int slot) { + setChanged(); + syncToClient(); + } + + @Override + public boolean isItemValid(int slot, ItemStack stack) { + if (slot == SLOT_INPUT) return FlyItem.isFlyItem(stack); + return false; + } + + @Override + public int getSlotLimit(int slot) { + return 64; + } + }; + + // 自动化:上面/侧面 = 输入(只接受苍蝇) + private final IItemHandler topSideHandler = new RangedWrapper(itemHandler, SLOT_INPUT, SLOT_INPUT + 1) { + @Override + public boolean isItemValid(int slot, ItemStack stack) { + return super.isItemValid(slot, stack) && FlyItem.isFlyItem(stack); + } + + @Override + public ItemStack extractItem(int slot, int amount, boolean simulate) { + return ItemStack.EMPTY; + } + }; + + // 自动化:下面 = 输出(只允许提取) + private final IItemHandler bottomHandler = new RangedWrapper(itemHandler, SLOT_OUTPUT_1, TOTAL_SLOTS) { + @Override + public boolean isItemValid(int slot, ItemStack stack) { + return false; + } + + @Override + public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) { + return stack; + } + }; + + public FlyNestBlockEntity(BlockPos pos, BlockState blockState) { + super(PBlockEntityType.FLY_NEST.get(), pos, blockState); + } + + public static void tick(Level level, BlockPos pos, BlockState state, FlyNestBlockEntity be) { + if (level.isClientSide) return; + + ItemStack inputStack = be.itemHandler.getStackInSlot(SLOT_INPUT); + if (inputStack.isEmpty() || !FlyItem.isFlyItem(inputStack)) { + be.progress = 0; + return; + } + + // 所有输出槽满则停止生产 + if (be.areOutputsFull()) return; + + // 环境加速:附近的粪便块越多,生产越快 + int envBonus = be.getEnvironmentBonus(level, pos); + int currentInterval = Math.max(20, BASE_TICK_INTERVAL - envBonus * 15); + be.progress++; + + if (be.progress >= currentInterval) { + be.produce(); + be.progress = 0; + } + + be.setChanged(); + } + + private int getEnvironmentBonus(Level level, BlockPos pos) { + int bonus = 0; + for (BlockPos checkPos : BlockPos.betweenClosed(pos.offset(-1, -1, -1), pos.offset(1, 1, 1))) { + if (checkPos.equals(pos)) continue; + BlockState state = level.getBlockState(checkPos); + if (state.is(PSBlockTags.POOP_BLOCKS)) { + bonus++; + if (bonus >= MAX_ENVIRONMENT_BONUS) return bonus; + } + } + return bonus; + } + + private boolean areOutputsFull() { + for (int i = SLOT_OUTPUT_1; i <= SLOT_OUTPUT_4; i++) { + ItemStack stack = itemHandler.getStackInSlot(i); + if (stack.getCount() < stack.getMaxStackSize()) return false; + } + return true; + } + + private void produce() { + ItemStack inputStack = itemHandler.getStackInSlot(SLOT_INPUT); + PFlyTypes.FlyType type = FlyItem.getFlyType(inputStack); + ItemStack product = PFlyRecipes.getProduct(level, type); + if (product.isEmpty()) return; + + int envBonus = getEnvironmentBonus(level, worldPosition); + int count = Math.min(BASE_PRODUCT_COUNT + envBonus / 3, MAX_PRODUCT_COUNT); + + ItemStack toInsert = product.copyWithCount(count); + for (int i = SLOT_OUTPUT_1; i <= SLOT_OUTPUT_4; i++) { + toInsert = tryInsert(i, toInsert); + if (toInsert.isEmpty()) break; + } + } + + private ItemStack tryInsert(int slot, ItemStack stack) { + ItemStack current = itemHandler.getStackInSlot(slot); + if (current.isEmpty()) { + itemHandler.setStackInSlot(slot, stack.copy()); + return ItemStack.EMPTY; + } + if (ItemStack.isSameItemSameComponents(current, stack)) { + int space = current.getMaxStackSize() - current.getCount(); + int toAdd = Math.min(space, stack.getCount()); + if (toAdd > 0) { + current.grow(toAdd); + itemHandler.setStackInSlot(slot, current); + stack.shrink(toAdd); + } + } + return stack; + } + + @Override + public Component getDisplayName() { + return Component.translatable("container.poopsky.fly_nest"); + } + + @Override + public @Nullable AbstractContainerMenu createMenu(int id, Inventory playerInventory, Player player) { + return new com.altnoir.poopsky.inventory.FlyNestMenu(id, playerInventory, createContainerProxy()); + } + + private Container createContainerProxy() { + return new SimpleContainer(TOTAL_SLOTS) { + @Override + public ItemStack getItem(int slot) { + return itemHandler.getStackInSlot(slot); + } + + @Override + public void setItem(int slot, ItemStack stack) { + itemHandler.setStackInSlot(slot, stack); + } + + @Override + public ItemStack removeItem(int slot, int amount) { + return itemHandler.extractItem(slot, amount, false); + } + + @Override + public ItemStack removeItemNoUpdate(int slot) { + ItemStack stack = itemHandler.getStackInSlot(slot); + itemHandler.setStackInSlot(slot, ItemStack.EMPTY); + return stack; + } + + @Override + public boolean canPlaceItem(int slot, ItemStack stack) { + return itemHandler.isItemValid(slot, stack); + } + + @Override + public void setChanged() { + FlyNestBlockEntity.this.setChanged(); + } + + @Override + public boolean stillValid(Player player) { + return Container.stillValidBlockEntity(FlyNestBlockEntity.this, player); + } + + @Override + public int getContainerSize() { + return TOTAL_SLOTS; + } + + @Override + public boolean isEmpty() { + for (int i = 0; i < TOTAL_SLOTS; i++) { + if (!itemHandler.getStackInSlot(i).isEmpty()) return false; + } + return true; + } + }; + } + + public IItemHandler getItemHandler() { + return itemHandler; + } + + public IItemHandler getTopSideHandler() { + return topSideHandler; + } + + public IItemHandler getBottomHandler() { + return bottomHandler; + } + + @Override + protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.saveAdditional(tag, registries); + tag.put("inventory", itemHandler.serializeNBT(registries)); + tag.putInt("progress", progress); + } + + @Override + protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.loadAdditional(tag, registries); + itemHandler.deserializeNBT(registries, tag.getCompound("inventory")); + progress = tag.getInt("progress"); + } + + @Override + public CompoundTag getUpdateTag(HolderLookup.Provider registries) { + CompoundTag tag = new CompoundTag(); + saveAdditional(tag, registries); + return tag; + } + + @Override + public @Nullable ClientboundBlockEntityDataPacket getUpdatePacket() { + return ClientboundBlockEntityDataPacket.create(this); + } + + private void syncToClient() { + if (level != null && !level.isClientSide) { + BlockState state = getBlockState(); + level.sendBlockUpdated(worldPosition, state, state, Block.UPDATE_ALL); + } + } +} diff --git a/src/main/java/com/altnoir/poopsky/block/p/BreedingBoxBlock.java b/src/main/java/com/altnoir/poopsky/block/p/BreedingBoxBlock.java new file mode 100644 index 00000000..f04ade1f --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/block/p/BreedingBoxBlock.java @@ -0,0 +1,79 @@ +package com.altnoir.poopsky.block.p; + +import com.altnoir.poopsky.block.entity.BreedingBoxBlockEntity; +import com.altnoir.poopsky.init.PBlockEntityType; +import com.mojang.serialization.MapCodec; +import net.minecraft.core.BlockPos; +import net.minecraft.world.Containers; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BaseEntityBlock; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; +import org.jetbrains.annotations.Nullable; + +public class BreedingBoxBlock extends BaseEntityBlock { + protected static final MapCodec CODEC = simpleCodec(BreedingBoxBlock::new); + + public BreedingBoxBlock(Properties properties) { + super(properties); + } + + @Override + protected MapCodec codec() { + return CODEC; + } + + @Override + public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { + return new BreedingBoxBlockEntity(blockPos, blockState); + } + + @Override + public @Nullable BlockEntityTicker getTicker(Level level, BlockState state, BlockEntityType blockEntityType) { + return createTickerHelper(blockEntityType, PBlockEntityType.BREEDING_BOX.get(), BreedingBoxBlockEntity::tick); + } + + @Override + protected RenderShape getRenderShape(BlockState state) { + return RenderShape.MODEL; + } + + @Override + protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { + if (!level.isClientSide) { + if (level.getBlockEntity(pos) instanceof BreedingBoxBlockEntity be) { + player.openMenu(be); + } + } + return InteractionResult.SUCCESS; + } + + @Override + protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { + if (!state.is(newState.getBlock())) { + if (level.getBlockEntity(pos) instanceof BreedingBoxBlockEntity be) { + for (int i = 0; i < BreedingBoxBlockEntity.TOTAL_SLOTS; i++) { + Containers.dropItemStack(level, pos.getX(), pos.getY(), pos.getZ(), be.getItemHandler().getStackInSlot(i)); + } + } + } + super.onRemove(state, level, pos, newState, movedByPiston); + } + + @Override + protected boolean hasAnalogOutputSignal(BlockState state) { + return true; + } + + @Override + protected int getAnalogOutputSignal(BlockState blockState, Level level, BlockPos pos) { + return AbstractContainerMenu.getRedstoneSignalFromBlockEntity(level.getBlockEntity(pos)); + } +} diff --git a/src/main/java/com/altnoir/poopsky/block/p/FlyNestBlock.java b/src/main/java/com/altnoir/poopsky/block/p/FlyNestBlock.java new file mode 100644 index 00000000..9d827110 --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/block/p/FlyNestBlock.java @@ -0,0 +1,80 @@ +package com.altnoir.poopsky.block.p; + +import com.altnoir.poopsky.block.entity.FlyNestBlockEntity; +import com.altnoir.poopsky.init.PBlockEntityType; +import com.mojang.serialization.MapCodec; +import net.minecraft.core.BlockPos; +import net.minecraft.world.Containers; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BaseEntityBlock; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; +import org.jetbrains.annotations.Nullable; + +public class FlyNestBlock extends BaseEntityBlock { + protected static final MapCodec CODEC = simpleCodec(FlyNestBlock::new); + + public FlyNestBlock(Properties properties) { + super(properties); + } + + @Override + protected MapCodec codec() { + return CODEC; + } + + @Override + public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { + return new FlyNestBlockEntity(blockPos, blockState); + } + + @Override + public @Nullable BlockEntityTicker getTicker(Level level, BlockState state, BlockEntityType blockEntityType) { + return createTickerHelper(blockEntityType, PBlockEntityType.FLY_NEST.get(), FlyNestBlockEntity::tick); + } + + @Override + protected RenderShape getRenderShape(BlockState state) { + return RenderShape.MODEL; + } + + @Override + protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { + if (!level.isClientSide) { + if (level.getBlockEntity(pos) instanceof FlyNestBlockEntity be) { + player.openMenu(be); + } + } + return InteractionResult.SUCCESS; + } + + @Override + protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { + if (!state.is(newState.getBlock())) { + if (level.getBlockEntity(pos) instanceof FlyNestBlockEntity be) { + // 掉落所有物品 + for (int i = 0; i < FlyNestBlockEntity.TOTAL_SLOTS; i++) { + Containers.dropItemStack(level, pos.getX(), pos.getY(), pos.getZ(), be.getItemHandler().getStackInSlot(i)); + } + } + } + super.onRemove(state, level, pos, newState, movedByPiston); + } + + @Override + protected boolean hasAnalogOutputSignal(BlockState state) { + return true; + } + + @Override + protected int getAnalogOutputSignal(BlockState blockState, Level level, BlockPos pos) { + return AbstractContainerMenu.getRedstoneSignalFromBlockEntity(level.getBlockEntity(pos)); + } +} diff --git a/src/main/java/com/altnoir/poopsky/compat/jei/BreedingBoxJeiRecipe.java b/src/main/java/com/altnoir/poopsky/compat/jei/BreedingBoxJeiRecipe.java new file mode 100644 index 00000000..c86ba2f9 --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/compat/jei/BreedingBoxJeiRecipe.java @@ -0,0 +1,5 @@ +package com.altnoir.poopsky.compat.jei; + +import net.minecraft.world.item.ItemStack; + +public record BreedingBoxJeiRecipe(ItemStack flyInput1, ItemStack flyInput2, ItemStack fecesInput, ItemStack resultFly, ItemStack fallbackFly1, ItemStack fallbackFly2, float chance) {} diff --git a/src/main/java/com/altnoir/poopsky/compat/jei/BreedingBoxRecipeCategory.java b/src/main/java/com/altnoir/poopsky/compat/jei/BreedingBoxRecipeCategory.java new file mode 100644 index 00000000..592fa000 --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/compat/jei/BreedingBoxRecipeCategory.java @@ -0,0 +1,81 @@ +package com.altnoir.poopsky.compat.jei; + +import com.altnoir.poopsky.PoopSky; +import com.altnoir.poopsky.block.PSBlocks; +import com.altnoir.poopsky.item.PSItems; +import mezz.jei.api.gui.builder.IRecipeLayoutBuilder; +import mezz.jei.api.gui.drawable.IDrawable; +import mezz.jei.api.gui.ingredient.IRecipeSlotsView; +import mezz.jei.api.helpers.IJeiHelpers; +import mezz.jei.api.recipe.IFocusGroup; +import mezz.jei.api.recipe.RecipeIngredientRole; +import mezz.jei.api.recipe.RecipeType; +import mezz.jei.api.recipe.category.IRecipeCategory; +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; + +public class BreedingBoxRecipeCategory implements IRecipeCategory { + public static final RecipeType TYPE = RecipeType.create(PoopSky.MOD_ID, "breeding_box", BreedingBoxJeiRecipe.class); + + private static final int WIDTH = 112; + private static final int HEIGHT = 54; + + private final IDrawable icon; + private final Component title; + private final IDrawable arrow; + private final IDrawable slot; + + public BreedingBoxRecipeCategory(IJeiHelpers helpers, IDrawable arrow) { + var guiHelper = helpers.getGuiHelper(); + this.icon = guiHelper.createDrawableItemStack(new ItemStack(PSBlocks.BREEDING_BOX.get())); + this.title = Component.translatable("jei.category." + PoopSky.MOD_ID + ".breeding_box"); + this.arrow = arrow; + this.slot = guiHelper.getSlotDrawable(); + } + + @Override public RecipeType getRecipeType() { return TYPE; } + @Override public Component getTitle() { return title; } + @Override public IDrawable getIcon() { return icon; } + @Override public int getWidth() { return WIDTH; } + @Override public int getHeight() { return HEIGHT; } + + @Override + public void setRecipe(IRecipeLayoutBuilder builder, BreedingBoxJeiRecipe recipe, IFocusGroup focuses) { + builder.addSlot(RecipeIngredientRole.INPUT, 1, 1).addItemStack(recipe.flyInput1()); + builder.addSlot(RecipeIngredientRole.INPUT, 1, 19).addItemStack(recipe.flyInput2()); + builder.addSlot(RecipeIngredientRole.INPUT, 1, 37).addItemStack(recipe.fecesInput()); + + // 变异成功产物 + builder.addSlot(RecipeIngredientRole.OUTPUT, 56, 10) + .addItemStack(recipe.resultFly()) + .addRichTooltipCallback((view, tooltip) -> { + float chance = recipe.chance(); + tooltip.add(Component.translatable("jei.poopsky.breeding_box_chance", + String.format("%.0f", chance * 100)).withStyle(ChatFormatting.GRAY)); + }); + // 变异失败可能产物(亲本品种) + builder.addSlot(RecipeIngredientRole.OUTPUT, 74, 10) + .addItemStack(recipe.fallbackFly1()) + .addRichTooltipCallback((view, tooltip) -> { + tooltip.add(Component.translatable("jei.poopsky.breeding_box_fallback").withStyle(ChatFormatting.GRAY)); + }); + builder.addSlot(RecipeIngredientRole.OUTPUT, 92, 10) + .addItemStack(recipe.fallbackFly2()) + .addRichTooltipCallback((view, tooltip) -> { + tooltip.add(Component.translatable("jei.poopsky.breeding_box_fallback").withStyle(ChatFormatting.GRAY)); + }); + } + + @Override + public void draw(BreedingBoxJeiRecipe recipe, IRecipeSlotsView slotsView, GuiGraphics graphics, double mouseX, double mouseY) { + this.slot.draw(graphics, 0, 0); + this.slot.draw(graphics, 0, 18); + this.slot.draw(graphics, 0, 36); + this.arrow.draw(graphics, 32, 8); + this.slot.draw(graphics, 55, 9); + this.slot.draw(graphics, 73, 9); + this.slot.draw(graphics, 91, 9); + } +} diff --git a/src/main/java/com/altnoir/poopsky/compat/jei/FlyNestJeiRecipe.java b/src/main/java/com/altnoir/poopsky/compat/jei/FlyNestJeiRecipe.java new file mode 100644 index 00000000..77309039 --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/compat/jei/FlyNestJeiRecipe.java @@ -0,0 +1,5 @@ +package com.altnoir.poopsky.compat.jei; + +import net.minecraft.world.item.ItemStack; + +public record FlyNestJeiRecipe(ItemStack flyInput, ItemStack product) {} diff --git a/src/main/java/com/altnoir/poopsky/compat/jei/FlyNestRecipeCategory.java b/src/main/java/com/altnoir/poopsky/compat/jei/FlyNestRecipeCategory.java new file mode 100644 index 00000000..60dbebbe --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/compat/jei/FlyNestRecipeCategory.java @@ -0,0 +1,56 @@ +package com.altnoir.poopsky.compat.jei; + +import com.altnoir.poopsky.PoopSky; +import com.altnoir.poopsky.block.PSBlocks; +import com.altnoir.poopsky.init.PFlyTypes; +import com.altnoir.poopsky.item.p.FlyItem; +import mezz.jei.api.gui.builder.IRecipeLayoutBuilder; +import mezz.jei.api.gui.drawable.IDrawable; +import mezz.jei.api.gui.ingredient.IRecipeSlotsView; +import mezz.jei.api.helpers.IJeiHelpers; +import mezz.jei.api.recipe.IFocusGroup; +import mezz.jei.api.recipe.RecipeIngredientRole; +import mezz.jei.api.recipe.RecipeType; +import mezz.jei.api.recipe.category.IRecipeCategory; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; + +public class FlyNestRecipeCategory implements IRecipeCategory { + public static final RecipeType TYPE = RecipeType.create(PoopSky.MOD_ID, "fly_nest", FlyNestJeiRecipe.class); + + private static final int WIDTH = 82; + private static final int HEIGHT = 36; + + private final IDrawable icon; + private final Component title; + private final IDrawable arrow; + private final IDrawable slot; + + public FlyNestRecipeCategory(IJeiHelpers helpers, IDrawable arrow) { + var guiHelper = helpers.getGuiHelper(); + this.icon = guiHelper.createDrawableItemStack(new ItemStack(PSBlocks.FLY_NEST.get())); + this.title = Component.translatable("jei.category." + PoopSky.MOD_ID + ".fly_nest"); + this.arrow = arrow; + this.slot = guiHelper.getSlotDrawable(); + } + + @Override public RecipeType getRecipeType() { return TYPE; } + @Override public Component getTitle() { return title; } + @Override public IDrawable getIcon() { return icon; } + @Override public int getWidth() { return WIDTH; } + @Override public int getHeight() { return HEIGHT; } + + @Override + public void setRecipe(IRecipeLayoutBuilder builder, FlyNestJeiRecipe recipe, IFocusGroup focuses) { + builder.addSlot(RecipeIngredientRole.INPUT, 1, 9).addItemStack(recipe.flyInput()); + builder.addSlot(RecipeIngredientRole.OUTPUT, 57, 9).addItemStack(recipe.product()); + } + + @Override + public void draw(FlyNestJeiRecipe recipe, IRecipeSlotsView slotsView, GuiGraphics graphics, double mouseX, double mouseY) { + this.slot.draw(graphics, 0, 8); + this.arrow.draw(graphics, 24, 10); + this.slot.draw(graphics, 56, 8); + } +} diff --git a/src/main/java/com/altnoir/poopsky/compat/jei/PSJEIPlugin.java b/src/main/java/com/altnoir/poopsky/compat/jei/PSJEIPlugin.java index a40658b9..fa56c320 100644 --- a/src/main/java/com/altnoir/poopsky/compat/jei/PSJEIPlugin.java +++ b/src/main/java/com/altnoir/poopsky/compat/jei/PSJEIPlugin.java @@ -2,17 +2,24 @@ import com.altnoir.poopsky.PoopSky; import com.altnoir.poopsky.block.PSBlocks; +import com.altnoir.poopsky.init.PFlyTypes; import com.altnoir.poopsky.item.PSItems; import com.altnoir.poopsky.init.PRecipes; +import com.altnoir.poopsky.item.p.FlyItem; +import com.altnoir.poopsky.recipe.BreedingBoxRecipe; +import com.altnoir.poopsky.recipe.FlyNestRecipe; import mezz.jei.api.IModPlugin; import mezz.jei.api.JeiPlugin; import mezz.jei.api.registration.IRecipeCatalystRegistration; import mezz.jei.api.registration.IRecipeCategoryRegistration; import mezz.jei.api.registration.IRecipeRegistration; +import mezz.jei.api.runtime.IJeiRuntime; +import net.minecraft.client.Minecraft; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.item.crafting.RecipeHolder; import java.util.List; import java.util.stream.Stream; @@ -30,15 +37,17 @@ public ResourceLocation getPluginUid() { public void registerCategories(IRecipeCategoryRegistration registration) { var helper = registration.getJeiHelpers().getGuiHelper(); var arrow = helper.createDrawable(PS_JEI_TEXTURE, 0, 18, 22, 15); - var plus = helper.createDrawable(PS_JEI_TEXTURE, 22, 18, 8, 8); registration.addRecipeCategories( new CompooperRecipeCategory(registration.getJeiHelpers(), arrow), - new SieveRecipeCategory(registration.getJeiHelpers(), arrow)); + new SieveRecipeCategory(registration.getJeiHelpers(), arrow), + new FlyNestRecipeCategory(registration.getJeiHelpers(), arrow), + new BreedingBoxRecipeCategory(registration.getJeiHelpers(), arrow)); } @Override public void registerRecipes(IRecipeRegistration registration) { + // 1. 注册不需要 level 的硬编码配方 registration.addRecipes(CompooperRecipeCategory.TYPE, List.of( new CompooperRecipe( Ingredient.of(Stream.empty()), new ItemStack(PSItems.MAGGOTS_SEEDS.get()), PSBlocks.URINE_COMPOOPER.get().defaultBlockState()), @@ -47,18 +56,65 @@ public void registerRecipes(IRecipeRegistration registration) { new CompooperRecipe( Ingredient.of(Items.STICK), new ItemStack(Items.BREEZE_ROD), PSBlocks.POWDER_SNOW_COMPOOPER.get().defaultBlockState()) )); - var level = net.minecraft.client.Minecraft.getInstance().level; - if (level != null) { - registration.addRecipes(SieveRecipeCategory.TYPE, - level.getRecipeManager().getAllRecipesFor(PRecipes.SIEVE_TYPE.get()).stream() - .map(net.minecraft.world.item.crafting.RecipeHolder::value) - .toList()); + + // 2. 获取 level 并进行非空判断 + var level = Minecraft.getInstance().level; + if (level == null) { + // 如果玩家还在主界面,直接返回。 + // 不用担心,当玩家进入游戏时,JEI 会自动重载配方并再次调用这个方法! + return; } + + var recipeManager = level.getRecipeManager(); + + // 3. 注册需要 level 读取的数据包配方 + registration.addRecipes(SieveRecipeCategory.TYPE, + recipeManager.getAllRecipesFor(PRecipes.SIEVE_TYPE.get()).stream() + .map(RecipeHolder::value) + .toList()); + + registration.addRecipes(FlyNestRecipeCategory.TYPE, + recipeManager.getAllRecipesFor(PRecipes.FLY_NEST_TYPE.get()).stream() + .map(holder -> { + FlyNestRecipe recipe = holder.value(); + return new FlyNestJeiRecipe( + FlyItem.withType(PFlyTypes.byId(recipe.flyTypeId())), + recipe.result() + ); + }) + .toList()); + + registration.addRecipes(BreedingBoxRecipeCategory.TYPE, + recipeManager.getAllRecipesFor(PRecipes.BREEDING_BOX_TYPE.get()).stream() + .map(holder -> { + BreedingBoxRecipe recipe = holder.value(); + ItemStack resultFly = FlyItem.withType(PFlyTypes.byId(recipe.result())); + ItemStack parentFly1 = FlyItem.withType(PFlyTypes.byId(recipe.parent1())); + ItemStack parentFly2 = FlyItem.withType(PFlyTypes.byId(recipe.parent2())); + return new BreedingBoxJeiRecipe( + parentFly1, + parentFly2, + new ItemStack(PSItems.POOP.get()), + resultFly, + parentFly1, + parentFly2, + recipe.chance() + ); + }) + .toList()); + } + + @Override + public void onRuntimeAvailable(IJeiRuntime jeiRuntime) { + // 现在的 onRuntimeAvailable 可以保持为空。 + // 这个方法主要是让你拿到 IJeiRuntime 实例,用来在代码里动态控制 JEI 侧边栏(比如动态隐藏某些物品),而不是用来注册配方的。 } @Override public void registerRecipeCatalysts(IRecipeCatalystRegistration registration) { registration.addRecipeCatalyst(new ItemStack(PSBlocks.COMPOOPER.get()), CompooperRecipeCategory.TYPE); registration.addRecipeCatalyst(new ItemStack(PSBlocks.SIEVE.get()), SieveRecipeCategory.TYPE); + registration.addRecipeCatalyst(new ItemStack(PSBlocks.FLY_NEST.get()), FlyNestRecipeCategory.TYPE); + registration.addRecipeCatalyst(new ItemStack(PSBlocks.BREEDING_BOX.get()), BreedingBoxRecipeCategory.TYPE); } } diff --git a/src/main/java/com/altnoir/poopsky/datagen/BreedingBoxRecipeBuilder.java b/src/main/java/com/altnoir/poopsky/datagen/BreedingBoxRecipeBuilder.java new file mode 100644 index 00000000..4b61ba88 --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/datagen/BreedingBoxRecipeBuilder.java @@ -0,0 +1,72 @@ +package com.altnoir.poopsky.datagen; + +import com.altnoir.poopsky.recipe.BreedingBoxRecipe; +import net.minecraft.advancements.Advancement; +import net.minecraft.advancements.AdvancementRequirements; +import net.minecraft.advancements.AdvancementRewards; +import net.minecraft.advancements.Criterion; +import net.minecraft.advancements.critereon.RecipeUnlockedTrigger; +import net.minecraft.data.recipes.RecipeBuilder; +import net.minecraft.data.recipes.RecipeOutput; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.LinkedHashMap; +import java.util.Map; + +public final class BreedingBoxRecipeBuilder implements RecipeBuilder { + private final String parent1; + private final String parent2; + private final String result; + private final float chance; + private final Map> criteria = new LinkedHashMap<>(); + + public BreedingBoxRecipeBuilder(String parent1, String parent2, String result, float chance) { + this.parent1 = parent1; + this.parent2 = parent2; + this.result = result; + this.chance = chance; + } + + public static BreedingBoxRecipeBuilder breedingBox(String parent1, String parent2, String result, float chance) { + return new BreedingBoxRecipeBuilder(parent1, parent2, result, chance); + } + + @Override + public @NotNull RecipeBuilder unlockedBy(String name, Criterion criterion) { + this.criteria.put(name, criterion); + return this; + } + + @Override + public @NotNull RecipeBuilder group(@Nullable String group) { + return this; + } + + @Override + public @NotNull Item getResult() { + return ItemStack.EMPTY.getItem(); + } + + @Override + public void save(@NotNull RecipeOutput recipeOutput, @NotNull ResourceLocation id) { + ensureValid(id); + Advancement.Builder advancementBuilder = recipeOutput.advancement() + .addCriterion("has_the_recipe", RecipeUnlockedTrigger.unlocked(id)) + .rewards(AdvancementRewards.Builder.recipe(id)) + .requirements(AdvancementRequirements.Strategy.OR); + criteria.forEach(advancementBuilder::addCriterion); + + BreedingBoxRecipe recipe = new BreedingBoxRecipe(parent1, parent2, result, chance); + recipeOutput.accept(id, recipe, advancementBuilder.build(id.withPrefix("recipes/"))); + } + + private void ensureValid(ResourceLocation id) { + if (criteria.isEmpty()) { + throw new IllegalStateException("No way of obtaining recipe " + id); + } + } +} diff --git a/src/main/java/com/altnoir/poopsky/datagen/FlyNestRecipeBuilder.java b/src/main/java/com/altnoir/poopsky/datagen/FlyNestRecipeBuilder.java new file mode 100644 index 00000000..e239a443 --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/datagen/FlyNestRecipeBuilder.java @@ -0,0 +1,73 @@ +package com.altnoir.poopsky.datagen; + +import com.altnoir.poopsky.recipe.FlyNestRecipe; +import net.minecraft.advancements.Advancement; +import net.minecraft.advancements.AdvancementRequirements; +import net.minecraft.advancements.AdvancementRewards; +import net.minecraft.advancements.Criterion; +import net.minecraft.advancements.critereon.RecipeUnlockedTrigger; +import net.minecraft.data.recipes.RecipeBuilder; +import net.minecraft.data.recipes.RecipeOutput; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ItemLike; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.LinkedHashMap; +import java.util.Map; + +public final class FlyNestRecipeBuilder implements RecipeBuilder { + private final String flyTypeId; + private final ItemStack result; + private final Map> criteria = new LinkedHashMap<>(); + + public FlyNestRecipeBuilder(String flyTypeId, ItemStack result) { + this.flyTypeId = flyTypeId; + this.result = result; + } + + public static FlyNestRecipeBuilder flyNest(String flyTypeId, ItemStack result) { + return new FlyNestRecipeBuilder(flyTypeId, result); + } + + public static FlyNestRecipeBuilder flyNest(String flyTypeId, ItemLike result) { + return new FlyNestRecipeBuilder(flyTypeId, new ItemStack(result)); + } + + @Override + public @NotNull RecipeBuilder unlockedBy(String name, Criterion criterion) { + this.criteria.put(name, criterion); + return this; + } + + @Override + public @NotNull RecipeBuilder group(@Nullable String group) { + return this; + } + + @Override + public @NotNull Item getResult() { + return result.getItem(); + } + + @Override + public void save(@NotNull RecipeOutput recipeOutput, @NotNull ResourceLocation id) { + ensureValid(id); + Advancement.Builder advancementBuilder = recipeOutput.advancement() + .addCriterion("has_the_recipe", RecipeUnlockedTrigger.unlocked(id)) + .rewards(AdvancementRewards.Builder.recipe(id)) + .requirements(AdvancementRequirements.Strategy.OR); + criteria.forEach(advancementBuilder::addCriterion); + + FlyNestRecipe recipe = new FlyNestRecipe(flyTypeId, result.copy()); + recipeOutput.accept(id, recipe, advancementBuilder.build(id.withPrefix("recipes/"))); + } + + private void ensureValid(ResourceLocation id) { + if (criteria.isEmpty()) { + throw new IllegalStateException("No way of obtaining recipe " + id); + } + } +} diff --git a/src/main/java/com/altnoir/poopsky/datagen/PSBlockLootTableProvider.java b/src/main/java/com/altnoir/poopsky/datagen/PSBlockLootTableProvider.java index 3cab0613..e153232e 100644 --- a/src/main/java/com/altnoir/poopsky/datagen/PSBlockLootTableProvider.java +++ b/src/main/java/com/altnoir/poopsky/datagen/PSBlockLootTableProvider.java @@ -149,6 +149,9 @@ protected void generate() { .setProperties(StatePropertiesPredicate.Builder.properties().hasProperty(RoundwormVinesPlantBlock.SEEDS, true)); add(PSBlocks.ROUNDWORM_VINES_PLANT.get(), createRoundwormVinesDrop(PSItems.ROUNDWORM.get(), builder2)); dropOther(PSBlocks.ROUNDWORM_VINES.get(), PSItems.ROUNDWORM.get()); + + dropSelf(PSBlocks.FLY_NEST.get()); + dropSelf(PSBlocks.BREEDING_BOX.get()); } protected LootTable.@NotNull Builder createPoopPieceDrop(Block block, Item item) { diff --git a/src/main/java/com/altnoir/poopsky/datagen/PSBlockStateProvider.java b/src/main/java/com/altnoir/poopsky/datagen/PSBlockStateProvider.java index f275e752..28569d40 100644 --- a/src/main/java/com/altnoir/poopsky/datagen/PSBlockStateProvider.java +++ b/src/main/java/com/altnoir/poopsky/datagen/PSBlockStateProvider.java @@ -202,6 +202,19 @@ protected void registerStatesAndModels() { registerToiletLava(AllToiletBlocks.RAINBOW_TOILET.get(), "rainbow_concrete"); fluidBlockWithItem(PSBlocks.POOP_LIQUID.get(), "block/poop_liquid"); + + // ——— 苍蝇系统方块 ——— + // ——— 苍蝇系统方块 ——— + var flyNestModel = models().singleTexture("fly_nest", mcLoc("block/cube_all"), mcLoc("block/beehive_side")); + getVariantBuilder(PSBlocks.FLY_NEST.get()).partialState().addModels( + new ConfiguredModel(flyNestModel)); + simpleBlockItem(PSBlocks.FLY_NEST.get(), flyNestModel); + + var breedingBoxModel = models().singleTexture("breeding_box", mcLoc("block/cube_all"), mcLoc("block/bee_nest_side")); + getVariantBuilder(PSBlocks.BREEDING_BOX.get()).partialState().addModels( + new ConfiguredModel(breedingBoxModel)); + simpleBlockItem(PSBlocks.BREEDING_BOX.get(), breedingBoxModel); + makeCropBlock((CropBlock) PSBlocks.MAGGOTS.get(), "maggots_stage", "maggots_stage"); } diff --git a/src/main/java/com/altnoir/poopsky/datagen/PSItemModelProvider.java b/src/main/java/com/altnoir/poopsky/datagen/PSItemModelProvider.java index d8d443f9..89d7c503 100644 --- a/src/main/java/com/altnoir/poopsky/datagen/PSItemModelProvider.java +++ b/src/main/java/com/altnoir/poopsky/datagen/PSItemModelProvider.java @@ -3,6 +3,7 @@ import com.altnoir.poopsky.PoopSky; import com.altnoir.poopsky.block.PSBlocks; import com.altnoir.poopsky.item.PSItems; +import com.altnoir.poopsky.init.PFlyTypes; import net.minecraft.data.PackOutput; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; @@ -97,8 +98,52 @@ protected void registerModels() { trimmedArmorItem(PSItems.OMEN_CHESTPLATE); trimmedArmorItem(PSItems.OMEN_LEGGINGS); trimmedArmorItem(PSItems.OMEN_BOOTS); + + flyCatcherItem(); + flyItemWithOverrides(); + spawnEggItem(PSItems.FLY_SPAWN_EGG); + blockItemModel(PSBlocks.FLY_NEST); + blockItemModel(PSBlocks.BREEDING_BOX); + } + + + private void flyItemWithOverrides() { + for (var entry : PFlyTypes.getAll().entrySet()) { + String id = entry.getKey(); + String texture = id.equals("normal") + ? "minecraft:item/bone_meal" + : "minecraft:item/" + id + "_dye"; + getBuilder("fly_" + id) + .parent(new ModelFile.UncheckedModelFile("item/generated")) + .texture("layer0", texture); + } + + var flyBuilder = getBuilder("fly") + .parent(new ModelFile.UncheckedModelFile("item/generated")) + .texture("layer0", mcLoc("item/bone_meal")); + for (var entry : PFlyTypes.getAll().entrySet()) { + flyBuilder.override() + .predicate(PoopSky.loc("fly_type"), PFlyTypes.getIndex(entry.getValue())) + .model(new ModelFile.UncheckedModelFile(PoopSky.MOD_ID + ":item/fly_" + entry.getKey())) + .end(); + } } + private void spawnEggItem(DeferredItem egg) { + withExistingParent(egg.getId().getPath(), mcLoc("item/template_spawn_egg")); + } + + private void blockItemModel(DeferredBlock block) { + withExistingParent(block.getId().getPath(), modLoc("block/" + block.getId().getPath())); + } + + private void flyCatcherItem() { + getBuilder("fly_catcher") + .parent(new ModelFile.UncheckedModelFile("item/generated")) + .texture("layer0", PoopSky.loc("item/fly_catcher")); + } + + private void wallItem(DeferredBlock block, DeferredBlock baseBlock) { this.withExistingParent(block.getId().getPath(), mcLoc("block/wall_inventory")) .texture("wall", PoopSky.loc("block/" + baseBlock.getId().getPath())); @@ -141,4 +186,4 @@ private void trimmedArmorItem(DeferredItem itemDeferredItem) { }); } } -} \ No newline at end of file +} diff --git a/src/main/java/com/altnoir/poopsky/datagen/PSRecipeProvider.java b/src/main/java/com/altnoir/poopsky/datagen/PSRecipeProvider.java index d0989715..231b32f1 100644 --- a/src/main/java/com/altnoir/poopsky/datagen/PSRecipeProvider.java +++ b/src/main/java/com/altnoir/poopsky/datagen/PSRecipeProvider.java @@ -5,12 +5,13 @@ import com.altnoir.poopsky.block.PSBlocks; import com.altnoir.poopsky.item.PSItems; import com.altnoir.poopsky.recipe.SieveRecipeBuilder; +import com.altnoir.poopsky.init.PRecipes; +import net.minecraft.world.item.Items; import net.minecraft.core.HolderLookup; import net.minecraft.data.PackOutput; import net.minecraft.data.recipes.*; import net.minecraft.tags.ItemTags; import net.minecraft.world.item.Item; -import net.minecraft.world.item.Items; import net.minecraft.world.item.crafting.*; import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.block.Blocks; @@ -524,6 +525,8 @@ protected void buildRecipes(RecipeOutput recipeOutput) { .save(recipeOutput); buildSieveRecipes(recipeOutput); + buildFlyNestRecipes(recipeOutput); + buildBreedingBoxRecipes(recipeOutput); } private void buildSieveRecipes(RecipeOutput recipeOutput) { @@ -727,4 +730,55 @@ protected static String getConversionRecipeName(ItemLike result, ItemLike input) return PoopSky.MOD_ID + ":" + getItemName(result) + "_from_" + getItemName(input); } + + + private void buildFlyNestRecipes(RecipeOutput recipeOutput) { + var flyNestMap = new java.util.LinkedHashMap(); + flyNestMap.put("normal", PSItems.MAGGOTS_SEEDS); + flyNestMap.put("white", Items.BONE_MEAL); + flyNestMap.put("black", Items.COAL); + flyNestMap.put("green", Items.CACTUS); + flyNestMap.put("yellow", Items.YELLOW_DYE); + flyNestMap.put("blue", Items.LAPIS_LAZULI); + flyNestMap.put("red", Items.REDSTONE); + flyNestMap.put("brown", Items.COCOA_BEANS); + flyNestMap.put("gray", Items.GRAY_DYE); + flyNestMap.put("light_gray", Items.LIGHT_GRAY_DYE); + flyNestMap.put("light_blue", Items.LIGHT_BLUE_DYE); + flyNestMap.put("lime", Items.SEA_PICKLE); + flyNestMap.put("magenta", Items.MAGENTA_DYE); + flyNestMap.put("cyan", Items.PRISMARINE_CRYSTALS); + flyNestMap.put("pink", Items.PINK_DYE); + flyNestMap.put("orange", Items.ORANGE_DYE); + flyNestMap.put("purple", Items.AMETHYST_SHARD); + + flyNestMap.forEach((typeId, result) -> { + FlyNestRecipeBuilder.flyNest(typeId, result) + .unlockedBy(getHasName(PSBlocks.FLY_NEST), has(PSBlocks.FLY_NEST)) + .save(recipeOutput, PoopSky.loc(PRecipes.FLY_NEST_FOLDER + "/" + typeId)); + }); + } + + private void buildBreedingBoxRecipes(RecipeOutput recipeOutput) { + record MutationRecipe(String p1, String p2, String result, float chance) {} + var breedingRecipes = java.util.List.of( + new MutationRecipe("green", "blue", "cyan", 0.2f), + new MutationRecipe("purple", "pink", "magenta", 0.2f), + new MutationRecipe("red", "blue", "purple", 0.2f), + new MutationRecipe("red", "yellow", "orange", 0.2f), + new MutationRecipe("white", "black", "gray", 0.2f), + new MutationRecipe("white", "blue", "light_blue", 0.2f), + new MutationRecipe("white", "gray", "light_gray", 0.2f), + new MutationRecipe("white", "green", "lime", 0.2f), + new MutationRecipe("white", "red", "pink", 0.2f) + ); + + for (var recipe : breedingRecipes) { + String id = recipe.p1 + "_plus_" + recipe.p2; + BreedingBoxRecipeBuilder.breedingBox(recipe.p1, recipe.p2, recipe.result, recipe.chance) + .unlockedBy(getHasName(PSBlocks.FLY_NEST), has(PSBlocks.FLY_NEST)) + .save(recipeOutput, PoopSky.loc(PRecipes.BREEDING_BOX_FOLDER + "/" + id)); + } + } + } diff --git a/src/main/java/com/altnoir/poopsky/event/PSModEvents.java b/src/main/java/com/altnoir/poopsky/event/PSModEvents.java index 7f2af16f..d7c7c976 100644 --- a/src/main/java/com/altnoir/poopsky/event/PSModEvents.java +++ b/src/main/java/com/altnoir/poopsky/event/PSModEvents.java @@ -59,5 +59,25 @@ public static void CapabilitiesRegister(RegisterCapabilitiesEvent event) { PBlockEntityType.TOILET_BLOCK_ENTITY.get(), (blockEntity, side) -> blockEntity.fluidTank ); + event.registerBlockEntity( + Capabilities.ItemHandler.BLOCK, + PBlockEntityType.FLY_NEST.get(), + (blockEntity, direction) -> { + if (direction == null || direction == Direction.DOWN) { + return blockEntity.getBottomHandler(); + } + return blockEntity.getTopSideHandler(); + } + ); + event.registerBlockEntity( + Capabilities.ItemHandler.BLOCK, + PBlockEntityType.BREEDING_BOX.get(), + (blockEntity, direction) -> { + if (direction == null || direction == Direction.DOWN) { + return blockEntity.getBottomHandler(); + } + return blockEntity.getTopSideHandler(); + } + ); } } diff --git a/src/main/java/com/altnoir/poopsky/init/PBlockEntityType.java b/src/main/java/com/altnoir/poopsky/init/PBlockEntityType.java index f18f0ecc..b71d326a 100644 --- a/src/main/java/com/altnoir/poopsky/init/PBlockEntityType.java +++ b/src/main/java/com/altnoir/poopsky/init/PBlockEntityType.java @@ -3,9 +3,13 @@ import com.altnoir.poopsky.PoopSky; import com.altnoir.poopsky.block.AllToiletBlocks; import com.altnoir.poopsky.block.PSBlocks; +import com.altnoir.poopsky.block.p.BreedingBoxBlock; +import com.altnoir.poopsky.block.p.FlyNestBlock; import com.altnoir.poopsky.block.entity.PlacerBlockEntity; import com.altnoir.poopsky.block.entity.SieveBlockEntity; import com.altnoir.poopsky.block.entity.ToiletBlockEntity; +import com.altnoir.poopsky.block.entity.FlyNestBlockEntity; +import com.altnoir.poopsky.block.entity.BreedingBoxBlockEntity; import net.minecraft.core.registries.Registries; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntityType; @@ -34,6 +38,14 @@ public class PBlockEntityType { BLOCK_ENTITY_TYPES.register("placer_entity", () -> BlockEntityType.Builder.of(PlacerBlockEntity::new, PSBlocks.PLACER.get()).build(null)); + public static final DeferredHolder, BlockEntityType> FLY_NEST = + BLOCK_ENTITY_TYPES.register("fly_nest", () -> + BlockEntityType.Builder.of(FlyNestBlockEntity::new, PSBlocks.FLY_NEST.get()).build(null)); + + public static final DeferredHolder, BlockEntityType> BREEDING_BOX = + BLOCK_ENTITY_TYPES.register("breeding_box", () -> + BlockEntityType.Builder.of(BreedingBoxBlockEntity::new, PSBlocks.BREEDING_BOX.get()).build(null)); + public static void register(IEventBus eventBus) { BLOCK_ENTITY_TYPES.register(eventBus); } diff --git a/src/main/java/com/altnoir/poopsky/init/PComponents.java b/src/main/java/com/altnoir/poopsky/init/PComponents.java index 08d2418b..356f3439 100644 --- a/src/main/java/com/altnoir/poopsky/init/PComponents.java +++ b/src/main/java/com/altnoir/poopsky/init/PComponents.java @@ -2,8 +2,15 @@ import com.altnoir.poopsky.PoopSky; import com.altnoir.poopsky.block.ToiletComponent; +import com.altnoir.poopsky.init.PFlyTypes.FlyType; +import com.mojang.serialization.Codec; +import io.netty.buffer.ByteBuf; import net.minecraft.core.component.DataComponentType; +import net.minecraft.core.component.DataComponents; import net.minecraft.core.registries.Registries; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.util.ExtraCodecs; import net.neoforged.bus.api.IEventBus; import net.neoforged.neoforge.registries.DeferredHolder; import net.neoforged.neoforge.registries.DeferredRegister; @@ -18,6 +25,13 @@ public class PComponents { .networkSynchronized(ToiletComponent.STREAM_CODEC) ); + public static final DeferredHolder, DataComponentType> FLY_TYPE = REGISTRAR.registerComponentType( + "fly_type", + builder -> builder + .persistent(ExtraCodecs.NON_EMPTY_STRING) + .networkSynchronized(ByteBufCodecs.STRING_UTF8) + ); + public static void register(IEventBus eventBus) { REGISTRAR.register(eventBus); } diff --git a/src/main/java/com/altnoir/poopsky/init/PFlyRecipes.java b/src/main/java/com/altnoir/poopsky/init/PFlyRecipes.java new file mode 100644 index 00000000..1b63f4f8 --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/init/PFlyRecipes.java @@ -0,0 +1,73 @@ +package com.altnoir.poopsky.init; + +import com.altnoir.poopsky.item.PSItems; +import com.altnoir.poopsky.recipe.BreedingBoxRecipe; +import com.altnoir.poopsky.recipe.FlyNestRecipe; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.crafting.RecipeHolder; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; + +import java.util.List; +import java.util.Optional; +import java.util.Random; + +/** + * 苍蝇变异与产物查询工具类。 + * 所有数据从 RecipeManager 中读取 JSON 配方,不硬编码。 + */ +public class PFlyRecipes { + + public record JeiMutationRecipe(PFlyTypes.FlyType parent1, PFlyTypes.FlyType parent2, PFlyTypes.FlyType result, float chance) {} + + /** + * 获取苍蝇品种对应的产物 ItemStack。 + */ + public static ItemStack getProduct(Level level, PFlyTypes.FlyType type) { + if (level == null) return ItemStack.EMPTY; + return level.getRecipeManager() + .getAllRecipesFor(PRecipes.FLY_NEST_TYPE.get()) + .stream() + .filter(holder -> holder.value().matches(type.getSerializedName())) + .findFirst() + .map(holder -> holder.value().result().copy()) + .orElse(ItemStack.EMPTY); + } + + /** + * 变异判定:查找配方;找到配方且概率通过 -> 返回新品种; + * 找不到配方 -> 两个不同品种随机选一个返回,相同品种返回自身。 + */ + public static MutationResult tryMutate(Level level, PFlyTypes.FlyType parent1, PFlyTypes.FlyType parent2) { + if (level == null) return fallbackResult(parent1, parent2); + + // 查找匹配的变异配方 + List> recipes = level.getRecipeManager() + .getAllRecipesFor(PRecipes.BREEDING_BOX_TYPE.get()); + + Random random = new Random(); + for (var holder : recipes) { + BreedingBoxRecipe recipe = holder.value(); + if (recipe.matches(parent1.getSerializedName(), parent2.getSerializedName())) { + if (random.nextFloat() < recipe.chance()) { + return new MutationResult(PFlyTypes.byId(recipe.result()), true); + } + // 变异失败 -> 返回父母中随机一个 + return new MutationResult(random.nextBoolean() ? parent1 : parent2, false); + } + } + + // 没有匹配配方 + return fallbackResult(parent1, parent2); + } + + private static MutationResult fallbackResult(PFlyTypes.FlyType parent1, PFlyTypes.FlyType parent2) { + if (parent1.equals(parent2)) { + return new MutationResult(parent1, false); + } + return new MutationResult(new Random().nextBoolean() ? parent1 : parent2, false); + } + + public record MutationResult(PFlyTypes.FlyType result, boolean isMutation) {} +} + diff --git a/src/main/java/com/altnoir/poopsky/init/PFlyTypes.java b/src/main/java/com/altnoir/poopsky/init/PFlyTypes.java new file mode 100644 index 00000000..a40d7aef --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/init/PFlyTypes.java @@ -0,0 +1,62 @@ +package com.altnoir.poopsky.init; + +import net.minecraft.network.chat.Component; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * 苍蝇品种枚举。 + * 只有一种苍蝇实体对应所有品种,品种由 DataComponent 区分。 + * 翻译写在语言文件中,键为 fly_type.poopsky.<id>。 + */ +public class PFlyTypes { + private static final Map BY_ID = new LinkedHashMap<>(); + + // ——— 基础品种(可通过普通手段获得) ——— + public static final FlyType NORMAL = register("normal"); + public static final FlyType WHITE = register("white"); + public static final FlyType BLACK = register("black"); + public static final FlyType GREEN = register("green"); + public static final FlyType YELLOW = register("yellow"); + public static final FlyType BLUE = register("blue"); + public static final FlyType RED = register("red"); + public static final FlyType BROWN = register("brown"); + + // ——— 只能变异获得 ——— + public static final FlyType GRAY = register("gray"); + public static final FlyType LIGHT_GRAY = register("light_gray"); + public static final FlyType LIGHT_BLUE = register("light_blue"); + public static final FlyType LIME = register("lime"); + public static final FlyType MAGENTA = register("magenta"); + public static final FlyType CYAN = register("cyan"); + public static final FlyType PINK = register("pink"); + public static final FlyType ORANGE = register("orange"); + public static final FlyType PURPLE = register("purple"); + + public static Map getAll() { + return Map.copyOf(BY_ID); + } + + public static int getIndex(FlyType type) { int i = 0; for (var e : BY_ID.entrySet()) { if (e.getValue().equals(type)) return i; i++; } return 0; } + + public static FlyType byId(String id) { + return BY_ID.getOrDefault(id, NORMAL); + } + + private static FlyType register(String id) { + var type = new FlyType(id); + BY_ID.put(id, type); + return type; + } + + public record FlyType(String id) { + public Component getDisplayName() { + return Component.translatable("fly_type.poopsky." + id); + } + + public String getSerializedName() { + return id; + } + } +} diff --git a/src/main/java/com/altnoir/poopsky/init/PMenuTypes.java b/src/main/java/com/altnoir/poopsky/init/PMenuTypes.java new file mode 100644 index 00000000..05fc1be7 --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/init/PMenuTypes.java @@ -0,0 +1,26 @@ +package com.altnoir.poopsky.init; + +import com.altnoir.poopsky.PoopSky; +import com.altnoir.poopsky.inventory.BreedingBoxMenu; +import com.altnoir.poopsky.inventory.FlyNestMenu; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.flag.FeatureFlags; +import net.minecraft.world.inventory.MenuType; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.neoforge.registries.DeferredHolder; +import net.neoforged.neoforge.registries.DeferredRegister; + +public class PMenuTypes { + public static final DeferredRegister> MENU_TYPES = + DeferredRegister.create(BuiltInRegistries.MENU, PoopSky.MOD_ID); + + public static final DeferredHolder, MenuType> FLY_NEST = MENU_TYPES.register( + "fly_nest", () -> new MenuType<>(FlyNestMenu::new, FeatureFlags.DEFAULT_FLAGS)); + + public static final DeferredHolder, MenuType> BREEDING_BOX = MENU_TYPES.register( + "breeding_box", () -> new MenuType<>(BreedingBoxMenu::new, FeatureFlags.DEFAULT_FLAGS)); + + public static void register(IEventBus eventBus) { + MENU_TYPES.register(eventBus); + } +} diff --git a/src/main/java/com/altnoir/poopsky/init/PRecipes.java b/src/main/java/com/altnoir/poopsky/init/PRecipes.java index d1af05b1..a3a940a8 100644 --- a/src/main/java/com/altnoir/poopsky/init/PRecipes.java +++ b/src/main/java/com/altnoir/poopsky/init/PRecipes.java @@ -1,6 +1,8 @@ package com.altnoir.poopsky.init; import com.altnoir.poopsky.PoopSky; +import com.altnoir.poopsky.recipe.BreedingBoxRecipe; +import com.altnoir.poopsky.recipe.FlyNestRecipe; import com.altnoir.poopsky.recipe.SieveRecipe; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.item.crafting.RecipeSerializer; @@ -11,6 +13,8 @@ public class PRecipes { public static final String SIEVE_RECIPE_FOLDER = "sieve"; + public static final String FLY_NEST_FOLDER = "fly_nest"; + public static final String BREEDING_BOX_FOLDER = "breeding_box"; public static final DeferredRegister> SERIALIZERS = DeferredRegister.create(BuiltInRegistries.RECIPE_SERIALIZER, PoopSky.MOD_ID); public static final DeferredRegister> TYPES = DeferredRegister.create(BuiltInRegistries.RECIPE_TYPE, PoopSky.MOD_ID); @@ -22,8 +26,24 @@ public class PRecipes { .register("sieve", () -> RecipeType.simple(PoopSky.loc("sieve"))); + // ——— 苍蝇窝配方 ——— + public static final DeferredHolder, FlyNestRecipe.Serializer> FLY_NEST_SERIALIZER = SERIALIZERS + .register("fly_nest", FlyNestRecipe.Serializer::new); + + public static final DeferredHolder, RecipeType> FLY_NEST_TYPE = TYPES + .register("fly_nest", () -> + RecipeType.simple(PoopSky.loc("fly_nest"))); + + // ——— 繁育箱配方 ——— + public static final DeferredHolder, BreedingBoxRecipe.Serializer> BREEDING_BOX_SERIALIZER = SERIALIZERS + .register("breeding_box", BreedingBoxRecipe.Serializer::new); + + public static final DeferredHolder, RecipeType> BREEDING_BOX_TYPE = TYPES + .register("breeding_box", () -> + RecipeType.simple(PoopSky.loc("breeding_box"))); + public static void register(IEventBus eventBus) { SERIALIZERS.register(eventBus); TYPES.register(eventBus); } -} \ No newline at end of file +} diff --git a/src/main/java/com/altnoir/poopsky/inventory/BreedingBoxMenu.java b/src/main/java/com/altnoir/poopsky/inventory/BreedingBoxMenu.java new file mode 100644 index 00000000..7065d06e --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/inventory/BreedingBoxMenu.java @@ -0,0 +1,159 @@ +package com.altnoir.poopsky.inventory; + +import com.altnoir.poopsky.init.PMenuTypes; +import com.altnoir.poopsky.item.p.FlyItem; +import com.altnoir.poopsky.tag.PSItemTags; +import net.minecraft.world.Container; +import net.minecraft.world.SimpleContainer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; + +/** + * 繁育箱GUI菜单。 + * 布局: + * 3个输入槽(左列:粪便消耗槽 + 两只苍蝇槽,不消耗) + * 3个输出槽(右列) + * 玩家背包 + 快捷栏 + */ +public class BreedingBoxMenu extends AbstractContainerMenu { + // 输入槽 + private static final int FECES_SLOT = 0; + private static final int FLY_SLOT_1 = 1; + private static final int FLY_SLOT_2 = 2; + // 输出槽 + private static final int OUTPUT_SLOT_1 = 3; + + private static final int SLOT_COUNT = 6; + private static final int INV_SLOT_START = 6; + private static final int HOTBAR_SLOT_START = 33; + private static final int HOTBAR_SLOT_END = 42; + + private final Container breedingBox; + + // 客户端用 + public BreedingBoxMenu(int containerId, Inventory playerInventory) { + this(containerId, playerInventory, new SimpleContainer(SLOT_COUNT)); + } + + // 服务端用 + public BreedingBoxMenu(int containerId, Inventory playerInventory, Container container) { + super(PMenuTypes.BREEDING_BOX.get(), containerId); + checkContainerSize(container, SLOT_COUNT); + this.breedingBox = container; + container.startOpen(playerInventory.player); + + // 粪便消耗槽 + this.addSlot(new Slot(container, FECES_SLOT, 44, 17) { + @Override + public boolean mayPlace(ItemStack stack) { + return stack.is(PSItemTags.POOPS); + } + }); + + // 苍蝇槽1(不消耗) + this.addSlot(new Slot(container, FLY_SLOT_1, 44, 35) { + @Override + public boolean mayPlace(ItemStack stack) { + return FlyItem.isFlyItem(stack); + } + }); + + // 苍蝇槽2(不消耗) + this.addSlot(new Slot(container, FLY_SLOT_2, 44, 53) { + @Override + public boolean mayPlace(ItemStack stack) { + return FlyItem.isFlyItem(stack); + } + }); + + // 3个输出槽 + for (int i = 0; i < 3; i++) { + final int slotIndex = OUTPUT_SLOT_1 + i; + this.addSlot(new Slot(container, slotIndex, 116 + (i % 3) * 18, 35) { + @Override + public boolean mayPlace(ItemStack stack) { + return false; + } + }); + } + + // 玩家背包 + for (int k = 0; k < 3; ++k) { + for (int i1 = 0; i1 < 9; ++i1) { + this.addSlot(new Slot(playerInventory, i1 + k * 9 + 9, 8 + i1 * 18, 84 + k * 18)); + } + } + + // 玩家快捷栏 + for (int l = 0; l < 9; ++l) { + this.addSlot(new Slot(playerInventory, l, 8 + l * 18, 142)); + } + } + + @Override + public ItemStack quickMoveStack(Player player, int index) { + ItemStack itemstack = ItemStack.EMPTY; + Slot slot = this.slots.get(index); + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + itemstack = itemstack1.copy(); + + if (index >= FECES_SLOT && index < SLOT_COUNT) { + // 从繁育箱移出到玩家背包 + if (!this.moveItemStackTo(itemstack1, INV_SLOT_START, HOTBAR_SLOT_END, true)) { + return ItemStack.EMPTY; + } + } else if (itemstack1.is(PSItemTags.POOPS)) { + // 粪便 -> 粪便槽 + if (!this.moveItemStackTo(itemstack1, FECES_SLOT, FECES_SLOT + 1, false)) { + return ItemStack.EMPTY; + } + } else if (FlyItem.isFlyItem(itemstack1)) { + // 苍蝇 -> 苍蝇槽 + if (!this.moveItemStackTo(itemstack1, FLY_SLOT_1, FLY_SLOT_2 + 1, false)) { + return ItemStack.EMPTY; + } + } else if (index >= INV_SLOT_START && index < HOTBAR_SLOT_START) { + if (!this.moveItemStackTo(itemstack1, HOTBAR_SLOT_START, HOTBAR_SLOT_END, false)) { + return ItemStack.EMPTY; + } + } else if (index >= HOTBAR_SLOT_START && index < HOTBAR_SLOT_END) { + if (!this.moveItemStackTo(itemstack1, INV_SLOT_START, HOTBAR_SLOT_START, false)) { + return ItemStack.EMPTY; + } + } + + if (itemstack1.isEmpty()) { + slot.setByPlayer(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + + if (itemstack1.getCount() == itemstack.getCount()) { + return ItemStack.EMPTY; + } + + slot.onTake(player, itemstack1); + } + + return itemstack; + } + + @Override + public boolean stillValid(Player player) { + return this.breedingBox.stillValid(player); + } + + @Override + public void removed(Player player) { + super.removed(player); + this.breedingBox.stopOpen(player); + } + + public Container getContainer() { + return breedingBox; + } +} diff --git a/src/main/java/com/altnoir/poopsky/inventory/BreedingBoxScreen.java b/src/main/java/com/altnoir/poopsky/inventory/BreedingBoxScreen.java new file mode 100644 index 00000000..ee40f545 --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/inventory/BreedingBoxScreen.java @@ -0,0 +1,33 @@ +package com.altnoir.poopsky.inventory; + +import com.altnoir.poopsky.PoopSky; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; + +public class BreedingBoxScreen extends AbstractContainerScreen { + private static final ResourceLocation CONTAINER_BACKGROUND = + PoopSky.loc("textures/gui/breeding_box.png"); + + public BreedingBoxScreen(BreedingBoxMenu menu, Inventory playerInventory, Component title) { + super(menu, playerInventory, title); + this.imageHeight = 168; + this.imageWidth = 176; + this.inventoryLabelY = this.imageHeight - 94; + } + + @Override + protected void renderBg(GuiGraphics graphics, float partialTick, int mouseX, int mouseY) { + int x = (this.width - this.imageWidth) / 2; + int y = (this.height - this.imageHeight) / 2; + graphics.blit(CONTAINER_BACKGROUND, x, y, 0, 0, this.imageWidth, 168); + } + + @Override + public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTick) { + super.render(graphics, mouseX, mouseY, partialTick); + this.renderTooltip(graphics, mouseX, mouseY); + } +} diff --git a/src/main/java/com/altnoir/poopsky/inventory/FlyNestMenu.java b/src/main/java/com/altnoir/poopsky/inventory/FlyNestMenu.java new file mode 100644 index 00000000..e8b51da2 --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/inventory/FlyNestMenu.java @@ -0,0 +1,152 @@ +package com.altnoir.poopsky.inventory; + +import com.altnoir.poopsky.init.PMenuTypes; +import com.altnoir.poopsky.item.p.FlyItem; +import net.minecraft.world.Container; +import net.minecraft.world.SimpleContainer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; + +/** + * 苍蝇窝GUI菜单。 + * 布局: + * 1个输入槽(左中,接苍蝇物品,不消耗) + * 4个输出槽(右侧2×2) + * 玩家背包 + 快捷栏 + */ +public class FlyNestMenu extends AbstractContainerMenu { + private static final int INPUT_SLOT = 0; + private static final int OUTPUT_SLOT_1 = 1; + private static final int OUTPUT_SLOT_2 = 2; + private static final int OUTPUT_SLOT_3 = 3; + private static final int OUTPUT_SLOT_4 = 4; + private static final int SLOT_COUNT = 5; + private static final int INV_SLOT_START = 5; + private static final int INV_SLOT_END = 32; + private static final int HOTBAR_SLOT_START = 32; + private static final int HOTBAR_SLOT_END = 41; + + private final Container flyNest; + + // 客户端用构造函数 + public FlyNestMenu(int containerId, Inventory playerInventory) { + this(containerId, playerInventory, new SimpleContainer(SLOT_COUNT)); + } + + // 服务端用构造函数 + public FlyNestMenu(int containerId, Inventory playerInventory, Container container) { + super(PMenuTypes.FLY_NEST.get(), containerId); + checkContainerSize(container, SLOT_COUNT); + this.flyNest = container; + container.startOpen(playerInventory.player); + + // 输入槽(苍蝇,不消耗) + this.addSlot(new Slot(container, INPUT_SLOT, 44, 35) { + @Override + public boolean mayPlace(ItemStack stack) { + return FlyItem.isFlyItem(stack); + } + }); + + // 4个输出槽(2×2网格) + this.addSlot(new Slot(container, OUTPUT_SLOT_1, 116, 17) { + @Override + public boolean mayPlace(ItemStack stack) { + return false; + } + }); + this.addSlot(new Slot(container, OUTPUT_SLOT_2, 134, 17) { + @Override + public boolean mayPlace(ItemStack stack) { + return false; + } + }); + this.addSlot(new Slot(container, OUTPUT_SLOT_3, 116, 35) { + @Override + public boolean mayPlace(ItemStack stack) { + return false; + } + }); + this.addSlot(new Slot(container, OUTPUT_SLOT_4, 134, 35) { + @Override + public boolean mayPlace(ItemStack stack) { + return false; + } + }); + + // 玩家背包 + for (int k = 0; k < 3; ++k) { + for (int i1 = 0; i1 < 9; ++i1) { + this.addSlot(new Slot(playerInventory, i1 + k * 9 + 9, 8 + i1 * 18, 84 + k * 18)); + } + } + + // 玩家快捷栏 + for (int l = 0; l < 9; ++l) { + this.addSlot(new Slot(playerInventory, l, 8 + l * 18, 142)); + } + } + + @Override + public ItemStack quickMoveStack(Player player, int index) { + ItemStack itemstack = ItemStack.EMPTY; + Slot slot = this.slots.get(index); + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + itemstack = itemstack1.copy(); + + if (index >= INPUT_SLOT && index < SLOT_COUNT) { + // 从苍蝇窝移出到玩家背包 + if (!this.moveItemStackTo(itemstack1, INV_SLOT_START, HOTBAR_SLOT_END, true)) { + return ItemStack.EMPTY; + } + } else if (FlyItem.isFlyItem(itemstack1)) { + // 从玩家背包移到输入槽 + if (!this.moveItemStackTo(itemstack1, INPUT_SLOT, INPUT_SLOT + 1, false)) { + return ItemStack.EMPTY; + } + } else if (index >= INV_SLOT_START && index < HOTBAR_SLOT_START) { + // 背包间移动 + if (!this.moveItemStackTo(itemstack1, HOTBAR_SLOT_START, HOTBAR_SLOT_END, false)) { + return ItemStack.EMPTY; + } + } else if (index >= HOTBAR_SLOT_START && index < HOTBAR_SLOT_END) { + if (!this.moveItemStackTo(itemstack1, INV_SLOT_START, HOTBAR_SLOT_START, false)) { + return ItemStack.EMPTY; + } + } + + if (itemstack1.isEmpty()) { + slot.setByPlayer(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + + if (itemstack1.getCount() == itemstack.getCount()) { + return ItemStack.EMPTY; + } + + slot.onTake(player, itemstack1); + } + + return itemstack; + } + + @Override + public boolean stillValid(Player player) { + return this.flyNest.stillValid(player); + } + + @Override + public void removed(Player player) { + super.removed(player); + this.flyNest.stopOpen(player); + } + + public Container getContainer() { + return flyNest; + } +} diff --git a/src/main/java/com/altnoir/poopsky/inventory/FlyNestScreen.java b/src/main/java/com/altnoir/poopsky/inventory/FlyNestScreen.java new file mode 100644 index 00000000..8a52a832 --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/inventory/FlyNestScreen.java @@ -0,0 +1,33 @@ +package com.altnoir.poopsky.inventory; + +import com.altnoir.poopsky.PoopSky; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; + +public class FlyNestScreen extends AbstractContainerScreen { + private static final ResourceLocation CONTAINER_BACKGROUND = + PoopSky.loc("textures/gui/fly_nest.png"); + + public FlyNestScreen(FlyNestMenu menu, Inventory playerInventory, Component title) { + super(menu, playerInventory, title); + this.imageHeight = 168; + this.imageWidth = 176; + this.inventoryLabelY = this.imageHeight - 94; + } + + @Override + protected void renderBg(GuiGraphics graphics, float partialTick, int mouseX, int mouseY) { + int x = (this.width - this.imageWidth) / 2; + int y = (this.height - this.imageHeight) / 2; + graphics.blit(CONTAINER_BACKGROUND, x, y, 0, 0, this.imageWidth, 168); + } + + @Override + public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTick) { + super.render(graphics, mouseX, mouseY, partialTick); + this.renderTooltip(graphics, mouseX, mouseY); + } +} diff --git a/src/main/java/com/altnoir/poopsky/item/PSItems.java b/src/main/java/com/altnoir/poopsky/item/PSItems.java index 67174297..fdfc72a4 100644 --- a/src/main/java/com/altnoir/poopsky/item/PSItems.java +++ b/src/main/java/com/altnoir/poopsky/item/PSItems.java @@ -14,7 +14,6 @@ import net.neoforged.neoforge.registries.DeferredItem; import net.neoforged.neoforge.registries.DeferredRegister; - public class PSItems { public static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(PoopSky.MOD_ID); @@ -125,6 +124,13 @@ public class PSItems { public static final DeferredItem MOON_BOWL_MUSIC_DISC = ITEMS.register("music_disc_moon_bowl", () -> new Item(new Item.Properties().jukeboxPlayable(PSoundEvents.MOON_BOWL_KEY).rarity(Rarity.RARE).stacksTo(1))); + public static final DeferredItem FLY = ITEMS.register("fly", + () -> new FlyItem(new Item.Properties().stacksTo(88))); + public static final DeferredItem FLY_CATCHER = ITEMS.register("fly_catcher", + () -> new FlyCatcherItem(new Item.Properties().stacksTo(1).durability(64))); + public static final DeferredItem FLY_SPAWN_EGG = ITEMS.register("fly_spawn_egg", + () -> new DeferredSpawnEggItem(PEntityType.FLY, 0xC8C800, 0x8B4513, + new Item.Properties())); public static void register(IEventBus eventBus) { ITEMS.register(eventBus); } diff --git a/src/main/java/com/altnoir/poopsky/item/p/FlyCatcherItem.java b/src/main/java/com/altnoir/poopsky/item/p/FlyCatcherItem.java new file mode 100644 index 00000000..90295406 --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/item/p/FlyCatcherItem.java @@ -0,0 +1,48 @@ +package com.altnoir.poopsky.item.p; + +import com.altnoir.poopsky.entity.p.FlyEntity; +import com.altnoir.poopsky.init.PFlyTypes; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; + +/** + * 捕蝇网:右击苍蝇实体将其变为物品形式。 + */ +public class FlyCatcherItem extends Item { + public FlyCatcherItem(Properties properties) { + super(properties); + } + + @Override + public InteractionResult interactLivingEntity(ItemStack stack, Player player, LivingEntity target, InteractionHand hand) { + if (target instanceof FlyEntity fly && fly.isAlive()) { + if (!player.level().isClientSide) { + PFlyTypes.FlyType type = getFlyTypeFromEntity(fly); + ItemStack flyItem = FlyItem.withType(type); + + fly.spawnAtLocation(flyItem); + + player.level().playSound(null, fly.getX(), fly.getY(), fly.getZ(), + SoundEvents.BEEHIVE_EXIT, SoundSource.NEUTRAL, 1.0F, 1.0F); + + if (!player.getAbilities().instabuild) { + stack.hurtAndBreak(1, player, LivingEntity.getSlotForHand(hand)); + } + + fly.discard(); + } + return InteractionResult.SUCCESS; + } + return InteractionResult.PASS; + } + + private static PFlyTypes.FlyType getFlyTypeFromEntity(FlyEntity fly) { + return PFlyTypes.NORMAL; + } +} diff --git a/src/main/java/com/altnoir/poopsky/item/p/FlyItem.java b/src/main/java/com/altnoir/poopsky/item/p/FlyItem.java new file mode 100644 index 00000000..cadbcb00 --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/item/p/FlyItem.java @@ -0,0 +1,52 @@ +package com.altnoir.poopsky.item.p; + +import com.altnoir.poopsky.init.PComponents; +import com.altnoir.poopsky.init.PFlyTypes; +import com.altnoir.poopsky.item.PSItems; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; + +import java.util.List; + +/** + * 苍蝇物品。 + * 只有一种实体对应所有品种,品种由DataComponent区分。 + */ +public class FlyItem extends Item { + public FlyItem(Properties properties) { + super(properties); + } + + /** + * 用指定品种创建苍蝇。 + */ + public static ItemStack withType(PFlyTypes.FlyType type) { + var stack = new ItemStack(PSItems.FLY.get()); + stack.set(PComponents.FLY_TYPE.get(), type.getSerializedName()); + return stack; + } + + // 获取苍蝇品种 + public static PFlyTypes.FlyType getFlyType(ItemStack stack) { + String typeId = stack.get(PComponents.FLY_TYPE.get()); + return typeId != null ? PFlyTypes.byId(typeId) : PFlyTypes.NORMAL; + } + + // 判断物品栈是否为苍蝇(有FLY_TYPE组件) + public static boolean isFlyItem(ItemStack stack) { + return stack.has(PComponents.FLY_TYPE.get()); + } + + @Override + public void appendHoverText(ItemStack stack, TooltipContext context, List tooltipComponents, TooltipFlag tooltipFlag) { + super.appendHoverText(stack, context, tooltipComponents, tooltipFlag); + PFlyTypes.FlyType type = getFlyType(stack); + tooltipComponents.add(Component.translatable("tooltip.poopsky.fly_type") + .append(": ") + .append(type.getDisplayName()) + .withStyle(ChatFormatting.GRAY)); + } +} diff --git a/src/main/java/com/altnoir/poopsky/recipe/BreedingBoxRecipe.java b/src/main/java/com/altnoir/poopsky/recipe/BreedingBoxRecipe.java new file mode 100644 index 00000000..e29f3ebf --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/recipe/BreedingBoxRecipe.java @@ -0,0 +1,86 @@ +package com.altnoir.poopsky.recipe; + +import com.altnoir.poopsky.init.PRecipes; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.HolderLookup; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.item.crafting.RecipeInput; +import net.minecraft.world.item.crafting.RecipeSerializer; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.Level; + +/** + * 繁育箱变异配方:两个父本苍蝇品种 -> 子代品种 + 概率 + */ +public record BreedingBoxRecipe(String parent1, String parent2, String result, float chance) implements Recipe { + + /** + * 双向匹配:parent1+parent2 或 parent2+parent1。 + */ + public boolean matches(String p1, String p2) { + return (parent1.equals(p1) && parent2.equals(p2)) + || (parent1.equals(p2) && parent2.equals(p1)); + } + + @Override + public boolean matches(RecipeInput input, Level level) { + return false; // 不使用标准 RecipeInput 匹配 + } + + @Override + public ItemStack assemble(RecipeInput input, HolderLookup.Provider registries) { + return ItemStack.EMPTY; + } + + @Override + public boolean canCraftInDimensions(int width, int height) { + return true; + } + + @Override + public ItemStack getResultItem(HolderLookup.Provider registries) { + return ItemStack.EMPTY; + } + + @Override + public RecipeSerializer getSerializer() { + return PRecipes.BREEDING_BOX_SERIALIZER.get(); + } + + @Override + public RecipeType getType() { + return PRecipes.BREEDING_BOX_TYPE.get(); + } + + public static class Serializer implements RecipeSerializer { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(inst -> inst.group( + net.minecraft.util.ExtraCodecs.NON_EMPTY_STRING.fieldOf("parent1").forGetter(BreedingBoxRecipe::parent1), + net.minecraft.util.ExtraCodecs.NON_EMPTY_STRING.fieldOf("parent2").forGetter(BreedingBoxRecipe::parent2), + net.minecraft.util.ExtraCodecs.NON_EMPTY_STRING.fieldOf("result").forGetter(BreedingBoxRecipe::result), + com.mojang.serialization.Codec.floatRange(0.0F, 1.0F).fieldOf("chance").forGetter(BreedingBoxRecipe::chance) + ).apply(inst, BreedingBoxRecipe::new)); + + public static final StreamCodec STREAM_CODEC = + StreamCodec.composite( + ByteBufCodecs.STRING_UTF8, BreedingBoxRecipe::parent1, + ByteBufCodecs.STRING_UTF8, BreedingBoxRecipe::parent2, + ByteBufCodecs.STRING_UTF8, BreedingBoxRecipe::result, + ByteBufCodecs.FLOAT, BreedingBoxRecipe::chance, + BreedingBoxRecipe::new); + + @Override + public MapCodec codec() { + return CODEC; + } + + @Override + public StreamCodec streamCodec() { + return STREAM_CODEC; + } + } +} diff --git a/src/main/java/com/altnoir/poopsky/recipe/FlyNestRecipe.java b/src/main/java/com/altnoir/poopsky/recipe/FlyNestRecipe.java new file mode 100644 index 00000000..3ac085de --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/recipe/FlyNestRecipe.java @@ -0,0 +1,78 @@ +package com.altnoir.poopsky.recipe; + +import com.altnoir.poopsky.init.PRecipes; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.HolderLookup; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.item.crafting.RecipeInput; +import net.minecraft.world.item.crafting.RecipeSerializer; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.Level; + +/** + * 苍蝇窝产出配方:fly_type -> 产物 ItemStack + */ +public record FlyNestRecipe(String flyTypeId, ItemStack result) implements Recipe { + + public boolean matches(String flyTypeId) { + return this.flyTypeId.equals(flyTypeId); + } + + @Override + public boolean matches(RecipeInput input, Level level) { + return false; // 不使用标准 RecipeInput 匹配 + } + + @Override + public ItemStack assemble(RecipeInput input, HolderLookup.Provider registries) { + return result.copy(); + } + + @Override + public boolean canCraftInDimensions(int width, int height) { + return true; + } + + @Override + public ItemStack getResultItem(HolderLookup.Provider registries) { + return result; + } + + @Override + public RecipeSerializer getSerializer() { + return PRecipes.FLY_NEST_SERIALIZER.get(); + } + + @Override + public RecipeType getType() { + return PRecipes.FLY_NEST_TYPE.get(); + } + + public static class Serializer implements RecipeSerializer { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(inst -> inst.group( + net.minecraft.util.ExtraCodecs.NON_EMPTY_STRING.fieldOf("fly_type").forGetter(FlyNestRecipe::flyTypeId), + ItemStack.STRICT_CODEC.fieldOf("result").forGetter(FlyNestRecipe::result) + ).apply(inst, FlyNestRecipe::new)); + + public static final StreamCodec STREAM_CODEC = + StreamCodec.composite( + ByteBufCodecs.STRING_UTF8, FlyNestRecipe::flyTypeId, + ItemStack.STREAM_CODEC, FlyNestRecipe::result, + FlyNestRecipe::new); + + @Override + public MapCodec codec() { + return CODEC; + } + + @Override + public StreamCodec streamCodec() { + return STREAM_CODEC; + } + } +} diff --git a/src/main/resources/assets/poopsky/lang/en_us.json b/src/main/resources/assets/poopsky/lang/en_us.json index df164149..46828512 100644 --- a/src/main/resources/assets/poopsky/lang/en_us.json +++ b/src/main/resources/assets/poopsky/lang/en_us.json @@ -293,6 +293,38 @@ "stat.poopsky.poop_stat": "Poop Stat", "itemgroup.poopsky": "POOPSKY", + "fly_type.poopsky.normal": "Normal Fly", + "fly_type.poopsky.white": "White Fly", + "fly_type.poopsky.black": "Black Fly", + "fly_type.poopsky.green": "Green Fly", + "fly_type.poopsky.yellow": "Yellow Fly", + "fly_type.poopsky.blue": "Blue Fly", + "fly_type.poopsky.red": "Red Fly", + "fly_type.poopsky.brown": "Brown Fly", + "fly_type.poopsky.gray": "Gray Fly", + "fly_type.poopsky.light_gray": "Light Gray Fly", + "fly_type.poopsky.light_blue": "Light Blue Fly", + "fly_type.poopsky.lime": "Lime Fly", + "fly_type.poopsky.magenta": "Magenta Fly", + "fly_type.poopsky.cyan": "Cyan Fly", + "fly_type.poopsky.pink": "Pink Fly", + "fly_type.poopsky.orange": "Orange Fly", + "fly_type.poopsky.purple": "Purple Fly", + + "item.poopsky.fly": "Fly", + "item.poopsky.fly_catcher": "Fly Catcher", + "block.poopsky.fly_nest": "Fly Nest", + "block.poopsky.breeding_box": "Breeding Box", + "container.poopsky.fly_nest": "Fly Nest", + "container.poopsky.breeding_box": "Breeding Box", + "tooltip.poopsky.fly_type": "Type", + + "item.poopsky.fly_spawn_egg": "Fly Spawn Egg", + "jei.category.poopsky.fly_nest": "Fly Nest", + "jei.category.poopsky.breeding_box": "Breeding Box", + "jei.poopsky.breeding_box_chance": "Mutation Chance: %.0f%%", + "jei.poopsky.breeding_box_fallback": "Mutation Failed (Random Parent)", + "poopsky.configuration.title": "%s Options", "poopsky.configuration.lavaFluid": "Disable Underground Lava Lakes", "poopsky.configuration.lavaFluid.tooltip": "Disables underground lava lakes during PoopSky world generation", diff --git a/src/main/resources/assets/poopsky/lang/zh_cn.json b/src/main/resources/assets/poopsky/lang/zh_cn.json index c5f27b11..3c74f4d5 100644 --- a/src/main/resources/assets/poopsky/lang/zh_cn.json +++ b/src/main/resources/assets/poopsky/lang/zh_cn.json @@ -293,6 +293,38 @@ "stat.poopsky.poop_stat": "排便数", "itemgroup.poopsky": "空中厕所", + "fly_type.poopsky.normal": "普通苍蝇", + "fly_type.poopsky.white": "白苍蝇", + "fly_type.poopsky.black": "黑苍蝇", + "fly_type.poopsky.green": "绿苍蝇", + "fly_type.poopsky.yellow": "黄苍蝇", + "fly_type.poopsky.blue": "蓝苍蝇", + "fly_type.poopsky.red": "红苍蝇", + "fly_type.poopsky.brown": "棕苍蝇", + "fly_type.poopsky.gray": "灰苍蝇", + "fly_type.poopsky.light_gray": "淡灰苍蝇", + "fly_type.poopsky.light_blue": "淡蓝苍蝇", + "fly_type.poopsky.lime": "黄绿苍蝇", + "fly_type.poopsky.magenta": "品红苍蝇", + "fly_type.poopsky.cyan": "青苍蝇", + "fly_type.poopsky.pink": "粉红苍蝇", + "fly_type.poopsky.orange": "橙苍蝇", + "fly_type.poopsky.purple": "紫苍蝇", + + "item.poopsky.fly": "苍蝇", + "item.poopsky.fly_catcher": "捕蝇网", + "block.poopsky.fly_nest": "苍蝇窝", + "block.poopsky.breeding_box": "繁育箱", + "container.poopsky.fly_nest": "苍蝇窝", + "container.poopsky.breeding_box": "繁育箱", + "tooltip.poopsky.fly_type": "品种", + + "item.poopsky.fly_spawn_egg": "苍蝇刷怪蛋", + "jei.category.poopsky.fly_nest": "苍蝇窝", + "jei.category.poopsky.breeding_box": "繁育箱", + "jei.poopsky.breeding_box_chance": "变异概率: %.0f%%", + "jei.poopsky.breeding_box_fallback": "变异失败(随机亲本)", + "poopsky.configuration.title": "%s 配置", "poopsky.configuration.lavaFluid": "禁用地下熔岩湖", "poopsky.configuration.lavaFluid.tooltip": "在空中厕所世界生成中禁用地下熔岩湖", diff --git a/src/main/resources/assets/poopsky/textures/gui/breeding_box.png b/src/main/resources/assets/poopsky/textures/gui/breeding_box.png new file mode 100644 index 00000000..55276aa4 Binary files /dev/null and b/src/main/resources/assets/poopsky/textures/gui/breeding_box.png differ diff --git a/src/main/resources/assets/poopsky/textures/gui/fly_nest.png b/src/main/resources/assets/poopsky/textures/gui/fly_nest.png new file mode 100644 index 00000000..e02b3297 Binary files /dev/null and b/src/main/resources/assets/poopsky/textures/gui/fly_nest.png differ diff --git a/src/main/resources/assets/poopsky/textures/item/fly_catcher.png b/src/main/resources/assets/poopsky/textures/item/fly_catcher.png new file mode 100644 index 00000000..2e9d0de8 Binary files /dev/null and b/src/main/resources/assets/poopsky/textures/item/fly_catcher.png differ