From 378164b8b9dafc991cbb616f5c985e8321bf13fc Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Tue, 6 Oct 2020 20:43:13 +0200 Subject: [PATCH] More unfinished basin business - Fixed players flailing their limbs around while standing still on a moving contraption - Attempted to reduce drag of remote player positions while on a contraption - Contraptions no longer log out with the player riding them - Attribute filters are now made of brass - Added the ability to disable auto-compat with vanilla recipe types in the configs - Added a recipe type for custom basin/press compacting - Basins can now process items and liquids in recipes - Input items/fluids of a basin can now be extracted or reused in further processing - A basin diagonally below another basin with collect outputs of recipes processed in the top basin for ease of automation - (Temporary debug recipes) --- src/generated/resources/.cache/cache | 12 +- .../assets/create/blockstates/basin.json | 19 +- .../create/blockstates/radial_chassis.json | 24 +- .../assets/create/models/item/basin.json | 2 +- .../crafting/kinetics/attribute_filter.json | 2 +- .../data/create/recipes/compacting/ice.json | 14 + .../recipes/compacting/temp_gabbro.json | 18 ++ .../crafting/kinetics/attribute_filter.json | 2 +- .../create/recipes/emptying/water_bottle.json | 18 ++ .../create/recipes/mixing/temp_cobble.json | 19 ++ .../java/com/simibubi/create/AllBlocks.java | 6 +- .../com/simibubi/create/AllRecipeTypes.java | 15 +- .../simibubi/create/compat/jei/CreateJEI.java | 124 +++++---- .../compat/jei/category/BasinCategory.java | 88 +++++++ .../compat/jei/category/MixingCategory.java | 78 +----- .../compat/jei/category/PackingCategory.java | 47 ++-- .../components/crafter/RecipeGridHandler.java | 15 +- .../CrushingWheelControllerTileEntity.java | 6 +- .../millstone/MillstoneTileEntity.java | 9 +- .../components/mixer/CompactingRecipe.java | 13 + .../mixer/MechanicalMixerTileEntity.java | 72 +---- .../components/mixer/MixingRecipe.java | 89 +------ .../press/MechanicalPressTileEntity.java | 246 ++++++++---------- .../components/press/PressingRecipe.java | 6 +- .../components/saw/SawTileEntity.java | 8 +- .../ContraptionCollider.java | 59 ++++- .../structureMovement/ContraptionEntity.java | 24 +- .../structureMovement/ContraptionHandler.java | 4 +- ...ler.java => ContraptionHandlerClient.java} | 46 +++- .../sync/ClientMotionPacket.java | 10 +- .../sync/LimbSwingUpdatePacket.java | 62 +++++ .../fluids/actors/FillingBySpout.java | 9 +- .../contraptions/processing/BasinBlock.java | 91 ++++++- .../processing/BasinGenerator.java | 32 +++ .../processing/BasinInputInventory.java | 27 -- .../processing/BasinInventory.java | 23 ++ .../processing/BasinOperatingTileEntity.java | 150 +++-------- .../contraptions/processing/BasinRecipe.java | 199 ++++++++++++++ .../processing/BasinRenderer.java | 18 +- .../processing/BasinTileEntity.java | 108 +++++++- .../processing/EmptyingByBasin.java | 75 ++++++ .../processing/EmptyingRecipe.java | 42 +++ .../content/logistics/InWorldProcessing.java | 6 +- .../logistics/item/filter/ItemAttribute.java | 4 +- .../create/foundation/config/CKinetics.java | 4 +- .../create/foundation/config/CRecipes.java | 26 ++ .../create/foundation/config/CServer.java | 2 + .../data/recipe/CompactingRecipeGen.java | 35 +++ .../data/recipe/EmptyingRecipeGen.java | 33 +++ .../data/recipe/MixingRecipeGen.java | 6 + .../data/recipe/ProcessingRecipeGen.java | 2 + .../data/recipe/StandardRecipeGen.java | 4 +- .../foundation/fluid/CombinedTankWrapper.java | 6 + .../create/foundation/fluid/FluidHelper.java | 3 + .../foundation/item/SmartInventory.java | 56 +++- .../foundation/networking/AllPackets.java | 2 + .../fluid/SmartFluidTankBehaviour.java | 43 +-- .../models/block/basin/basin_canal.bbmodel | 1 + .../block/{basin.json => basin/block.json} | 0 .../models/block/basin/block_directional.json | 237 +++++++++++++++++ .../create/textures/block/basin_canal.png | Bin 0 -> 1646 bytes .../textures/item/logistical_filter.png | Bin 376 -> 0 bytes .../create/textures/item/property_filter.png | Bin 329 -> 259 bytes 63 files changed, 1685 insertions(+), 716 deletions(-) create mode 100644 src/generated/resources/data/create/recipes/compacting/ice.json create mode 100644 src/generated/resources/data/create/recipes/compacting/temp_gabbro.json create mode 100644 src/generated/resources/data/create/recipes/emptying/water_bottle.json create mode 100644 src/generated/resources/data/create/recipes/mixing/temp_cobble.json create mode 100644 src/main/java/com/simibubi/create/compat/jei/category/BasinCategory.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/components/mixer/CompactingRecipe.java rename src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/{ContraptionInteractionHandler.java => ContraptionHandlerClient.java} (72%) create mode 100644 src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/sync/LimbSwingUpdatePacket.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/processing/BasinGenerator.java delete mode 100644 src/main/java/com/simibubi/create/content/contraptions/processing/BasinInputInventory.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/processing/BasinInventory.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/processing/BasinRecipe.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/processing/EmptyingByBasin.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/processing/EmptyingRecipe.java create mode 100644 src/main/java/com/simibubi/create/foundation/config/CRecipes.java create mode 100644 src/main/java/com/simibubi/create/foundation/data/recipe/CompactingRecipeGen.java create mode 100644 src/main/java/com/simibubi/create/foundation/data/recipe/EmptyingRecipeGen.java create mode 100644 src/main/resources/assets/create/models/block/basin/basin_canal.bbmodel rename src/main/resources/assets/create/models/block/{basin.json => basin/block.json} (100%) create mode 100644 src/main/resources/assets/create/models/block/basin/block_directional.json create mode 100644 src/main/resources/assets/create/textures/block/basin_canal.png delete mode 100644 src/main/resources/assets/create/textures/item/logistical_filter.png diff --git a/src/generated/resources/.cache/cache b/src/generated/resources/.cache/cache index 5e874a19e..da4fb047b 100644 --- a/src/generated/resources/.cache/cache +++ b/src/generated/resources/.cache/cache @@ -18,7 +18,7 @@ a579c40c43dc2174afb66f42d00d0c4a0efaaeee assets/create/blockstates/andesite_bric 11908c2f8603e61bec88010bc6d0890e6339c6b1 assets/create/blockstates/andesite_funnel.json 398922758a6219544e5b85c91c9cf8a543b437e5 assets/create/blockstates/andesite_pillar.json 1d2d8081581e07d9be4b382aede4f2de4401cc6b assets/create/blockstates/andesite_tunnel.json -f9fa6aa530eb0891a74eadfbebc663172a57147a assets/create/blockstates/basin.json +e555e3c2b2d3f01440e48db4ba88f7e00fd99b6f assets/create/blockstates/basin.json f25693a9429f6337149ff24f27900dc4eb82a7c2 assets/create/blockstates/belt.json cf9045eb16e5299a1d917c4cb536289f49411276 assets/create/blockstates/birch_window.json 94a1a91403eb4b035fec48071e7fcae57a8a6abd assets/create/blockstates/birch_window_pane.json @@ -1045,7 +1045,7 @@ b0f664dd6de3d0ee9afcb6223fbcd53b97fa0d65 assets/create/models/item/andesite_cobb 7490819e7e5445019b6b8cb2538f12a5b6717a46 assets/create/models/item/andesite_funnel.json 75b8b00c2418b9660d35a7fabd0774925cf1c02f assets/create/models/item/andesite_pillar.json c0e35daccfb398947532e9499d6bda963387cd9c assets/create/models/item/andesite_tunnel.json -bf1fc6bdf7fca6f1958a2d3e96202c1cecb50669 assets/create/models/item/basin.json +421e481b7fbca4c4a1080ed703401eb25375e087 assets/create/models/item/basin.json 1da382e7e58eaa9788f5b1d92221ccac573e068f assets/create/models/item/belt_connector.json 9044243882cfd49a2827e1b910a4c9b0e46daa47 assets/create/models/item/birch_window.json 6ed49f59ea91068ef68720f43e67a9237594bdf0 assets/create/models/item/birch_window_pane.json @@ -1481,7 +1481,7 @@ d531f87f425d199aee4777a588c1cd6cab6f5173 data/create/advancements/recipes/create 2eef3201017af03f6a2f0f015645e3ff5e25d9c1 data/create/advancements/recipes/create.base/crafting/curiosities/wand_of_symmetry.json d97d96e1b2ddd25df15fe1ef1c3d084f15bb9789 data/create/advancements/recipes/create.base/crafting/kinetics/adjustable_pulley.json 92416ced6ede6965fd728e1c7336bb05a3e41ea2 data/create/advancements/recipes/create.base/crafting/kinetics/analog_lever.json -2105b4f1fd9a170a100efc083a794fdb9e068924 data/create/advancements/recipes/create.base/crafting/kinetics/attribute_filter.json +3e9753006da898d4569bbeabf95997e8c90847c8 data/create/advancements/recipes/create.base/crafting/kinetics/attribute_filter.json bec8c280b717306f87050b08a418feab53be71cb data/create/advancements/recipes/create.base/crafting/kinetics/basin.json 5af08853632fb5970fe542b3ecbde0ad16d64714 data/create/advancements/recipes/create.base/crafting/kinetics/belt_connector.json 80d87f1dde60adb5334e0cff25a9f0b7f67c1526 data/create/advancements/recipes/create.base/crafting/kinetics/black_seat.json @@ -2428,6 +2428,8 @@ d9021504be855cd2d4d91503a82b84233052adb0 data/create/recipes/blasting/gold_ingot c323b106e88b7de77fea71ff12494abdbb818d15 data/create/recipes/chiseled_limestone_from_limestone_stonecutting.json da9a919b476954c1de34826aa7706bf6056a8f12 data/create/recipes/chiseled_scoria_from_scoria_stonecutting.json 09faa4ddcf9f3907dcdb3ab3e8b68c1deb2486e5 data/create/recipes/chiseled_weathered_limestone_from_weathered_limestone_stonecutting.json +c3cfdc9552a23e4749c42e71fbddd153b76ca708 data/create/recipes/compacting/ice.json +5758a1804287c261e1c953bda599d8495ba7c40a data/create/recipes/compacting/temp_gabbro.json 19526da3a59fc136654ff1bc93c0251581f397a9 data/create/recipes/crafting/appliances/dough.json 7b5f863dda3d05a79cb85943a178eba0bd8a7dc7 data/create/recipes/crafting/appliances/slime_ball.json b159ba84428eee6ef6e23df1766f2a18f2c8a63e data/create/recipes/crafting/appliances/tree_fertilizer.json @@ -2437,7 +2439,7 @@ b159ba84428eee6ef6e23df1766f2a18f2c8a63e data/create/recipes/crafting/appliances fcbc04d0a7eaf820a74bc7e4736a4a581e0a9dff data/create/recipes/crafting/curiosities/wand_of_symmetry.json 696df0fe5f8e29220ea15527f8c119c39b418819 data/create/recipes/crafting/kinetics/adjustable_pulley.json 88de51b451469698665b7319e5b9cfb9a87ae3e0 data/create/recipes/crafting/kinetics/analog_lever.json -6912101930aae627820783c27358dcf2ff4016aa data/create/recipes/crafting/kinetics/attribute_filter.json +cf1f3a6306d47025cebe153cf05949ef69ccbe5a data/create/recipes/crafting/kinetics/attribute_filter.json 059d12526529b2896ed583555373afa31839a0de data/create/recipes/crafting/kinetics/basin.json dcf98e667d321fb4bd9fa6dfec7927a84cdbd5d6 data/create/recipes/crafting/kinetics/belt_connector.json 1123903a11b13448b61cf8f8a5dc2e8013d39ac0 data/create/recipes/crafting/kinetics/black_seat.json @@ -2675,6 +2677,7 @@ ddda28bb6efc43b7e3149756daf53e1664187283 data/create/recipes/dolomite_cobbleston 500ecdfdcf34e9d26256948e206aab4f0b79e659 data/create/recipes/dolomite_cobblestone_wall_from_dolomite_cobblestone_stonecutting.json ff39e629b242ae91e23aec86b0a1f757dd938305 data/create/recipes/dolomite_pillar.json b4a8d14d9a20e812e0acb691b5b511a87e8b0576 data/create/recipes/dolomite_pillar_from_dolomite_stonecutting.json +ae6698363e49f7cb5f2ed52c6b8805bebed31fa2 data/create/recipes/emptying/water_bottle.json 0e11aa1accb71ed62e212f23a7069b7b7b4e8119 data/create/recipes/fancy_andesite_bricks_from_andesite_stonecutting.json 8b86fc9a9416adeaab3f26192a73a481887675c3 data/create/recipes/fancy_andesite_bricks_slab.json c7b762b25c7a6705dba3e922e981be851ac4f36b data/create/recipes/fancy_andesite_bricks_slab_from_fancy_andesite_bricks_stonecutting.json @@ -2860,6 +2863,7 @@ e7bfaa806d57573d060fac0a5e7a84f345b8bb85 data/create/recipes/mixing/andesite_all 76939e4d3e5b615ae21d14c0ff7b917a622bcf80 data/create/recipes/mixing/chromatic_compound.json d9a3dff1288d675ab812eef1eb73cb27dcc71bd2 data/create/recipes/mixing/crushed_brass.json 00b165ea38d834c7955440e87062004a8182c3f8 data/create/recipes/mixing/gunpowder.json +35c4e8a765479861f307afb9ec650f912f92b998 data/create/recipes/mixing/temp_cobble.json 1998c6f84f871d6da58ec29d729401d18f8f1aa1 data/create/recipes/mossy_andesite_from_andesite_stonecutting.json 89929d9cb11b5c589b2ecfa821c61add1ef7b62b data/create/recipes/mossy_dark_scoria_from_dark_scoria_stonecutting.json 4b8b1191dd3a21294293dc5ad237af89b849df28 data/create/recipes/mossy_diorite_from_diorite_stonecutting.json diff --git a/src/generated/resources/assets/create/blockstates/basin.json b/src/generated/resources/assets/create/blockstates/basin.json index 2bc6fd9d1..0e511666e 100644 --- a/src/generated/resources/assets/create/blockstates/basin.json +++ b/src/generated/resources/assets/create/blockstates/basin.json @@ -1,7 +1,22 @@ { "variants": { - "": { - "model": "create:block/basin" + "facing=down": { + "model": "create:block/basin/block" + }, + "facing=north": { + "model": "create:block/basin/block_directional", + "y": 180 + }, + "facing=south": { + "model": "create:block/basin/block_directional" + }, + "facing=west": { + "model": "create:block/basin/block_directional", + "y": 90 + }, + "facing=east": { + "model": "create:block/basin/block_directional", + "y": 270 } } } \ No newline at end of file diff --git a/src/generated/resources/assets/create/blockstates/radial_chassis.json b/src/generated/resources/assets/create/blockstates/radial_chassis.json index c930cf0b5..1aa3d3728 100644 --- a/src/generated/resources/assets/create/blockstates/radial_chassis.json +++ b/src/generated/resources/assets/create/blockstates/radial_chassis.json @@ -149,8 +149,8 @@ }, { "when": { - "sticky_north": "true", - "axis": "x" + "axis": "x", + "sticky_north": "true" }, "apply": { "model": "create:block/radial_chassis_side_x_sticky" @@ -158,8 +158,8 @@ }, { "when": { - "sticky_north": "true", - "axis": "y" + "axis": "y", + "sticky_north": "true" }, "apply": { "model": "create:block/radial_chassis_side_y_sticky", @@ -168,8 +168,8 @@ }, { "when": { - "sticky_north": "true", - "axis": "z" + "axis": "z", + "sticky_north": "true" }, "apply": { "model": "create:block/radial_chassis_side_x_sticky", @@ -178,8 +178,8 @@ }, { "when": { - "sticky_north": "false", - "axis": "x" + "axis": "x", + "sticky_north": "false" }, "apply": { "model": "create:block/radial_chassis_side_x" @@ -187,8 +187,8 @@ }, { "when": { - "sticky_north": "false", - "axis": "y" + "axis": "y", + "sticky_north": "false" }, "apply": { "model": "create:block/radial_chassis_side_y", @@ -197,8 +197,8 @@ }, { "when": { - "sticky_north": "false", - "axis": "z" + "axis": "z", + "sticky_north": "false" }, "apply": { "model": "create:block/radial_chassis_side_x", diff --git a/src/generated/resources/assets/create/models/item/basin.json b/src/generated/resources/assets/create/models/item/basin.json index 1dc14b2e8..a1ed06ff7 100644 --- a/src/generated/resources/assets/create/models/item/basin.json +++ b/src/generated/resources/assets/create/models/item/basin.json @@ -1,3 +1,3 @@ { - "parent": "create:block/basin" + "parent": "create:block/basin/block" } \ No newline at end of file diff --git a/src/generated/resources/data/create/advancements/recipes/create.base/crafting/kinetics/attribute_filter.json b/src/generated/resources/data/create/advancements/recipes/create.base/crafting/kinetics/attribute_filter.json index 5b1c63f80..e41c26b07 100644 --- a/src/generated/resources/data/create/advancements/recipes/create.base/crafting/kinetics/attribute_filter.json +++ b/src/generated/resources/data/create/advancements/recipes/create.base/crafting/kinetics/attribute_filter.json @@ -11,7 +11,7 @@ "conditions": { "items": [ { - "item": "create:andesite_alloy" + "tag": "forge:ingots/brass" } ] } diff --git a/src/generated/resources/data/create/recipes/compacting/ice.json b/src/generated/resources/data/create/recipes/compacting/ice.json new file mode 100644 index 000000000..98ae992e9 --- /dev/null +++ b/src/generated/resources/data/create/recipes/compacting/ice.json @@ -0,0 +1,14 @@ +{ + "type": "create:compacting", + "ingredients": [ + { + "item": "minecraft:ice" + } + ], + "results": [ + { + "fluid": "minecraft:water", + "amount": 250 + } + ] +} \ No newline at end of file diff --git a/src/generated/resources/data/create/recipes/compacting/temp_gabbro.json b/src/generated/resources/data/create/recipes/compacting/temp_gabbro.json new file mode 100644 index 000000000..c5474f6f5 --- /dev/null +++ b/src/generated/resources/data/create/recipes/compacting/temp_gabbro.json @@ -0,0 +1,18 @@ +{ + "type": "create:compacting", + "ingredients": [ + { + "item": "minecraft:cobblestone" + }, + { + "fluidTag": "minecraft:lava", + "amount": 250 + } + ], + "results": [ + { + "item": "create:gabbro", + "count": 1 + } + ] +} \ No newline at end of file diff --git a/src/generated/resources/data/create/recipes/crafting/kinetics/attribute_filter.json b/src/generated/resources/data/create/recipes/crafting/kinetics/attribute_filter.json index 15bd0d895..5e1cfd2cc 100644 --- a/src/generated/resources/data/create/recipes/crafting/kinetics/attribute_filter.json +++ b/src/generated/resources/data/create/recipes/crafting/kinetics/attribute_filter.json @@ -8,7 +8,7 @@ "tag": "minecraft:wool" }, "A": { - "tag": "forge:nuggets/copper" + "tag": "forge:nuggets/brass" } }, "result": { diff --git a/src/generated/resources/data/create/recipes/emptying/water_bottle.json b/src/generated/resources/data/create/recipes/emptying/water_bottle.json new file mode 100644 index 000000000..1a34fd570 --- /dev/null +++ b/src/generated/resources/data/create/recipes/emptying/water_bottle.json @@ -0,0 +1,18 @@ +{ + "type": "create:emptying", + "ingredients": [ + { + "item": "minecraft:potion" + } + ], + "results": [ + { + "item": "minecraft:glass_bottle", + "count": 1 + }, + { + "fluid": "minecraft:water", + "amount": 250 + } + ] +} \ No newline at end of file diff --git a/src/generated/resources/data/create/recipes/mixing/temp_cobble.json b/src/generated/resources/data/create/recipes/mixing/temp_cobble.json new file mode 100644 index 000000000..f20d0e6cf --- /dev/null +++ b/src/generated/resources/data/create/recipes/mixing/temp_cobble.json @@ -0,0 +1,19 @@ +{ + "type": "create:mixing", + "ingredients": [ + { + "fluidTag": "minecraft:water", + "amount": 250 + }, + { + "fluidTag": "minecraft:lava", + "amount": 250 + } + ], + "results": [ + { + "item": "minecraft:cobblestone", + "count": 1 + } + ] +} \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/AllBlocks.java b/src/main/java/com/simibubi/create/AllBlocks.java index c90ae2de4..c7f9c8cef 100644 --- a/src/main/java/com/simibubi/create/AllBlocks.java +++ b/src/main/java/com/simibubi/create/AllBlocks.java @@ -73,6 +73,7 @@ import com.simibubi.create.content.contraptions.fluids.tank.FluidTankGenerator; import com.simibubi.create.content.contraptions.fluids.tank.FluidTankItem; import com.simibubi.create.content.contraptions.fluids.tank.FluidTankModel; import com.simibubi.create.content.contraptions.processing.BasinBlock; +import com.simibubi.create.content.contraptions.processing.BasinGenerator; import com.simibubi.create.content.contraptions.processing.BasinMovementBehaviour; import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock; import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlockItem; @@ -408,9 +409,10 @@ public class AllBlocks { public static final BlockEntry BASIN = REGISTRATE.block("basin", BasinBlock::new) .initialProperties(SharedProperties::stone) - .blockstate((ctx, prov) -> prov.simpleBlock(ctx.getEntry(), AssetLookup.standardModel(ctx, prov))) + .blockstate(new BasinGenerator()::generate) .onRegister(addMovementBehaviour(new BasinMovementBehaviour())) - .simpleItem() + .item() + .transform(customItemModel("_", "block")) .register(); public static final BlockEntry BLAZE_BURNER = diff --git a/src/main/java/com/simibubi/create/AllRecipeTypes.java b/src/main/java/com/simibubi/create/AllRecipeTypes.java index 2e2e6515e..908e39e87 100644 --- a/src/main/java/com/simibubi/create/AllRecipeTypes.java +++ b/src/main/java/com/simibubi/create/AllRecipeTypes.java @@ -1,5 +1,6 @@ package com.simibubi.create; +import java.util.Optional; import java.util.function.Supplier; import com.simibubi.create.compat.jei.ConversionRecipe; @@ -7,10 +8,13 @@ import com.simibubi.create.content.contraptions.components.crafter.MechanicalCra import com.simibubi.create.content.contraptions.components.crusher.CrushingRecipe; import com.simibubi.create.content.contraptions.components.fan.SplashingRecipe; import com.simibubi.create.content.contraptions.components.millstone.MillingRecipe; +import com.simibubi.create.content.contraptions.components.mixer.CompactingRecipe; import com.simibubi.create.content.contraptions.components.mixer.MixingRecipe; import com.simibubi.create.content.contraptions.components.press.PressingRecipe; import com.simibubi.create.content.contraptions.components.saw.CuttingRecipe; import com.simibubi.create.content.contraptions.fluids.actors.FillingRecipe; +import com.simibubi.create.content.contraptions.processing.BasinRecipe; +import com.simibubi.create.content.contraptions.processing.EmptyingRecipe; import com.simibubi.create.content.contraptions.processing.ProcessingRecipe; import com.simibubi.create.content.contraptions.processing.ProcessingRecipeBuilder.ProcessingRecipeFactory; import com.simibubi.create.content.contraptions.processing.ProcessingRecipeSerializer; @@ -25,22 +29,26 @@ import net.minecraft.item.crafting.IRecipeType; import net.minecraft.item.crafting.ShapedRecipe; import net.minecraft.util.ResourceLocation; import net.minecraft.util.registry.Registry; +import net.minecraft.world.World; import net.minecraftforge.event.RegistryEvent; public enum AllRecipeTypes { BLOCKZAPPER_UPGRADE(BlockzapperUpgradeRecipe.Serializer::new, IRecipeType.CRAFTING), MECHANICAL_CRAFTING(MechanicalCraftingRecipe.Serializer::new), - + CONVERSION(processingSerializer(ConversionRecipe::new)), CRUSHING(processingSerializer(CrushingRecipe::new)), CUTTING(processingSerializer(CuttingRecipe::new)), MILLING(processingSerializer(MillingRecipe::new)), + BASIN(processingSerializer(BasinRecipe::new)), MIXING(processingSerializer(MixingRecipe::new)), + COMPACTING(processingSerializer(CompactingRecipe::new)), PRESSING(processingSerializer(PressingRecipe::new)), SANDPAPER_POLISHING(processingSerializer(SandPaperPolishingRecipe::new)), SPLASHING(processingSerializer(SplashingRecipe::new)), FILLING(processingSerializer(FillingRecipe::new)), + EMPTYING(processingSerializer(EmptyingRecipe::new)), ; @@ -89,4 +97,9 @@ public enum AllRecipeTypes { public > T getType() { return (T) type; } + + public > Optional find(C inv, World world) { + return world.getRecipeManager() + .getRecipe(getType(), inv, world); + } } diff --git a/src/main/java/com/simibubi/create/compat/jei/CreateJEI.java b/src/main/java/com/simibubi/create/compat/jei/CreateJEI.java index 41ba41b63..bdc276c35 100644 --- a/src/main/java/com/simibubi/create/compat/jei/CreateJEI.java +++ b/src/main/java/com/simibubi/create/compat/jei/CreateJEI.java @@ -23,10 +23,12 @@ import com.simibubi.create.compat.jei.category.PressingCategory; import com.simibubi.create.compat.jei.category.SawingCategory; import com.simibubi.create.compat.jei.category.SmokingViaFanCategory; import com.simibubi.create.compat.jei.category.SplashingCategory; -import com.simibubi.create.content.contraptions.components.mixer.MixingRecipe; import com.simibubi.create.content.contraptions.components.press.MechanicalPressTileEntity; +import com.simibubi.create.content.contraptions.processing.BasinRecipe; import com.simibubi.create.content.logistics.block.inventories.AdjustableCrateScreen; import com.simibubi.create.content.schematics.block.SchematicannonScreen; +import com.simibubi.create.foundation.config.AllConfigs; +import com.simibubi.create.foundation.config.CRecipes; import com.simibubi.create.foundation.utility.Lang; import mezz.jei.api.IModPlugin; @@ -96,44 +98,62 @@ public class CreateJEI implements IModPlugin { @Override public void registerCategories(IRecipeCategoryRegistration registration) { - registration - .addRecipeCategories(millingCategory, crushingCategory, splashingCategory, pressingCategory, - smokingCategory, blastingCategory, blockzapperCategory, mixingCategory, sawingCategory, - blockCuttingCategory, packingCategory, polishingCategory, mysteryConversionCategory, - mechanicalCraftingCategory); + registration.addRecipeCategories(millingCategory, crushingCategory, splashingCategory, pressingCategory, + smokingCategory, blastingCategory, blockzapperCategory, mixingCategory, sawingCategory, + blockCuttingCategory, packingCategory, polishingCategory, mysteryConversionCategory, + mechanicalCraftingCategory); } @Override public void registerRecipes(IRecipeRegistration registration) { + CRecipes recipeConfig = AllConfigs.SERVER.recipes; + registration.addRecipes(findRecipes(AllRecipeTypes.MILLING), millingCategory.getUid()); registration.addRecipes(findRecipes(AllRecipeTypes.CRUSHING), crushingCategory.getUid()); - registration.addRecipes(findRecipesByTypeExcluding(AllRecipeTypes.MILLING.getType(), AllRecipeTypes.CRUSHING.getType()), - crushingCategory.getUid()); + registration.addRecipes( + findRecipesByTypeExcluding(AllRecipeTypes.MILLING.getType(), AllRecipeTypes.CRUSHING.getType()), + crushingCategory.getUid()); registration.addRecipes(findRecipes(AllRecipeTypes.SPLASHING), splashingCategory.getUid()); registration.addRecipes(findRecipes(AllRecipeTypes.PRESSING), pressingCategory.getUid()); registration.addRecipes(findRecipesById(AllRecipeTypes.BLOCKZAPPER_UPGRADE.serializer.getRegistryName()), - blockzapperCategory.getUid()); + blockzapperCategory.getUid()); registration.addRecipes(findRecipesByType(IRecipeType.SMOKING), smokingCategory.getUid()); registration.addRecipes(findRecipesByTypeExcluding(IRecipeType.SMELTING, IRecipeType.SMOKING), - blastingCategory.getUid()); + blastingCategory.getUid()); registration.addRecipes(findRecipes(AllRecipeTypes.MIXING), mixingCategory.getUid()); - registration.addRecipes(findRecipes(r -> r.getSerializer() == IRecipeSerializer.CRAFTING_SHAPELESS - && !MechanicalPressTileEntity.canCompress(r.getIngredients())).stream().map(MixingRecipe::convertShapeless) - .collect(Collectors.toList()), + + if (recipeConfig.allowShapelessInMixer.get()) + registration.addRecipes(findRecipes(r -> r.getSerializer() == IRecipeSerializer.CRAFTING_SHAPELESS + && !MechanicalPressTileEntity.canCompress(r.getIngredients())).stream() + .map(BasinRecipe::convert) + .collect(Collectors.toList()), mixingCategory.getUid()); + registration.addRecipes(findRecipes(AllRecipeTypes.CUTTING), sawingCategory.getUid()); - registration.addRecipes( + + if (recipeConfig.allowStonecuttingOnSaw.get()) + registration.addRecipes( CondensedBlockCuttingRecipe.condenseRecipes(findRecipesByType(IRecipeType.STONECUTTING)), blockCuttingCategory.getUid()); - registration.addRecipes(findRecipes( - r -> (r instanceof ICraftingRecipe) && MechanicalPressTileEntity.canCompress(r.getIngredients())), + + registration.addRecipes(findRecipes(AllRecipeTypes.COMPACTING), packingCategory.getUid()); + if (recipeConfig.allowShapedSquareInPress.get()) + registration.addRecipes(findRecipes( + r -> (r instanceof ICraftingRecipe) && MechanicalPressTileEntity.canCompress(r.getIngredients())) + .stream() + .map(BasinRecipe::convert) + .collect(Collectors.toList()), packingCategory.getUid()); + registration.addRecipes(findRecipes(AllRecipeTypes.SANDPAPER_POLISHING), polishingCategory.getUid()); registration.addRecipes(MysteriousItemConversionCategory.getRecipes(), mysteryConversionCategory.getUid()); registration.addRecipes(findRecipes(r -> (r.getType() == AllRecipeTypes.MECHANICAL_CRAFTING.type)), - mechanicalCraftingCategory.getUid()); - registration.addRecipes(findRecipes(r -> (r.getType() == IRecipeType.CRAFTING - && r.getType() != AllRecipeTypes.MECHANICAL_CRAFTING.type) && (r instanceof ShapedRecipe)), + mechanicalCraftingCategory.getUid()); + + if (recipeConfig.allowRegularCraftingInCrafter.get()) + registration.addRecipes(findRecipes( + r -> (r.getType() == IRecipeType.CRAFTING && r.getType() != AllRecipeTypes.MECHANICAL_CRAFTING.type) + && (r instanceof ShapedRecipe)), mechanicalCraftingCategory.getUid()); } @@ -141,17 +161,13 @@ public class CreateJEI implements IModPlugin { public void registerRecipeCatalysts(IRecipeCatalystRegistration registration) { ItemStack fan = new ItemStack(AllBlocks.ENCASED_FAN.get()); - ItemStack splashingFan = fan - .copy() - .setDisplayName(new StringTextComponent(TextFormatting.RESET + Lang.translate("recipe.splashing.fan"))); - ItemStack smokingFan = fan - .copy() - .setDisplayName( - new StringTextComponent(TextFormatting.RESET + Lang.translate("recipe.smokingViaFan.fan"))); - ItemStack blastingFan = fan - .copy() - .setDisplayName( - new StringTextComponent(TextFormatting.RESET + Lang.translate("recipe.blastingViaFan.fan"))); + ItemStack splashingFan = fan.copy() + .setDisplayName(new StringTextComponent(TextFormatting.RESET + Lang.translate("recipe.splashing.fan"))); + ItemStack smokingFan = fan.copy() + .setDisplayName(new StringTextComponent(TextFormatting.RESET + Lang.translate("recipe.smokingViaFan.fan"))); + ItemStack blastingFan = fan.copy() + .setDisplayName( + new StringTextComponent(TextFormatting.RESET + Lang.translate("recipe.blastingViaFan.fan"))); registration.addRecipeCatalyst(new ItemStack(AllBlocks.MILLSTONE.get()), millingCategory.getUid()); registration.addRecipeCatalyst(new ItemStack(AllBlocks.CRUSHING_WHEEL.get()), crushingCategory.getUid()); @@ -170,9 +186,8 @@ public class CreateJEI implements IModPlugin { registration.addRecipeCatalyst(new ItemStack(AllBlocks.BASIN.get()), packingCategory.getUid()); registration.addRecipeCatalyst(AllItems.SAND_PAPER.asStack(), polishingCategory.getUid()); registration.addRecipeCatalyst(AllItems.RED_SAND_PAPER.asStack(), polishingCategory.getUid()); - registration - .addRecipeCatalyst(new ItemStack(AllBlocks.MECHANICAL_CRAFTER.get()), - mechanicalCraftingCategory.getUid()); + registration.addRecipeCatalyst(new ItemStack(AllBlocks.MECHANICAL_CRAFTER.get()), + mechanicalCraftingCategory.getUid()); } @Override @@ -186,30 +201,29 @@ public class CreateJEI implements IModPlugin { } private static List> findRecipes(Predicate> pred) { - return Minecraft.getInstance().world - .getRecipeManager() - .getRecipes() - .stream() - .filter(pred) - .collect(Collectors.toList()); + return Minecraft.getInstance().world.getRecipeManager() + .getRecipes() + .stream() + .filter(pred) + .collect(Collectors.toList()); } private static List> findRecipesByType(IRecipeType type) { - return Minecraft.getInstance().world - .getRecipeManager() - .getRecipes() - .stream() - .filter(r -> r.getType() == type) - .collect(Collectors.toList()); + return Minecraft.getInstance().world.getRecipeManager() + .getRecipes() + .stream() + .filter(r -> r.getType() == type) + .collect(Collectors.toList()); } private static List> findRecipesById(ResourceLocation id) { - return Minecraft.getInstance().world - .getRecipeManager() - .getRecipes() - .stream() - .filter(r -> r.getSerializer().getRegistryName().equals(id)) - .collect(Collectors.toList()); + return Minecraft.getInstance().world.getRecipeManager() + .getRecipes() + .stream() + .filter(r -> r.getSerializer() + .getRegistryName() + .equals(id)) + .collect(Collectors.toList()); } private static List> findRecipesByTypeExcluding(IRecipeType type, IRecipeType excludingType) { @@ -217,10 +231,14 @@ public class CreateJEI implements IModPlugin { List> byExcludingType = findRecipesByType(excludingType); byType.removeIf(recipe -> { for (IRecipe r : byExcludingType) { - ItemStack[] matchingStacks = recipe.getIngredients().get(0).getMatchingStacks(); + ItemStack[] matchingStacks = recipe.getIngredients() + .get(0) + .getMatchingStacks(); if (matchingStacks.length == 0) return true; - if (r.getIngredients().get(0).test(matchingStacks[0])) + if (r.getIngredients() + .get(0) + .test(matchingStacks[0])) return true; } return false; diff --git a/src/main/java/com/simibubi/create/compat/jei/category/BasinCategory.java b/src/main/java/com/simibubi/create/compat/jei/category/BasinCategory.java new file mode 100644 index 000000000..02f1463cb --- /dev/null +++ b/src/main/java/com/simibubi/create/compat/jei/category/BasinCategory.java @@ -0,0 +1,88 @@ +package com.simibubi.create.compat.jei.category; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.mutable.MutableInt; + +import com.simibubi.create.content.contraptions.processing.BasinRecipe; +import com.simibubi.create.content.contraptions.processing.HeatCondition; +import com.simibubi.create.foundation.gui.AllGuiTextures; +import com.simibubi.create.foundation.item.ItemHelper; +import com.simibubi.create.foundation.utility.Pair; + +import mezz.jei.api.constants.VanillaTypes; +import mezz.jei.api.gui.IRecipeLayout; +import mezz.jei.api.gui.drawable.IDrawable; +import mezz.jei.api.gui.ingredient.IGuiItemStackGroup; +import mezz.jei.api.ingredients.IIngredients; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.Ingredient; +import net.minecraft.util.NonNullList; + +public class BasinCategory extends CreateRecipeCategory { + + public BasinCategory(String id, IDrawable icon, IDrawable background) { + super(id, icon, background); + } + + @Override + public Class getRecipeClass() { + return BasinRecipe.class; + } + + @Override + public void setIngredients(BasinRecipe recipe, IIngredients ingredients) { + ingredients.setInputIngredients(recipe.getIngredients()); + ingredients.setOutput(VanillaTypes.ITEM, recipe.getRecipeOutput()); + } + + @Override + public void setRecipe(IRecipeLayout recipeLayout, BasinRecipe recipe, IIngredients ingredients) { + IGuiItemStackGroup itemStacks = recipeLayout.getItemStacks(); + NonNullList recipeIngredients = recipe.getIngredients(); + List> actualIngredients = ItemHelper.condenseIngredients(recipeIngredients); + + int size = actualIngredients.size(); + int xOffset = size < 3 ? (3 - size) * 19 / 2 : 0; + int yOffset = recipe.getRequiredHeat() != HeatCondition.NONE ? 30 : 10; + + int i; + for (i = 0; i < actualIngredients.size(); i++) { + itemStacks.init(i, true, 16 + xOffset + (i % 3) * 19, 50 - (i / 3) * 19 + yOffset); + List stacks = new ArrayList<>(); + Pair pair = actualIngredients.get(i); + Ingredient ingredient = pair.getFirst(); + MutableInt amount = pair.getSecond(); + + for (ItemStack itemStack : ingredient.getMatchingStacks()) { + ItemStack stack = itemStack.copy(); + stack.setCount(amount.getValue()); + stacks.add(stack); + } + + itemStacks.set(i, stacks); + } + + itemStacks.init(i, false, 141, 50 + yOffset); + itemStacks.set(i, recipe.getRecipeOutput() + .getStack()); + } + + @Override + public void draw(BasinRecipe recipe, double mouseX, double mouseY) { + List> actualIngredients = ItemHelper.condenseIngredients(recipe.getIngredients()); + + int size = actualIngredients.size(); + int xOffset = size < 3 ? (3 - size) * 19 / 2 : 0; + HeatCondition requiredHeat = recipe.getRequiredHeat(); + int yOffset = requiredHeat != HeatCondition.NONE ? 30 : 10; + for (int i = 0; i < size; i++) + AllGuiTextures.JEI_SLOT.draw(16 + xOffset + (i % 3) * 19, 50 - (i / 3) * 19 + yOffset); + + AllGuiTextures.JEI_SLOT.draw(141, 50 + yOffset); + AllGuiTextures.JEI_DOWN_ARROW.draw(136, 32 + yOffset); + AllGuiTextures.JEI_SHADOW.draw(81, 57 + yOffset); + } + +} diff --git a/src/main/java/com/simibubi/create/compat/jei/category/MixingCategory.java b/src/main/java/com/simibubi/create/compat/jei/category/MixingCategory.java index ce8697c94..d379b3436 100644 --- a/src/main/java/com/simibubi/create/compat/jei/category/MixingCategory.java +++ b/src/main/java/com/simibubi/create/compat/jei/category/MixingCategory.java @@ -1,28 +1,12 @@ package com.simibubi.create.compat.jei.category; -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.lang3.mutable.MutableInt; - import com.simibubi.create.AllBlocks; import com.simibubi.create.compat.jei.category.animations.AnimatedBlazeBurner; import com.simibubi.create.compat.jei.category.animations.AnimatedMixer; -import com.simibubi.create.content.contraptions.components.mixer.MixingRecipe; +import com.simibubi.create.content.contraptions.processing.BasinRecipe; import com.simibubi.create.content.contraptions.processing.HeatCondition; -import com.simibubi.create.foundation.gui.AllGuiTextures; -import com.simibubi.create.foundation.item.ItemHelper; -import com.simibubi.create.foundation.utility.Pair; -import mezz.jei.api.constants.VanillaTypes; -import mezz.jei.api.gui.IRecipeLayout; -import mezz.jei.api.gui.ingredient.IGuiItemStackGroup; -import mezz.jei.api.ingredients.IIngredients; -import net.minecraft.item.ItemStack; -import net.minecraft.item.crafting.Ingredient; -import net.minecraft.util.NonNullList; - -public class MixingCategory extends CreateRecipeCategory { +public class MixingCategory extends BasinCategory { private AnimatedMixer mixer = new AnimatedMixer(); private AnimatedBlazeBurner heater = new AnimatedBlazeBurner(); @@ -33,63 +17,9 @@ public class MixingCategory extends CreateRecipeCategory { } @Override - public Class getRecipeClass() { - return MixingRecipe.class; - } - - @Override - public void setIngredients(MixingRecipe recipe, IIngredients ingredients) { - ingredients.setInputIngredients(recipe.getIngredients()); - ingredients.setOutput(VanillaTypes.ITEM, recipe.getRecipeOutput()); - } - - @Override - public void setRecipe(IRecipeLayout recipeLayout, MixingRecipe recipe, IIngredients ingredients) { - IGuiItemStackGroup itemStacks = recipeLayout.getItemStacks(); - NonNullList recipeIngredients = recipe.getIngredients(); - List> actualIngredients = ItemHelper.condenseIngredients(recipeIngredients); - - int size = actualIngredients.size(); - int xOffset = size < 3 ? (3 - size) * 19 / 2 : 0; - int yOffset = recipe.getRequiredHeat() != HeatCondition.NONE ? 30 : 10; - - int i; - for (i = 0; i < actualIngredients.size(); i++) { - itemStacks.init(i, true, 16 + xOffset + (i % 3) * 19, 50 - (i / 3) * 19 + yOffset); - List stacks = new ArrayList<>(); - Pair pair = actualIngredients.get(i); - Ingredient ingredient = pair.getFirst(); - MutableInt amount = pair.getSecond(); - - for (ItemStack itemStack : ingredient.getMatchingStacks()) { - ItemStack stack = itemStack.copy(); - stack.setCount(amount.getValue()); - stacks.add(stack); - } - - itemStacks.set(i, stacks); - } - - itemStacks.init(i, false, 141, 50 + yOffset); - itemStacks.set(i, recipe.getRecipeOutput() - .getStack()); - } - - @Override - public void draw(MixingRecipe recipe, double mouseX, double mouseY) { - List> actualIngredients = ItemHelper.condenseIngredients(recipe.getIngredients()); - - int size = actualIngredients.size(); - int xOffset = size < 3 ? (3 - size) * 19 / 2 : 0; + public void draw(BasinRecipe recipe, double mouseX, double mouseY) { + super.draw(recipe, mouseX, mouseY); HeatCondition requiredHeat = recipe.getRequiredHeat(); - int yOffset = requiredHeat != HeatCondition.NONE ? 30 : 10; - for (int i = 0; i < size; i++) - AllGuiTextures.JEI_SLOT.draw(16 + xOffset + (i % 3) * 19, 50 - (i / 3) * 19 + yOffset); - - AllGuiTextures.JEI_SLOT.draw(141, 50 + yOffset); - AllGuiTextures.JEI_DOWN_ARROW.draw(136, 32 + yOffset); - AllGuiTextures.JEI_SHADOW.draw(81, 57 + yOffset); - if (requiredHeat != HeatCondition.NONE) heater.withHeat(requiredHeat.visualizeAsBlazeBurner()) .draw(getBackground().getWidth() / 2 + 3, 55); diff --git a/src/main/java/com/simibubi/create/compat/jei/category/PackingCategory.java b/src/main/java/com/simibubi/create/compat/jei/category/PackingCategory.java index daaee43a5..b554fea4f 100644 --- a/src/main/java/com/simibubi/create/compat/jei/category/PackingCategory.java +++ b/src/main/java/com/simibubi/create/compat/jei/category/PackingCategory.java @@ -4,18 +4,16 @@ import java.util.Arrays; import com.simibubi.create.AllBlocks; import com.simibubi.create.compat.jei.category.animations.AnimatedPress; +import com.simibubi.create.content.contraptions.processing.BasinRecipe; import com.simibubi.create.foundation.gui.AllGuiTextures; -import mezz.jei.api.constants.VanillaTypes; import mezz.jei.api.gui.IRecipeLayout; import mezz.jei.api.gui.ingredient.IGuiItemStackGroup; import mezz.jei.api.ingredients.IIngredients; -import net.minecraft.item.crafting.ICraftingRecipe; -import net.minecraft.item.crafting.IRecipe; import net.minecraft.item.crafting.Ingredient; import net.minecraft.util.NonNullList; -public class PackingCategory extends CreateRecipeCategory> { +public class PackingCategory extends BasinCategory { private AnimatedPress press = new AnimatedPress(true); @@ -25,18 +23,12 @@ public class PackingCategory extends CreateRecipeCategory> { } @Override - public Class> getRecipeClass() { - return ICraftingRecipe.class; - } - - @Override - public void setIngredients(IRecipe recipe, IIngredients ingredients) { - ingredients.setInputIngredients(recipe.getIngredients()); - ingredients.setOutput(VanillaTypes.ITEM, recipe.getRecipeOutput()); - } - - @Override - public void setRecipe(IRecipeLayout recipeLayout, IRecipe recipe, IIngredients ingredients) { + public void setRecipe(IRecipeLayout recipeLayout, BasinRecipe recipe, IIngredients ingredients) { + if (!recipe.convertedRecipe) { + super.setRecipe(recipeLayout, recipe, ingredients); + return; + } + IGuiItemStackGroup itemStacks = recipeLayout.getItemStacks(); int i = 0; @@ -55,16 +47,21 @@ public class PackingCategory extends CreateRecipeCategory> { } @Override - public void draw(IRecipe recipe, double mouseX, double mouseY) { - NonNullList ingredients2 = recipe.getIngredients(); - int size = ingredients2.size(); - int rows = size == 4 ? 2 : 3; - for (int i = 0; i < size; i++) { - AllGuiTextures.JEI_SLOT.draw((rows == 2 ? 26 : 17) + (i % rows) * 19, 50 - (i / rows) * 19); + public void draw(BasinRecipe recipe, double mouseX, double mouseY) { + if (!recipe.convertedRecipe) { + super.draw(recipe, mouseX, mouseY); + + } else { + NonNullList ingredients2 = recipe.getIngredients(); + int size = ingredients2.size(); + int rows = size == 4 ? 2 : 3; + for (int i = 0; i < size; i++) + AllGuiTextures.JEI_SLOT.draw((rows == 2 ? 26 : 17) + (i % rows) * 19, 50 - (i / rows) * 19); + AllGuiTextures.JEI_SLOT.draw(141, 50); + AllGuiTextures.JEI_DOWN_ARROW.draw(136, 32); + AllGuiTextures.JEI_SHADOW.draw(81, 57); } - AllGuiTextures.JEI_SLOT.draw(141, 50); - AllGuiTextures.JEI_DOWN_ARROW.draw(136, 32); - AllGuiTextures.JEI_SHADOW.draw(81, 57); + press.draw(getBackground().getWidth() / 2 + 6, 30); } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/crafter/RecipeGridHandler.java b/src/main/java/com/simibubi/create/content/contraptions/components/crafter/RecipeGridHandler.java index 55c04b434..20de1a509 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/crafter/RecipeGridHandler.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/crafter/RecipeGridHandler.java @@ -15,6 +15,7 @@ import org.apache.commons.lang3.tuple.Pair; import com.google.common.base.Predicates; import com.simibubi.create.AllBlocks; import com.simibubi.create.AllRecipeTypes; +import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.utility.Pointing; import net.minecraft.block.BlockState; @@ -139,16 +140,16 @@ public class RecipeGridHandler { public static ItemStack tryToApplyRecipe(World world, GroupedItems items) { items.calcStats(); CraftingInventory craftinginventory = new MechanicalCraftingInventory(items); - ItemStack result = world.getRecipeManager() - .getRecipe(IRecipeType.CRAFTING, craftinginventory, world) - .map(r -> r.getCraftingResult(craftinginventory)) - .orElse(null); - if (result == null) + ItemStack result = null; + if (AllConfigs.SERVER.recipes.allowRegularCraftingInCrafter.get()) result = world.getRecipeManager() - .getRecipe(AllRecipeTypes.MECHANICAL_CRAFTING.getType(), craftinginventory, world) + .getRecipe(IRecipeType.CRAFTING, craftinginventory, world) + .map(r -> r.getCraftingResult(craftinginventory)) + .orElse(null); + if (result == null) + result = AllRecipeTypes.MECHANICAL_CRAFTING.find(craftinginventory, world) .map(r -> r.getCraftingResult(craftinginventory)) .orElse(null); - return result; } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/crusher/CrushingWheelControllerTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/crusher/CrushingWheelControllerTileEntity.java index d78798576..010bf1610 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/crusher/CrushingWheelControllerTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/crusher/CrushingWheelControllerTileEntity.java @@ -206,11 +206,9 @@ public class CrushingWheelControllerTileEntity extends SmartTileEntity { } public Optional> findRecipe() { - Optional> crushingRecipe = world.getRecipeManager() - .getRecipe(AllRecipeTypes.CRUSHING.getType(), wrapper, world); + Optional> crushingRecipe = AllRecipeTypes.CRUSHING.find(wrapper, world); if (!crushingRecipe.isPresent()) - crushingRecipe = world.getRecipeManager() - .getRecipe(AllRecipeTypes.MILLING.getType(), wrapper, world); + crushingRecipe = AllRecipeTypes.MILLING.find(wrapper, world); return crushingRecipe; } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/millstone/MillstoneTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/millstone/MillstoneTileEntity.java index e70a13bae..a438ccd31 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/millstone/MillstoneTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/millstone/MillstoneTileEntity.java @@ -65,8 +65,7 @@ public class MillstoneTileEntity extends KineticTileEntity { RecipeWrapper inventoryIn = new RecipeWrapper(inputInv); if (lastRecipe == null || !lastRecipe.matches(inventoryIn, world)) { - Optional recipe = world.getRecipeManager() - .getRecipe(AllRecipeTypes.MILLING.getType(), inventoryIn, world); + Optional recipe = AllRecipeTypes.MILLING.find(inventoryIn, world); if (!recipe.isPresent()) { timer = 100; sendData(); @@ -86,8 +85,7 @@ public class MillstoneTileEntity extends KineticTileEntity { RecipeWrapper inventoryIn = new RecipeWrapper(inputInv); if (lastRecipe == null || !lastRecipe.matches(inventoryIn, world)) { - Optional recipe = world.getRecipeManager() - .getRecipe(AllRecipeTypes.MILLING.getType(), inventoryIn, world); + Optional recipe = AllRecipeTypes.MILLING.find(inventoryIn, world); if (!recipe.isPresent()) return; lastRecipe = recipe.get(); @@ -153,8 +151,7 @@ public class MillstoneTileEntity extends KineticTileEntity { if (lastRecipe != null && lastRecipe.matches(inventoryIn, world)) return true; - return world.getRecipeManager() - .getRecipe(AllRecipeTypes.MILLING.getType(), inventoryIn, world) + return AllRecipeTypes.MILLING.find(inventoryIn, world) .isPresent(); } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/mixer/CompactingRecipe.java b/src/main/java/com/simibubi/create/content/contraptions/components/mixer/CompactingRecipe.java new file mode 100644 index 000000000..7e25e410e --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/components/mixer/CompactingRecipe.java @@ -0,0 +1,13 @@ +package com.simibubi.create.content.contraptions.components.mixer; + +import com.simibubi.create.AllRecipeTypes; +import com.simibubi.create.content.contraptions.processing.BasinRecipe; +import com.simibubi.create.content.contraptions.processing.ProcessingRecipeBuilder.ProcessingRecipeParams; + +public class CompactingRecipe extends BasinRecipe { + + public CompactingRecipe(ProcessingRecipeParams params) { + super(AllRecipeTypes.COMPACTING, params); + } + +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/mixer/MechanicalMixerTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/mixer/MechanicalMixerTileEntity.java index 360c4b2db..17bbf9fce 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/mixer/MechanicalMixerTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/mixer/MechanicalMixerTileEntity.java @@ -1,37 +1,29 @@ package com.simibubi.create.content.contraptions.components.mixer; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.LinkedList; -import java.util.List; import java.util.Optional; import com.simibubi.create.AllRecipeTypes; -import com.simibubi.create.AllTags; import com.simibubi.create.content.contraptions.components.press.MechanicalPressTileEntity; import com.simibubi.create.content.contraptions.processing.BasinOperatingTileEntity; import com.simibubi.create.content.contraptions.processing.BasinTileEntity; -import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock; -import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock.HeatLevel; +import com.simibubi.create.foundation.advancement.AllTriggers; +import com.simibubi.create.foundation.advancement.ITriggerable; +import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.item.SmartInventory; import com.simibubi.create.foundation.utility.VecHelper; -import net.minecraft.block.BlockState; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.IRecipe; import net.minecraft.item.crafting.IRecipeSerializer; -import net.minecraft.item.crafting.Ingredient; import net.minecraft.nbt.CompoundNBT; import net.minecraft.particles.ItemParticleData; import net.minecraft.particles.ParticleTypes; import net.minecraft.tileentity.TileEntityType; import net.minecraft.util.Direction.Axis; -import net.minecraft.util.NonNullList; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; -import net.minecraftforge.fluids.FluidStack; public class MechanicalMixerTileEntity extends BasinOperatingTileEntity { @@ -99,13 +91,6 @@ public class MechanicalMixerTileEntity extends BasinOperatingTileEntity { super.write(compound, clientPacket); } - @Override - public void lazyTick() { - super.lazyTick(); - if (world != null && world.isRemote && running && !basinItemInv.isPresent()) - updateBasin(); - } - @Override public void tick() { super.tick(); @@ -168,45 +153,11 @@ public class MechanicalMixerTileEntity extends BasinOperatingTileEntity { @Override protected boolean matchStaticFilters(IRecipe r) { - return (r.getSerializer() == IRecipeSerializer.CRAFTING_SHAPELESS || r.getType() == AllRecipeTypes.MIXING.type) + return ((r.getSerializer() == IRecipeSerializer.CRAFTING_SHAPELESS + && AllConfigs.SERVER.recipes.allowShapelessInMixer.get()) || r.getType() == AllRecipeTypes.MIXING.type) && !MechanicalPressTileEntity.canCompress(r.getIngredients()); } - @Override - protected boolean matchBasinRecipe(IRecipe recipe) { - if (!super.matchBasinRecipe(recipe)) - return false; - - NonNullList ingredients = recipe.getIngredients(); - List remainingItems = new ArrayList<>(); - itemInputs.forEach(stack -> remainingItems.add(stack.copy())); - List remainingFluids = new ArrayList<>(); - fluidInputs.forEach(stack -> remainingFluids.add(stack.copy())); - - // TODO: match fluid inputs - - // Sort by leniency - List sortedIngredients = new LinkedList<>(ingredients); - sortedIngredients.sort(Comparator.comparingInt(i -> i.getMatchingStacks().length)); - - Ingredients: for (Ingredient ingredient : sortedIngredients) { - for (ItemStack stack : remainingItems) { - if (stack.isEmpty()) - continue; - if (ingredient.test(stack)) { - stack.shrink(1); - continue Ingredients; - } - } - return false; - } - - if (!(recipe instanceof MixingRecipe)) - return true; - return ((MixingRecipe) recipe).getRequiredHeat() - .testBlazeBurner(getHeatLevel()); - } - @Override public void startProcessingBasin() { if (running && runningTicks <= 20) @@ -239,13 +190,10 @@ public class MechanicalMixerTileEntity extends BasinOperatingTileEntity { protected boolean isRunning() { return running; } - - private HeatLevel getHeatLevel() { - if (world == null) - return HeatLevel.NONE; - BlockState state = world.getBlockState(pos.down(3)); - if (state.has(BlazeBurnerBlock.HEAT_LEVEL)) - return state.get(BlazeBurnerBlock.HEAT_LEVEL); - return AllTags.AllBlockTags.FAN_HEATERS.matches(state) ? HeatLevel.SMOULDERING : HeatLevel.NONE; + + @Override + protected Optional getProcessedRecipeTrigger() { + return Optional.of(AllTriggers.MIXER_MIX); } + } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/mixer/MixingRecipe.java b/src/main/java/com/simibubi/create/content/contraptions/components/mixer/MixingRecipe.java index c36564c57..12e1d7b0f 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/mixer/MixingRecipe.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/mixer/MixingRecipe.java @@ -1,98 +1,13 @@ package com.simibubi.create.content.contraptions.components.mixer; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.LinkedList; -import java.util.List; - -import javax.annotation.Nonnull; - import com.simibubi.create.AllRecipeTypes; -import com.simibubi.create.content.contraptions.processing.ProcessingRecipe; -import com.simibubi.create.content.contraptions.processing.ProcessingRecipeBuilder; +import com.simibubi.create.content.contraptions.processing.BasinRecipe; import com.simibubi.create.content.contraptions.processing.ProcessingRecipeBuilder.ProcessingRecipeParams; -import com.simibubi.create.foundation.item.SmartInventory; -import net.minecraft.item.ItemStack; -import net.minecraft.item.crafting.IRecipe; -import net.minecraft.item.crafting.Ingredient; -import net.minecraft.util.NonNullList; -import net.minecraft.world.World; - -public class MixingRecipe extends ProcessingRecipe { - - /** - * For JEI purposes only - */ - public static MixingRecipe convertShapeless(IRecipe recipe) { - return new ProcessingRecipeBuilder<>(MixingRecipe::new, recipe.getId()) - .withItemIngredients(recipe.getIngredients()) - .withSingleItemOutput(recipe.getRecipeOutput()) - .build(); - } +public class MixingRecipe extends BasinRecipe { public MixingRecipe(ProcessingRecipeParams params) { super(AllRecipeTypes.MIXING, params); } - @Override - protected int getMaxInputCount() { - return 9; - } - - @Override - protected int getMaxOutputCount() { - return 1;// TODO increase - } - - @Override - protected int getMaxFluidInputCount() { - return 2; - } - - @Override - protected int getMaxFluidOutputCount() { - return 1;// TODO increase? - } - - @Override - protected boolean canRequireHeat() { - return true; - } - - @Override - public boolean matches(SmartInventory inv, @Nonnull World worldIn) { - if (inv.isEmpty()) - return false; - - NonNullList ingredients = this.getIngredients(); - if (!ingredients.stream() - .allMatch(Ingredient::isSimple)) - return false; - - List remaining = new ArrayList<>(); - for (int slot = 0; slot < inv.getSizeInventory(); ++slot) { - ItemStack itemstack = inv.getStackInSlot(slot); - if (!itemstack.isEmpty()) { - remaining.add(itemstack.copy()); - } - } - - // sort by leniency - List sortedIngredients = new LinkedList<>(ingredients); - sortedIngredients.sort(Comparator.comparingInt(i -> i.getMatchingStacks().length)); - Ingredients: for (Ingredient ingredient : sortedIngredients) { - for (ItemStack stack : remaining) { - if (stack.isEmpty()) - continue; - if (ingredient.test(stack)) { - stack.shrink(1); - continue Ingredients; - } - } - return false; - } - return true; - } - } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/press/MechanicalPressTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/press/MechanicalPressTileEntity.java index a93bc77a1..bc8a0818d 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/press/MechanicalPressTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/press/MechanicalPressTileEntity.java @@ -10,6 +10,8 @@ import com.simibubi.create.content.contraptions.processing.BasinOperatingTileEnt import com.simibubi.create.content.contraptions.processing.BasinTileEntity; import com.simibubi.create.content.logistics.InWorldProcessing; import com.simibubi.create.foundation.advancement.AllTriggers; +import com.simibubi.create.foundation.advancement.ITriggerable; +import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.item.ItemHelper; import com.simibubi.create.foundation.item.SmartInventory; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; @@ -41,29 +43,12 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity { private static final Object compressingRecipesKey = new Object(); public List pressedItems = new ArrayList<>(); - - public static class PressingInv extends RecipeWrapper { - public PressingInv() { - super(new ItemStackHandler(1)); - } - } - - enum Mode { - WORLD(1), BELT(19f / 16f), BASIN(22f / 16f) - - ; - - float headOffset; - - Mode(float headOffset) { - this.headOffset = headOffset; - } - } - - private static final PressingInv pressingInv = new PressingInv(); public BeltProcessingBehaviour processingBehaviour; + public int prevRunningTicks; public int runningTicks; + static final int CYCLE = 240; + public boolean running; public Mode mode; public boolean finished; @@ -87,7 +72,7 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity { running = compound.getBoolean("Running"); mode = Mode.values()[compound.getInt("Mode")]; finished = compound.getBoolean("Finished"); - runningTicks = compound.getInt("Ticks"); + prevRunningTicks = runningTicks = compound.getInt("Ticks"); super.read(compound, clientPacket); if (clientPacket) { @@ -105,8 +90,10 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity { compound.putInt("Ticks", runningTicks); super.write(compound, clientPacket); - if (clientPacket) + if (clientPacket) { compound.put("ParticleItems", NBTHelper.writeCompoundList(pressedItems, ItemStack::serializeNBT)); + pressedItems.clear(); + } } @Override @@ -116,15 +103,12 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity { } public float getRenderedHeadOffset(float partialTicks) { - if (running) { - if (runningTicks < 40) { - float num = (runningTicks - 1 + partialTicks) / 30f; - return MathHelper.clamp(num * num * num, 0, mode.headOffset); - } - return MathHelper.clamp(((60 - runningTicks) + 1 - partialTicks) / 20f * mode.headOffset, 0, - mode.headOffset); - } - return 0; + if (!running) + return 0; + float ticks = MathHelper.lerp(partialTicks, prevRunningTicks, runningTicks); + if (runningTicks < (CYCLE * 2) / 3) + return (float) MathHelper.clamp(Math.pow(ticks / CYCLE * 2, 3), 0, mode.headOffset); + return MathHelper.clamp((CYCLE - ticks) / CYCLE * 3 * mode.headOffset, 0, mode.headOffset); } public void start(Mode mode) { @@ -150,50 +134,11 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity { if (!running || world == null) return; - if (runningTicks == 30) { - - if (inWorld()) { - AxisAlignedBB bb = new AxisAlignedBB(pos.down(1)); - pressedItems.clear(); - for (Entity entity : world.getEntitiesWithinAABBExcludingEntity(null, bb)) { - if (!(entity instanceof ItemEntity)) - continue; - - ItemEntity itemEntity = (ItemEntity) entity; - - if (!world.isRemote) { - pressedItems.add(itemEntity.getItem()); - sendData(); - Optional recipe = getRecipe(itemEntity.getItem()); - if (recipe.isPresent()) { - InWorldProcessing.applyRecipeOn(itemEntity, recipe.get()); - AllTriggers.triggerForNearbyPlayers(AllTriggers.BONK, world, pos, 4); - } - } - } - } - - if (onBasin()) { - if (!world.isRemote) { - pressedItems.clear(); - applyBasinRecipe(); - - Optional basin = getBasin(); - SmartInventory inputs = basin.get() - .getInputInventory(); - if (basin.isPresent()) { - for (int slot = 0; slot < inputs.getSlots(); slot++) { - ItemStack stackInSlot = inputs.getStackInSlot(slot); - if (stackInSlot.isEmpty()) - continue; - pressedItems.add(stackInSlot); - } - } - sendData(); - } - - } - + if (runningTicks == CYCLE / 2) { + if (inWorld()) + applyPressingInWorld(); + if (onBasin()) + applyCompactingOnBasin(); if (!world.isRemote) { world.playSound(null, getPos(), AllSoundEvents.MECHANICAL_PRESS_ITEM_BREAK.get(), SoundCategory.BLOCKS, .5f, 1f); @@ -202,105 +147,125 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity { } } - if (!world.isRemote && runningTicks > 60) { + if (!world.isRemote && runningTicks > CYCLE) { finished = true; if (inWorld()) finished = world.isBlockPowered(pos); running = false; - if (onBasin()) { - gatherInputs(); - if (matchBasinRecipe(lastRecipe)) { - startProcessingBasin(); - } - } + if (onBasin() && matchBasinRecipe(currentRecipe)) + startProcessingBasin(); pressedItems.clear(); sendData(); return; } - runningTicks++; + prevRunningTicks = runningTicks; + runningTicks += getRunningTickSpeed(); + if (prevRunningTicks < CYCLE / 2 && runningTicks >= CYCLE / 2) + runningTicks = CYCLE / 2; + } + + protected void applyCompactingOnBasin() { + if (world.isRemote) + return; + pressedItems.clear(); + applyBasinRecipe(); + Optional basin = getBasin(); + SmartInventory inputs = basin.get() + .getInputInventory(); + if (basin.isPresent()) { + for (int slot = 0; slot < inputs.getSlots(); slot++) { + ItemStack stackInSlot = inputs.getStackInSlot(slot); + if (stackInSlot.isEmpty()) + continue; + pressedItems.add(stackInSlot); + } + } + sendData(); + } + + protected void applyPressingInWorld() { + AxisAlignedBB bb = new AxisAlignedBB(pos.down(1)); + pressedItems.clear(); + if (world.isRemote) + return; + for (Entity entity : world.getEntitiesWithinAABBExcludingEntity(null, bb)) { + if (!(entity instanceof ItemEntity)) + continue; + ItemEntity itemEntity = (ItemEntity) entity; + pressedItems.add(itemEntity.getItem()); + sendData(); + Optional recipe = getRecipe(itemEntity.getItem()); + if (!recipe.isPresent()) + continue; + InWorldProcessing.applyRecipeOn(itemEntity, recipe.get()); + AllTriggers.triggerForNearbyPlayers(AllTriggers.BONK, world, pos, 4); + } + } + + public int getRunningTickSpeed() { + if (getSpeed() == 0) + return 0; + return (int) MathHelper.lerp(MathHelper.clamp(Math.abs(getSpeed()) / 512f, 0, 1), 1, 60); } protected void spawnParticles() { if (pressedItems.isEmpty()) return; - if (mode == Mode.BASIN) { + if (mode == Mode.BASIN) pressedItems.forEach(stack -> makeCompactingParticleEffect(VecHelper.getCenterOf(pos.down(2)), stack)); - } - if (mode == Mode.BELT) { + if (mode == Mode.BELT) pressedItems.forEach(stack -> makePressingParticleEffect(VecHelper.getCenterOf(pos.down(2)) .add(0, 8 / 16f, 0), stack)); - } - if (mode == Mode.WORLD) { + if (mode == Mode.WORLD) pressedItems.forEach(stack -> makePressingParticleEffect(VecHelper.getCenterOf(pos.down(1)) .add(0, -1 / 4f, 0), stack)); - } pressedItems.clear(); } public void makePressingParticleEffect(Vec3d pos, ItemStack stack) { - if (world != null && world.isRemote) { - for (int i = 0; i < 20; i++) { - Vec3d motion = VecHelper.offsetRandomly(Vec3d.ZERO, world.rand, .125f) - .mul(1, 0, 1); - world.addParticle(new ItemParticleData(ParticleTypes.ITEM, stack), pos.x, pos.y - .25f, pos.z, motion.x, - motion.y + .125f, motion.z); - } + if (world == null || !world.isRemote) + return; + for (int i = 0; i < 20; i++) { + Vec3d motion = VecHelper.offsetRandomly(Vec3d.ZERO, world.rand, .125f) + .mul(1, 0, 1); + world.addParticle(new ItemParticleData(ParticleTypes.ITEM, stack), pos.x, pos.y - .25f, pos.z, motion.x, + motion.y + .125f, motion.z); } } public void makeCompactingParticleEffect(Vec3d pos, ItemStack stack) { - if (world != null && world.isRemote) { - for (int i = 0; i < 20; i++) { - Vec3d motion = VecHelper.offsetRandomly(Vec3d.ZERO, world.rand, .175f) - .mul(1, 0, 1); - world.addParticle(new ItemParticleData(ParticleTypes.ITEM, stack), pos.x, pos.y, pos.z, motion.x, - motion.y + .25f, motion.z); - } + if (world == null || !world.isRemote) + return; + for (int i = 0; i < 20; i++) { + Vec3d motion = VecHelper.offsetRandomly(Vec3d.ZERO, world.rand, .175f) + .mul(1, 0, 1); + world.addParticle(new ItemParticleData(ParticleTypes.ITEM, stack), pos.x, pos.y, pos.z, motion.x, + motion.y + .25f, motion.z); } } + private static final RecipeWrapper pressingInv = new RecipeWrapper(new ItemStackHandler(1)); + public Optional getRecipe(ItemStack item) { pressingInv.setInventorySlotContents(0, item); - return world.getRecipeManager() - .getRecipe(AllRecipeTypes.PRESSING.getType(), pressingInv, world); + return AllRecipeTypes.PRESSING.find(pressingInv, world); } public static boolean canCompress(NonNullList ingredients) { - return (ingredients.size() == 4 || ingredients.size() == 9) && ItemHelper.condenseIngredients(ingredients) - .size() == 1; + return AllConfigs.SERVER.recipes.allowShapedSquareInPress.get() + && (ingredients.size() == 4 || ingredients.size() == 9) && ItemHelper.condenseIngredients(ingredients) + .size() == 1; } @Override protected boolean matchStaticFilters(IRecipe recipe) { - return recipe instanceof ICraftingRecipe && canCompress(recipe.getIngredients()); - } - - @Override - protected boolean matchBasinRecipe(IRecipe recipe) { - if (!super.matchBasinRecipe(recipe)) - return false; - - NonNullList ingredients = recipe.getIngredients(); - List remainingItems = new ArrayList<>(); - itemInputs.forEach(stack -> remainingItems.add(stack.copy())); - - Ingredients: for (Ingredient ingredient : ingredients) { - for (ItemStack stack : remainingItems) { - if (stack.isEmpty()) - continue; - if (ingredient.test(stack)) { - stack.shrink(1); - continue Ingredients; - } - } - return false; - } - return true; + return (recipe instanceof ICraftingRecipe && canCompress(recipe.getIngredients())) + || recipe.getType() == AllRecipeTypes.COMPACTING.type; } @Override @@ -310,7 +275,7 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity { @Override public void startProcessingBasin() { - if (running && runningTicks <= 30) + if (running && runningTicks <= CYCLE / 2) return; super.startProcessingBasin(); start(Mode.BASIN); @@ -329,4 +294,21 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity { return running; } + @Override + protected Optional getProcessedRecipeTrigger() { + return Optional.of(AllTriggers.PRESS_COMPACT); + } + + enum Mode { + WORLD(1), BELT(19f / 16f), BASIN(22f / 16f) + + ; + + float headOffset; + + Mode(float headOffset) { + this.headOffset = headOffset; + } + } + } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/press/PressingRecipe.java b/src/main/java/com/simibubi/create/content/contraptions/components/press/PressingRecipe.java index b08c0e1c9..481acc8e0 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/press/PressingRecipe.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/press/PressingRecipe.java @@ -3,21 +3,21 @@ package com.simibubi.create.content.contraptions.components.press; import javax.annotation.ParametersAreNonnullByDefault; import com.simibubi.create.AllRecipeTypes; -import com.simibubi.create.content.contraptions.components.press.MechanicalPressTileEntity.PressingInv; import com.simibubi.create.content.contraptions.processing.ProcessingRecipe; import com.simibubi.create.content.contraptions.processing.ProcessingRecipeBuilder.ProcessingRecipeParams; import net.minecraft.world.World; +import net.minecraftforge.items.wrapper.RecipeWrapper; @ParametersAreNonnullByDefault -public class PressingRecipe extends ProcessingRecipe { +public class PressingRecipe extends ProcessingRecipe { public PressingRecipe(ProcessingRecipeParams params) { super(AllRecipeTypes.PRESSING, params); } @Override - public boolean matches(PressingInv inv, World worldIn) { + public boolean matches(RecipeWrapper inv, World worldIn) { if (inv.isEmpty()) return false; return ingredients.get(0) diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/saw/SawTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/saw/SawTileEntity.java index 97ab1000a..e824c1ebc 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/saw/SawTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/saw/SawTileEntity.java @@ -8,9 +8,11 @@ import java.util.List; import java.util.Random; import java.util.stream.Collectors; +import com.google.common.base.Predicate; import com.simibubi.create.AllRecipeTypes; import com.simibubi.create.content.contraptions.components.actors.BlockBreakingKineticTileEntity; import com.simibubi.create.content.contraptions.processing.ProcessingInventory; +import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.item.ItemHelper; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.behaviour.belt.DirectBeltInputBehaviour; @@ -264,8 +266,10 @@ public class SawTileEntity extends BlockBreakingKineticTileEntity { } private List> getRecipes() { - List> startedSearch = RecipeFinder.get(cuttingRecipesKey, world, - RecipeConditions.isOfType(IRecipeType.STONECUTTING, AllRecipeTypes.CUTTING.getType())); + Predicate> types = AllConfigs.SERVER.recipes.allowStonecuttingOnSaw.get() + ? RecipeConditions.isOfType(IRecipeType.STONECUTTING, AllRecipeTypes.CUTTING.getType()) + : RecipeConditions.isOfType(AllRecipeTypes.CUTTING.getType()); + List> startedSearch = RecipeFinder.get(cuttingRecipesKey, world, types); return startedSearch.stream() .filter(RecipeConditions.outputMatchesFilter(filtering)) .filter(RecipeConditions.firstIngredientMatches(inventory.getStackInSlot(0))) diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java index 9cc0d311c..9484af64c 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java @@ -9,13 +9,13 @@ import java.util.Iterator; import java.util.List; import java.util.stream.Stream; -import com.simibubi.create.AllMovementBehaviours; import org.apache.commons.lang3.mutable.MutableBoolean; import org.apache.commons.lang3.mutable.MutableObject; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableSet; import com.simibubi.create.AllBlocks; +import com.simibubi.create.AllMovementBehaviours; import com.simibubi.create.content.contraptions.components.actors.BlockBreakingMovementBehaviour; import com.simibubi.create.content.contraptions.components.structureMovement.sync.ClientMotionPacket; import com.simibubi.create.foundation.collision.ContinuousOBBCollider.ContinuousSeparationManifold; @@ -28,6 +28,7 @@ import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.block.BlockState; import net.minecraft.block.CocoaBlock; +import net.minecraft.client.entity.player.ClientPlayerEntity; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.entity.player.PlayerEntity; @@ -37,6 +38,7 @@ import net.minecraft.util.Direction.AxisDirection; import net.minecraft.util.ReuseableStream; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.shapes.IBooleanFunction; import net.minecraft.util.math.shapes.ISelectionContext; @@ -44,6 +46,9 @@ import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraft.world.World; import net.minecraft.world.gen.feature.template.Template.BlockInfo; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.fml.DistExecutor; public class ContraptionCollider { @@ -54,14 +59,20 @@ public class ContraptionCollider { for (Iterator> iterator = list.iterator(); iterator.hasNext();) { WeakReference weakReference = iterator.next(); ContraptionEntity contraptionEntity = weakReference.get(); - if (contraptionEntity == null || !contraptionEntity.isAlive()) { + if (contraptionEntity == null) { iterator.remove(); continue; } + if (!contraptionEntity.isAlive()) + continue; collideEntities(contraptionEntity); } } + enum PlayerType { + NONE, CLIENT, REMOTE, SERVER + } + private static void collideEntities(ContraptionEntity contraptionEntity) { World world = contraptionEntity.getEntityWorld(); Contraption contraption = contraptionEntity.getContraption(); @@ -87,8 +98,10 @@ public class ContraptionCollider { for (Entity entity : world.getEntitiesWithinAABB((EntityType) null, bounds.grow(2) .expand(0, 32, 0), contraptionEntity::canCollideWith)) { - boolean player = entity instanceof PlayerEntity; - boolean serverPlayer = player && !world.isRemote; + + PlayerType playerType = getPlayerType(entity); + if (playerType == PlayerType.REMOTE) + continue; // Init matrix if (rotation == null) { @@ -199,7 +212,7 @@ public class ContraptionCollider { totalResponse = rotation.transform(totalResponse); rotation.transpose(); - if (futureCollision.isTrue() && !serverPlayer) { + if (futureCollision.isTrue() && playerType != PlayerType.SERVER) { if (motionResponse.y != entityMotion.y) { entity.setMotion(entityMotion.mul(1, 0, 1) .add(0, motionResponse.y, 0)); @@ -212,7 +225,7 @@ public class ContraptionCollider { entity.fallDistance = 0; entity.onGround = true; contraptionEntity.collidingEntities.add(entity); - if (!serverPlayer) + if (playerType != PlayerType.SERVER) contactPointMotion = contraptionEntity.getContactPointMotion(entityPosition); } @@ -236,7 +249,7 @@ public class ContraptionCollider { if (!hardCollision && surfaceCollision.isFalse()) continue; - if (serverPlayer && entity instanceof ServerPlayerEntity) { + if (playerType == PlayerType.SERVER && entity instanceof ServerPlayerEntity) { ((ServerPlayerEntity) entity).connection.floatingTickCount = 0; continue; } @@ -249,8 +262,15 @@ public class ContraptionCollider { entityPosition.z + allowedMovement.z); entity.setMotion(entityMotion); - if (!serverPlayer && player) - AllPackets.channel.sendToServer(new ClientMotionPacket(entityMotion, true)); + if (playerType != PlayerType.CLIENT) + continue; + + double d0 = entity.getX() - entity.prevPosX - contactPointMotion.x; + double d1 = entity.getZ() - entity.prevPosZ - contactPointMotion.z; + float limbSwing = MathHelper.sqrt(d0 * d0 + d1 * d1) * 4.0F; + if (limbSwing > 1.0F) + limbSwing = 1.0F; + AllPackets.channel.sendToServer(new ClientMotionPacket(entityMotion, true, limbSwing)); } } @@ -296,6 +316,21 @@ public class ContraptionCollider { return vec3d; } + private static PlayerType getPlayerType(Entity entity) { + if (!(entity instanceof PlayerEntity)) + return PlayerType.NONE; + if (!entity.world.isRemote) + return PlayerType.SERVER; + MutableBoolean isClient = new MutableBoolean(false); + DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> isClient.setValue(isClientPlayerEntity(entity))); + return isClient.booleanValue() ? PlayerType.CLIENT : PlayerType.REMOTE; + } + + @OnlyIn(Dist.CLIENT) + private static boolean isClientPlayerEntity(Entity entity) { + return entity instanceof ClientPlayerEntity; + } + private static ReuseableStream getPotentiallyCollidedShapes(World world, Contraption contraption, AxisAlignedBB localBB) { @@ -395,10 +430,10 @@ public class ContraptionCollider { BlockInfo blockInfo = contraption.blocks.get(pos); if (AllMovementBehaviours.hasMovementBehaviour(blockInfo.state.getBlock())) { - MovementBehaviour movementBehaviour = AllMovementBehaviours.getMovementBehaviour(blockInfo.state.getBlock()); + MovementBehaviour movementBehaviour = + AllMovementBehaviours.getMovementBehaviour(blockInfo.state.getBlock()); if (movementBehaviour instanceof BlockBreakingMovementBehaviour) { - BlockBreakingMovementBehaviour behaviour = - (BlockBreakingMovementBehaviour) movementBehaviour; + BlockBreakingMovementBehaviour behaviour = (BlockBreakingMovementBehaviour) movementBehaviour; if (!behaviour.canBreak(world, colliderPos, collidedState) && !collidedState.getCollisionShape(world, pos) .isEmpty()) { diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntity.java index 29766dc28..072407d4e 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntity.java @@ -78,6 +78,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD final List collidingEntities = new ArrayList<>(); private boolean isSerializingFurnaceCart; private boolean attachedExtraInventories; + private boolean prevPosInvalid; private static final Ingredient FUEL_ITEMS = Ingredient.fromItems(Items.COAL, Items.CHARCOAL); private static final DataParameter STALLED = @@ -107,6 +108,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD isSerializingFurnaceCart = false; attachedExtraInventories = false; forcedAngle = -1; + prevPosInvalid = true; } public static ContraptionEntity createMounted(World world, Contraption contraption, float initialAngle) { @@ -289,26 +291,32 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD remove(); return; } + + prevPosX = getX(); + prevPosY = getY(); + prevPosZ = getZ(); + prevPosInvalid = false; if (!initialized) contraptionInitialize(); - checkController(); Entity mountedEntity = getRidingEntity(); if (mountedEntity != null) { tickAsPassenger(mountedEntity); + super.tick(); return; } if (getMotion().length() < 1 / 4098f) setMotion(Vec3d.ZERO); - + move(getMotion().x, getMotion().y, getMotion().z); if (ContraptionCollider.collideBlocks(this)) getController().collided(); - tickActors(getPositionVec().subtract(prevPosX, prevPosY, prevPosZ)); + Vec3d movement = getPositionVec().subtract(prevPosX, prevPosY, prevPosZ); + tickActors(movement); prevYaw = yaw; prevPitch = pitch; @@ -439,8 +447,6 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD furnaceCart.deserializeNBT(nbt); } } - - super.tick(); } public void tickActors(Vec3d movementVector) { @@ -846,6 +852,9 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD } public Vec3d getContactPointMotion(Vec3d globalContactPoint) { + if (prevPosInvalid) + return Vec3d.ZERO; + Vec3d positionVec = getPositionVec(); Vec3d conMotion = positionVec.subtract(getPrevPositionVec()); Vec3d conAngularMotion = getRotationVec().subtract(getPrevRotationVec()); @@ -906,5 +915,10 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD public void setCoupledCart(UUID id) { dataManager.set(COUPLED_CART, Optional.ofNullable(id)); } + + @Override + public boolean isOnePlayerRiding() { + return false; + } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandler.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandler.java index ff63f6662..e959ea513 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandler.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandler.java @@ -4,6 +4,7 @@ import static java.util.concurrent.TimeUnit.SECONDS; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; @@ -28,7 +29,8 @@ public class ContraptionHandler { if (!(entity instanceof ContraptionEntity)) return; try { - List> list = activeContraptions.get(world, ArrayList::new); + List> list = + activeContraptions.get(world, () -> Collections.synchronizedList(new ArrayList<>())); ContraptionEntity contraption = (ContraptionEntity) entity; list.add(new WeakReference<>(contraption)); } catch (ExecutionException e) { diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionInteractionHandler.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandlerClient.java similarity index 72% rename from src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionInteractionHandler.java rename to src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandlerClient.java index 36ada9e88..191f19bc3 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionInteractionHandler.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandlerClient.java @@ -1,4 +1,5 @@ package com.simibubi.create.content.contraptions.components.structureMovement; + import org.apache.commons.lang3.mutable.MutableObject; import com.simibubi.create.content.contraptions.components.structureMovement.sync.ContraptionInteractionPacket; @@ -9,6 +10,8 @@ import com.simibubi.create.foundation.utility.RaycastHelper.PredicateTraceResult import net.minecraft.block.BlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.entity.player.ClientPlayerEntity; +import net.minecraft.client.entity.player.RemoteClientPlayerEntity; +import net.minecraft.nbt.CompoundNBT; import net.minecraft.util.Direction; import net.minecraft.util.Hand; import net.minecraft.util.math.AxisAlignedBB; @@ -20,11 +23,38 @@ import net.minecraft.world.gen.feature.template.Template.BlockInfo; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.client.event.InputEvent.ClickInputEvent; +import net.minecraftforge.event.TickEvent.Phase; +import net.minecraftforge.event.TickEvent.PlayerTickEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod.EventBusSubscriber; @EventBusSubscriber -public class ContraptionInteractionHandler { +public class ContraptionHandlerClient { + + @SubscribeEvent + @OnlyIn(Dist.CLIENT) + public static void preventRemotePlayersWalkingAnimations(PlayerTickEvent event) { + if (event.phase == Phase.START) + return; + if (!(event.player instanceof RemoteClientPlayerEntity)) + return; + RemoteClientPlayerEntity remotePlayer = (RemoteClientPlayerEntity) event.player; + CompoundNBT data = remotePlayer.getPersistentData(); + if (!data.contains("LastOverrideLimbSwingUpdate")) + return; + + int lastOverride = data.getInt("LastOverrideLimbSwingUpdate"); + data.putInt("LastOverrideLimbSwingUpdate", lastOverride + 1); + if (lastOverride > 5) { + data.remove("LastOverrideLimbSwingUpdate"); + data.remove("OverrideLimbSwing"); + return; + } + + float limbSwing = data.getFloat("OverrideLimbSwing"); + remotePlayer.prevPosX = remotePlayer.getX() - (limbSwing / 4); + remotePlayer.prevPosZ = remotePlayer.getZ(); + } @SubscribeEvent @OnlyIn(Dist.CLIENT) @@ -38,11 +68,12 @@ public class ContraptionInteractionHandler { if (!event.isUseItem()) return; Vec3d origin = RaycastHelper.getTraceOrigin(player); - + double reach = mc.playerController.getBlockReachDistance(); - if (mc.objectMouseOver != null && mc.objectMouseOver.getHitVec() != null) - reach = Math.min(mc.objectMouseOver.getHitVec().distanceTo(origin), reach); - + if (mc.objectMouseOver != null && mc.objectMouseOver.getHitVec() != null) + reach = Math.min(mc.objectMouseOver.getHitVec() + .distanceTo(origin), reach); + Vec3d target = RaycastHelper.getTraceTarget(player, reach, origin); for (ContraptionEntity contraptionEntity : mc.world.getEntitiesWithinAABB(ContraptionEntity.class, new AxisAlignedBB(origin, target))) { @@ -75,11 +106,10 @@ public class ContraptionInteractionHandler { Hand hand = event.getHand(); Direction face = rayTraceResult.getFace(); BlockPos pos = rayTraceResult.getPos(); - + if (!contraptionEntity.handlePlayerInteraction(player, pos, face, hand)) return; - AllPackets.channel.sendToServer(new ContraptionInteractionPacket(contraptionEntity, hand, - pos, face)); + AllPackets.channel.sendToServer(new ContraptionInteractionPacket(contraptionEntity, hand, pos, face)); event.setCanceled(true); event.setSwingHand(false); } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/sync/ClientMotionPacket.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/sync/ClientMotionPacket.java index a80351080..f5bb6758e 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/sync/ClientMotionPacket.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/sync/ClientMotionPacket.java @@ -2,26 +2,31 @@ package com.simibubi.create.content.contraptions.components.structureMovement.sy import java.util.function.Supplier; +import com.simibubi.create.foundation.networking.AllPackets; import com.simibubi.create.foundation.networking.SimplePacketBase; import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.network.PacketBuffer; import net.minecraft.util.math.Vec3d; import net.minecraftforge.fml.network.NetworkEvent.Context; +import net.minecraftforge.fml.network.PacketDistributor; public class ClientMotionPacket extends SimplePacketBase { private Vec3d motion; private boolean onGround; + private float limbSwing; - public ClientMotionPacket(Vec3d motion, boolean onGround) { + public ClientMotionPacket(Vec3d motion, boolean onGround, float limbSwing) { this.motion = motion; this.onGround = onGround; + this.limbSwing = limbSwing; } public ClientMotionPacket(PacketBuffer buffer) { motion = new Vec3d(buffer.readFloat(), buffer.readFloat(), buffer.readFloat()); onGround = buffer.readBoolean(); + limbSwing = buffer.readFloat(); } @Override @@ -30,6 +35,7 @@ public class ClientMotionPacket extends SimplePacketBase { buffer.writeFloat((float) motion.y); buffer.writeFloat((float) motion.z); buffer.writeBoolean(onGround); + buffer.writeFloat(limbSwing); } @Override @@ -47,6 +53,8 @@ public class ClientMotionPacket extends SimplePacketBase { sender.fallDistance = 0; sender.connection.floatingTickCount = 0; } + AllPackets.channel.send(PacketDistributor.TRACKING_ENTITY.with(() -> sender), + new LimbSwingUpdatePacket(sender.getEntityId(), sender.getPositionVec(), limbSwing)); }); context.get() .setPacketHandled(true); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/sync/LimbSwingUpdatePacket.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/sync/LimbSwingUpdatePacket.java new file mode 100644 index 000000000..4d3bfea33 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/sync/LimbSwingUpdatePacket.java @@ -0,0 +1,62 @@ +package com.simibubi.create.content.contraptions.components.structureMovement.sync; + +import java.util.function.Supplier; + +import com.simibubi.create.foundation.networking.SimplePacketBase; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.Entity; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.math.Vec3d; +import net.minecraftforge.fml.network.NetworkEvent.Context; + +public class LimbSwingUpdatePacket extends SimplePacketBase { + + private int entityId; + private Vec3d position; + private float limbSwing; + + public LimbSwingUpdatePacket(int entityId, Vec3d position, float limbSwing) { + this.entityId = entityId; + this.position = position; + this.limbSwing = limbSwing; + } + + public LimbSwingUpdatePacket(PacketBuffer buffer) { + entityId = buffer.readInt(); + position = new Vec3d(buffer.readFloat(), buffer.readFloat(), buffer.readFloat()); + limbSwing = buffer.readFloat(); + } + + @Override + public void write(PacketBuffer buffer) { + buffer.writeInt(entityId); + buffer.writeFloat((float) position.x); + buffer.writeFloat((float) position.y); + buffer.writeFloat((float) position.z); + buffer.writeFloat(limbSwing); + } + + @Override + public void handle(Supplier context) { + context.get() + .enqueueWork(() -> { + ClientWorld world = Minecraft.getInstance().world; + if (world == null) + return; + Entity entity = world.getEntityByID(entityId); + if (entity == null) + return; + CompoundNBT data = entity.getPersistentData(); + data.putInt("LastOverrideLimbSwingUpdate", 0); + data.putFloat("OverrideLimbSwing", limbSwing); + entity.setPositionAndRotationDirect(position.x, position.y, position.z, entity.rotationYaw, + entity.rotationPitch, 2, false); + }); + context.get() + .setPacketHandled(true); + } + +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/actors/FillingBySpout.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/actors/FillingBySpout.java index 59a3b3ada..4afe6f0d6 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/actors/FillingBySpout.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/actors/FillingBySpout.java @@ -24,8 +24,7 @@ public class FillingBySpout { public static boolean canItemBeFilled(World world, ItemStack stack) { wrapper.setInventorySlotContents(0, stack); - if (world.getRecipeManager() - .getRecipe(AllRecipeTypes.FILLING.getType(), wrapper, world) + if (AllRecipeTypes.FILLING.find(wrapper, world) .isPresent()) return true; @@ -44,8 +43,7 @@ public class FillingBySpout { public static int getRequiredAmountForItem(World world, ItemStack stack, FluidStack availableFluid) { wrapper.setInventorySlotContents(0, stack); - Optional> recipe = world.getRecipeManager() - .getRecipe(AllRecipeTypes.FILLING.getType(), wrapper, world); + Optional> recipe = AllRecipeTypes.FILLING.find(wrapper, world); if (recipe.isPresent()) { FillingRecipe fillingRecipe = (FillingRecipe) recipe.get(); FluidIngredient requiredFluid = fillingRecipe.getRequiredFluid(); @@ -71,8 +69,7 @@ public class FillingBySpout { availableFluid.shrink(requiredAmount); wrapper.setInventorySlotContents(0, stack); - Optional> recipe = world.getRecipeManager() - .getRecipe(AllRecipeTypes.FILLING.getType(), wrapper, world); + Optional> recipe = AllRecipeTypes.FILLING.find(wrapper, world); if (recipe.isPresent()) { FillingRecipe fillingRecipe = (FillingRecipe) recipe.get(); FluidIngredient requiredFluid = fillingRecipe.getRequiredFluid(); diff --git a/src/main/java/com/simibubi/create/content/contraptions/processing/BasinBlock.java b/src/main/java/com/simibubi/create/content/contraptions/processing/BasinBlock.java index 39fbddf41..6a57ebbb1 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/processing/BasinBlock.java +++ b/src/main/java/com/simibubi/create/content/contraptions/processing/BasinBlock.java @@ -9,16 +9,23 @@ import com.simibubi.create.foundation.block.ITE; import com.simibubi.create.foundation.item.ItemHelper; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour; +import com.simibubi.create.foundation.utility.Iterate; +import com.simibubi.create.foundation.utility.Pair; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.entity.Entity; import net.minecraft.entity.item.ItemEntity; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.BlockItemUseContext; import net.minecraft.item.ItemStack; import net.minecraft.item.ItemUseContext; +import net.minecraft.state.DirectionProperty; +import net.minecraft.state.StateContainer.Builder; +import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ActionResultType; +import net.minecraft.util.Direction; import net.minecraft.util.Hand; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockRayTraceResult; @@ -26,14 +33,22 @@ import net.minecraft.util.math.shapes.ISelectionContext; import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.world.IBlockReader; import net.minecraft.world.World; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.capability.CapabilityFluidHandler; +import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction; import net.minecraftforge.items.IItemHandlerModifiable; import net.minecraftforge.items.ItemHandlerHelper; import net.minecraftforge.items.ItemStackHandler; public class BasinBlock extends Block implements ITE, IWrenchable { + public static final DirectionProperty FACING = BlockStateProperties.FACING_EXCEPT_UP; + public BasinBlock(Properties p_i48440_1_) { super(p_i48440_1_); + setDefaultState(getDefaultState().with(FACING, Direction.DOWN)); } @Override @@ -41,6 +56,11 @@ public class BasinBlock extends Block implements ITE, IWrenchab return true; } + @Override + protected void fillStateContainer(Builder p_206840_1_) { + super.fillStateContainer(p_206840_1_.add(FACING)); + } + @Override public TileEntity createTileEntity(BlockState state, IBlockReader world) { return AllTileEntities.BASIN.create(); @@ -54,12 +74,16 @@ public class BasinBlock extends Block implements ITE, IWrenchab @Override public ActionResultType onUse(BlockState state, World worldIn, BlockPos pos, PlayerEntity player, Hand handIn, BlockRayTraceResult hit) { - if (!player.getHeldItem(handIn) - .isEmpty()) - return ActionResultType.PASS; + ItemStack heldItem = player.getHeldItem(handIn); try { BasinTileEntity te = getTileEntity(worldIn, pos); + if (!heldItem.isEmpty()) { + if (tryEmptyItemIntoBasin(worldIn, player, handIn, heldItem, te)) + return ActionResultType.SUCCESS; + return ActionResultType.PASS; + } + IItemHandlerModifiable inv = te.itemCapability.orElse(new ItemStackHandler(1)); for (int slot = 0; slot < inv.getSlots(); slot++) { player.inventory.placeItemBackInInventory(worldIn, inv.getStackInSlot(slot)); @@ -72,6 +96,30 @@ public class BasinBlock extends Block implements ITE, IWrenchab return ActionResultType.SUCCESS; } + protected boolean tryEmptyItemIntoBasin(World worldIn, PlayerEntity player, Hand handIn, ItemStack heldItem, + BasinTileEntity te) { + if (!EmptyingByBasin.canItemBeEmptied(worldIn, heldItem)) + return false; + + Pair emptyItem = EmptyingByBasin.emptyItem(worldIn, heldItem, true); + LazyOptional capability = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY); + IFluidHandler tank = capability.orElse(null); + FluidStack fluidStack = emptyItem.getFirst(); + + if (tank == null || fluidStack.getAmount() != tank.fill(fluidStack, FluidAction.SIMULATE)) + return false; + if (worldIn.isRemote) + return true; + + EmptyingByBasin.emptyItem(worldIn, heldItem, false); + tank.fill(fluidStack, FluidAction.EXECUTE); + if (heldItem.isEmpty()) + player.setHeldItem(handIn, emptyItem.getSecond()); + else + player.inventory.placeItemBackInInventory(worldIn, emptyItem.getSecond()); + return true; + } + @Override public void onLanded(IBlockReader worldIn, Entity entityIn) { super.onLanded(worldIn, entityIn); @@ -102,6 +150,11 @@ public class BasinBlock extends Block implements ITE, IWrenchab return AllShapes.BASIN_BLOCK_SHAPE; } + @Override + public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState p_220082_4_, boolean p_220082_5_) { + updateDiagonalNeighbours(state, world, pos); + } + @Override public VoxelShape getCollisionShape(BlockState state, IBlockReader reader, BlockPos pos, ISelectionContext ctx) { if (ctx.getEntity() instanceof ItemEntity) @@ -111,6 +164,7 @@ public class BasinBlock extends Block implements ITE, IWrenchab @Override public void onReplaced(BlockState state, World worldIn, BlockPos pos, BlockState newState, boolean isMoving) { + updateDiagonalNeighbours(state, worldIn, pos); if (!state.hasTileEntity() || state.getBlock() == newState.getBlock()) return; TileEntityBehaviour.destroy(worldIn, pos, FilteringBehaviour.TYPE); @@ -140,4 +194,35 @@ public class BasinBlock extends Block implements ITE, IWrenchab return BasinTileEntity.class; } + @Override + public BlockState getStateForPlacement(BlockItemUseContext ctx) { + BlockState state = super.getStateForPlacement(ctx); + World world = ctx.getWorld(); + BlockPos pos = ctx.getPos(); + return updateDiagonalState(state, world, pos); + } + + protected void updateDiagonalNeighbours(BlockState state, World world, BlockPos pos) { + for (Direction direction : Iterate.horizontalDirections) { + BlockPos toUpdate = pos.up() + .offset(direction); + BlockState stateToUpdate = world.getBlockState(toUpdate); + BlockState updated = updateDiagonalState(stateToUpdate, world, toUpdate); + if (stateToUpdate != updated && !world.isRemote) + world.setBlockState(toUpdate, updated); + } + } + + public static BlockState updateDiagonalState(BlockState state, IBlockReader world, BlockPos pos) { + if (!(state.getBlock() instanceof BasinBlock)) + return state; + for (Direction direction : Iterate.horizontalDirections) { + BlockState diagonaloutputBasin = world.getBlockState(pos.down() + .offset(direction)); + if (diagonaloutputBasin.getBlock() instanceof BasinBlock) + return state.with(FACING, direction); + } + return state.with(FACING, Direction.DOWN); + } + } diff --git a/src/main/java/com/simibubi/create/content/contraptions/processing/BasinGenerator.java b/src/main/java/com/simibubi/create/content/contraptions/processing/BasinGenerator.java new file mode 100644 index 000000000..fe96055ac --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/processing/BasinGenerator.java @@ -0,0 +1,32 @@ +package com.simibubi.create.content.contraptions.processing; + +import com.simibubi.create.foundation.data.AssetLookup; +import com.simibubi.create.foundation.data.SpecialBlockStateGen; +import com.tterrag.registrate.providers.DataGenContext; +import com.tterrag.registrate.providers.RegistrateBlockstateProvider; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraftforge.client.model.generators.ModelFile; + +public class BasinGenerator extends SpecialBlockStateGen { + + @Override + protected int getXRotation(BlockState state) { + return 0; + } + + @Override + protected int getYRotation(BlockState state) { + return horizontalAngle(state.get(BasinBlock.FACING)); + } + + @Override + public ModelFile getModel(DataGenContext ctx, RegistrateBlockstateProvider prov, + BlockState state) { + if (state.get(BasinBlock.FACING).getAxis().isVertical()) + return AssetLookup.partialBaseModel(ctx, prov); + return AssetLookup.partialBaseModel(ctx, prov, "directional"); + } + +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/processing/BasinInputInventory.java b/src/main/java/com/simibubi/create/content/contraptions/processing/BasinInputInventory.java deleted file mode 100644 index 6f105dd5e..000000000 --- a/src/main/java/com/simibubi/create/content/contraptions/processing/BasinInputInventory.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.simibubi.create.content.contraptions.processing; - -import com.simibubi.create.foundation.item.SmartInventory; - -import net.minecraft.item.ItemStack; -import net.minecraftforge.items.ItemHandlerHelper; - -public class BasinInputInventory extends SmartInventory { - - public BasinInputInventory(int slots, BasinTileEntity te) { - super(slots, te); - } - - @Override - public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) { - // Only insert if no other slot already has a 'full' stack of this item - for (int i = 0; i < getSlots(); i++) { - ItemStack stackInSlot = getStackInSlot(i); - if (ItemHandlerHelper.canItemStacksStack(stack, stackInSlot) - && stackInSlot.getCount() == getStackLimit(i, stackInSlot)) - return stack; - } - - return super.insertItem(slot, stack, simulate); - } - -} diff --git a/src/main/java/com/simibubi/create/content/contraptions/processing/BasinInventory.java b/src/main/java/com/simibubi/create/content/contraptions/processing/BasinInventory.java new file mode 100644 index 000000000..3e3ac469a --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/processing/BasinInventory.java @@ -0,0 +1,23 @@ +package com.simibubi.create.content.contraptions.processing; + +import com.simibubi.create.foundation.item.SmartInventory; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.items.ItemHandlerHelper; + +public class BasinInventory extends SmartInventory { + + public BasinInventory(int slots, BasinTileEntity te) { + super(slots, te, 16, true); + } + + @Override + public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) { + // Only insert if no other slot already has a stack of this item + for (int i = 0; i < getSlots(); i++) + if (i != slot && ItemHandlerHelper.canItemStacksStack(stack, inv.getStackInSlot(i))) + return stack; + return super.insertItem(slot, stack, simulate); + } + +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/processing/BasinOperatingTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/processing/BasinOperatingTileEntity.java index 26320f935..20383e4f4 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/processing/BasinOperatingTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/processing/BasinOperatingTileEntity.java @@ -1,49 +1,29 @@ package com.simibubi.create.content.contraptions.processing; -import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; -import com.simibubi.create.AllTileEntities; import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.foundation.advancement.AllTriggers; -import com.simibubi.create.foundation.advancement.SimpleTrigger; -import com.simibubi.create.foundation.item.SmartInventory; +import com.simibubi.create.foundation.advancement.ITriggerable; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.behaviour.simple.DeferralBehaviour; import com.simibubi.create.foundation.utility.recipe.RecipeFinder; import net.minecraft.inventory.IInventory; -import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.IRecipe; -import net.minecraft.item.crafting.Ingredient; import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntityType; -import net.minecraft.util.NonNullList; -import net.minecraftforge.common.util.LazyOptional; -import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fluids.capability.CapabilityFluidHandler; -import net.minecraftforge.fluids.capability.IFluidHandler; -import net.minecraftforge.items.CapabilityItemHandler; -import net.minecraftforge.items.IItemHandler; -import net.minecraftforge.items.ItemHandlerHelper; public abstract class BasinOperatingTileEntity extends KineticTileEntity { public DeferralBehaviour basinChecker; public boolean basinRemoved; - protected IRecipe lastRecipe; - - protected LazyOptional basinItemInv = LazyOptional.empty(); - protected List itemInputs; - protected LazyOptional basinFluidInv = LazyOptional.empty(); - protected List fluidInputs; + protected IRecipe currentRecipe; public BasinOperatingTileEntity(TileEntityType typeIn) { super(typeIn); - itemInputs = new ArrayList<>(); - fluidInputs = new ArrayList<>(); } @Override @@ -58,29 +38,10 @@ public abstract class BasinOperatingTileEntity extends KineticTileEntity { super.onSpeedChanged(prevSpeed); if (getSpeed() == 0) basinRemoved = true; + basinRemoved = false; basinChecker.scheduleUpdate(); } - public void gatherInputs() { - itemInputs.clear(); - basinItemInv.ifPresent(handler -> { - for (int slot = 0; slot < handler.getSlots(); ++slot) { - ItemStack itemstack = handler.getStackInSlot(slot); - if (!itemstack.isEmpty()) - itemInputs.add(itemstack); - } - }); - - fluidInputs.clear(); - basinFluidInv.ifPresent(handler -> { - for (int tank = 0; tank < handler.getTanks(); tank++) { - FluidStack fluidInTank = handler.getFluidInTank(tank); - if (!fluidInTank.isEmpty()) - fluidInputs.add(fluidInTank); - } - }); - } - @Override public void tick() { if (basinRemoved) { @@ -100,28 +61,13 @@ public abstract class BasinOperatingTileEntity extends KineticTileEntity { return true; if (isRunning()) return false; - - Optional basinTe = getBasin(); - if (!basinTe.isPresent()) - return true; - if (!basinItemInv.isPresent()) - basinItemInv = basinTe.get() - .getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY); - if (!basinFluidInv.isPresent()) - basinFluidInv = basinTe.get() - .getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY); - if (!basinFluidInv.isPresent() || !basinItemInv.isPresent()) - return true; - if (world == null || world.isRemote) return true; - gatherInputs(); List> recipes = getMatchingRecipes(); if (recipes.isEmpty()) return true; - - lastRecipe = recipes.get(0); + currentRecipe = recipes.get(0); startProcessingBasin(); sendData(); return true; @@ -135,58 +81,37 @@ public abstract class BasinOperatingTileEntity extends KineticTileEntity { return true; } - public void applyBasinRecipe() { - if (lastRecipe == null) - return; - if (!basinItemInv.isPresent() || !basinFluidInv.isPresent()) - return; - + protected boolean matchBasinRecipe(IRecipe recipe) { + if (recipe == null) + return false; Optional basin = getBasin(); if (!basin.isPresent()) + return false; + return BasinRecipe.match(basin.get(), recipe); + } + + protected void applyBasinRecipe() { + if (currentRecipe == null) return; - SmartInventory inputs = basin.get().getInputInventory(); - SmartInventory outputs = basin.get().getOutputInventory(); - List containers = new ArrayList<>(); - - NonNullList ingredients = lastRecipe.getIngredients(); - Ingredients: for (int i = 0; i < ingredients.size(); i++) { - Ingredient ingredient = ingredients.get(i); - - for (int slot = 0; slot < inputs.getSlots(); slot++) { - if (!ingredient.test(inputs.extractItem(slot, 1, true))) - continue; - ItemStack extracted = inputs.extractItem(slot, 1, false); - if (extracted.hasContainerItem()) - containers.add(extracted.getContainerItem() - .copy()); - continue Ingredients; - } - - // something wasn't found + + Optional optionalBasin = getBasin(); + if (!optionalBasin.isPresent()) return; - } - - if (world != null && !world.isRemote) { - SimpleTrigger trigger = AllTriggers.MIXER_MIX; - if (AllTileEntities.MECHANICAL_PRESS.is(this)) - trigger = AllTriggers.PRESS_COMPACT; - AllTriggers.triggerForNearbyPlayers(trigger, world, pos, 4); - } - - outputs.allowInsertion(); - ItemHandlerHelper.insertItemStacked(outputs, lastRecipe.getRecipeOutput() - .copy(), false); // TODO only works for single item output - containers.forEach(stack -> ItemHandlerHelper.insertItemStacked(outputs, stack, false)); - outputs.forbidInsertion(); - + BasinTileEntity basin = optionalBasin.get(); + if (!BasinRecipe.apply(basin, currentRecipe)) + return; + Optional processedRecipeTrigger = getProcessedRecipeTrigger(); + if (world != null && !world.isRemote && processedRecipeTrigger.isPresent()) + AllTriggers.triggerForNearbyPlayers(processedRecipeTrigger.get(), world, pos, 4); + basin.inputTank.sendDataImmediately(); + // Continue mixing - gatherInputs(); - if (matchBasinRecipe(lastRecipe)) { + if (matchBasinRecipe(currentRecipe)) { continueWithPreviousRecipe(); sendData(); } - getBasin().ifPresent(BasinTileEntity::notifyChangeOfContents); + basin.notifyChangeOfContents(); } protected List> getMatchingRecipes() { @@ -210,28 +135,13 @@ public abstract class BasinOperatingTileEntity extends KineticTileEntity { return Optional.empty(); return Optional.of((BasinTileEntity) basinTE); } + + protected Optional getProcessedRecipeTrigger() { + return Optional.empty(); + } protected abstract boolean matchStaticFilters(IRecipe recipe); - protected boolean matchBasinRecipe(IRecipe recipe) { - if (recipe == null) - return false; - - Optional basin = getBasin(); - if (!basin.isPresent()) - return false; - BasinTileEntity basinTileEntity = basin.get(); - if (!basinTileEntity.getFilter() - .test(recipe.getRecipeOutput())) - return false; - - NonNullList ingredients = recipe.getIngredients(); - if (!ingredients.stream() - .allMatch(ingredient -> (ingredient.isSimple() || ingredient.getMatchingStacks().length == 1))) - return false; - return true; - } - protected abstract Object getRecipeCacheKey(); } diff --git a/src/main/java/com/simibubi/create/content/contraptions/processing/BasinRecipe.java b/src/main/java/com/simibubi/create/content/contraptions/processing/BasinRecipe.java new file mode 100644 index 000000000..6fc0a451d --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/processing/BasinRecipe.java @@ -0,0 +1,199 @@ +package com.simibubi.create.content.contraptions.processing; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; + +import javax.annotation.Nonnull; + +import com.simibubi.create.AllRecipeTypes; +import com.simibubi.create.content.contraptions.processing.ProcessingRecipeBuilder.ProcessingRecipeParams; +import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock.HeatLevel; +import com.simibubi.create.foundation.fluid.FluidIngredient; +import com.simibubi.create.foundation.item.SmartInventory; +import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour; +import com.simibubi.create.foundation.tileEntity.behaviour.fluid.SmartFluidTankBehaviour; +import com.simibubi.create.foundation.tileEntity.behaviour.fluid.SmartFluidTankBehaviour.TankSegment; +import com.simibubi.create.foundation.utility.Iterate; + +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.item.crafting.Ingredient; +import net.minecraft.world.World; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.capability.CapabilityFluidHandler; +import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.items.CapabilityItemHandler; +import net.minecraftforge.items.IItemHandler; + +public class BasinRecipe extends ProcessingRecipe { + + public static boolean match(BasinTileEntity basin, IRecipe recipe) { + FilteringBehaviour filter = basin.getFilter(); + if (filter == null || !filter.test(recipe.getRecipeOutput())) + return false; + return apply(basin, recipe, true); + } + + public static boolean apply(BasinTileEntity basin, IRecipe recipe) { + return apply(basin, recipe, false); + } + + private static boolean apply(BasinTileEntity basin, IRecipe recipe, boolean test) { + boolean isBasinRecipe = recipe instanceof BasinRecipe; + IItemHandler availableItems = basin.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) + .orElse(null); + IFluidHandler availableFluids = basin.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) + .orElse(null); + + if (availableItems == null || availableFluids == null) + return false; + + HeatLevel heat = basin.getHeatLevel(); + if (isBasinRecipe && !((BasinRecipe) recipe).getRequiredHeat() + .testBlazeBurner(heat)) + return false; + + List recipeOutputItems = new ArrayList<>(); + List recipeOutputFluids = new ArrayList<>(); + + List ingredients = new LinkedList<>(recipe.getIngredients()); + ingredients.sort(Comparator.comparingInt(i -> i.getMatchingStacks().length)); + List fluidIngredients = + isBasinRecipe ? ((BasinRecipe) recipe).getFluidIngredients() : Collections.emptyList(); + + for (boolean simulate : Iterate.trueAndFalse) { + + if (!simulate && test) + return true; + + int[] extractedItemsFromSlot = new int[availableItems.getSlots()]; + int[] extractedFluidsFromTank = new int[availableFluids.getTanks()]; + + Ingredients: for (int i = 0; i < ingredients.size(); i++) { + Ingredient ingredient = ingredients.get(i); + + for (int slot = 0; slot < availableItems.getSlots(); slot++) { + if (simulate && availableItems.getStackInSlot(slot) + .getCount() <= extractedItemsFromSlot[slot]) + continue; + ItemStack extracted = availableItems.extractItem(slot, 1, true); + if (!ingredient.test(extracted)) + continue; + if (!simulate) + availableItems.extractItem(slot, 1, false); + else if (extracted.hasContainerItem()) + recipeOutputItems.add(extracted.getContainerItem() + .copy()); + extractedItemsFromSlot[slot]++; + continue Ingredients; + } + + // something wasn't found + return false; + } + + boolean fluidsAffected = false; + FluidIngredients: for (int i = 0; i < fluidIngredients.size(); i++) { + FluidIngredient fluidIngredient = fluidIngredients.get(i); + int amountRequired = fluidIngredient.getRequiredAmount(); + + for (int tank = 0; tank < availableFluids.getTanks(); tank++) { + FluidStack fluidStack = availableFluids.getFluidInTank(tank); + if (simulate && fluidStack.getAmount() <= extractedFluidsFromTank[tank]) + continue; + if (!fluidIngredient.test(fluidStack)) + continue; + int drainedAmount = Math.min(amountRequired, fluidStack.getAmount()); + if (!simulate) { + fluidStack.shrink(drainedAmount); + fluidsAffected = true; + } + amountRequired -= drainedAmount; + if (amountRequired != 0) + continue; + extractedFluidsFromTank[tank] += drainedAmount; + continue FluidIngredients; + } + + // something wasn't found + return false; + } + + if (fluidsAffected) { + basin.getBehaviour(SmartFluidTankBehaviour.INPUT) + .foreach(TankSegment::onFluidStackChanged); + basin.getBehaviour(SmartFluidTankBehaviour.OUTPUT) + .foreach(TankSegment::onFluidStackChanged); + } + + if (simulate) { + if (recipe instanceof BasinRecipe) { + recipeOutputItems.addAll(((BasinRecipe) recipe).rollResults()); + recipeOutputFluids.addAll(((BasinRecipe) recipe).getFluidResults()); + } else + recipeOutputItems.add(recipe.getRecipeOutput()); + } + + if (!basin.acceptOutputs(recipeOutputItems, recipeOutputFluids, simulate)) + return false; + } + + return true; + } + + /** + * For JEI purposes only + */ + public boolean convertedRecipe; + + public static BasinRecipe convert(IRecipe recipe) { + BasinRecipe basinRecipe = new ProcessingRecipeBuilder<>(BasinRecipe::new, recipe.getId()) + .withItemIngredients(recipe.getIngredients()) + .withSingleItemOutput(recipe.getRecipeOutput()) + .build(); + basinRecipe.convertedRecipe = true; + return basinRecipe; + } + + protected BasinRecipe(AllRecipeTypes type, ProcessingRecipeParams params) { + super(type, params); + } + + public BasinRecipe(ProcessingRecipeParams params) { + this(AllRecipeTypes.BASIN, params); + } + + @Override + protected int getMaxInputCount() { + return 9; + } + + @Override + protected int getMaxOutputCount() { + return 4; + } + + @Override + protected int getMaxFluidInputCount() { + return 2; + } + + @Override + protected int getMaxFluidOutputCount() { + return 2; + } + + @Override + protected boolean canRequireHeat() { + return true; + } + + @Override + public boolean matches(SmartInventory inv, @Nonnull World worldIn) { + return false; + } + +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/processing/BasinRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/processing/BasinRenderer.java index e9e232abd..a02cb4eed 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/processing/BasinRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/processing/BasinRenderer.java @@ -79,16 +79,19 @@ public class BasinRenderer extends SmartTileEntityRenderer { if (tankSegment.getRenderedFluid() .isEmpty()) continue; - totalUnits += tankSegment.getTotalUnits(partialTicks); + float units = tankSegment.getTotalUnits(partialTicks); + if (units < 1) + continue; + totalUnits += units; renderedFluids++; } } if (renderedFluids == 0) return 0; - if (totalUnits == 0) + if (totalUnits < 1) return 0; - + float fluidLevel = MathHelper.clamp(totalUnits / 2000, 0, 1); float xMin = 2 / 16f; @@ -105,8 +108,11 @@ public class BasinRenderer extends SmartTileEntityRenderer { FluidStack renderedFluid = tankSegment.getRenderedFluid(); if (renderedFluid.isEmpty()) continue; - - float partial = tankSegment.getTotalUnits(partialTicks) / totalUnits; + float units = tankSegment.getTotalUnits(partialTicks); + if (units < 1) + continue; + + float partial = units / totalUnits; xMax += partial * 12 / 16f; FluidRenderer.renderTiledFluidBB(renderedFluid, xMin, yMin, zMin, xMax, yMax, zMax, buffer, ms, light, false); @@ -114,7 +120,7 @@ public class BasinRenderer extends SmartTileEntityRenderer { xMin = xMax; } } - + return fluidLevel; } diff --git a/src/main/java/com/simibubi/create/content/contraptions/processing/BasinTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/processing/BasinTileEntity.java index 6b437512f..1ee7746c8 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/processing/BasinTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/processing/BasinTileEntity.java @@ -5,6 +5,9 @@ import java.util.Optional; import javax.annotation.Nonnull; +import com.simibubi.create.AllTags; +import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock; +import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock.HeatLevel; import com.simibubi.create.foundation.fluid.CombinedTankWrapper; import com.simibubi.create.foundation.item.SmartInventory; import com.simibubi.create.foundation.tileEntity.SmartTileEntity; @@ -13,30 +16,36 @@ import com.simibubi.create.foundation.tileEntity.behaviour.ValueBoxTransform; import com.simibubi.create.foundation.tileEntity.behaviour.belt.DirectBeltInputBehaviour; import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour; import com.simibubi.create.foundation.tileEntity.behaviour.fluid.SmartFluidTankBehaviour; +import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.block.BlockState; +import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundNBT; import net.minecraft.tileentity.ITickableTileEntity; import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntityType; import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction; import net.minecraftforge.items.CapabilityItemHandler; +import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.IItemHandlerModifiable; +import net.minecraftforge.items.ItemHandlerHelper; import net.minecraftforge.items.wrapper.CombinedInvWrapper; public class BasinTileEntity extends SmartTileEntity implements ITickableTileEntity { - public BasinInputInventory inputInventory; + public BasinInventory inputInventory; public SmartFluidTankBehaviour inputTank; - protected SmartInventory outputInventory; protected SmartFluidTankBehaviour outputTank; @@ -48,12 +57,11 @@ public class BasinTileEntity extends SmartTileEntity implements ITickableTileEnt public BasinTileEntity(TileEntityType type) { super(type); - inputInventory = new BasinInputInventory(9, this); - inputInventory.withMaxStackSize(8) - .forbidExtraction(); - outputInventory = new SmartInventory(9, this).forbidInsertion(); - itemCapability = LazyOptional.of(() -> new CombinedInvWrapper(inputInventory, outputInventory)); + inputInventory = new BasinInventory(9, this); + inputInventory.whenContentsChanged(() -> contentsChanged = true); + outputInventory = new BasinInventory(9, this).forbidInsertion(); + itemCapability = LazyOptional.of(() -> new CombinedInvWrapper(inputInventory, outputInventory)); contentsChanged = true; } @@ -65,7 +73,8 @@ public class BasinTileEntity extends SmartTileEntity implements ITickableTileEnt .forRecipes(); behaviours.add(filtering); - inputTank = new SmartFluidTankBehaviour(SmartFluidTankBehaviour.INPUT, this, 2, 1000, true).forbidExtraction(); + inputTank = new SmartFluidTankBehaviour(SmartFluidTankBehaviour.INPUT, this, 2, 1000, true) + .whenFluidUpdates(() -> contentsChanged = true); outputTank = new SmartFluidTankBehaviour(SmartFluidTankBehaviour.OUTPUT, this, 2, 1000, true).forbidInsertion(); behaviours.add(inputTank); behaviours.add(outputTank); @@ -112,6 +121,16 @@ public class BasinTileEntity extends SmartTileEntity implements ITickableTileEnt return super.getCapability(cap, side); } + @Override + public void notifyUpdate() { + super.notifyUpdate(); + } + + @Override + public void lazyTick() { + super.lazyTick(); + } + @Override public void tick() { super.tick(); @@ -119,6 +138,17 @@ public class BasinTileEntity extends SmartTileEntity implements ITickableTileEnt return; contentsChanged = false; getOperator().ifPresent(te -> te.basinChecker.scheduleUpdate()); + + for (Direction offset : Iterate.horizontalDirections) { + BlockPos toUpdate = pos.up() + .offset(offset); + BlockState stateToUpdate = world.getBlockState(toUpdate); + if (stateToUpdate.getBlock() instanceof BasinBlock && stateToUpdate.get(BasinBlock.FACING) == offset.getOpposite()) { + TileEntity te = world.getTileEntity(toUpdate); + if (te instanceof BasinTileEntity) + ((BasinTileEntity) te).contentsChanged = true; + } + } } private Optional getOperator() { @@ -152,11 +182,64 @@ public class BasinTileEntity extends SmartTileEntity implements ITickableTileEnt return 256; } + public boolean acceptOutputs(List outputItems, List outputFluids, boolean simulate) { + outputInventory.allowInsertion(); + outputTank.allowInsertion(); + boolean acceptOutputsInner = acceptOutputsInner(outputItems, outputFluids, simulate); + outputInventory.forbidInsertion(); + outputTank.forbidInsertion(); + return acceptOutputsInner; + } + + private boolean acceptOutputsInner(List outputItems, List outputFluids, boolean simulate) { + BlockState blockState = getBlockState(); + if (!(blockState.getBlock() instanceof BasinBlock)) + return false; + Direction direction = blockState.get(BasinBlock.FACING); + + IItemHandler targetInv = null; + IFluidHandler targetTank = null; + + if (direction == Direction.DOWN) { + // No output basin, gather locally + targetInv = outputInventory; + targetTank = outputTank.getCapability() + .orElse(null); + + } else { + // Output basin, try moving items to it + TileEntity te = world.getTileEntity(pos.down() + .offset(direction)); + if (!(te instanceof BasinTileEntity)) + return false; + targetInv = te.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) + .orElse(null); + targetTank = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) + .orElse(null); + } + + if (targetInv == null) + return false; + for (ItemStack itemStack : outputItems) + if (!ItemHandlerHelper.insertItemStacked(targetInv, itemStack.copy(), simulate) + .isEmpty()) + return false; + + if (targetTank == null) + return false; + for (FluidStack fluidStack : outputFluids) + if (targetTank.fill(fluidStack.copy(), simulate ? FluidAction.SIMULATE : FluidAction.EXECUTE) != fluidStack + .getAmount()) + return false; + + return true; + } + class BasinValueBox extends ValueBoxTransform.Sided { @Override protected Vec3d getSouthLocation() { - return VecHelper.voxelSpace(8, 12, 16); + return VecHelper.voxelSpace(8, 12, 15.75); } @Override @@ -171,4 +254,11 @@ public class BasinTileEntity extends SmartTileEntity implements ITickableTileEnt inputInventory.deserializeNBT(compound.getCompound("InputItems")); outputInventory.deserializeNBT(compound.getCompound("OutputItems")); } + + public HeatLevel getHeatLevel() { + BlockState state = world.getBlockState(pos.down(1)); + if (state.has(BlazeBurnerBlock.HEAT_LEVEL)) + return state.get(BlazeBurnerBlock.HEAT_LEVEL); + return AllTags.AllBlockTags.FAN_HEATERS.matches(state) ? HeatLevel.SMOULDERING : HeatLevel.NONE; + } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/processing/EmptyingByBasin.java b/src/main/java/com/simibubi/create/content/contraptions/processing/EmptyingByBasin.java new file mode 100644 index 000000000..041647651 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/processing/EmptyingByBasin.java @@ -0,0 +1,75 @@ +package com.simibubi.create.content.contraptions.processing; + +import java.util.List; +import java.util.Optional; + +import com.simibubi.create.AllRecipeTypes; +import com.simibubi.create.foundation.utility.Pair; + +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.world.World; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.capability.CapabilityFluidHandler; +import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction; +import net.minecraftforge.fluids.capability.IFluidHandlerItem; +import net.minecraftforge.items.ItemStackHandler; +import net.minecraftforge.items.wrapper.RecipeWrapper; + +public class EmptyingByBasin { + + static RecipeWrapper wrapper = new RecipeWrapper(new ItemStackHandler(1)); + + public static boolean canItemBeEmptied(World world, ItemStack stack) { + wrapper.setInventorySlotContents(0, stack); + if (AllRecipeTypes.EMPTYING.find(wrapper, world) + .isPresent()) + return true; + + LazyOptional capability = + stack.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY); + IFluidHandlerItem tank = capability.orElse(null); + if (tank == null) + return false; + for (int i = 0; i < tank.getTanks(); i++) { + if (tank.getFluidInTank(i) + .getAmount() > 0) + return true; + } + return false; + } + + public static Pair emptyItem(World world, ItemStack stack, boolean simulate) { + FluidStack resultingFluid = FluidStack.EMPTY; + ItemStack resultingItem = ItemStack.EMPTY; + + wrapper.setInventorySlotContents(0, stack); + Optional> recipe = AllRecipeTypes.EMPTYING.find(wrapper, world); + if (recipe.isPresent()) { + EmptyingRecipe emptyingRecipe = (EmptyingRecipe) recipe.get(); + List results = emptyingRecipe.rollResults(); + if (!simulate) + stack.shrink(1); + resultingItem = results.isEmpty() ? ItemStack.EMPTY : results.get(0); + resultingFluid = emptyingRecipe.getResultingFluid(); + return Pair.of(resultingFluid, resultingItem); + } + + ItemStack split = stack.copy(); + split.setCount(1); + LazyOptional capability = + split.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY); + IFluidHandlerItem tank = capability.orElse(null); + if (tank == null) + return Pair.of(resultingFluid, resultingItem); + resultingFluid = tank.drain(1000, simulate ? FluidAction.SIMULATE : FluidAction.EXECUTE); + resultingItem = tank.getContainer() + .copy(); + if (!simulate) + stack.shrink(1); + + return Pair.of(resultingFluid, resultingItem); + } + +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/processing/EmptyingRecipe.java b/src/main/java/com/simibubi/create/content/contraptions/processing/EmptyingRecipe.java new file mode 100644 index 000000000..fbed9d886 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/processing/EmptyingRecipe.java @@ -0,0 +1,42 @@ +package com.simibubi.create.content.contraptions.processing; + +import com.simibubi.create.AllRecipeTypes; +import com.simibubi.create.content.contraptions.processing.ProcessingRecipeBuilder.ProcessingRecipeParams; + +import net.minecraft.world.World; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.items.wrapper.RecipeWrapper; + +public class EmptyingRecipe extends ProcessingRecipe { + + public EmptyingRecipe(ProcessingRecipeParams params) { + super(AllRecipeTypes.EMPTYING, params); + } + + @Override + public boolean matches(RecipeWrapper inv, World p_77569_2_) { + return ingredients.get(0).test(inv.getStackInSlot(0)); + } + + @Override + protected int getMaxInputCount() { + return 1; + } + + @Override + protected int getMaxOutputCount() { + return 1; + } + + @Override + protected int getMaxFluidOutputCount() { + return 1; + } + + public FluidStack getResultingFluid() { + if (fluidResults.isEmpty()) + throw new IllegalStateException("Emptying Recipe: " + id.toString() + " has no fluid output!"); + return fluidResults.get(0); + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/InWorldProcessing.java b/src/main/java/com/simibubi/create/content/logistics/InWorldProcessing.java index ca60ccfd3..97aea25f9 100644 --- a/src/main/java/com/simibubi/create/content/logistics/InWorldProcessing.java +++ b/src/main/java/com/simibubi/create/content/logistics/InWorldProcessing.java @@ -120,8 +120,7 @@ public class InWorldProcessing { public static boolean isWashable(ItemStack stack, World world) { splashingInv.setInventorySlotContents(0, stack); - Optional recipe = world.getRecipeManager() - .getRecipe(AllRecipeTypes.SPLASHING.getType(), splashingInv, world); + Optional recipe = AllRecipeTypes.SPLASHING.find(splashingInv, world); return recipe.isPresent(); } @@ -176,8 +175,7 @@ public class InWorldProcessing { private static List process(ItemStack stack, Type type, World world) { if (type == Type.SPLASHING) { splashingInv.setInventorySlotContents(0, stack); - Optional recipe = world.getRecipeManager() - .getRecipe(AllRecipeTypes.SPLASHING.getType(), splashingInv, world); + Optional recipe = AllRecipeTypes.SPLASHING.find(splashingInv, world); if (recipe.isPresent()) return applyRecipeOn(stack, recipe.get()); return null; diff --git a/src/main/java/com/simibubi/create/content/logistics/item/filter/ItemAttribute.java b/src/main/java/com/simibubi/create/content/logistics/item/filter/ItemAttribute.java index 1be6edd4b..fe8dca539 100644 --- a/src/main/java/com/simibubi/create/content/logistics/item/filter/ItemAttribute.java +++ b/src/main/java/com/simibubi/create/content/logistics/item/filter/ItemAttribute.java @@ -127,10 +127,10 @@ public interface ItemAttribute { this.test = test; } - private static boolean testRecipe(ItemStack s, World w, IRecipeType> smelting) { + private static boolean testRecipe(ItemStack s, World w, IRecipeType> type) { RECIPE_WRAPPER.setInventorySlotContents(0, s.copy()); return w.getRecipeManager() - .getRecipe(smelting, RECIPE_WRAPPER, w) + .getRecipe(type, RECIPE_WRAPPER, w) .isPresent(); } diff --git a/src/main/java/com/simibubi/create/foundation/config/CKinetics.java b/src/main/java/com/simibubi/create/foundation/config/CKinetics.java index 3f83c62f2..31a728123 100644 --- a/src/main/java/com/simibubi/create/foundation/config/CKinetics.java +++ b/src/main/java/com/simibubi/create/foundation/config/CKinetics.java @@ -27,6 +27,8 @@ public class CKinetics extends ConfigBase { public ConfigInt maxPistonPoles = i(64, 1, "maxPistonPoles", Comments.maxPistonPoles); public ConfigInt maxRopeLength = i(128, 1, "maxRopeLength", Comments.maxRopeLength); public ConfigInt maxCartCouplingLength = i(32, 1, "maxCartCouplingLength", Comments.maxCartCouplingLength); + + public CStress stressValues = nested(0, CStress::new, Comments.stress); public ConfigGroup state = group(0, "stats", Comments.stats); public ConfigFloat mediumSpeed = f(30, 0, 4096, "mediumSpeed", Comments.rpm, Comments.mediumSpeed); @@ -37,8 +39,6 @@ public class CKinetics extends ConfigBase { public ConfigFloat mediumCapacity = f(128, 0, 4096, "mediumCapacity", Comments.su, Comments.mediumCapacity); public ConfigFloat highCapacity = f(512, 0, 65535, "highCapacity", Comments.su, Comments.highCapacity); - public CStress stressValues = nested(0, CStress::new, Comments.stress); - @Override public String getName() { return "kinetics"; diff --git a/src/main/java/com/simibubi/create/foundation/config/CRecipes.java b/src/main/java/com/simibubi/create/foundation/config/CRecipes.java new file mode 100644 index 000000000..b7eba1242 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/config/CRecipes.java @@ -0,0 +1,26 @@ +package com.simibubi.create.foundation.config; + +public class CRecipes extends ConfigBase { + + public ConfigBool allowShapelessInMixer = b(true, "allowShapelessInMixer", Comments.allowShapelessInMixer); + public ConfigBool allowShapedSquareInPress = b(true, "allowShapedSquareInPress", Comments.allowShapedSquareInPress); + public ConfigBool allowRegularCraftingInCrafter = b(true, "allowRegularCraftingInCrafter", Comments.allowRegularCraftingInCrafter); + public ConfigBool allowStonecuttingOnSaw = b(true, "allowStonecuttingOnSaw", Comments.allowStonecuttingOnSaw); + + @Override + public String getName() { + return "recipes"; + } + + private static class Comments { + static String allowShapelessInMixer = + "When true, allows any shapeless crafting recipes to be processed by a Mechanical Mixer + Basin."; + static String allowShapedSquareInPress = + "When true, allows any single-ingredient 2x2 or 3x3 crafting recipes to be processed by a Mechanical Press + Basin."; + static String allowRegularCraftingInCrafter = + "When true, allows any standard crafting recipes to be processed by Mechanical Crafters."; + static String allowStonecuttingOnSaw = + "When true, allows any stonecutting recipes to be processed by a Mechanical Saw."; + } + +} diff --git a/src/main/java/com/simibubi/create/foundation/config/CServer.java b/src/main/java/com/simibubi/create/foundation/config/CServer.java index 5534e8df7..8ce3783bd 100644 --- a/src/main/java/com/simibubi/create/foundation/config/CServer.java +++ b/src/main/java/com/simibubi/create/foundation/config/CServer.java @@ -6,6 +6,7 @@ public class CServer extends ConfigBase { public ConfigInt tickrateSyncTimer = i(20, 5, "tickrateSyncTimer", "[in Ticks]", Comments.tickrateSyncTimer, Comments.tickrateSyncTimer2); + public CRecipes recipes = nested(0, CRecipes::new, Comments.recipes); public CKinetics kinetics = nested(0, CKinetics::new, Comments.kinetics); public CFluids fluids = nested(0, CFluids::new, Comments.fluids); public CLogistics logistics = nested(0, CLogistics::new, Comments.logistics); @@ -19,6 +20,7 @@ public class CServer extends ConfigBase { } private static class Comments { + static String recipes = "Packmakers' control panel for internal recipe compat"; static String schematics = "Everything related to Schematic tools"; static String kinetics = "Parameters and abilities of Create's kinetic mechanisms"; static String fluids = "Create's liquid manipulation tools"; diff --git a/src/main/java/com/simibubi/create/foundation/data/recipe/CompactingRecipeGen.java b/src/main/java/com/simibubi/create/foundation/data/recipe/CompactingRecipeGen.java new file mode 100644 index 000000000..97e135718 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/data/recipe/CompactingRecipeGen.java @@ -0,0 +1,35 @@ +package com.simibubi.create.foundation.data.recipe; + +import com.simibubi.create.AllRecipeTypes; +import com.simibubi.create.content.palettes.AllPaletteBlocks; + +import net.minecraft.data.DataGenerator; +import net.minecraft.fluid.Fluids; +import net.minecraft.item.Items; +import net.minecraft.tags.FluidTags; + +public class CompactingRecipeGen extends ProcessingRecipeGen { + + GeneratedRecipe + + TEMPGABBRO = create("temp_gabbro", b -> b + .require(Items.COBBLESTONE) + .require(FluidTags.LAVA, 250) + .output(AllPaletteBlocks.GABBRO.get(), 1)), + + ICE = create("ice", b -> b + .require(Items.ICE) + .output(Fluids.WATER, 250)) + + ; + + public CompactingRecipeGen(DataGenerator p_i48262_1_) { + super(p_i48262_1_); + } + + @Override + protected AllRecipeTypes getRecipeType() { + return AllRecipeTypes.COMPACTING; + } + +} diff --git a/src/main/java/com/simibubi/create/foundation/data/recipe/EmptyingRecipeGen.java b/src/main/java/com/simibubi/create/foundation/data/recipe/EmptyingRecipeGen.java new file mode 100644 index 000000000..9106e54a8 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/data/recipe/EmptyingRecipeGen.java @@ -0,0 +1,33 @@ +package com.simibubi.create.foundation.data.recipe; + +import com.simibubi.create.AllRecipeTypes; + +import net.minecraft.data.DataGenerator; +import net.minecraft.fluid.Fluids; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.potion.PotionUtils; +import net.minecraft.potion.Potions; +import net.minecraftforge.common.crafting.NBTIngredient; + +public class EmptyingRecipeGen extends ProcessingRecipeGen { + + GeneratedRecipe + + WATER_BOTTLE = create("water_bottle", b -> b + .require(NBTIngredient.fromStacks(PotionUtils.addPotionToItemStack(new ItemStack(Items.POTION), Potions.WATER))) + .output(Fluids.WATER, 250) + .output(Items.GLASS_BOTTLE)) + + ; + + public EmptyingRecipeGen(DataGenerator p_i48262_1_) { + super(p_i48262_1_); + } + + @Override + protected AllRecipeTypes getRecipeType() { + return AllRecipeTypes.EMPTYING; + } + +} diff --git a/src/main/java/com/simibubi/create/foundation/data/recipe/MixingRecipeGen.java b/src/main/java/com/simibubi/create/foundation/data/recipe/MixingRecipeGen.java index 524846b60..31369b8b9 100644 --- a/src/main/java/com/simibubi/create/foundation/data/recipe/MixingRecipeGen.java +++ b/src/main/java/com/simibubi/create/foundation/data/recipe/MixingRecipeGen.java @@ -8,6 +8,7 @@ import com.simibubi.create.content.contraptions.processing.HeatCondition; import net.minecraft.block.Blocks; import net.minecraft.data.DataGenerator; import net.minecraft.item.Items; +import net.minecraft.tags.FluidTags; import net.minecraft.tags.ItemTags; import net.minecraftforge.common.Tags; @@ -15,6 +16,11 @@ public class MixingRecipeGen extends ProcessingRecipeGen { GeneratedRecipe + TEMPCOBBLE = create("temp_cobble", b -> b + .require(FluidTags.WATER, 250) + .require(FluidTags.LAVA, 250) + .output(Blocks.COBBLESTONE, 1)), + BRASS_INGOT = create("brass_ingot", b -> b.require(I.copper()) .require(I.zinc()) .output(AllItems.BRASS_INGOT.get(), 2) diff --git a/src/main/java/com/simibubi/create/foundation/data/recipe/ProcessingRecipeGen.java b/src/main/java/com/simibubi/create/foundation/data/recipe/ProcessingRecipeGen.java index a5daca809..094d59133 100644 --- a/src/main/java/com/simibubi/create/foundation/data/recipe/ProcessingRecipeGen.java +++ b/src/main/java/com/simibubi/create/foundation/data/recipe/ProcessingRecipeGen.java @@ -32,8 +32,10 @@ public abstract class ProcessingRecipeGen extends CreateRecipeProvider { generators.add(new WashingRecipeGen(gen)); generators.add(new PolishingRecipeGen(gen)); generators.add(new MixingRecipeGen(gen)); + generators.add(new CompactingRecipeGen(gen)); generators.add(new PressingRecipeGen(gen)); generators.add(new FillingRecipeGen(gen)); + generators.add(new EmptyingRecipeGen(gen)); gen.addProvider(new IDataProvider() { diff --git a/src/main/java/com/simibubi/create/foundation/data/recipe/StandardRecipeGen.java b/src/main/java/com/simibubi/create/foundation/data/recipe/StandardRecipeGen.java index 4f8716c89..4b72a6da6 100644 --- a/src/main/java/com/simibubi/create/foundation/data/recipe/StandardRecipeGen.java +++ b/src/main/java/com/simibubi/create/foundation/data/recipe/StandardRecipeGen.java @@ -201,9 +201,9 @@ public class StandardRecipeGen extends CreateRecipeProvider { .key('A', Tags.Items.NUGGETS_IRON) .patternLine("ASA")), - ATTRIBUTE_FILTER = create(AllItems.ATTRIBUTE_FILTER).unlockedBy(I::andesite) + ATTRIBUTE_FILTER = create(AllItems.ATTRIBUTE_FILTER).unlockedByTag(I::brass) .viaShaped(b -> b.key('S', ItemTags.WOOL) - .key('A', I.copperNugget()) + .key('A', I.brassNugget()) .patternLine("ASA")), BRASS_HAND = create(AllItems.BRASS_HAND).unlockedByTag(I::brass) diff --git a/src/main/java/com/simibubi/create/foundation/fluid/CombinedTankWrapper.java b/src/main/java/com/simibubi/create/foundation/fluid/CombinedTankWrapper.java index 2a5bb5661..67fce3854 100644 --- a/src/main/java/com/simibubi/create/foundation/fluid/CombinedTankWrapper.java +++ b/src/main/java/com/simibubi/create/foundation/fluid/CombinedTankWrapper.java @@ -62,6 +62,9 @@ public class CombinedTankWrapper implements IFluidHandler { @Override public int fill(FluidStack resource, FluidAction action) { + if (resource.isEmpty()) + return 0; + int filled = 0; resource = resource.copy(); @@ -85,6 +88,9 @@ public class CombinedTankWrapper implements IFluidHandler { @Override public FluidStack drain(FluidStack resource, FluidAction action) { + if (resource.isEmpty()) + return resource; + FluidStack drained = FluidStack.EMPTY; resource = resource.copy(); diff --git a/src/main/java/com/simibubi/create/foundation/fluid/FluidHelper.java b/src/main/java/com/simibubi/create/foundation/fluid/FluidHelper.java index 774927b19..581e9c087 100644 --- a/src/main/java/com/simibubi/create/foundation/fluid/FluidHelper.java +++ b/src/main/java/com/simibubi/create/foundation/fluid/FluidHelper.java @@ -74,6 +74,9 @@ public class FluidHelper { int amount = JSONUtils.getInt(json, "amount"); FluidStack stack = new FluidStack(fluid, amount); + if (!json.has("nbt")) + return stack; + try { JsonElement element = json.get("nbt"); stack.setTag(JsonToNBT.getTagFromJson( diff --git a/src/main/java/com/simibubi/create/foundation/item/SmartInventory.java b/src/main/java/com/simibubi/create/foundation/item/SmartInventory.java index 7e5050848..19773a317 100644 --- a/src/main/java/com/simibubi/create/foundation/item/SmartInventory.java +++ b/src/main/java/com/simibubi/create/foundation/item/SmartInventory.java @@ -10,17 +10,29 @@ import net.minecraftforge.common.util.INBTSerializable; import net.minecraftforge.items.ItemStackHandler; import net.minecraftforge.items.wrapper.RecipeWrapper; -public class SmartInventory extends RecipeWrapper implements IItemHandlerModifiableIntermediate, INBTSerializable { +public class SmartInventory extends RecipeWrapper + implements IItemHandlerModifiableIntermediate, INBTSerializable { - private boolean extractionAllowed; - private boolean insertionAllowed; - private int stackSize; + protected boolean extractionAllowed; + protected boolean insertionAllowed; + protected boolean stackNonStackables; + protected int stackSize; public SmartInventory(int slots, SyncedTileEntity te) { - super(new SyncedStackHandler(slots, te)); + this(slots, te, 64, false); + } + + public SmartInventory(int slots, SyncedTileEntity te, int stackSize, boolean stackNonStackables) { + super(new SyncedStackHandler(slots, te, stackNonStackables, stackSize)); + this.stackNonStackables = stackNonStackables; insertionAllowed = true; extractionAllowed = true; - stackSize = 64; + this.stackSize = stackSize; + } + + public SmartInventory whenContentsChanged(Runnable updateCallback) { + ((SyncedStackHandler) inv).whenContentsChange(updateCallback); + return this; } public SmartInventory allowInsertion() { @@ -43,11 +55,6 @@ public class SmartInventory extends RecipeWrapper implements IItemHandlerModifia return this; } - public SmartInventory withMaxStackSize(int stackSize) { - this.stackSize = stackSize; - return this; - } - @Override public int getSlots() { return inv.getSlots(); @@ -64,6 +71,11 @@ public class SmartInventory extends RecipeWrapper implements IItemHandlerModifia public ItemStack extractItem(int slot, int amount, boolean simulate) { if (!extractionAllowed) return ItemStack.EMPTY; + if (stackNonStackables) { + ItemStack extractItem = inv.extractItem(slot, amount, true); + if (!extractItem.isEmpty() && extractItem.getMaxStackSize() < extractItem.getCount()) + amount = extractItem.getMaxStackSize(); + } return inv.extractItem(slot, amount, simulate); } @@ -86,7 +98,7 @@ public class SmartInventory extends RecipeWrapper implements IItemHandlerModifia public ItemStack getStackInSlot(int slot) { return super.getStackInSlot(slot); } - + public int getStackLimit(int slot, @Nonnull ItemStack stack) { return Math.min(getSlotLimit(slot), stack.getMaxStackSize()); } @@ -100,7 +112,7 @@ public class SmartInventory extends RecipeWrapper implements IItemHandlerModifia public void deserializeNBT(CompoundNBT nbt) { getInv().deserializeNBT(nbt); } - + private SyncedStackHandler getInv() { return (SyncedStackHandler) inv; } @@ -108,18 +120,34 @@ public class SmartInventory extends RecipeWrapper implements IItemHandlerModifia private static class SyncedStackHandler extends ItemStackHandler { private SyncedTileEntity te; + private boolean stackNonStackables; + private int stackSize; + private Runnable updateCallback; - public SyncedStackHandler(int slots, SyncedTileEntity te) { + public SyncedStackHandler(int slots, SyncedTileEntity te, boolean stackNonStackables, int stackSize) { super(slots); this.te = te; + this.stackNonStackables = stackNonStackables; + this.stackSize = stackSize; } @Override protected void onContentsChanged(int slot) { super.onContentsChanged(slot); + if (updateCallback != null) + updateCallback.run(); te.notifyUpdate(); } + @Override + public int getSlotLimit(int slot) { + return Math.min(stackNonStackables ? 64 : super.getSlotLimit(slot), stackSize); + } + + public void whenContentsChange(Runnable updateCallback) { + this.updateCallback = updateCallback; + } + } @Override diff --git a/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java b/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java index ed8de400c..b1e6a8a25 100644 --- a/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java +++ b/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java @@ -10,6 +10,7 @@ import com.simibubi.create.content.contraptions.components.structureMovement.glu import com.simibubi.create.content.contraptions.components.structureMovement.sync.ClientMotionPacket; import com.simibubi.create.content.contraptions.components.structureMovement.sync.ContraptionInteractionPacket; import com.simibubi.create.content.contraptions.components.structureMovement.sync.ContraptionSeatMappingPacket; +import com.simibubi.create.content.contraptions.components.structureMovement.sync.LimbSwingUpdatePacket; import com.simibubi.create.content.contraptions.components.structureMovement.train.MinecartCouplingCreationPacket; import com.simibubi.create.content.contraptions.components.structureMovement.train.MinecartCouplingSyncPacket; import com.simibubi.create.content.contraptions.components.structureMovement.train.PersistantDataPacket; @@ -68,6 +69,7 @@ public enum AllPackets { MINECART_COUPLING_SYNC(MinecartCouplingSyncPacket.class, MinecartCouplingSyncPacket::new), CONTRAPTION_SEAT_MAPPING(ContraptionSeatMappingPacket.class, ContraptionSeatMappingPacket::new), PERSISTANT_DATA(PersistantDataPacket.class, PersistantDataPacket::new), + LIMBSWING_UPDATE(LimbSwingUpdatePacket.class, LimbSwingUpdatePacket::new), ; diff --git a/src/main/java/com/simibubi/create/foundation/tileEntity/behaviour/fluid/SmartFluidTankBehaviour.java b/src/main/java/com/simibubi/create/foundation/tileEntity/behaviour/fluid/SmartFluidTankBehaviour.java index 810f84b60..445ab0a91 100644 --- a/src/main/java/com/simibubi/create/foundation/tileEntity/behaviour/fluid/SmartFluidTankBehaviour.java +++ b/src/main/java/com/simibubi/create/foundation/tileEntity/behaviour/fluid/SmartFluidTankBehaviour.java @@ -34,6 +34,7 @@ public class SmartFluidTankBehaviour extends TileEntityBehaviour { protected LazyOptional capability; protected boolean extractionAllowed; protected boolean insertionAllowed; + protected Runnable fluidUpdateCallback; private BehaviourType behaviourType; @@ -55,6 +56,13 @@ public class SmartFluidTankBehaviour extends TileEntityBehaviour { handlers[i] = tankSegment.tank; } capability = LazyOptional.of(() -> new InternalFluidHandler(handlers, enforceVariety)); + fluidUpdateCallback = () -> { + }; + } + + public SmartFluidTankBehaviour whenFluidUpdates(Runnable fluidUpdateCallback) { + this.fluidUpdateCallback = fluidUpdateCallback; + return this; } public SmartFluidTankBehaviour allowInsertion() { @@ -76,7 +84,7 @@ public class SmartFluidTankBehaviour extends TileEntityBehaviour { extractionAllowed = false; return this; } - + @Override public void initialize() { super.initialize(); @@ -84,7 +92,7 @@ public class SmartFluidTankBehaviour extends TileEntityBehaviour { return; foreach(ts -> { ts.fluidLevel.forceNextSync(); - ts.onFluidStackChanged(ts.tank.getFluid()); + ts.onFluidStackChanged(); }); } @@ -94,8 +102,8 @@ public class SmartFluidTankBehaviour extends TileEntityBehaviour { if (syncCooldown > 0) { syncCooldown--; - if (syncCooldown == 0 && queuedSync) - tileEntity.sendData(); + if (syncCooldown == 0 && queuedSync) + updateFluids(); } foreach(te -> { @@ -108,7 +116,7 @@ public class SmartFluidTankBehaviour extends TileEntityBehaviour { public void sendDataImmediately() { syncCooldown = 0; queuedSync = false; - tileEntity.sendData(); + updateFluids(); } public void sendDataLazily() { @@ -116,11 +124,16 @@ public class SmartFluidTankBehaviour extends TileEntityBehaviour { queuedSync = true; return; } - tileEntity.sendData(); + updateFluids(); queuedSync = false; syncCooldown = SYNC_RATE; } + protected void updateFluids() { + fluidUpdateCallback.run(); + tileEntity.sendData(); + } + @Override public void remove() { super.remove(); @@ -174,36 +187,36 @@ public class SmartFluidTankBehaviour extends TileEntityBehaviour { index.increment(); }); } - + class InternalFluidHandler extends CombinedTankWrapper { - + public InternalFluidHandler(IFluidHandler[] handlers, boolean enforceVariety) { super(handlers); if (enforceVariety) enforceVariety(); } - + @Override public int fill(FluidStack resource, FluidAction action) { if (!insertionAllowed) return 0; return super.fill(resource, action); } - + @Override public FluidStack drain(FluidStack resource, FluidAction action) { if (!extractionAllowed) return FluidStack.EMPTY; return super.drain(resource, action); } - + @Override public FluidStack drain(int maxDrain, FluidAction action) { if (!extractionAllowed) return FluidStack.EMPTY; return super.drain(maxDrain, action); } - + } public class TankSegment { @@ -213,14 +226,14 @@ public class SmartFluidTankBehaviour extends TileEntityBehaviour { protected FluidStack renderedFluid; public TankSegment(int capacity) { - tank = new SmartFluidTank(1000, f -> onFluidStackChanged(f)); + tank = new SmartFluidTank(1000, f -> onFluidStackChanged()); fluidLevel = LerpedFloat.linear() .startWithValue(0) .chase(0, .25, Chaser.EXP); renderedFluid = FluidStack.EMPTY; } - protected void onFluidStackChanged(FluidStack newFluidStack) { + public void onFluidStackChanged() { if (!tileEntity.hasWorld()) return; fluidLevel.chase(tank.getFluidAmount() / (float) tank.getCapacity(), .25, Chaser.EXP); @@ -235,7 +248,7 @@ public class SmartFluidTankBehaviour extends TileEntityBehaviour { public LerpedFloat getFluidLevel() { return fluidLevel; } - + public float getTotalUnits(float partialTicks) { return fluidLevel.getValue(partialTicks) * tank.getCapacity(); } diff --git a/src/main/resources/assets/create/models/block/basin/basin_canal.bbmodel b/src/main/resources/assets/create/models/block/basin/basin_canal.bbmodel new file mode 100644 index 000000000..63122598a --- /dev/null +++ b/src/main/resources/assets/create/models/block/basin/basin_canal.bbmodel @@ -0,0 +1 @@ +{"meta":{"format_version":"3.2","model_format":"java_block","box_uv":false},"name":"basin","parent":"block/block","ambientocclusion":true,"front_gui_light":false,"resolution":{"width":16,"height":16},"elements":[{"name":"Side1","from":[0,2,0],"to":[2,16,16],"autouv":0,"color":2,"locked":false,"origin":[8,24,8],"faces":{"north":{"uv":[14,0,16,14],"texture":0},"east":{"uv":[0,0,16,14],"texture":0},"south":{"uv":[0,0,2,14],"texture":0},"west":{"uv":[0,0,16,14],"texture":0},"up":{"uv":[0,0,2,16],"texture":1},"down":{"uv":[0,0,2,16],"texture":1}},"uuid":"e372420e-1abe-a2b9-ae96-df6b128888ad"},{"name":"BasinBottom","from":[2,0,2],"to":[14,2,14],"autouv":0,"color":0,"locked":false,"origin":[8,25,8],"faces":{"north":{"uv":[2,14,14,16],"texture":0},"east":{"uv":[2,14,14,16],"texture":0},"south":{"uv":[2,14,14,16],"texture":0},"west":{"uv":[2,14,14,16],"texture":0},"up":{"uv":[2,2,14,14],"texture":1},"down":{"uv":[2,2,14,14],"texture":1}},"uuid":"e82556b1-3f66-7c35-71b2-f33c0c4c1385"},{"name":"Side4","from":[2,2,0],"to":[14,16,2],"autouv":0,"color":1,"locked":false,"origin":[8,24,8],"faces":{"north":{"uv":[2,0,14,14],"texture":0},"east":{"uv":[0,0,0,0],"texture":null},"south":{"uv":[2,0,14,14],"texture":0},"west":{"uv":[0,0,0,0],"texture":null},"up":{"uv":[2,0,14,2],"texture":1},"down":{"uv":[2,14,14,16],"texture":1}},"uuid":"2e5cfd25-7f80-2f04-e080-b005bc67fecb"},{"name":"Side2","from":[2,2,14],"to":[14,16,16],"autouv":0,"color":4,"locked":false,"origin":[8,24,8],"faces":{"north":{"uv":[2,0,14,14],"texture":0},"east":{"uv":[0,0,0,0],"texture":null},"south":{"uv":[2,0,14,14],"texture":0},"west":{"uv":[0,0,0,0],"texture":null},"up":{"uv":[2,14,14,16],"texture":1},"down":{"uv":[2,0,14,2],"texture":1}},"uuid":"e007a6de-4ee0-52db-0763-61d4c3ab8a3f"},{"name":"Side3","from":[14,2,0],"to":[16,16,16],"autouv":0,"color":5,"locked":false,"origin":[8,40,8],"faces":{"north":{"uv":[0,0,2,14],"texture":0},"east":{"uv":[0,0,16,14],"texture":0},"south":{"uv":[14,0,16,14],"texture":0},"west":{"uv":[0,0,16,14],"texture":0},"up":{"uv":[14,0,16,16],"texture":1},"down":{"uv":[14,0,16,16],"texture":1}},"uuid":"c5b067a4-6114-bb79-a5bf-1321988a497c"},{"name":"Side2","from":[0,-14,-16],"to":[2,0,0],"autouv":0,"color":2,"locked":false,"origin":[8,8,-8],"faces":{"north":{"uv":[14,0,16,14],"texture":0},"east":{"uv":[0,0,16,14],"texture":0},"south":{"uv":[0,0,2,14],"texture":0},"west":{"uv":[0,0,16,14],"texture":0},"up":{"uv":[0,0,2,16],"texture":1},"down":{"uv":[0,0,2,16],"texture":1}},"uuid":"b641f651-2872-3ebc-e9e3-2dfbbb10e8e4"},{"name":"BasinBottom","from":[2,-16,-14],"to":[14,-14,-2],"autouv":0,"color":0,"locked":false,"origin":[8,9,-8],"faces":{"north":{"uv":[2,14,14,16],"texture":0},"east":{"uv":[2,14,14,16],"texture":0},"south":{"uv":[2,14,14,16],"texture":0},"west":{"uv":[2,14,14,16],"texture":0},"up":{"uv":[2,2,14,14],"texture":1},"down":{"uv":[2,2,14,14],"texture":1}},"uuid":"07500784-c31b-dc51-7e03-f0ec55ee6028"},{"name":"Side5","from":[2,-14,-16],"to":[14,0,-14],"autouv":0,"color":1,"locked":false,"origin":[8,8,-8],"faces":{"north":{"uv":[2,0,14,14],"texture":0},"east":{"uv":[0,0,0,0],"texture":null},"south":{"uv":[2,0,14,14],"texture":0},"west":{"uv":[0,0,0,0],"texture":null},"up":{"uv":[2,0,14,2],"texture":1},"down":{"uv":[2,14,14,16],"texture":1}},"uuid":"dd76fad0-e0ed-1147-e7bd-3870f72987da"},{"name":"Side3","from":[2,-14,-2],"to":[14,0,0],"autouv":0,"color":4,"locked":false,"origin":[8,8,-8],"faces":{"north":{"uv":[2,0,14,14],"texture":0},"east":{"uv":[0,0,0,0],"texture":null},"south":{"uv":[2,0,14,14],"texture":0},"west":{"uv":[0,0,0,0],"texture":null},"up":{"uv":[2,14,14,16],"texture":1},"down":{"uv":[2,0,14,2],"texture":1}},"uuid":"32636eab-627e-34f5-7be6-2a31400bd957"},{"name":"Side4","from":[14,-14,-16],"to":[16,0,0],"autouv":0,"color":5,"locked":false,"origin":[8,24,-8],"faces":{"north":{"uv":[0,0,2,14],"texture":0},"east":{"uv":[0,0,16,14],"texture":0},"south":{"uv":[14,0,16,14],"texture":0},"west":{"uv":[0,0,16,14],"texture":0},"up":{"uv":[14,0,16,16],"texture":1},"down":{"uv":[14,0,16,16],"texture":1}},"uuid":"06344246-e61c-e8fd-c700-dae02563797e"},{"name":"cube","from":[3,0,0],"to":[4,2,2],"autouv":0,"color":6,"locked":false,"origin":[11,8,8],"faces":{"north":{"uv":[0,0,10,2],"texture":2},"east":{"uv":[14,10,16,12],"texture":2},"south":{"uv":[14,10,16,11],"rotation":90,"texture":2},"west":{"uv":[14,10,16,12],"rotation":90,"texture":2},"up":{"uv":[14,10,15,12],"rotation":180,"texture":2},"down":{"uv":[15,10,16,12],"texture":2}},"uuid":"41346be7-9b03-225c-14be-3be6e1719311"},{"name":"cube","from":[3,0,-1],"to":[4,10,0],"autouv":0,"color":6,"locked":false,"origin":[11,8,7],"faces":{"north":{"uv":[6,12,16,13],"rotation":90,"texture":2},"east":{"uv":[6,12,16,13],"rotation":90,"texture":2},"south":{"uv":[6,12,16,13],"rotation":90,"texture":2},"west":{"uv":[6,12,16,13],"rotation":90,"texture":2},"up":{"uv":[6,12,7,13],"texture":2},"down":{"uv":[15,12,16,13],"texture":2}},"uuid":"fa4b61d1-8e47-aa7d-207c-025608871716"},{"name":"cube","from":[5,1,-6],"to":[11,2,3],"autouv":0,"color":4,"locked":false,"rotation":[-22.5,0,0],"origin":[8,9,-1],"faces":{"north":{"uv":[9,7,15,8],"texture":2},"east":{"uv":[0,0,9,1],"texture":2},"south":{"uv":[9,7,15,8],"texture":2},"west":{"uv":[0,0,9,1],"texture":2},"up":{"uv":[1,0,7,9],"texture":2},"down":{"uv":[1,0,7,9],"rotation":180,"texture":2}},"uuid":"c4b10bec-d6bb-cac8-b369-d588c98601ce"},{"name":"cube","from":[4,1,-6],"to":[5,9,3],"autouv":0,"color":4,"locked":false,"rotation":[-22.5,0,0],"origin":[8,9,-1],"faces":{"north":{"uv":[15,0,16,8],"texture":2},"east":{"uv":[0,0,8,9],"rotation":90,"texture":2},"south":{"uv":[8,0,9,8],"texture":2},"west":{"uv":[0,0,8,9],"rotation":90,"texture":2},"up":{"uv":[0,0,1,9],"rotation":180,"texture":2},"down":{"uv":[0,0,1,9],"texture":2}},"uuid":"655e4f5c-68aa-c6ca-ea54-6ee2f1eb0cbf"},{"name":"cube","from":[11,1,-6],"to":[12,9,3],"autouv":0,"color":4,"locked":false,"rotation":[-22.5,0,0],"origin":[15,9,-1],"faces":{"north":{"uv":[8,0,9,8],"texture":2},"east":{"uv":[0,0,8,9],"rotation":270,"texture":2},"south":{"uv":[15,0,16,8],"texture":2},"west":{"uv":[0,0,8,9],"rotation":90,"texture":2},"up":{"uv":[0,0,1,9],"rotation":180,"texture":2},"down":{"uv":[0,0,1,9],"texture":2}},"uuid":"cf5d8d2d-6849-5958-087c-a48f1ab945e5"},{"name":"cube","from":[5,8,-6],"to":[11,9,3],"autouv":0,"color":4,"locked":false,"rotation":[-22.5,0,0],"origin":[8,9,-1],"faces":{"north":{"uv":[9,0,15,1],"texture":2},"east":{"uv":[0,0,9,1],"texture":2},"south":{"uv":[9,0,15,1],"texture":2},"west":{"uv":[0,0,9,1],"texture":2},"up":{"uv":[1,0,7,9],"rotation":180,"texture":2},"down":{"uv":[1,0,7,9],"texture":2}},"uuid":"1655c96c-ae98-f14d-527c-6052c1fab719"},{"name":"cube","from":[5,2,-4],"to":[11,8,2],"autouv":0,"color":4,"locked":false,"rotation":[-22.5,0,0],"origin":[8,9,-1],"faces":{"north":{"uv":[9,1,15,7],"texture":2},"east":{"uv":[0,0,0,0],"texture":null},"south":{"uv":[9,1,15,7],"texture":2},"west":{"uv":[0,0,0,0],"texture":null},"up":{"uv":[0,0,0,0],"texture":null},"down":{"uv":[0,0,0,0],"texture":null}},"uuid":"a2eb892a-15a7-46c2-d526-c062c9c151ba"},{"name":"cube","from":[12,0,-1],"to":[13,10,0],"autouv":0,"color":6,"locked":false,"origin":[20,8,7],"faces":{"north":{"uv":[6,12,16,13],"rotation":90,"texture":2},"east":{"uv":[6,12,16,13],"rotation":90,"texture":2},"south":{"uv":[6,12,16,13],"rotation":90,"texture":2},"west":{"uv":[6,12,16,13],"rotation":90,"texture":2},"up":{"uv":[6,12,7,13],"texture":2},"down":{"uv":[15,12,16,13],"texture":2}},"uuid":"baaf226d-53b0-814c-ca53-75621435fc45"},{"name":"cube","from":[4,9,-1],"to":[12,10,0],"autouv":0,"color":6,"locked":false,"origin":[12,8,7],"faces":{"north":{"uv":[8,9,16,10],"texture":2},"east":{"uv":[0,0,0,0],"rotation":90,"texture":null},"south":{"uv":[8,9,16,10],"texture":2},"west":{"uv":[0,0,0,0],"rotation":90,"texture":null},"up":{"uv":[8,9,16,10],"texture":2},"down":{"uv":[0,0,0,0],"texture":null}},"uuid":"7aebcd85-9ae9-b4b0-9af3-a8a96d6e3709"},{"name":"cube","from":[12,0,0],"to":[13,2,2],"autouv":0,"color":6,"locked":false,"origin":[20,8,8],"faces":{"north":{"uv":[0,0,10,2],"texture":2},"east":{"uv":[14,10,16,12],"texture":2},"south":{"uv":[14,10,16,11],"rotation":90,"texture":2},"west":{"uv":[14,10,16,12],"rotation":90,"texture":2},"up":{"uv":[14,10,15,12],"rotation":180,"texture":2},"down":{"uv":[15,10,16,12],"texture":2}},"uuid":"25e35b80-a713-fd17-aac6-033104233acd"},{"name":"cube","from":[4,0,-1],"to":[12,1,2],"autouv":0,"color":6,"locked":false,"origin":[12,8,7],"faces":{"north":{"uv":[7,15,15,16],"texture":2},"east":{"uv":[14,10,16,12],"texture":2},"south":{"uv":[8,14,9,15],"rotation":90,"texture":2},"west":{"uv":[14,10,16,12],"rotation":90,"texture":2},"up":{"uv":[11,10,12,11],"rotation":180,"texture":2},"down":{"uv":[7,13,15,16],"texture":2}},"uuid":"18b2aeec-e7d6-9262-7e3d-995f9d7320ac"}],"outliner":[{"name":"Basins","uuid":"4822b3b4-2c19-aadd-c096-85534b94f809","export":true,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"origin":[8,8,8],"children":[{"name":"Basin top","uuid":"ef36da1c-a925-5b8d-5f56-b9b2136ca98f","export":true,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"origin":[8,8,8],"children":["e372420e-1abe-a2b9-ae96-df6b128888ad","e82556b1-3f66-7c35-71b2-f33c0c4c1385","2e5cfd25-7f80-2f04-e080-b005bc67fecb","e007a6de-4ee0-52db-0763-61d4c3ab8a3f","c5b067a4-6114-bb79-a5bf-1321988a497c"]},{"name":"Basin bottom","uuid":"52077833-3587-b2ad-4a2a-8a61173f4d54","export":true,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"origin":[8,8,8],"children":["b641f651-2872-3ebc-e9e3-2dfbbb10e8e4","07500784-c31b-dc51-7e03-f0ec55ee6028","dd76fad0-e0ed-1147-e7bd-3870f72987da","32636eab-627e-34f5-7be6-2a31400bd957","06344246-e61c-e8fd-c700-dae02563797e"]}]},{"name":"Canal","uuid":"c916ca14-dcb2-5442-2b8a-1fa3ea05e167","export":true,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"origin":[8,8,8],"children":[{"name":"Frame","uuid":"1dfe7732-4e1a-7674-3237-0ecf68c5c60e","export":true,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"origin":[8,8,8],"children":["41346be7-9b03-225c-14be-3be6e1719311","25e35b80-a713-fd17-aac6-033104233acd","18b2aeec-e7d6-9262-7e3d-995f9d7320ac","fa4b61d1-8e47-aa7d-207c-025608871716","7aebcd85-9ae9-b4b0-9af3-a8a96d6e3709","baaf226d-53b0-814c-ca53-75621435fc45"]},"c4b10bec-d6bb-cac8-b369-d588c98601ce","1655c96c-ae98-f14d-527c-6052c1fab719","a2eb892a-15a7-46c2-d526-c062c9c151ba","cf5d8d2d-6849-5958-087c-a48f1ab945e5","655e4f5c-68aa-c6ca-ea54-6ee2f1eb0cbf"]}],"textures":[{"path":"C:\\Users\\krypp\\Desktop\\create 1.15.2\\assets\\create\\textures\\block\\basin_side.png","name":"basin_side.png","folder":"block","namespace":"create","id":"1","particle":false,"mode":"bitmap","saved":true,"uuid":"a20a92f6-149c-8147-a096-ebf6df449714","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAB4UlEQVQ4T2VTy27bMBAcAhVF2bAlpacATr/BQFs7pzy+I0Bv6cXH9Fd8aT4p7U8UObVoTMe1JUoHFrMUJSchIOxyuTuaHXLV19tbn2gNrswYsVzjNO19OnvnZF/VdR9vmwbq5uaLH49SEISBaJkVgRnfHxyYRys/6Hx1cXHlnatRFCWqqpJD7tPUIMsyiXHPFXMYt3YjOWq5PPfGZAA88ryA66hOJtMXLXDTNK4/j8BqPp97rQ3KskQqfSsMxR5N00BrxoPPtd1a1J0WarE490VRyIExRpIJlCQJ2rZFkmi0bdPb3W4nuQSo6wrq8vLas4BtaK07FsBoNMbhcBCgJHknPtkNbXhYawlw5SkGPzKIYvHvchMdE+5ZHER2AwOKmOel9DidFkjT8CZIfWgj6Ri81UEYeB/650cBh76DDjxXircQRIwMnKugFoulL8sTOWAbQXH6ursBMtHY7/+JRhRRxFMKm80T1Le7O//w42cnYigOtxEAuEiRf2VRLKb9/Okj1Gq18tbu8Pj4S0C4yIKCRfs69vxsMZt9QFFMoOIwxTngQHFgjgeLADFGezwzKr5XAv192vZaHL/jOAvUiP77kxzf7++ltgdgK0T+/WfTD08siAKz+Gx2KlO6Xq+l9j+hVCuQux00ywAAAABJRU5ErkJggg=="},{"path":"C:\\Users\\krypp\\Desktop\\create 1.15.2\\assets\\create\\textures\\block\\basin.png","name":"basin.png","folder":"block","namespace":"create","id":"12","particle":true,"mode":"bitmap","saved":true,"uuid":"e8bd46f1-4674-7125-0c6a-15be8f476bed","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABmElEQVQ4T41TyU7DMBB9dpykS8oFKIhFwMfBpcdy4sJXcuHAIhaxHgJp3cR2jGaiJG0poiNFHsfxy7w388TZ6akPowgU3U6HV4p+HDd5nUzyvHmnZzOYooAYjUb+8fGFD8Iw5LXXbYFoP9Wz5qIxpskPDnZbgKJo0ZUKIQUAIaCURFkCZenhnIVzrqqwn2BnZxPifDz2d/dPmEwydLsdKBUgzw1fEEIgigI452Gt4XOtC863t7Yw2OgvAhAFIYA4jvhDKQW89/AenNPfCdRayxUcH+1XAG9vn0jTFKUvIYSsLvxBgconIKpgONyEuLy48LcPz0i/vhAEci0KQRBgY5AsAaQpfKXbvxSiUDGFk8O9toKpniDPC1DlURyy8iSoMQ4lbUBCWhayanV/ESDLMuiZhpSSdaDH2gJKKb5YB7W46sJ2S+Hq+obVdc6srQEBL3Th/eODJ3EdDX61sR4k4vyfBr1egiz7bieRvPD6+smTSJEkA2g9XaEBQbfRjHJtptpItZnIQPOmWmUoNtO8nZctvWzrVXb+ARkgOfimiFHjAAAAAElFTkSuQmCC"},{"path":"C:\\Users\\krypp\\Documents\\Pixel Art\\Create Mod\\basin\\Basin Canal\\Basin Canal.png","name":"Basin Canal.png","folder":"Basin Canal","namespace":"","id":"2","particle":false,"mode":"bitmap","saved":true,"uuid":"4459ee5f-e243-a1a9-b39e-fd8adcac4b69","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABc0lEQVQ4T4VTTU/CQBB9TWi3WiGIMdGYcFD4J0iFhMQDB5Mm3PDSYzlx8Q/I3+FifwrxB5B4kNja3ZKsmdElwdBlLrMfndf33sw6syTR30WBUim4ngcKsz7xfd7b7p1+/0EL4cP3fXieB6UUFxVFASkLdLt3SNN0d86XAH/b6/XgjEaPmoqD4AxlqeC6HmcCIpDrq0u8pSmklKaWsxAC9wQQRRNNB1Toui7KsuSc5zkAjWazjuVyeRBgOBzCCcOB9jzBiOYvZq2URKdzawcYj5+YgRDE4Jc+5Sz7YqrnzbpdAnnQaDRYszHRZPKg3b6xmxjHsV6t3kEyyEwqojD0qZXWNh6bg9fFYs/9/xuHrbbELEmYgRkw7tjfwBG7owBhOGB5RhatSR5l8u4oQBzHB8d8vf7gDlUCvMznyKTcM5DeSJZLBKcCqtQIAlENEEUTq3nmspIBAWw2n9hut6yXdNdqtR0onbdaF9UMnqdTpmsLkvIDiqLjULkp6X8AAAAASUVORK5CYII="}]} \ No newline at end of file diff --git a/src/main/resources/assets/create/models/block/basin.json b/src/main/resources/assets/create/models/block/basin/block.json similarity index 100% rename from src/main/resources/assets/create/models/block/basin.json rename to src/main/resources/assets/create/models/block/basin/block.json diff --git a/src/main/resources/assets/create/models/block/basin/block_directional.json b/src/main/resources/assets/create/models/block/basin/block_directional.json new file mode 100644 index 000000000..6a6e9a8ba --- /dev/null +++ b/src/main/resources/assets/create/models/block/basin/block_directional.json @@ -0,0 +1,237 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "1": "create:block/basin_side", + "2": "create:block/basin_canal", + "12": "create:block/basin", + "particle": "create:block/basin" + }, + "elements": [ + { + "name": "Side1", + "from": [14, 2, 0], + "to": [16, 16, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 24, 8]}, + "faces": { + "north": {"uv": [0, 0, 2, 14], "texture": "#1"}, + "east": {"uv": [0, 0, 16, 14], "texture": "#1"}, + "south": {"uv": [14, 0, 16, 14], "texture": "#1"}, + "west": {"uv": [0, 0, 16, 14], "texture": "#1"}, + "up": {"uv": [0, 0, 2, 16], "rotation": 180, "texture": "#12"}, + "down": {"uv": [0, 0, 2, 16], "rotation": 180, "texture": "#12"} + } + }, + { + "name": "BasinBottom", + "from": [2, 0, 2], + "to": [14, 2, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 25, 8]}, + "faces": { + "north": {"uv": [2, 14, 14, 16], "texture": "#1"}, + "east": {"uv": [2, 14, 14, 16], "texture": "#1"}, + "south": {"uv": [2, 14, 14, 16], "texture": "#1"}, + "west": {"uv": [2, 14, 14, 16], "texture": "#1"}, + "up": {"uv": [2, 2, 14, 14], "rotation": 180, "texture": "#12"}, + "down": {"uv": [2, 2, 14, 14], "rotation": 180, "texture": "#12"} + } + }, + { + "name": "Side4", + "from": [2, 2, 14], + "to": [14, 16, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 24, 8]}, + "faces": { + "north": {"uv": [2, 0, 14, 14], "texture": "#1"}, + "south": {"uv": [2, 0, 14, 14], "texture": "#1"}, + "up": {"uv": [2, 0, 14, 2], "rotation": 180, "texture": "#12"}, + "down": {"uv": [2, 14, 14, 16], "rotation": 180, "texture": "#12"} + } + }, + { + "name": "Side2", + "from": [2, 2, 0], + "to": [14, 16, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 24, 8]}, + "faces": { + "north": {"uv": [2, 0, 14, 14], "texture": "#1"}, + "south": {"uv": [2, 0, 14, 14], "texture": "#1"}, + "up": {"uv": [2, 14, 14, 16], "rotation": 180, "texture": "#12"}, + "down": {"uv": [2, 0, 14, 2], "rotation": 180, "texture": "#12"} + } + }, + { + "name": "Side3", + "from": [0, 2, 0], + "to": [2, 16, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 40, 8]}, + "faces": { + "north": {"uv": [14, 0, 16, 14], "texture": "#1"}, + "east": {"uv": [0, 0, 16, 14], "texture": "#1"}, + "south": {"uv": [0, 0, 2, 14], "texture": "#1"}, + "west": {"uv": [0, 0, 16, 14], "texture": "#1"}, + "up": {"uv": [14, 0, 16, 16], "rotation": 180, "texture": "#12"}, + "down": {"uv": [14, 0, 16, 16], "rotation": 180, "texture": "#12"} + } + }, + { + "from": [12, 0, 14], + "to": [13, 2, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [5, 8, 8]}, + "faces": { + "north": {"uv": [14, 10, 16, 11], "rotation": 90, "texture": "#2"}, + "east": {"uv": [14, 10, 16, 12], "rotation": 90, "texture": "#2"}, + "south": {"uv": [0, 0, 10, 2], "texture": "#2"}, + "west": {"uv": [14, 10, 16, 12], "texture": "#2"}, + "up": {"uv": [14, 10, 15, 12], "texture": "#2"}, + "down": {"uv": [15, 10, 16, 12], "rotation": 180, "texture": "#2"} + } + }, + { + "from": [3, 0, 14], + "to": [4, 2, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [-4, 8, 8]}, + "faces": { + "north": {"uv": [14, 10, 16, 11], "rotation": 90, "texture": "#2"}, + "east": {"uv": [14, 10, 16, 12], "rotation": 90, "texture": "#2"}, + "south": {"uv": [0, 0, 10, 2], "texture": "#2"}, + "west": {"uv": [14, 10, 16, 12], "texture": "#2"}, + "up": {"uv": [14, 10, 15, 12], "texture": "#2"}, + "down": {"uv": [15, 10, 16, 12], "rotation": 180, "texture": "#2"} + } + }, + { + "from": [4, 0, 14], + "to": [12, 1, 17], + "rotation": {"angle": 0, "axis": "y", "origin": [4, 8, 9]}, + "faces": { + "north": {"uv": [8, 14, 9, 15], "rotation": 90, "texture": "#2"}, + "east": {"uv": [14, 10, 16, 12], "rotation": 90, "texture": "#2"}, + "south": {"uv": [7, 15, 15, 16], "texture": "#2"}, + "west": {"uv": [14, 10, 16, 12], "texture": "#2"}, + "up": {"uv": [11, 10, 12, 11], "texture": "#2"}, + "down": {"uv": [7, 13, 15, 16], "rotation": 180, "texture": "#2"} + } + }, + { + "from": [12, 0, 16], + "to": [13, 10, 17], + "rotation": {"angle": 0, "axis": "y", "origin": [5, 8, 9]}, + "faces": { + "north": {"uv": [6, 12, 16, 13], "rotation": 90, "texture": "#2"}, + "east": {"uv": [6, 12, 16, 13], "rotation": 90, "texture": "#2"}, + "south": {"uv": [6, 12, 16, 13], "rotation": 90, "texture": "#2"}, + "west": {"uv": [6, 12, 16, 13], "rotation": 90, "texture": "#2"}, + "up": {"uv": [6, 12, 7, 13], "rotation": 180, "texture": "#2"}, + "down": {"uv": [15, 12, 16, 13], "rotation": 180, "texture": "#2"} + } + }, + { + "from": [4, 9, 16], + "to": [12, 10, 17], + "rotation": {"angle": 0, "axis": "y", "origin": [4, 8, 9]}, + "faces": { + "north": {"uv": [8, 9, 16, 10], "texture": "#2"}, + "south": {"uv": [8, 9, 16, 10], "texture": "#2"}, + "up": {"uv": [8, 9, 16, 10], "rotation": 180, "texture": "#2"} + } + }, + { + "from": [3, 0, 16], + "to": [4, 10, 17], + "rotation": {"angle": 0, "axis": "y", "origin": [-4, 8, 9]}, + "faces": { + "north": {"uv": [6, 12, 16, 13], "rotation": 90, "texture": "#2"}, + "east": {"uv": [6, 12, 16, 13], "rotation": 90, "texture": "#2"}, + "south": {"uv": [6, 12, 16, 13], "rotation": 90, "texture": "#2"}, + "west": {"uv": [6, 12, 16, 13], "rotation": 90, "texture": "#2"}, + "up": {"uv": [6, 12, 7, 13], "rotation": 180, "texture": "#2"}, + "down": {"uv": [15, 12, 16, 13], "rotation": 180, "texture": "#2"} + } + }, + { + "from": [5, 1, 13], + "to": [11, 2, 22], + "rotation": {"angle": 22.5, "axis": "x", "origin": [8, 9, 17]}, + "faces": { + "north": {"uv": [9, 7, 15, 8], "texture": "#2"}, + "east": {"uv": [0, 0, 9, 1], "texture": "#2"}, + "south": {"uv": [9, 7, 15, 8], "texture": "#2"}, + "west": {"uv": [0, 0, 9, 1], "texture": "#2"}, + "up": {"uv": [1, 0, 7, 9], "rotation": 180, "texture": "#2"}, + "down": {"uv": [1, 0, 7, 9], "texture": "#2"} + } + }, + { + "from": [5, 8, 13], + "to": [11, 9, 22], + "rotation": {"angle": 22.5, "axis": "x", "origin": [8, 9, 17]}, + "faces": { + "north": {"uv": [9, 0, 15, 1], "texture": "#2"}, + "east": {"uv": [0, 0, 9, 1], "texture": "#2"}, + "south": {"uv": [9, 0, 15, 1], "texture": "#2"}, + "west": {"uv": [0, 0, 9, 1], "texture": "#2"}, + "up": {"uv": [1, 0, 7, 9], "texture": "#2"}, + "down": {"uv": [1, 0, 7, 9], "rotation": 180, "texture": "#2"} + } + }, + { + "from": [5, 2, 14], + "to": [11, 8, 20], + "rotation": {"angle": 22.5, "axis": "x", "origin": [8, 9, 17]}, + "faces": { + "north": {"uv": [9, 1, 15, 7], "texture": "#2"}, + "south": {"uv": [9, 1, 15, 7], "texture": "#2"} + } + }, + { + "from": [4, 1, 13], + "to": [5, 9, 22], + "rotation": {"angle": 22.5, "axis": "x", "origin": [1, 9, 17]}, + "faces": { + "north": {"uv": [15, 0, 16, 8], "texture": "#2"}, + "east": {"uv": [0, 0, 8, 9], "rotation": 90, "texture": "#2"}, + "south": {"uv": [8, 0, 9, 8], "texture": "#2"}, + "west": {"uv": [0, 0, 8, 9], "rotation": 270, "texture": "#2"}, + "up": {"uv": [0, 0, 1, 9], "texture": "#2"}, + "down": {"uv": [0, 0, 1, 9], "rotation": 180, "texture": "#2"} + } + }, + { + "from": [11, 1, 13], + "to": [12, 9, 22], + "rotation": {"angle": 22.5, "axis": "x", "origin": [8, 9, 17]}, + "faces": { + "north": {"uv": [8, 0, 9, 8], "texture": "#2"}, + "east": {"uv": [0, 0, 8, 9], "rotation": 90, "texture": "#2"}, + "south": {"uv": [15, 0, 16, 8], "texture": "#2"}, + "west": {"uv": [0, 0, 8, 9], "rotation": 90, "texture": "#2"}, + "up": {"uv": [0, 0, 1, 9], "texture": "#2"}, + "down": {"uv": [0, 0, 1, 9], "rotation": 180, "texture": "#2"} + } + } + ], + "groups": [ + { + "name": "Basins", + "origin": [8, 8, 8], + "children": [ + { + "name": "Basin top", + "origin": [8, 8, 8], + "children": [0, 1, 2, 3, 4] + } + ] + }, + { + "name": "Canal", + "origin": [8, 8, 8], + "children": [ + { + "name": "Frame", + "origin": [8, 8, 8], + "children": [5, 6, 7, 8, 9, 10] + }, 11, 12, 13, 14, 15] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/create/textures/block/basin_canal.png b/src/main/resources/assets/create/textures/block/basin_canal.png new file mode 100644 index 0000000000000000000000000000000000000000..c300a2a1b4d77cd8bc0ac49bdb2153fe41dd4a32 GIT binary patch literal 1646 zcmV-!29f!RP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1Y;mNO>|{Ld+J1SGK@hh}*%H^}i*G>h%FduF~# zjNQ-zBvDm>RsHAhrvAc*wb9C0A6#@!<71+UoX`k*d`3+e*Pru6uf~gA?GA=XP|J9A zb&gN4+s6cNru}%eE1##IFdZ$=)1mapjK&-nrX%5Dr}?hpoR`ega^_F`>0%GQz8eoP ziiJ^FaTOAcC-*2aeyQ8=)FOQlO1D@%@%fCM+RY1iyLG`%r)ig0D3o?{fV*Hc?PNb= zcOrCQZAE_RsI)`8Vb@v-t<|x*WE_xkYA$Lm~%0KI_C z10^ep#6Y%@p+boo)w9l&k)q+s9yN1jIc-5aN6w&u#PmXqy+kQW0yxQFO8n_X0ASZB-jX9pqv21ymakPL4D^5J9iIPB;F&6sqCO|{UiZ#kw>#VoI zMw^^CWqOwQ;BV(&E12!FL_he{1I<_$DHca{U6M!PTgegowpCH zm2}=HauU>cVaHTU!@-uYIx@JTtWO;9MgMBJ+0eH=ocm+~YkD+*dF?$22mnSSrrF@z zo`Yr<%JWDFTiIWN=)PS?1IOCg_z-mlQM$$v2EvM^+)TXIj}SG)GZEGAoiQsv^0dd9 zTnsoPP8;Ms!Z~fm3it`H0}j;bc6xxUbG_!Q@9MZlHe(X2;WMqI_Nbk}F|#y_+-C>(Zi$5a0EnlQKx>a zm3^~(PFsDyuJ?7CeVjeypuW+GTRO4(7L&K@pT;eMmbp8XWgnpHvTt*6{Q1(T=Hm+e z{j$|Jcx=vm!+#^Bq5cK5s-YkIfCEAR000JJOGiWi{{a60|De66lK=n!32;bRa{vGf z6951U69E94oEQKA00(qQO+^Rf3Ii4oA3QBDCftfG-dnpFlQ1}R{dTVfW_212^|n<{-*eb+C$I9n`U zo)h|R$ZVDlw8kx^$ zOsu7C+u>)NSj&7q8!WiIx*FX<0lbVAoGlhShmeK)?E|k9@C|N$+-7^K5fPK4_x^i< zA$#lfJ+3yx0vG4!Y+}sg_syH9hy$E!USDBIVDPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!T+ z`=1t}@*fBou3S0zf8B~nSlNdUu7a~cG)T|(xhemdK7aYd0K!+V9{PXl%0-62@LWc0 zY#X~!I2#0*m>C(qegDnC1m*z&vWE9>Ut(p$)IhZ`{P^{Ufk{|MfB}SY*@n!9sR3zb zWr5hu#LUdhz{to*&vo^AS)up3ykTJivQWz*cc`)xWVY4CH!B2 zla1l;zyH911)2^bpnwq+=s>&PeEP|7_|`kdyabK^no7D1j4&6F3*ZTfTrg??0|Nl8 W(J;=`&GxAP0000v19bQjlHTwWi2WV$y{jbh^32q48*666>B9~m&X9=LM?D8pIc5m^jWa1ewUcTCKF z3l#M5ba4#P2;SObDAb@Jz-+weBE!%B}};QTsy(& zrab+s#(_N-d3xO%GL^5c2vAx2jd9N%{sVQ4+1ogmi}>2I01ajEboFyt=akR{04Lj6 A=>Px# delta 285 zcmV+&0pk9H0?7i983+ad001BJ|6!3KAb$yPNLh0L01m%gf2Y%FWHd^!>*lFozhM(Otm8&VPyQ z1=3sq28@j4hXKgt|9<~sU;@R!i