From d06f8ab8ff2560106886844a248cc43ead308b14 Mon Sep 17 00:00:00 2001 From: thedarkcolour <30441001+thedarkcolour@users.noreply.github.com> Date: Fri, 12 Jan 2024 17:20:51 -0800 Subject: [PATCH] Ex Deorum 1.14 --- build.gradle | 21 +- changelog.md | 7 + gradle.properties | 2 + .../59eb3dbb5f86130e09b3c62d89b9525ee01cf52d | 3 +- .../93943142017732f21fbc4fa325d116c728b69767 | 4 +- .../9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e | 4 +- .../fc2b6ffd874afaa6f2f20b450921dbfbbc8b86bd | 3 +- .../resources/assets/exdeorum/lang/en_us.json | 12 + .../models/item/mechanical_sieve.json | 3 + .../recipes/misc/mechanical_sieve.json | 35 +++ .../loot_tables/blocks/mechanical_sieve.json | 26 ++ .../exdeorum/recipes/mechanical_sieve.json | 27 ++ .../java/thedarkcolour/exdeorum/ExDeorum.java | 2 + .../exdeorum/block/BarrelBlock.java | 19 +- .../exdeorum/block/MechanicalSieveBlock.java | 128 ++++++++ .../AbstractCrucibleBlockEntity.java | 94 ++++-- .../blockentity/AbstractSieveBlockEntity.java | 94 ++++++ .../blockentity/BarrelBlockEntity.java | 18 +- .../exdeorum/blockentity/EBlockEntity.java | 15 +- .../InfestedLeavesBlockEntity.java | 41 ++- .../MechanicalSieveBlockEntity.java | 283 ++++++++++++++++++ .../blockentity/SieveBlockEntity.java | 226 ++++---------- .../blockentity/helper/EnergyHelper.java | 43 +++ .../blockentity/helper/ItemHelper.java | 69 +++++ .../blockentity/logic/SieveLogic.java | 226 ++++++++++++++ .../exdeorum/blockentity/package-info.java | 6 - .../exdeorum/client/ClientHandler.java | 12 +- .../exdeorum/client/RenderFace.java | 23 +- .../exdeorum/client/RenderUtil.java | 5 +- .../client/screen/MechanicalSieveScreen.java | 99 ++++++ .../client/screen/RedstoneControlWidget.java | 215 +++++++++++++ .../exdeorum/client/screen/package-info.java | 21 ++ .../client/ter/InfestedLeavesRenderer.java | 3 +- .../exdeorum/client/ter/SieveRenderer.java | 29 +- ...cipeGroup.java => GroupedSieveRecipe.java} | 35 +-- .../compat/jei/BarrelCompostCategory.java | 43 +-- .../exdeorum/compat/jei/ClientJeiUtil.java | 99 +++++- .../compat/jei/ExDeorumJeiPlugin.java | 41 ++- .../exdeorum/compat/jei/HammerCategory.java | 2 +- .../exdeorum/compat/jei/SieveCategory.java | 68 +++-- .../compat/top/ExDeorumInfoProvider.java | 2 +- .../exdeorum/config/EConfig.java | 23 +- .../exdeorum/data/BlockLoot.java | 17 +- .../thedarkcolour/exdeorum/data/English.java | 16 +- .../exdeorum/data/ModCompatData.java | 6 +- .../exdeorum/data/TranslationKeys.java | 19 +- .../exdeorum/data/package-info.java | 6 - .../exdeorum/data/recipe/Recipes.java | 12 +- .../exdeorum/data/recipe/SieveRecipes.java | 1 + .../exdeorum/event/EventHandler.java | 11 +- .../exdeorum/fluid/WitchWaterFluid.java | 1 + .../exdeorum/item/WateringCanItem.java | 6 +- .../exdeorum/item/package-info.java | 21 ++ ...Count.java => InfestedStringFunction.java} | 12 +- .../exdeorum/loot/MachineLootFunction.java | 62 ++++ .../exdeorum/menu/EContainerMenu.java | 31 ++ .../exdeorum/menu/MechanicalSieveMenu.java | 178 +++++++++++ .../exdeorum/menu/package-info.java | 21 ++ .../network/ClientMessageHandler.java | 23 +- .../exdeorum/network/MenuPropertyMessage.java | 45 +++ .../exdeorum/network/NetworkHandler.java | 7 + .../exdeorum/network/VisualUpdateMessage.java | 73 +++++ .../exdeorum/network/VisualUpdateTracker.java | 69 +++++ .../exdeorum/network/VoidWorldMessage.java | 2 +- .../recipe/BarrelFluidMixingRecipeCache.java | 9 +- .../exdeorum/recipe/SieveRecipeCache.java | 14 +- .../recipe/SingleIngredientRecipeCache.java | 7 +- .../exdeorum/recipe/barrel/package-info.java | 21 ++ .../recipe/crucible/package-info.java | 21 ++ .../exdeorum/recipe/hammer/package-info.java | 6 - .../exdeorum/recipe/package-info.java | 6 - .../recipe/sieve/FinishedSieveRecipe.java | 6 +- .../exdeorum/recipe/sieve/SieveRecipe.java | 10 +- .../exdeorum/registry/EBlockEntities.java | 3 + .../exdeorum/registry/EBlocks.java | 2 + .../exdeorum/registry/EItems.java | 3 + .../exdeorum/registry/ELootFunctions.java | 7 +- .../exdeorum/registry/EMenus.java | 34 +++ .../exdeorum/tag/EBiomeTags.java | 44 +-- .../blockstates/mechanical_sieve.json | 7 + .../models/block/mechanical_sieve.bbmodel | 1 + .../models/block/mechanical_sieve.json | 115 +++++++ .../gui/container/mechanical_sieve.png | Bin 0 -> 3346 bytes .../exdeorum/test/OresTestMod.java | 30 -- src/test/resources/META-INF/mods.toml | 13 - .../data/forge/tags/blocks/ores/tin.json | 5 - .../data/forge/tags/items/ores/tin.json | 5 - src/test/resources/pack.mcmeta | 6 - 88 files changed, 2600 insertions(+), 545 deletions(-) create mode 100644 src/generated/resources/assets/exdeorum/models/item/mechanical_sieve.json create mode 100644 src/generated/resources/data/exdeorum/advancements/recipes/misc/mechanical_sieve.json create mode 100644 src/generated/resources/data/exdeorum/loot_tables/blocks/mechanical_sieve.json create mode 100644 src/generated/resources/data/exdeorum/recipes/mechanical_sieve.json create mode 100644 src/main/java/thedarkcolour/exdeorum/block/MechanicalSieveBlock.java create mode 100644 src/main/java/thedarkcolour/exdeorum/blockentity/AbstractSieveBlockEntity.java create mode 100644 src/main/java/thedarkcolour/exdeorum/blockentity/MechanicalSieveBlockEntity.java create mode 100644 src/main/java/thedarkcolour/exdeorum/blockentity/helper/EnergyHelper.java create mode 100644 src/main/java/thedarkcolour/exdeorum/blockentity/helper/ItemHelper.java create mode 100644 src/main/java/thedarkcolour/exdeorum/blockentity/logic/SieveLogic.java create mode 100644 src/main/java/thedarkcolour/exdeorum/client/screen/MechanicalSieveScreen.java create mode 100644 src/main/java/thedarkcolour/exdeorum/client/screen/RedstoneControlWidget.java create mode 100644 src/main/java/thedarkcolour/exdeorum/client/screen/package-info.java rename src/main/java/thedarkcolour/exdeorum/compat/{jei/JeiSieveRecipeGroup.java => GroupedSieveRecipe.java} (82%) create mode 100644 src/main/java/thedarkcolour/exdeorum/item/package-info.java rename src/main/java/thedarkcolour/exdeorum/loot/{InfestedStringCount.java => InfestedStringFunction.java} (87%) create mode 100644 src/main/java/thedarkcolour/exdeorum/loot/MachineLootFunction.java create mode 100644 src/main/java/thedarkcolour/exdeorum/menu/EContainerMenu.java create mode 100644 src/main/java/thedarkcolour/exdeorum/menu/MechanicalSieveMenu.java create mode 100644 src/main/java/thedarkcolour/exdeorum/menu/package-info.java create mode 100644 src/main/java/thedarkcolour/exdeorum/network/MenuPropertyMessage.java create mode 100644 src/main/java/thedarkcolour/exdeorum/network/VisualUpdateMessage.java create mode 100644 src/main/java/thedarkcolour/exdeorum/network/VisualUpdateTracker.java create mode 100644 src/main/java/thedarkcolour/exdeorum/recipe/barrel/package-info.java create mode 100644 src/main/java/thedarkcolour/exdeorum/recipe/crucible/package-info.java create mode 100644 src/main/java/thedarkcolour/exdeorum/registry/EMenus.java create mode 100644 src/main/resources/assets/exdeorum/blockstates/mechanical_sieve.json create mode 100644 src/main/resources/assets/exdeorum/models/block/mechanical_sieve.bbmodel create mode 100644 src/main/resources/assets/exdeorum/models/block/mechanical_sieve.json create mode 100644 src/main/resources/assets/exdeorum/textures/gui/container/mechanical_sieve.png delete mode 100644 src/test/java/thedarkcolour/exdeorum/test/OresTestMod.java delete mode 100644 src/test/resources/META-INF/mods.toml delete mode 100644 src/test/resources/data/forge/tags/blocks/ores/tin.json delete mode 100644 src/test/resources/data/forge/tags/items/ores/tin.json delete mode 100644 src/test/resources/pack.mcmeta diff --git a/build.gradle b/build.gradle index b1d2d71b..b18dbc5b 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { id 'org.spongepowered.mixin' version '0.7.+' } -version = '1.13' +version = '1.14' group = 'thedarkcolour.exdeorum' base { archivesName = 'exdeorum' @@ -112,6 +112,10 @@ repositories { url = 'https://maven.blamejared.com/' content { includeGroup "mezz.jei" } } + maven { + name = 'rei' + url = "https://maven.shedaniel.me" + } maven { name = 'Architectury API' url = "https://maven.architectury.dev" @@ -135,12 +139,6 @@ repositories { includeModule("maven.modrinth", "embeddium") } } - /*maven { - url "https://cursemaven.com" - content { - includeGroup "curse.maven" - } - }*/ } dependencies { @@ -153,7 +151,10 @@ dependencies { // JEI OPTIONAL compileOnly(fg.deobf("mezz.jei:jei-${mc_version}-common-api:${jei_version}")) compileOnly(fg.deobf("mezz.jei:jei-${mc_version}-forge-api:${jei_version}")) - runtimeOnly(fg.deobf("mezz.jei:jei-${mc_version}-forge:${jei_version}")) + implementation(fg.deobf("mezz.jei:jei-${mc_version}-forge:${jei_version}")) + // REI OPTIONAL + compileOnly(fg.deobf("me.shedaniel:RoughlyEnoughItems-forge:${rei_version}")) + compileOnly(fg.deobf("me.shedaniel.cloth:cloth-config-forge:${cloth_config_version}")) // KubeJS OPTIONAL implementation fg.deobf("dev.architectury:architectury-forge:${architectury_version}") implementation fg.deobf("dev.latvian.mods:rhino-forge:${rhino_version}") @@ -163,8 +164,8 @@ dependencies { implementation fg.deobf('com.github.thedarkcolour:ModKit:78f393bfac') // Oculus + Embeddium OPTIONAL - implementation fg.deobf('maven.modrinth:oculus:1.20.1-1.6.9') - implementation fg.deobf('maven.modrinth:embeddium:0.2.12+mc1.20.1') + compileOnly fg.deobf('maven.modrinth:oculus:1.20.1-1.6.9') + compileOnly fg.deobf('maven.modrinth:embeddium:0.2.12+mc1.20.1') // testing //implementation fg.deobf("curse.maven:allthecompressed-514045:4938351") diff --git a/changelog.md b/changelog.md index b1a56868..2497c676 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,10 @@ +## Ex Deorum 1.14 +- Added Mechanical Sieve, a machine that uses FE to sift blocks automatically. Uses 40 FE a tick by default and takes 100 ticks to sift an item with no efficiency enchantment. +- Added `by_hand_only` boolean field to Sieve recipes, which allows modpack makers to add sieve drops that don't drop from the Mechanical Sieve. +- Added JEI information telling the player that meshes can be enchanted with Fortune and Efficiency. +- Fixed minor rendering bug with infested leaves and Ars Nouveau leaves not rotating properly. +- Optimized syncing block entity visual updates from the server to the client. + ## Ex Deorum 1.13 - Added new icon for JEI compost recipes to help differentiate from the other categories. - Added `sieve_mesh` property to KubeJS's RecipeFilter, for usage in `RecipesEventJS.remove` to remove sieve recipes using a specific mesh. View the [updated documentation](https://exdeorum.readthedocs.io/en/latest). diff --git a/gradle.properties b/gradle.properties index 05772665..2ec3b600 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,6 +11,8 @@ parchment_mappings=1.20.1-2023.06.26 geckolib_version=4.2 modonomicon_version=1.36.0 jei_version=15.2.0.23 +rei_version=12.0.684 +cloth_config_version=11.1.118 curios_version=5.2.0-beta.3 top_version=10.0.1-3 diff --git a/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d b/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d index 2205b069..25abef90 100644 --- a/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d +++ b/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d @@ -1,4 +1,4 @@ -// 1.20.1 2023-11-25T19:04:39.9783304 Loot Tables +// 1.20.1 2024-01-10T20:18:25.7260241 Loot Tables 105d8a61ea7145d7798146d385d4aad24fd1588d data/exdeorum/loot_tables/blocks/acacia_barrel.json 1e77127a82cbba0937bb02694f65cf1893aeffcb data/exdeorum/loot_tables/blocks/acacia_crucible.json fcc00910a8cc94bed6339d6833fcec53c501a0d7 data/exdeorum/loot_tables/blocks/acacia_sieve.json @@ -73,6 +73,7 @@ b38104ee25127d9c65ad9e323ed879f76df7a048 data/exdeorum/loot_tables/blocks/mangro 475b89fd8f09834652f80c93d8a6d0964d708ead data/exdeorum/loot_tables/blocks/maple_barrel.json 54f36187d7fb97dedc4680d14e2ad7d70b5c64af data/exdeorum/loot_tables/blocks/maple_crucible.json 7ffe80360af055f3977d05b5684a299886bcb756 data/exdeorum/loot_tables/blocks/maple_sieve.json +a84508222cb36b07cb20ee31915d802bcc411149 data/exdeorum/loot_tables/blocks/mechanical_sieve.json cad973c873a2e50ccfac91e88eadb3c2462d39d1 data/exdeorum/loot_tables/blocks/oak_barrel.json f94bc97efbfd26ccf7dba32d414fb5e33decd5f6 data/exdeorum/loot_tables/blocks/oak_crucible.json 8d69a87e09fc8a179d5a1bc8eba5faab66e77a6c data/exdeorum/loot_tables/blocks/oak_sieve.json diff --git a/src/generated/resources/.cache/93943142017732f21fbc4fa325d116c728b69767 b/src/generated/resources/.cache/93943142017732f21fbc4fa325d116c728b69767 index 79aa3a7e..ec55f3e3 100644 --- a/src/generated/resources/.cache/93943142017732f21fbc4fa325d116c728b69767 +++ b/src/generated/resources/.cache/93943142017732f21fbc4fa325d116c728b69767 @@ -1,2 +1,2 @@ -// 1.20.1 2023-12-31T14:34:20.2470503 ModKit Language: en_us for mod 'exdeorum' -9889f8490849977914579142b0e2de1d19ecd6f9 assets/exdeorum/lang/en_us.json +// 1.20.1 2024-01-12T17:15:18.0568334 ModKit Language: en_us for mod 'exdeorum' +134e71f17cef95d72141a3d6431db5eb8438f3a0 assets/exdeorum/lang/en_us.json diff --git a/src/generated/resources/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e b/src/generated/resources/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e index a469bc76..714ab6f2 100644 --- a/src/generated/resources/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e +++ b/src/generated/resources/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e @@ -1,4 +1,4 @@ -// 1.20.1 2024-01-01T15:35:52.3392677 Recipes +// 1.20.1 2024-01-11T17:10:41.051025 Recipes e37b64428f17e304e91539ac0513456d7ce40cd1 data/exdeorum/advancements/recipes/building_blocks/sponge.json 5ad481a0c376c1a1785a5d3b992064d0ec0bf3b0 data/exdeorum/advancements/recipes/food/end_cake.json 25dd027e844a72b03c95dbe5e3c3dd8c738ceb00 data/exdeorum/advancements/recipes/misc/acacia_barrel.json @@ -43,6 +43,7 @@ f3d25fad0818f06ed341008daa157732a41702f0 data/exdeorum/advancements/recipes/misc ef344cd03adefba1ff628f494569cb1e61ff5a5f data/exdeorum/advancements/recipes/misc/mangrove_barrel.json a4325e653eba3a2cd86fa5055414c0b8f391d80d data/exdeorum/advancements/recipes/misc/mangrove_crucible.json 47657db106b7291a5fc2ebf281daa03dc7ec71c0 data/exdeorum/advancements/recipes/misc/mangrove_sieve.json +3dc9943b92b254be691be3f6f555992e687fe0e1 data/exdeorum/advancements/recipes/misc/mechanical_sieve.json 8cde46d6245da58277653bb9877518f099150e3d data/exdeorum/advancements/recipes/misc/moss_block.json a21315f6c45ed3300ae2dd79b1dbdbecce9d1305 data/exdeorum/advancements/recipes/misc/netherite_mesh.json d85bf493287a94e61ee13f713625b3dec0624706 data/exdeorum/advancements/recipes/misc/oak_barrel.json @@ -291,6 +292,7 @@ b012c1b39678d3e560365bc01a59c9a88182388e data/exdeorum/recipes/mahogany_barrel.j e288683a0ad5496b43667307e9a22b35a0482d77 data/exdeorum/recipes/maple_barrel.json c6ab33d03646b9af8b2d624e28b7d21083772dda data/exdeorum/recipes/maple_crucible.json f1b143b0f52102366fd79d608540e67e9909c43f data/exdeorum/recipes/maple_sieve.json +0e8f6c50263bda0f9c41da7df9f726f27ff0b374 data/exdeorum/recipes/mechanical_sieve.json 962586e6665baef84b573df40211ff3ce36d88b0 data/exdeorum/recipes/moss_block.json f92abca4ddd5f75b770e81fc745a735a7fd0ee68 data/exdeorum/recipes/netherite_hammer.json c7d0c0109b34ee2e325c42b6664d5fe6b8ea5117 data/exdeorum/recipes/netherite_mesh.json diff --git a/src/generated/resources/.cache/fc2b6ffd874afaa6f2f20b450921dbfbbc8b86bd b/src/generated/resources/.cache/fc2b6ffd874afaa6f2f20b450921dbfbbc8b86bd index c4b42d4a..57425b7f 100644 --- a/src/generated/resources/.cache/fc2b6ffd874afaa6f2f20b450921dbfbbc8b86bd +++ b/src/generated/resources/.cache/fc2b6ffd874afaa6f2f20b450921dbfbbc8b86bd @@ -1,4 +1,4 @@ -// 1.20.1 2023-12-31T14:34:20.2490508 ModKit Item Models for mod 'exdeorum' +// 1.20.1 2024-01-10T20:18:25.7240245 ModKit Item Models for mod 'exdeorum' 4ba3bb2c6174ac3728a4b85e34681f118ec8eb34 assets/exdeorum/models/item/acacia_barrel.json c03ce41f7c071498fcbd5f5225e91dcb2f365fbb assets/exdeorum/models/item/acacia_crucible.json 3b4f1d45c0d9c4cd1d9a5cdf6ddc8d2c9791bca5 assets/exdeorum/models/item/acacia_sieve.json @@ -105,6 +105,7 @@ ff89dc05408074da0e9d41bfef91dfe975302403 assets/exdeorum/models/item/mahogany_cr c3f2af2a88cd97148b05efbd6e24fc2558fcc0b8 assets/exdeorum/models/item/maple_barrel.json cc045825c562e9133858ce5cfe6e6f1dcb747d8e assets/exdeorum/models/item/maple_crucible.json a64e9b9ce91ac6b2f36690a770afc52b8900a614 assets/exdeorum/models/item/maple_sieve.json +1e2b482f5fc4d283f5ca12919b575f86dc4a9541 assets/exdeorum/models/item/mechanical_sieve.json d543d3e18bdcf2bf79a762b52cc61a4161124db1 assets/exdeorum/models/item/mycelium_spores.json 1f48b2ce3452ce5d02142c9f663ae7bdb7d1d934 assets/exdeorum/models/item/netherite_hammer.json 248ebed818e2e4a5eb7ce78363f56433a3066646 assets/exdeorum/models/item/netherite_mesh.json diff --git a/src/generated/resources/assets/exdeorum/lang/en_us.json b/src/generated/resources/assets/exdeorum/lang/en_us.json index d790a853..16e4643d 100644 --- a/src/generated/resources/assets/exdeorum/lang/en_us.json +++ b/src/generated/resources/assets/exdeorum/lang/en_us.json @@ -84,6 +84,7 @@ "block.exdeorum.maple_barrel": "Maple Barrel", "block.exdeorum.maple_crucible": "Maple Crucible", "block.exdeorum.maple_sieve": "Maple Sieve", + "block.exdeorum.mechanical_sieve": "Mechanical Sieve", "block.exdeorum.oak_barrel": "Oak Barrel", "block.exdeorum.oak_crucible": "Oak Crucible", "block.exdeorum.oak_sieve": "Oak Sieve", @@ -117,6 +118,7 @@ "block.exdeorum.willow_crucible": "Willow Crucible", "block.exdeorum.willow_sieve": "Willow Sieve", "block.exdeorum.witch_water": "Witch Water", + "exdeorum.container.mechanical_sieve": "Mechanical Sieve", "fluid_type.exdeorum.witch_water": "Witch Water", "generator.exdeorum.void_world": "Void World", "gui.exdeorum.category.barrel_compost": "Barrel Compost", @@ -129,15 +131,24 @@ "gui.exdeorum.category.lava_crucible": "Lava Crucible", "gui.exdeorum.category.sieve": "Sieve", "gui.exdeorum.category.sieve.average_output": "Avg. Output: %s", + "gui.exdeorum.category.sieve.by_hand_only": "Does not drop from Mechanical Sieve", "gui.exdeorum.category.sieve.chance": "Chance: %s%%", "gui.exdeorum.category.sieve.max_output": "Max: %s", "gui.exdeorum.category.sieve.min_output": "Min: %s", "gui.exdeorum.category.water_crucible": "Water Crucible", + "gui.exdeorum.energy_label": "Energy", + "gui.exdeorum.redstone_control.always": "Always", + "gui.exdeorum.redstone_control.label": "Redstone Mode", + "gui.exdeorum.redstone_control.mode": "Mode: ", + "gui.exdeorum.redstone_control.powered": "Powered", + "gui.exdeorum.redstone_control.unpowered": "Unpowered", "info.exdeorum.crimson_nylium_spores": "Use on netherrack to turn it into a crimson nylium block.", "info.exdeorum.grass_seeds": "Use on dirt to turn it into a grass block.", + "info.exdeorum.mechanical_sieve": "The Mechanical Sieve is a machine that, when supplied with a mesh and Forge Energy (FE), will sift blocks without a player having to do it themselves. It also supports three different modes of redstone control. Since Ex Deorum does not provide a way to generate FE, you will need another mod to provide power.", "info.exdeorum.mycelium_spores": "Use on dirt to turn it into mycelium.", "info.exdeorum.sculk_core": "Use a sculk core on a Sculk Shrieker to enable it to spawn Wardens. Normally, Sculk Shriekers placed by players cannot spawn Wardens, so this item is useful for obtaining Sculk items in a SkyBlock world.", "info.exdeorum.sieve": "Sieves are used to sift for items from soft blocks like gravel and dirt. A mesh is required to use the sieve. Meshes can be enchanted with Fortune and Efficiency. Sieves in a 3x3 area can be used simultaneously.", + "info.exdeorum.sieve_mesh": "Meshes are used in sieves. Different meshes yield different drops. Meshes can be enchanted with Fortune and Efficiency to increase likelihood of drops and sifting speed, respectively.", "info.exdeorum.silk_worm": "Silk worms have a 1 in 100 chance to drop from leaves harvested with a Crook. Using a silk worm on a tree's leaves will infest them, gradually spreading through the entire tree. 100% infested leaves can be harvested for string, but do not drop saplings.", "info.exdeorum.warped_nylium_spores": "Use on netherrack to turn it into a warped nylium block.", "info.exdeorum.watering_can": "Watering cans speed up crop growth, tree growth, and grass spreading, among other things. They can be filled with water from barrels and wooden crucibles. Golden and above watering cans do not need to be refilled once full. Diamond watering cans water in a 3x3 area, and Netherite watering cans are usable by machinery.", @@ -174,6 +185,7 @@ "item.exdeorum.lead_ore_chunk": "Lead Ore Chunk", "item.exdeorum.lithium_ore_chunk": "Lithium Ore Chunk", "item.exdeorum.magnesium_ore_chunk": "Magnesium Ore Chunk", + "item.exdeorum.mechanical_sieve.mesh_label": "Mesh: ", "item.exdeorum.mycelium_spores": "Mycelium Spores", "item.exdeorum.netherite_hammer": "Netherite Hammer", "item.exdeorum.netherite_mesh": "Netherite Mesh", diff --git a/src/generated/resources/assets/exdeorum/models/item/mechanical_sieve.json b/src/generated/resources/assets/exdeorum/models/item/mechanical_sieve.json new file mode 100644 index 00000000..5555169e --- /dev/null +++ b/src/generated/resources/assets/exdeorum/models/item/mechanical_sieve.json @@ -0,0 +1,3 @@ +{ + "parent": "exdeorum:block/mechanical_sieve" +} \ No newline at end of file diff --git a/src/generated/resources/data/exdeorum/advancements/recipes/misc/mechanical_sieve.json b/src/generated/resources/data/exdeorum/advancements/recipes/misc/mechanical_sieve.json new file mode 100644 index 00000000..e62de8f9 --- /dev/null +++ b/src/generated/resources/data/exdeorum/advancements/recipes/misc/mechanical_sieve.json @@ -0,0 +1,35 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": [ + "minecraft:hopper" + ] + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "exdeorum:mechanical_sieve" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_item", + "has_the_recipe" + ] + ], + "rewards": { + "recipes": [ + "exdeorum:mechanical_sieve" + ] + }, + "sends_telemetry_event": true +} \ No newline at end of file diff --git a/src/generated/resources/data/exdeorum/loot_tables/blocks/mechanical_sieve.json b/src/generated/resources/data/exdeorum/loot_tables/blocks/mechanical_sieve.json new file mode 100644 index 00000000..a854ca70 --- /dev/null +++ b/src/generated/resources/data/exdeorum/loot_tables/blocks/mechanical_sieve.json @@ -0,0 +1,26 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ], + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "exdeorum:machine" + } + ], + "name": "exdeorum:mechanical_sieve" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "exdeorum:blocks/mechanical_sieve" +} \ No newline at end of file diff --git a/src/generated/resources/data/exdeorum/recipes/mechanical_sieve.json b/src/generated/resources/data/exdeorum/recipes/mechanical_sieve.json new file mode 100644 index 00000000..38eb394d --- /dev/null +++ b/src/generated/resources/data/exdeorum/recipes/mechanical_sieve.json @@ -0,0 +1,27 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "#": { + "item": "minecraft:iron_block" + }, + "G": { + "item": "minecraft:glass" + }, + "H": { + "item": "minecraft:hopper" + }, + "I": { + "item": "minecraft:iron_bars" + } + }, + "pattern": [ + "#G#", + "IHI", + "I I" + ], + "result": { + "item": "exdeorum:mechanical_sieve" + }, + "show_notification": true +} \ No newline at end of file diff --git a/src/main/java/thedarkcolour/exdeorum/ExDeorum.java b/src/main/java/thedarkcolour/exdeorum/ExDeorum.java index 5c427655..bec5227b 100644 --- a/src/main/java/thedarkcolour/exdeorum/ExDeorum.java +++ b/src/main/java/thedarkcolour/exdeorum/ExDeorum.java @@ -38,6 +38,7 @@ import thedarkcolour.exdeorum.registry.EFluids; import thedarkcolour.exdeorum.registry.EGlobalLootModifiers; import thedarkcolour.exdeorum.registry.EItems; import thedarkcolour.exdeorum.registry.ELootFunctions; +import thedarkcolour.exdeorum.registry.EMenus; import thedarkcolour.exdeorum.registry.ERecipeSerializers; import thedarkcolour.exdeorum.registry.ERecipeTypes; import thedarkcolour.exdeorum.registry.EChunkGenerators; @@ -77,6 +78,7 @@ public class ExDeorum { EGlobalLootModifiers.GLOBAL_LOOT_MODIFIERS.register(modBus); EItems.ITEMS.register(modBus); ELootFunctions.LOOT_FUNCTIONS.register(modBus); + EMenus.MENUS.register(modBus); ERecipeSerializers.RECIPE_SERIALIZERS.register(modBus); ERecipeTypes.RECIPE_TYPES.register(modBus); } diff --git a/src/main/java/thedarkcolour/exdeorum/block/BarrelBlock.java b/src/main/java/thedarkcolour/exdeorum/block/BarrelBlock.java index 0cb78685..c31e6213 100644 --- a/src/main/java/thedarkcolour/exdeorum/block/BarrelBlock.java +++ b/src/main/java/thedarkcolour/exdeorum/block/BarrelBlock.java @@ -38,8 +38,9 @@ import net.minecraft.world.phys.shapes.VoxelShape; import org.jetbrains.annotations.Nullable; import thedarkcolour.exdeorum.blockentity.BarrelBlockEntity; import thedarkcolour.exdeorum.registry.EBlockEntities; +import thedarkcolour.exdeorum.registry.EBlocks; -public class BarrelBlock extends Block implements EntityBlock { +public class BarrelBlock extends EBlock { public static final VoxelShape SHAPE = Shapes.join( box(1, 0, 1, 15, 16, 15), box(2, 1, 2, 14, 16, 14), @@ -47,12 +48,7 @@ public class BarrelBlock extends Block implements EntityBlock { ); public BarrelBlock(Properties properties) { - super(properties); - } - - @Override - public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { - return new BarrelBlockEntity(pos, state); + super(properties, EBlockEntities.BARREL); } @Nullable @@ -66,15 +62,6 @@ public class BarrelBlock extends Block implements EntityBlock { return SHAPE; } - @Override - public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult result) { - if (level.getBlockEntity(pos) instanceof BarrelBlockEntity barrel) { - return barrel.use(level, pos, player, hand); - } - - return InteractionResult.PASS; - } - @Override public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { if (!level.isClientSide) { diff --git a/src/main/java/thedarkcolour/exdeorum/block/MechanicalSieveBlock.java b/src/main/java/thedarkcolour/exdeorum/block/MechanicalSieveBlock.java new file mode 100644 index 00000000..c379d9c4 --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/block/MechanicalSieveBlock.java @@ -0,0 +1,128 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.block; + +import net.minecraft.ChatFormatting; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.GameRules; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.pathfinder.PathComputationType; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; +import net.minecraftforge.items.ItemStackHandler; +import org.jetbrains.annotations.Nullable; +import thedarkcolour.exdeorum.blockentity.MechanicalSieveBlockEntity; +import thedarkcolour.exdeorum.config.EConfig; +import thedarkcolour.exdeorum.data.TranslationKeys; +import thedarkcolour.exdeorum.registry.EBlockEntities; + +import java.util.List; + +public class MechanicalSieveBlock extends EBlock { + private static final VoxelShape SHAPE = Shapes.or( + box(0, 8, 0, 16, 16, 16), + box(1, 0, 1, 3, 8, 3), + box(1, 0, 13, 3, 8, 15), + box(13, 0, 1, 15, 8, 3), + box(13, 0, 13, 15, 8, 15) + ); + + public MechanicalSieveBlock(Properties properties) { + super(properties, EBlockEntities.MECHANICAL_SIEVE); + } + + @Override + public VoxelShape getShape(BlockState pState, BlockGetter pLevel, BlockPos pPos, CollisionContext pContext) { + return SHAPE; + } + + @Nullable + @Override + public BlockEntityTicker getTicker(Level level, BlockState pState, BlockEntityType type) { + return type == EBlockEntities.MECHANICAL_SIEVE.get() && !level.isClientSide ? (BlockEntityTicker) new MechanicalSieveBlockEntity.ServerTicker() : null; + } + + @Override + public void appendHoverText(ItemStack stack, @Nullable BlockGetter level, List tooltip, TooltipFlag flag) { + var nbt = BlockItem.getBlockEntityData(stack); + if (nbt != null) { + var inventoryNbt = nbt.getCompound("inventory"); + var inventory = new ItemStackHandler(); + inventory.deserializeNBT(inventoryNbt); + var mesh = inventory.getStackInSlot(MechanicalSieveBlockEntity.MESH_SLOT); + if (!mesh.isEmpty()) { + tooltip.add(Component.translatable(TranslationKeys.MECHANICAL_SIEVE_MESH_LABEL).withStyle(ChatFormatting.GRAY).append(Component.translatable(mesh.getDescriptionId()))); + } + var energy = nbt.getInt("energy"); + tooltip.add(Component.translatable(TranslationKeys.ENERGY).withStyle(ChatFormatting.GRAY).append(Component.translatable(TranslationKeys.FRACTION_DISPLAY, energy, EConfig.SERVER.mechanicalSieveEnergyStorage.get())).append(" FE")); + } + } + + // Drops the item for creative mode players + @Override + public void playerWillDestroy(Level level, BlockPos pos, BlockState pState, Player player) { + if (!level.isClientSide && player.isCreative() && level.getGameRules().getBoolean(GameRules.RULE_DOBLOCKDROPS)) { + if (level.getBlockEntity(pos) instanceof MechanicalSieveBlockEntity sieve) { + if (!sieve.getMesh().isEmpty()) { + var stack = new ItemStack(this); + BlockItem.setBlockEntityData(stack, EBlockEntities.MECHANICAL_SIEVE.get(), sieve.saveWithoutMetadata()); + var itemEntity = new ItemEntity(level, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, stack); + itemEntity.setDefaultPickUpDelay(); + level.addFreshEntity(itemEntity); + } + } + } + + super.playerWillDestroy(level, pos, pState, player); + } + + @Override + public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving) { + if (!oldState.is(state.getBlock())) { + if (level.getBlockEntity(pos) instanceof MechanicalSieveBlockEntity sieve) { + sieve.checkPoweredState(level, pos); + } + } + } + + @Override + public void neighborChanged(BlockState state, Level level, BlockPos pos, Block block, BlockPos fromPos, boolean isMoving) { + if (level.getBlockEntity(pos) instanceof MechanicalSieveBlockEntity sieve) { + sieve.checkPoweredState(level, pos); + } + } + + @Override + public boolean isPathfindable(BlockState state, BlockGetter level, BlockPos pos, PathComputationType type) { + return false; + } +} diff --git a/src/main/java/thedarkcolour/exdeorum/blockentity/AbstractCrucibleBlockEntity.java b/src/main/java/thedarkcolour/exdeorum/blockentity/AbstractCrucibleBlockEntity.java index 9bce0941..eead6c77 100644 --- a/src/main/java/thedarkcolour/exdeorum/blockentity/AbstractCrucibleBlockEntity.java +++ b/src/main/java/thedarkcolour/exdeorum/blockentity/AbstractCrucibleBlockEntity.java @@ -20,7 +20,9 @@ package thedarkcolour.exdeorum.blockentity; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; @@ -58,6 +60,7 @@ import thedarkcolour.exdeorum.registry.EItems; import java.util.HashMap; import java.util.function.Consumer; +@SuppressWarnings("deprecation") public abstract class AbstractCrucibleBlockEntity extends EBlockEntity { public static final Lazy> MELT_OVERRIDES = Lazy.concurrentOf(() -> { var map = new HashMap(); @@ -70,8 +73,8 @@ public abstract class AbstractCrucibleBlockEntity extends EBlockEntity { private final AbstractCrucibleBlockEntity.ItemHandler item = new AbstractCrucibleBlockEntity.ItemHandler(); private final AbstractCrucibleBlockEntity.FluidHandler tank = new AbstractCrucibleBlockEntity.FluidHandler(); // Capabilities - private final LazyOptional itemHandler = LazyOptional.of(() -> item); - private final LazyOptional fluidHandler = LazyOptional.of(() -> tank); + private final LazyOptional itemHandler = LazyOptional.of(() -> this.item); + private final LazyOptional fluidHandler = LazyOptional.of(() -> this.tank); @Nullable private Block lastMelted; @@ -87,51 +90,80 @@ public abstract class AbstractCrucibleBlockEntity extends EBlockEntity { @NotNull @Override public LazyOptional getCapability(@NotNull Capability cap, @Nullable Direction side) { - if (!remove) { + if (!this.remove) { if (cap == ForgeCapabilities.FLUID_HANDLER) { - return fluidHandler.cast(); + return this.fluidHandler.cast(); } else if (cap == ForgeCapabilities.ITEM_HANDLER) { - return itemHandler.cast(); + return this.itemHandler.cast(); } } return super.getCapability(cap, side); } + @Override + public void invalidateCaps() { + super.invalidateCaps(); + this.fluidHandler.invalidate(); + this.itemHandler.invalidate(); + } + // NBT @Override public void saveAdditional(CompoundTag nbt) { super.saveAdditional(nbt); - nbt.put("Tank", tank.writeToNBT(new CompoundTag())); - nbt.putString("LastMelted", ForgeRegistries.BLOCKS.getKey(lastMelted).toString()); - nbt.putString("Fluid", ForgeRegistries.FLUIDS.getKey(fluid).toString()); - nbt.putShort("Solids", solids); + nbt.put("Tank", this.tank.writeToNBT(new CompoundTag())); + nbt.putString("LastMelted", ForgeRegistries.BLOCKS.getKey(this.lastMelted).toString()); + nbt.putString("Fluid", ForgeRegistries.FLUIDS.getKey(this.fluid).toString()); + nbt.putShort("Solids", this.solids); } @Override public void load(CompoundTag nbt) { super.load(nbt); - tank.readFromNBT(nbt.getCompound("Tank")); - lastMelted = ForgeRegistries.BLOCKS.getValue(new ResourceLocation(nbt.getString("LastMelted"))); - fluid = ForgeRegistries.FLUIDS.getValue(new ResourceLocation(nbt.getString("Fluid"))); - solids = nbt.getShort("Solids"); - needsLightUpdate = true; + this.tank.readFromNBT(nbt.getCompound("Tank")); + this.lastMelted = ForgeRegistries.BLOCKS.getValue(new ResourceLocation(nbt.getString("LastMelted"))); + this.fluid = ForgeRegistries.FLUIDS.getValue(new ResourceLocation(nbt.getString("Fluid"))); + this.solids = nbt.getShort("Solids"); + this.needsLightUpdate = true; + } + + @Override + public void writeVisualData(FriendlyByteBuf buffer) { + buffer.writeId(BuiltInRegistries.FLUID, this.tank.getFluid().getFluid()); + buffer.writeVarInt(this.tank.getFluidAmount()); + buffer.writeId(BuiltInRegistries.BLOCK, this.lastMelted != null ? this.lastMelted : Blocks.AIR); + buffer.writeShort(this.solids); + } + + @Override + public void readVisualData(FriendlyByteBuf buffer) { + Fluid fluid = buffer.readById(BuiltInRegistries.FLUID); + if (fluid == null) { + this.tank.setFluid(FluidStack.EMPTY); + buffer.readVarInt(); + } else { + this.tank.setFluid(new FluidStack(fluid, buffer.readVarInt())); + } + var lastMelted = buffer.readById(BuiltInRegistries.BLOCK); + this.lastMelted = lastMelted == Blocks.AIR ? null : lastMelted; + this.solids = buffer.readShort(); } public InteractionResult use(Level level, Player player, InteractionHand hand) { var playerItem = player.getItemInHand(hand); if (playerItem.getCapability(ForgeCapabilities.FLUID_HANDLER_ITEM).isPresent()) { - return FluidUtil.interactWithFluidHandler(player, hand, tank) ? InteractionResult.sidedSuccess(level.isClientSide) : InteractionResult.PASS; + return FluidUtil.interactWithFluidHandler(player, hand, this.tank) ? InteractionResult.sidedSuccess(level.isClientSide) : InteractionResult.PASS; } if (!level.isClientSide) { if (playerItem.getItem() == Items.GLASS_BOTTLE && this.getType() == EBlockEntities.WATER_CRUCIBLE.get() && EConfig.SERVER.allowWaterBottleTransfer.get()) { var fluid = new FluidStack(Fluids.WATER, 250); - if (tank.drain(fluid, IFluidHandler.FluidAction.SIMULATE).getAmount() == 250) { + if (this.tank.drain(fluid, IFluidHandler.FluidAction.SIMULATE).getAmount() == 250) { BarrelBlockEntity.extractWaterBottle(this.tank, level, player, playerItem, fluid); markUpdated(); } @@ -165,22 +197,22 @@ public abstract class AbstractCrucibleBlockEntity extends EBlockEntity { var result = recipe.getResult(); var contained = this.tank.getFluid(); shrinkAction.accept(item); - this.solids = (short) Math.min(solids + result.getAmount(), MAX_SOLIDS); + this.solids = (short) Math.min(this.solids + result.getAmount(), MAX_SOLIDS); if (contained.isEmpty()) { - fluid = result.getFluid(); - needsLightUpdate = true; + this.fluid = result.getFluid(); + this.needsLightUpdate = true; } var melts = MELT_OVERRIDES.get(); if (melts.containsKey(meltItem)) { - lastMelted = melts.get(meltItem); + this.lastMelted = melts.get(meltItem); } else if (meltItem.getClass() == BlockItem.class) { - lastMelted = ((BlockItem) meltItem).getBlock(); + this.lastMelted = ((BlockItem) meltItem).getBlock(); } else { // If we already have something else inside just use that instead of switching to default - if (lastMelted == null) { - lastMelted = getDefaultMeltBlock(); + if (this.lastMelted == null) { + this.lastMelted = getDefaultMeltBlock(); } } @@ -194,9 +226,9 @@ public abstract class AbstractCrucibleBlockEntity extends EBlockEntity { if (recipe != null) { var result = recipe.getResult(); - var contained = tank.getFluid(); + var contained = this.tank.getFluid(); - return (result.isFluidEqual(contained) || contained.isEmpty()) && result.getAmount() + solids <= MAX_SOLIDS; + return (result.isFluidEqual(contained) || contained.isEmpty()) && result.getAmount() + this.solids <= MAX_SOLIDS; } return false; @@ -207,24 +239,24 @@ public abstract class AbstractCrucibleBlockEntity extends EBlockEntity { } public int getSolids() { - return solids; + return this.solids; } public FluidTank getTank() { - return tank; + return this.tank; } public abstract Block getDefaultMeltBlock(); @Nullable public Block getLastMelted() { - return lastMelted; + return this.lastMelted; } @Override public void setRemoved() { - itemHandler.invalidate(); - fluidHandler.invalidate(); + this.itemHandler.invalidate(); + this.fluidHandler.invalidate(); super.setRemoved(); } @@ -288,7 +320,7 @@ public abstract class AbstractCrucibleBlockEntity extends EBlockEntity { } public ItemStack getItem() { - return stacks.get(0); + return this.stacks.get(0); } } diff --git a/src/main/java/thedarkcolour/exdeorum/blockentity/AbstractSieveBlockEntity.java b/src/main/java/thedarkcolour/exdeorum/blockentity/AbstractSieveBlockEntity.java new file mode 100644 index 00000000..a418163b --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/blockentity/AbstractSieveBlockEntity.java @@ -0,0 +1,94 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.blockentity; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import thedarkcolour.exdeorum.blockentity.logic.SieveLogic; + +import java.util.function.Function; + +public abstract class AbstractSieveBlockEntity extends EBlockEntity implements SieveLogic.Owner { + protected final SieveLogic logic; + + public AbstractSieveBlockEntity(BlockEntityType type, BlockPos pos, BlockState state, Function logic) { + super(type, pos, state); + + this.logic = logic.apply(this); + } + + public static ItemStack singleCopy(ItemStack stack) { + var copy = stack.copy(); + copy.setCount(1); + return copy; + } + + @Override + public ServerLevel getServerLevel() { + return (ServerLevel) this.level; + } + + @Override + protected void saveAdditional(CompoundTag nbt) { + super.saveAdditional(nbt); + + this.logic.saveNbt(nbt); + } + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + + this.logic.loadNbt(nbt); + } + + // Used for rendering and for TOP + public ItemStack getMesh() { + return this.logic.getMesh(); + } + + // Used for rendering + public float getProgress() { + return this.logic.getProgress(); + } + + // Used for rendering + public ItemStack getContents() { + return this.logic.getContents(); + } + + @Override + public void writeVisualData(FriendlyByteBuf buffer) { + buffer.writeItem(this.logic.getMesh()); + buffer.writeFloat(this.logic.getProgress()); + buffer.writeItem(this.logic.getContents()); + } + + @Override + public void readVisualData(FriendlyByteBuf buffer) { + this.logic.setMesh(buffer.readItem(), false); + this.logic.setProgress(buffer.readFloat()); + this.logic.setContents(buffer.readItem()); + } +} diff --git a/src/main/java/thedarkcolour/exdeorum/blockentity/BarrelBlockEntity.java b/src/main/java/thedarkcolour/exdeorum/blockentity/BarrelBlockEntity.java index d8080766..f5ce5510 100644 --- a/src/main/java/thedarkcolour/exdeorum/blockentity/BarrelBlockEntity.java +++ b/src/main/java/thedarkcolour/exdeorum/blockentity/BarrelBlockEntity.java @@ -96,6 +96,13 @@ public class BarrelBlockEntity extends EBlockEntity { return super.getCapability(cap, side); } + @Override + public void invalidateCaps() { + super.invalidateCaps(); + this.fluidHandler.invalidate(); + this.itemHandler.invalidate(); + } + @Override public void saveAdditional(CompoundTag nbt) { super.saveAdditional(nbt); @@ -175,10 +182,11 @@ public class BarrelBlockEntity extends EBlockEntity { return this.tank; } - public InteractionResult use(Level level, BlockPos pos, Player player, InteractionHand hand) { + @Override + public InteractionResult use(Level level, Player player, InteractionHand hand) { // Collect an item if (!getItem().isEmpty()) { - return giveResultItem(level, pos); + return giveResultItem(level); } // Handle item fluid interaction first @@ -249,7 +257,7 @@ public class BarrelBlockEntity extends EBlockEntity { if (!player.getAbilities().instabuild) { player.setItemInHand(hand, handItem); - giveResultItem(level, pos); + giveResultItem(level); } } @@ -270,9 +278,9 @@ public class BarrelBlockEntity extends EBlockEntity { } // Pops the item out of the barrel (ex. dirt that has finished composting) - private InteractionResult giveResultItem(Level level, BlockPos pos) { + private InteractionResult giveResultItem(Level level) { if (!level.isClientSide) { - popOutItem(level, pos, item.extract(false)); + popOutItem(level, this.worldPosition, item.extract(false)); // Empty contents setItem(ItemStack.EMPTY); diff --git a/src/main/java/thedarkcolour/exdeorum/blockentity/EBlockEntity.java b/src/main/java/thedarkcolour/exdeorum/blockentity/EBlockEntity.java index a4dd7ba3..b47fcb31 100644 --- a/src/main/java/thedarkcolour/exdeorum/blockentity/EBlockEntity.java +++ b/src/main/java/thedarkcolour/exdeorum/blockentity/EBlockEntity.java @@ -21,6 +21,7 @@ package thedarkcolour.exdeorum.blockentity; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.Connection; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; @@ -29,6 +30,7 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; +import thedarkcolour.exdeorum.network.VisualUpdateTracker; import javax.annotation.Nullable; @@ -50,6 +52,7 @@ public abstract class EBlockEntity extends BlockEntity { @Override public void onDataPacket(Connection net, ClientboundBlockEntityDataPacket pkt) { + // todo consider removing the load with an empty tag if (pkt.getTag() == null) { load(new CompoundTag()); } else { @@ -57,12 +60,20 @@ public abstract class EBlockEntity extends BlockEntity { } } - protected void markUpdated() { + public void markUpdated() { setChanged(); - level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 2); + VisualUpdateTracker.sendVisualUpdate(this); } public InteractionResult use(Level level, Player player, InteractionHand hand) { return InteractionResult.PASS; } + + public void writeVisualData(FriendlyByteBuf buffer) { + + } + + public void readVisualData(FriendlyByteBuf buffer) { + + } } diff --git a/src/main/java/thedarkcolour/exdeorum/blockentity/InfestedLeavesBlockEntity.java b/src/main/java/thedarkcolour/exdeorum/blockentity/InfestedLeavesBlockEntity.java index 38fd3459..20c3508a 100644 --- a/src/main/java/thedarkcolour/exdeorum/blockentity/InfestedLeavesBlockEntity.java +++ b/src/main/java/thedarkcolour/exdeorum/blockentity/InfestedLeavesBlockEntity.java @@ -23,6 +23,7 @@ import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtUtils; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.tags.BlockTags; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Blocks; @@ -54,25 +55,35 @@ public class InfestedLeavesBlockEntity extends EBlockEntity { super(EBlockEntities.INFESTED_LEAVES.get(), pos, state); } + @Override + public void writeVisualData(FriendlyByteBuf buffer) { + buffer.writeFloat(this.progress); + } + + @Override + public void readVisualData(FriendlyByteBuf buffer) { + buffer.readFloat(); + } + // Attempt to convert a leaf block within 1 block radius around this block private void trySpread() { // Get random offset - int x = level.random.nextInt(3) - 1; - int y = level.random.nextInt(3) - 1; - int z = level.random.nextInt(3) - 1; + int x = this.level.random.nextInt(3) - 1; + int y = this.level.random.nextInt(3) - 1; + int z = this.level.random.nextInt(3) - 1; // Get the block in the world BlockPos targetPos = getBlockPos().offset(x, y, z); - BlockState state = level.getBlockState(targetPos); + BlockState state = this.level.getBlockState(targetPos); // DO NOT SPREAD TO ALREADY INFESTED LEAVES if (state.is(BlockTags.LEAVES) && state.getBlock() != EBlocks.INFESTED_LEAVES.get()) { // Spread and keep distance/persistent properties - level.setBlock(targetPos, EBlocks.INFESTED_LEAVES.get().defaultBlockState() + this.level.setBlock(targetPos, EBlocks.INFESTED_LEAVES.get().defaultBlockState() .setValue(LeavesBlock.DISTANCE, state.getValue(LeavesBlock.DISTANCE)) .setValue(LeavesBlock.PERSISTENT, state.getValue(LeavesBlock.PERSISTENT)), 2); - var te = level.getBlockEntity(targetPos); + var te = this.level.getBlockEntity(targetPos); // Set mimic state of other block if (te instanceof InfestedLeavesBlockEntity leaves) { @@ -89,27 +100,27 @@ public class InfestedLeavesBlockEntity extends EBlockEntity { // From PistonMovingBlockEntity @SuppressWarnings("deprecation") var holderLookup = this.level != null ? this.level.holderLookup(Registries.BLOCK) : BuiltInRegistries.BLOCK.asLookup(); - mimic = NbtUtils.readBlockState(holderLookup, nbt.getCompound("mimic")); - progress = nbt.getFloat("progress"); + this.mimic = NbtUtils.readBlockState(holderLookup, nbt.getCompound("mimic")); + this.progress = nbt.getFloat("progress"); } @Override public void saveAdditional(CompoundTag nbt) { super.saveAdditional(nbt); - if (mimic == null || mimic.getBlock() == EBlocks.INFESTED_LEAVES.get()) { - mimic = Blocks.OAK_LEAVES.defaultBlockState(); + if (this.mimic == null || this.mimic.getBlock() == EBlocks.INFESTED_LEAVES.get()) { + this.mimic = Blocks.OAK_LEAVES.defaultBlockState(); } - nbt.put("mimic", NbtUtils.writeBlockState(mimic)); - nbt.putFloat("progress", progress); + nbt.put("mimic", NbtUtils.writeBlockState(this.mimic)); + nbt.putFloat("progress", this.progress); } public float getProgress() { - return progress; + return this.progress; } public BlockState getMimic() { - return mimic; + return this.mimic; } public void setMimic(BlockState mimic) { @@ -118,7 +129,7 @@ public class InfestedLeavesBlockEntity extends EBlockEntity { @Override public @NotNull ModelData getModelData() { - return ModelData.builder().with(MIMIC_PROPERTY, mimic).build(); + return ModelData.builder().with(MIMIC_PROPERTY, this.mimic).build(); } public static class Ticker implements BlockEntityTicker { diff --git a/src/main/java/thedarkcolour/exdeorum/blockentity/MechanicalSieveBlockEntity.java b/src/main/java/thedarkcolour/exdeorum/blockentity/MechanicalSieveBlockEntity.java new file mode 100644 index 00000000..cfec52a1 --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/blockentity/MechanicalSieveBlockEntity.java @@ -0,0 +1,283 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.blockentity; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.Mth; +import net.minecraft.util.RandomSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ForgeCapabilities; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.energy.EnergyStorage; +import net.minecraftforge.items.ItemHandlerHelper; +import net.minecraftforge.network.NetworkHooks; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import thedarkcolour.exdeorum.blockentity.helper.EnergyHelper; +import thedarkcolour.exdeorum.blockentity.helper.ItemHelper; +import thedarkcolour.exdeorum.blockentity.logic.SieveLogic; +import thedarkcolour.exdeorum.client.screen.RedstoneControlWidget; +import thedarkcolour.exdeorum.config.EConfig; +import thedarkcolour.exdeorum.data.TranslationKeys; +import thedarkcolour.exdeorum.menu.MechanicalSieveMenu; +import thedarkcolour.exdeorum.recipe.RecipeUtil; +import thedarkcolour.exdeorum.registry.EBlockEntities; +import thedarkcolour.exdeorum.tag.EItemTags; + +import javax.annotation.Nonnull; + +public class MechanicalSieveBlockEntity extends AbstractSieveBlockEntity implements MenuProvider { + private static final Component TITLE = Component.translatable(TranslationKeys.MECHANICAL_SIEVE_SCREEN_TITLE); + private static final int INPUT_SLOT = 0; + public static final int MESH_SLOT = 1; + + public final ItemHelper inventory; + public final EnergyHelper energy; + private int redstoneMode; + // not saved to NBT + public boolean hasRedstonePower; + + private final LazyOptional capabilityInventory; + private final LazyOptional capabilityEnergy; + + public MechanicalSieveBlockEntity(BlockPos pos, BlockState state) { + super(EBlockEntities.MECHANICAL_SIEVE.get(), pos, state, owner -> new SieveLogic(owner, false, true)); + + this.inventory = new ItemHandler(22); + this.energy = new EnergyHelper(40000); + + this.capabilityInventory = LazyOptional.of(() -> this.inventory); + this.capabilityEnergy = LazyOptional.of(() -> this.energy); + } + + @Override + protected void saveAdditional(CompoundTag nbt) { + super.saveAdditional(nbt); + + nbt.put("inventory", this.inventory.serializeNBT()); + nbt.putInt("energy", this.energy.getEnergyStored()); + nbt.putInt("redstoneMode", this.redstoneMode); + } + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + + this.inventory.deserializeNBT(nbt.getCompound("inventory")); + this.energy.setStoredEnergy(nbt.getInt("energy")); + this.redstoneMode = Mth.clamp(nbt.getInt("redstoneMode"), 0, 2); + } + + @Override + public void onLoad() { + checkPoweredState(this.level, this.worldPosition); + } + + @Override + public InteractionResult use(Level level, Player player, InteractionHand hand) { + if (player instanceof ServerPlayer serverPlayer) { + NetworkHooks.openScreen(serverPlayer, this, buffer -> { + buffer.writeBlockPos(getBlockPos()); + buffer.writeByte(this.redstoneMode); + }); + return InteractionResult.CONSUME; + } else { + return InteractionResult.SUCCESS; + } + } + + @Nonnull + @Override + public LazyOptional getCapability(@Nonnull Capability cap, @javax.annotation.Nullable Direction side) { + if (cap == ForgeCapabilities.ENERGY) { + return this.capabilityEnergy.cast(); + } else if (cap == ForgeCapabilities.ITEM_HANDLER) { + return this.capabilityInventory.cast(); + } + + return super.getCapability(cap, side); + } + + @Override + public void invalidateCaps() { + super.invalidateCaps(); + + this.capabilityEnergy.invalidate(); + this.capabilityInventory.invalidate(); + } + + private void serverTick() { + if (this.redstoneMode == RedstoneControlWidget.REDSTONE_MODE_IGNORED || ((this.redstoneMode == RedstoneControlWidget.REDSTONE_MODE_UNPOWERED)) != this.hasRedstonePower) { + var energyConsumption = EConfig.SERVER.mechanicalSieveEnergyConsumption.get(); + + if (this.energy.getEnergyStored() >= energyConsumption) { + if (this.logic.getContents().isEmpty()) { + var input = this.inventory.getStackInSlot(INPUT_SLOT); + + if (this.logic.isValidInput(input)) { + this.logic.startSifting(AbstractSieveBlockEntity.singleCopy(input)); + input.shrink(1); + } + } + if (!this.logic.getContents().isEmpty()) { + this.energy.extractEnergy(energyConsumption, false); + this.logic.sift(0.01f); + } + } + } + } + + @Override + public boolean handleResultItem(ItemStack result, ServerLevel level, RandomSource rand) { + var remainder = result.copy(); + + for (int i = 2; i < 22; ++i) { + // Try to forcefully insert remainder into the output slots, since insertItem will deny it + // See ItemStackHandler.insertItem for reference + var existing = this.inventory.getStackInSlot(i); + // The maximum number of items that can be added to the slot + var limit = this.inventory.getSlotLimit(i); + + if (!existing.isEmpty()) { + if (!ItemHandlerHelper.canItemStacksStack(remainder, existing)) { + continue; + } + limit -= existing.getCount(); + } + + // If slot is full + if (limit <= 0) { + continue; + } + + // If only part of the remainder can fit into the slot + var splitRemainder = remainder.getCount() > limit; + + if (existing.isEmpty()) { + this.inventory.setStackInSlot(i, splitRemainder ? ItemHandlerHelper.copyStackWithSize(remainder, limit) : remainder); + } else { + existing.grow(splitRemainder ? limit : remainder.getCount()); + } + if (splitRemainder) { + remainder = ItemHandlerHelper.copyStackWithSize(remainder, remainder.getCount() - limit); + } else { + return true; + } + } + + // item was "handled" if the remainder is smaller than the original result + return remainder.getCount() < result.getCount(); + } + + @Override + public Component getDisplayName() { + return TITLE; + } + + @Nullable + @Override + public AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player pPlayer) { + return new MechanicalSieveMenu(containerId, playerInventory, this); + } + + public boolean stillValid(Player player) { + if (this.level.getBlockEntity(this.worldPosition) != this) { + return false; + } else { + return player.distanceToSqr(this.worldPosition.getX() + 0.5, this.worldPosition.getY() + 0.5, this.worldPosition.getZ() + 0.5) <= 64.0; + } + } + + public SieveLogic getLogic() { + return this.logic; + } + + public void setRedstoneMode(int redstoneMode) { + this.redstoneMode = redstoneMode; + } + + public int getRedstoneMode() { + return this.redstoneMode; + } + + public void checkPoweredState(Level level, BlockPos pos) { + this.hasRedstonePower = level.hasNeighborSignal(pos); + } + + private class ItemHandler extends ItemHelper { + public ItemHandler(int size) { + super(size); + } + + @Override + public boolean isItemValid(int slot, @NotNull ItemStack stack) { + if (slot == INPUT_SLOT) { + return !RecipeUtil.getSieveRecipes(getStackInSlot(1).getItem(), stack).isEmpty(); + } else if (slot == MESH_SLOT) { + return stack.is(EItemTags.SIEVE_MESHES); + } else { + return false; + } + } + + @Override + public int getSlotLimit(int slot) { + return slot == MESH_SLOT ? 1 : super.getSlotLimit(slot); + } + + @Override + public boolean canMachineExtract(int slot) { + return slot > 1; + } + + @Override + protected void onContentsChanged(int slot) { + if (slot == MESH_SLOT) { + MechanicalSieveBlockEntity.this.logic.setMesh(MechanicalSieveBlockEntity.this.inventory.getStackInSlot(MESH_SLOT)); + } + } + + @Override + protected void onLoad() { + MechanicalSieveBlockEntity.this.logic.setMesh(MechanicalSieveBlockEntity.this.inventory.getStackInSlot(MESH_SLOT), false); + } + } + + public static class ServerTicker implements BlockEntityTicker { + @Override + public void tick(Level level, BlockPos pos, BlockState state, MechanicalSieveBlockEntity sieve) { + sieve.serverTick(); + } + } +} diff --git a/src/main/java/thedarkcolour/exdeorum/blockentity/SieveBlockEntity.java b/src/main/java/thedarkcolour/exdeorum/blockentity/SieveBlockEntity.java index fbbdfa7f..2e822399 100644 --- a/src/main/java/thedarkcolour/exdeorum/blockentity/SieveBlockEntity.java +++ b/src/main/java/thedarkcolour/exdeorum/blockentity/SieveBlockEntity.java @@ -19,68 +19,34 @@ package thedarkcolour.exdeorum.blockentity; import net.minecraft.core.BlockPos; -import net.minecraft.nbt.CompoundTag; import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.RandomSource; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; -import net.minecraft.world.item.enchantment.Enchantments; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.storage.loot.LootContext; -import net.minecraft.world.level.storage.loot.LootParams; import net.minecraftforge.common.util.FakePlayer; +import thedarkcolour.exdeorum.blockentity.logic.SieveLogic; import thedarkcolour.exdeorum.config.EConfig; -import thedarkcolour.exdeorum.recipe.RecipeUtil; -import thedarkcolour.exdeorum.recipe.sieve.SieveRecipe; import thedarkcolour.exdeorum.registry.EBlockEntities; -import thedarkcolour.exdeorum.tag.EItemTags; -import java.util.Map; - -public class SieveBlockEntity extends EBlockEntity { - public static final short MAX_SIEVE_CAPACITY = 100; - public static final short SIEVE_INTERVAL = 10; - - private ItemStack contents = ItemStack.EMPTY; - private ItemStack mesh = ItemStack.EMPTY; - private short progress = 0; // Max is 100 - private float efficiency = 1f; - private int fortune = 0; +public class SieveBlockEntity extends AbstractSieveBlockEntity { + public static final float SIEVE_INTERVAL = 0.1f; public SieveBlockEntity(BlockPos pos, BlockState state) { - super(EBlockEntities.SIEVE.get(), pos, state); + super(EBlockEntities.SIEVE.get(), pos, state, (owner) -> new SieveLogic(owner, true, false)); } @Override - protected void saveAdditional(CompoundTag nbt) { - super.saveAdditional(nbt); - - nbt.put("contents", contents.serializeNBT()); - nbt.putShort("progress", progress); - if (!mesh.isEmpty()) { - nbt.put("mesh", mesh.save(new CompoundTag())); - } - } - - @Override - public void load(CompoundTag nbt) { - if (nbt.contains("contents")) { - this.contents = ItemStack.of(nbt.getCompound("contents")); - } else { - this.contents = ItemStack.EMPTY; - } - this.progress = nbt.getShort("progress"); - if (nbt.contains("mesh")) { - setMesh(ItemStack.of(nbt.getCompound("mesh"))); - } else { - setMesh(ItemStack.EMPTY); - } - - super.load(nbt); + public boolean handleResultItem(ItemStack result, ServerLevel level, RandomSource rand) { + var pos = this.worldPosition; + var itemEntity = new ItemEntity(level, pos.getX() + 0.5, pos.getY() + 1.5, pos.getZ() + 0.5, result); + itemEntity.setDeltaMovement(rand.nextGaussian() * 0.05, 0.2, rand.nextGaussian() * 0.05); + level.addFreshEntity(itemEntity); + return true; } public InteractionResult use(Level level, Player player, InteractionHand hand) { @@ -88,14 +54,10 @@ public class SieveBlockEntity extends EBlockEntity { boolean isClientSide = level.isClientSide; // Try insert mesh - if (mesh.isEmpty()) { - if (isMesh(playerItem)) { - if (!level.isClientSide) { - var meshCopy = playerItem.copy(); - meshCopy.setCount(1); - setMesh(meshCopy); - - markUpdated(); + if (this.logic.getMesh().isEmpty()) { + if (this.logic.isValidMesh(playerItem)) { + if (!isClientSide) { + this.logic.setMesh(singleCopy(playerItem)); if (!player.getAbilities().instabuild) { playerItem.shrink(1); @@ -105,73 +67,71 @@ public class SieveBlockEntity extends EBlockEntity { return InteractionResult.SUCCESS; } } - } else if (contents.isEmpty()) { + } else if (this.logic.getContents().isEmpty()) { // remove mesh with sneak right click if (player.isShiftKeyDown() && player.getMainHandItem().isEmpty()) { - removeMesh(); + popOutMesh(level, this.worldPosition, this.logic); } } - if (contents.isEmpty()) { + if (!isClientSide) { // Insert an item - if (!isClientSide) { + if (this.logic.getContents().isEmpty()) { // If the input has any sieve drops, insert it into the mesh - if (!RecipeUtil.getSieveRecipes(mesh.getItem(), playerItem).isEmpty()) { - playerItem = this.insertContents(player, hand); - markUpdated(); + if (this.logic.isValidInput(playerItem)) { + playerItem = insertContents(player, hand, this.logic); if (EConfig.SERVER.simultaneousSieveUsage.get()) { - var cursor = worldPosition.mutable().move(-1, 0, -1); + int range = EConfig.SERVER.simultaneousSieveUsageRange.get(); + var cursor = this.worldPosition.mutable().move(-range, 0, -range); // Fill adjacent sieves otherSieves: - for (int x = -1; x <= 1; x++) { - for (int z = -1; z <= 1; z++) { + for (int x = -range; x <= range; x++) { + for (int z = -range; z <= range; z++) { if (playerItem.isEmpty()) { break otherSieves; } if ((x | z) != 0) { if (level.getBlockEntity(cursor) instanceof SieveBlockEntity other) { - if (other.contents.isEmpty()) { - if (this.mesh.getItem() == other.mesh.getItem()) { - playerItem = other.insertContents(player, hand); - other.markUpdated(); + if (other.logic.getContents().isEmpty()) { + if (this.logic.getMesh().getItem() == other.logic.getMesh().getItem()) { + playerItem = insertContents(player, hand, other.logic); } } } } - cursor.move(0, 0, 1); } - cursor.move(1, 0, -3); + cursor.move(1, 0, (-2 * range) - 1); } } } - } - } else { - var realPlayer = !(player instanceof FakePlayer); + } else { + var realPlayer = !(player instanceof FakePlayer); - if (realPlayer && EConfig.SERVER.simultaneousSieveUsage.get()) { - var cursor = worldPosition.mutable().move(-1, 0, -1); + if (realPlayer && EConfig.SERVER.simultaneousSieveUsage.get()) { + int range = EConfig.SERVER.simultaneousSieveUsageRange.get(); + var cursor = this.worldPosition.mutable().move(-range, 0, -range); - // Sieve with adjacent sieves - for (int x = -1; x <= 1; x++) { - for (int z = -1; z <= 1; z++) { - if (level.getBlockEntity(cursor) instanceof SieveBlockEntity other) { - if (!other.contents.isEmpty()) { - if (this.mesh.getItem() == other.mesh.getItem()) { - other.performSift(player); + // Sieve with adjacent sieves + for (int x = -range; x <= range; x++) { + for (int z = -range; z <= range; z++) { + if (level.getBlockEntity(cursor) instanceof SieveBlockEntity other) { + if (!other.logic.getContents().isEmpty()) { + if (this.logic.getMesh().getItem() == other.logic.getMesh().getItem()) { + other.logic.sift(SIEVE_INTERVAL); + } } } + cursor.move(0, 0, 1); } - - cursor.move(0, 0, 1); + cursor.move(1, 0, (-2 * range) - 1); } - cursor.move(1, 0, -3); + } else if (realPlayer || EConfig.SERVER.automatedSieves.get()) { + this.logic.sift(SIEVE_INTERVAL); } - } else if (realPlayer || EConfig.SERVER.automatedSieves.get()) { - performSift(player); } } @@ -179,115 +139,37 @@ public class SieveBlockEntity extends EBlockEntity { } // Fills the sieve (assumes contents is EMPTY) and returns the remaining item, putting it in the player's hand - private ItemStack insertContents(Player player, InteractionHand hand) { + public static ItemStack insertContents(Player player, InteractionHand hand, SieveLogic logic) { var consume = !player.getAbilities().instabuild; var playerItem = player.getItemInHand(hand); if (consume) { if (playerItem.getCount() == 1) { - this.contents = playerItem; + logic.startSifting(playerItem); player.setItemInHand(hand, ItemStack.EMPTY); playerItem = ItemStack.EMPTY; } else { - this.contents = singleCopy(playerItem); + logic.startSifting(singleCopy(playerItem)); playerItem.shrink(1); } } else { - this.contents = singleCopy(playerItem); + logic.startSifting(singleCopy(playerItem)); } - this.progress = MAX_SIEVE_CAPACITY; - return playerItem; } - private static ItemStack singleCopy(ItemStack stack) { - var copy = stack.copy(); - copy.setCount(1); - return copy; - } - - private void performSift(Player player) { - progress -= efficiency * SIEVE_INTERVAL; - - if (progress <= 0) { - progress = 0; - - if (!level.isClientSide) { - giveItems(player); - } - } else { - markUpdated(); - } - } - - private void removeMesh() { + // Do not call on client side + public static void popOutMesh(Level level, BlockPos sievePos, SieveLogic logic) { if (!level.isClientSide) { // Pop out item - var itemEntity = new ItemEntity(level, worldPosition.getX() + 0.5, worldPosition.getY() + 1.5, worldPosition.getZ() + 0.5, mesh); + var itemEntity = new ItemEntity(level, sievePos.getX() + 0.5, sievePos.getY() + 1.5, sievePos.getZ() + 0.5, logic.getMesh()); var rand = level.random; itemEntity.setDeltaMovement(rand.nextGaussian() * 0.05, 0.2, rand.nextGaussian() * 0.05); level.addFreshEntity(itemEntity); // Empty contents - setMesh(ItemStack.EMPTY); - markUpdated(); + logic.setMesh(ItemStack.EMPTY); } } - - private void setMesh(ItemStack mesh) { - this.mesh = mesh; - this.efficiency = 1f + mesh.getEnchantmentLevel(Enchantments.BLOCK_EFFICIENCY) * 0.17f; - this.fortune = mesh.getEnchantmentLevel(Enchantments.BLOCK_FORTUNE); - } - - private void giveItems(Player player) { - var pos = this.worldPosition; - var level = this.level; - var context = new LootContext.Builder(new LootParams((ServerLevel) level, Map.of(), Map.of(), player.getLuck())).create(null); - var rand = this.level.random; - var limitDrops = this.contents.getItem() == Items.MOSS_BLOCK && EConfig.SERVER.limitMossSieveDrops.get(); - - for (SieveRecipe recipe : RecipeUtil.getSieveRecipes(this.mesh.getItem(), this.contents)) { - var amount = recipe.resultAmount.getInt(context); - - for (int i = 0; i < this.fortune; i++) { - if (rand.nextFloat() < 0.3f) { - amount += recipe.resultAmount.getInt(context); - } - } - - if (amount >= 1) { - var itemEntity = new ItemEntity(level, pos.getX() + 0.5, pos.getY() + 1.5, pos.getZ() + 0.5, new ItemStack(recipe.result, amount)); - itemEntity.setDeltaMovement(rand.nextGaussian() * 0.05, 0.2, rand.nextGaussian() * 0.05); - level.addFreshEntity(itemEntity); - - // limit drops to 1 or two items (could be more than 2 but unlikely) - if (limitDrops && rand.nextInt(5) != 0) { - break; - } - } - } - - this.contents = ItemStack.EMPTY; - markUpdated(); - } - - private boolean isMesh(ItemStack stack) { - return stack.is(EItemTags.SIEVE_MESHES); - } - - public ItemStack getMesh() { - return this.mesh; - } - - // Used for rendering - public short getProgress() { - return this.progress; - } - - // Used for rendering - public ItemStack getContents() { - return this.contents; - } } diff --git a/src/main/java/thedarkcolour/exdeorum/blockentity/helper/EnergyHelper.java b/src/main/java/thedarkcolour/exdeorum/blockentity/helper/EnergyHelper.java new file mode 100644 index 00000000..ad208e05 --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/blockentity/helper/EnergyHelper.java @@ -0,0 +1,43 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.blockentity.helper; + +import net.minecraftforge.energy.EnergyStorage; + +public class EnergyHelper extends EnergyStorage { + public EnergyHelper(int capacity) { + super(capacity); + } + + public EnergyHelper(int capacity, int maxTransfer) { + super(capacity, maxTransfer); + } + + public EnergyHelper(int capacity, int maxReceive, int maxExtract) { + super(capacity, maxReceive, maxExtract); + } + + public EnergyHelper(int capacity, int maxReceive, int maxExtract, int energy) { + super(capacity, maxReceive, maxExtract, energy); + } + + public void setStoredEnergy(int energy) { + this.energy = energy; + } +} diff --git a/src/main/java/thedarkcolour/exdeorum/blockentity/helper/ItemHelper.java b/src/main/java/thedarkcolour/exdeorum/blockentity/helper/ItemHelper.java new file mode 100644 index 00000000..85aeb1a1 --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/blockentity/helper/ItemHelper.java @@ -0,0 +1,69 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.blockentity.helper; + +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.items.ItemStackHandler; +import net.minecraftforge.items.SlotItemHandler; +import org.jetbrains.annotations.NotNull; + +public class ItemHelper extends ItemStackHandler { + public ItemHelper(int size) { + super(size); + } + + // Whether an item can be extracted from this slot (GUI ignores this and just takes it out) + public boolean canMachineExtract(int slot) { + return true; + } + + @Override + public @NotNull ItemStack extractItem(int slot, int amount, boolean simulate) { + if (canMachineExtract(slot)) { + return takeOutItem(slot, amount, simulate); + } else { + return ItemStack.EMPTY; + } + } + + public ItemStack takeOutItem(int slot, int amount, boolean simulate) { + return super.extractItem(slot, amount, simulate); + } + + public ItemHelper.Slot createSlot(int index, int x, int y) { + return new ItemHelper.Slot(index, x, y); + } + + public class Slot extends SlotItemHandler { + public Slot(int index, int x, int y) { + super(ItemHelper.this, index, x, y); + } + + @Override + public @NotNull ItemStack remove(int amount) { + return ItemHelper.this.takeOutItem(getContainerSlot(), amount, false); + } + + @Override + public boolean mayPickup(Player playerIn) { + return !ItemHelper.this.takeOutItem(getContainerSlot(), 1, true).isEmpty(); + } + } +} diff --git a/src/main/java/thedarkcolour/exdeorum/blockentity/logic/SieveLogic.java b/src/main/java/thedarkcolour/exdeorum/blockentity/logic/SieveLogic.java new file mode 100644 index 00000000..c02c8fc2 --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/blockentity/logic/SieveLogic.java @@ -0,0 +1,226 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.blockentity.logic; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.Mth; +import net.minecraft.util.RandomSource; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.enchantment.Enchantments; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.LootParams; +import thedarkcolour.exdeorum.config.EConfig; +import thedarkcolour.exdeorum.recipe.RecipeUtil; +import thedarkcolour.exdeorum.recipe.sieve.SieveRecipe; +import thedarkcolour.exdeorum.tag.EItemTags; + +import java.util.Map; + +public class SieveLogic { + private final Owner owner; + private final boolean saveMesh; + private final boolean mechanical; + + // block currently being sifted + private ItemStack contents = ItemStack.EMPTY; + // mesh + private ItemStack mesh = ItemStack.EMPTY; + // from 0.0 to 1.0 + private float progress; + private float efficiency; + private int fortune; + + public SieveLogic(Owner owner, boolean saveMesh, boolean mechanical) { + this.owner = owner; + this.saveMesh = saveMesh; + this.mechanical = mechanical; + } + + public ItemStack getMesh() { + return this.mesh; + } + + public boolean isValidInput(ItemStack stack) { + return !RecipeUtil.getSieveRecipes(this.mesh.getItem(), stack).isEmpty(); + } + + public boolean isValidMesh(ItemStack stack) { + return stack.is(EItemTags.SIEVE_MESHES); + } + + // not pure, modifies the received stack + public void startSifting(ItemStack stack) { + this.contents = stack; + this.owner.markUpdated(); + } + + // Do not call on the client side + public void sift(float incrementProgress) { + this.progress += incrementProgress * this.efficiency; + + // Need epsilon because floating point decimals suck + if (this.progress >= 1.0f - Mth.EPSILON) { + var level = this.owner.getServerLevel(); + var context = new LootContext.Builder(new LootParams(level, Map.of(), Map.of(), 0)).create(null); + var rand = level.random; + var limitDrops = this.contents.getItem() == Items.MOSS_BLOCK && EConfig.SERVER.limitMossSieveDrops.get(); + var handledAnyDrops = false; + var hasDrops = false; + + for (SieveRecipe recipe : RecipeUtil.getSieveRecipes(this.mesh.getItem(), this.contents)) { + var amount = getResultAmount(recipe, context, rand); + + // Split overflowing stacks (64+) into multiple stacks + while (amount > 0) { + hasDrops = true; + // make a single item copy of recipe result + var result = new ItemStack(recipe.result, 1); + // the size of the stack respecting stack limits (ex. ender pearl limits to 16) + var stackAmount = Math.min(amount, recipe.result.getMaxStackSize(result)); + result.setCount(stackAmount); + amount -= stackAmount; + var handleDrop = this.owner.handleResultItem(result, level, rand); + handledAnyDrops = handledAnyDrops || handleDrop; + + // limit drops to 1 or two items (could be more than 2 but unlikely) + if (limitDrops && rand.nextInt(5) != 0) { + break; + } + } + } + + if (handledAnyDrops || !hasDrops) { + this.contents = ItemStack.EMPTY; + this.progress = 0.0f; + } else { + this.progress = 1.0f; + } + } + + this.owner.markUpdated(); + } + + protected int getResultAmount(SieveRecipe recipe, LootContext context, RandomSource rand) { + if (recipe.byHandOnly && this.mechanical) return 0; + + var amount = recipe.resultAmount.getInt(context); + + // Each level of fortune grants a 30% chance for an extra roll + for (int i = 0; i < this.fortune; ++i) { + if (rand.nextFloat() < 0.3f) { + amount += recipe.resultAmount.getInt(context); + } + } + + return amount; + } + + public void setMesh(ItemStack mesh) { + this.setMesh(mesh, true); + } + + public void setMesh(ItemStack mesh, boolean needsUpdate) { + this.mesh = mesh; + this.efficiency = 1f + mesh.getEnchantmentLevel(Enchantments.BLOCK_EFFICIENCY) * 0.17f; + this.fortune = mesh.getEnchantmentLevel(Enchantments.BLOCK_FORTUNE); + if (mesh.isEmpty()) { + this.progress = 0.0f; + this.contents = ItemStack.EMPTY; + } + if (needsUpdate) { + this.owner.markUpdated(); + } + } + + public void saveNbt(CompoundTag nbt) { + if (!this.contents.isEmpty()) { + nbt.put("contents", this.contents.serializeNBT()); + } + if (this.saveMesh && !this.mesh.isEmpty()) { + nbt.put("mesh", this.mesh.save(new CompoundTag())); + } + nbt.putFloat("progress", this.progress); + } + + public void loadNbt(CompoundTag nbt) { + if (nbt.contains("contents")) { + this.contents = ItemStack.of(nbt.getCompound("contents")); + } else { + this.contents = ItemStack.EMPTY; + } + if (nbt.getTagType("progress") == Tag.TAG_SHORT) { + this.progress = (float) nbt.getShort("progress") / 100f; + } else { + this.progress = nbt.getFloat("progress"); + } + if (this.saveMesh) { + if (nbt.contains("mesh")) { + setMesh(ItemStack.of(nbt.getCompound("mesh")), false); + } else { + setMesh(ItemStack.EMPTY, false); + } + } + } + + public ItemStack getContents() { + return this.contents; + } + + // client only + public void setContents(ItemStack contents) { + this.contents = contents; + } + + public float getProgress() { + return this.progress; + } + + // client only + public void setProgress(float progress) { + this.progress = progress; + } + + public CompoundTag writeUpdateTag() { + var tag = new CompoundTag(); + tag.putBoolean("logic", true); + tag.putFloat("progress", this.progress); + tag.put("mesh", this.mesh.serializeNBT()); + tag.put("contents", this.contents.serializeNBT()); + return tag; + } + + public void readUpdateTag(CompoundTag nbt) { + this.progress = nbt.getFloat("progress"); + this.mesh = nbt.contains("mesh") ? ItemStack.of(nbt.getCompound("mesh")) : ItemStack.EMPTY; + this.contents = nbt.contains("contents") ? ItemStack.of(nbt.getCompound("contents")) : ItemStack.EMPTY; + } + + // implement on the owner of this sieve logic + public interface Owner { + ServerLevel getServerLevel(); + + // Return whether the result item was consumed + boolean handleResultItem(ItemStack result, ServerLevel level, RandomSource rand); + + void markUpdated(); + } +} diff --git a/src/main/java/thedarkcolour/exdeorum/blockentity/package-info.java b/src/main/java/thedarkcolour/exdeorum/blockentity/package-info.java index ec76d08b..ff3d2da5 100644 --- a/src/main/java/thedarkcolour/exdeorum/blockentity/package-info.java +++ b/src/main/java/thedarkcolour/exdeorum/blockentity/package-info.java @@ -16,12 +16,6 @@ * along with this program. If not, see . */ -/** - * This package contains all data generation for Ex Deorum. It is not to be used outside of data generation. - *

- * Ex Deorum uses the ModKit library, which adds several utilities primarily for data generation - * without needing to be shaded or depended upon outside a development environment. - */ @net.minecraft.MethodsReturnNonnullByDefault @javax.annotation.ParametersAreNonnullByDefault package thedarkcolour.exdeorum.blockentity; diff --git a/src/main/java/thedarkcolour/exdeorum/client/ClientHandler.java b/src/main/java/thedarkcolour/exdeorum/client/ClientHandler.java index e3351cca..664886ed 100644 --- a/src/main/java/thedarkcolour/exdeorum/client/ClientHandler.java +++ b/src/main/java/thedarkcolour/exdeorum/client/ClientHandler.java @@ -21,6 +21,7 @@ package thedarkcolour.exdeorum.client; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.DefaultVertexFormat; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.MenuScreens; import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; import net.minecraft.client.gui.screens.worldselection.WorldCreationUiState; import net.minecraft.client.renderer.ItemBlockRenderTypes; @@ -42,6 +43,7 @@ import net.minecraftforge.fml.event.config.ModConfigEvent; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import thedarkcolour.exdeorum.ExDeorum; +import thedarkcolour.exdeorum.client.screen.MechanicalSieveScreen; import thedarkcolour.exdeorum.client.ter.BarrelRenderer; import thedarkcolour.exdeorum.client.ter.CrucibleRenderer; import thedarkcolour.exdeorum.client.ter.InfestedLeavesRenderer; @@ -52,9 +54,11 @@ import thedarkcolour.exdeorum.network.ClientMessageHandler; import thedarkcolour.exdeorum.recipe.RecipeUtil; import thedarkcolour.exdeorum.registry.EBlockEntities; import thedarkcolour.exdeorum.registry.EFluids; +import thedarkcolour.exdeorum.registry.EMenus; import thedarkcolour.exdeorum.registry.EWorldPresets; import java.io.IOException; +import java.text.DecimalFormat; public class ClientHandler { // Used for the composting recipe category in JEI @@ -95,7 +99,10 @@ public class ClientHandler { } private static void clientSetup(FMLClientSetupEvent event) { - event.enqueueWork(ClientHandler::setRenderLayers); + event.enqueueWork(() -> { + setRenderLayers(); + MenuScreens.register(EMenus.MECHANICAL_SIEVE.get(), MechanicalSieveScreen::new); + }); } private static void setRenderLayers() { @@ -128,7 +135,8 @@ public class ClientHandler { event.registerBlockEntityRenderer(EBlockEntities.BARREL.get(), BarrelRenderer::new); event.registerBlockEntityRenderer(EBlockEntities.LAVA_CRUCIBLE.get(), ctx -> new CrucibleRenderer()); event.registerBlockEntityRenderer(EBlockEntities.WATER_CRUCIBLE.get(), ctx -> new CrucibleRenderer()); - event.registerBlockEntityRenderer(EBlockEntities.SIEVE.get(), SieveRenderer::new); + event.registerBlockEntityRenderer(EBlockEntities.SIEVE.get(), ctx -> new SieveRenderer<>()); + event.registerBlockEntityRenderer(EBlockEntities.MECHANICAL_SIEVE.get(), ctx -> new SieveRenderer<>()); } private static void registerShaders(RegisterShadersEvent event) { diff --git a/src/main/java/thedarkcolour/exdeorum/client/RenderFace.java b/src/main/java/thedarkcolour/exdeorum/client/RenderFace.java index bfcc1466..19b09ee1 100644 --- a/src/main/java/thedarkcolour/exdeorum/client/RenderFace.java +++ b/src/main/java/thedarkcolour/exdeorum/client/RenderFace.java @@ -27,9 +27,7 @@ import net.minecraft.client.renderer.texture.TextureAtlasSprite; import java.util.List; public interface RenderFace { - void renderFlatSprite(MultiBufferSource buffers, PoseStack stack, float y, int r, int g, int b, int light, float edge); - - void renderFlatSpriteLerp(MultiBufferSource buffers, PoseStack stack, float percentage, int r, int g, int b, int light, float edge, float minY, float maxY); + void renderFlatSpriteLerp(MultiBufferSource buffers, PoseStack stack, float percentage, int r, int g, int b, int light, float edge, float yStart, float yEnd); boolean isMissingTexture(); @@ -37,14 +35,10 @@ public interface RenderFace { public Single(RenderType renderType, TextureAtlasSprite sprite) { this(renderType, sprite, RenderUtil.isMissingTexture(sprite)); } - @Override - public void renderFlatSprite(MultiBufferSource buffers, PoseStack stack, float y, int r, int g, int b, int light, float edge) { - RenderUtil.renderFlatSprite(buffers.getBuffer(this.renderType), stack, y, r, g, b, this.sprite, light, edge); - } @Override - public void renderFlatSpriteLerp(MultiBufferSource buffers, PoseStack stack, float percentage, int r, int g, int b, int light, float edge, float minY, float maxY) { - RenderUtil.renderFlatSpriteLerp(buffers.getBuffer(this.renderType), stack, percentage, r, g, b, this.sprite, light, edge, minY, maxY); + public void renderFlatSpriteLerp(MultiBufferSource buffers, PoseStack stack, float percentage, int r, int g, int b, int light, float edge, float yStart, float yEnd) { + RenderUtil.renderFlatSpriteLerp(buffers.getBuffer(this.renderType), stack, percentage, r, g, b, this.sprite, light, edge, yStart, yEnd); } } @@ -54,16 +48,9 @@ public interface RenderFace { } @Override - public void renderFlatSprite(MultiBufferSource buffers, PoseStack stack, float y, int r, int g, int b, int light, float edge) { + public void renderFlatSpriteLerp(MultiBufferSource buffers, PoseStack stack, float percentage, int r, int g, int b, int light, float edge, float yStart, float yEnd) { for (var layer : this.layers) { - RenderUtil.renderFlatSprite(buffers.getBuffer(layer.first()), stack, y, r, g, b, layer.second(), light, edge); - } - } - - @Override - public void renderFlatSpriteLerp(MultiBufferSource buffers, PoseStack stack, float percentage, int r, int g, int b, int light, float edge, float minY, float maxY) { - for (var layer : this.layers) { - RenderUtil.renderFlatSpriteLerp(buffers.getBuffer(layer.first()), stack, percentage, r, g, b, layer.second(), light, edge, minY, maxY); + RenderUtil.renderFlatSpriteLerp(buffers.getBuffer(layer.first()), stack, percentage, r, g, b, layer.second(), light, edge, yStart, yEnd); } } diff --git a/src/main/java/thedarkcolour/exdeorum/client/RenderUtil.java b/src/main/java/thedarkcolour/exdeorum/client/RenderUtil.java index 71f85341..1cc0f0d3 100644 --- a/src/main/java/thedarkcolour/exdeorum/client/RenderUtil.java +++ b/src/main/java/thedarkcolour/exdeorum/client/RenderUtil.java @@ -56,11 +56,12 @@ import thedarkcolour.exdeorum.client.ter.SieveRenderer; import java.awt.Color; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; -import java.util.IdentityHashMap; +import java.util.HashMap; +import java.util.Map; public class RenderUtil { private static final VarHandle COMPOSITE_MODEL_CHILDREN; - private static final IdentityHashMap TOP_FACES = new IdentityHashMap<>(); + private static final Map TOP_FACES = new HashMap<>(); public static final RenderStateShard.ShaderStateShard RENDER_TYPE_TINTED_CUTOUT_MIPPED_SHADER = new RenderStateShard.ShaderStateShard(RenderUtil::getRenderTypeTintedCutoutMippedShader); public static final RenderType TINTED_CUTOUT_MIPPED = RenderType.create(ExDeorum.ID + ":tinted_cutout_mipped", DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS, RenderType.SMALL_BUFFER_SIZE, false, false, RenderType.CompositeState.builder().setLightmapState(RenderStateShard.LIGHTMAP).setShaderState(RENDER_TYPE_TINTED_CUTOUT_MIPPED_SHADER).setTextureState(RenderStateShard.BLOCK_SHEET_MIPPED).createCompositeState(true)); public static TextureAtlas blockAtlas; diff --git a/src/main/java/thedarkcolour/exdeorum/client/screen/MechanicalSieveScreen.java b/src/main/java/thedarkcolour/exdeorum/client/screen/MechanicalSieveScreen.java new file mode 100644 index 00000000..c8d682a3 --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/client/screen/MechanicalSieveScreen.java @@ -0,0 +1,99 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.client.screen; + +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.player.Inventory; +import org.jetbrains.annotations.Nullable; +import thedarkcolour.exdeorum.ExDeorum; +import thedarkcolour.exdeorum.config.EConfig; +import thedarkcolour.exdeorum.data.TranslationKeys; +import thedarkcolour.exdeorum.menu.MechanicalSieveMenu; + +public class MechanicalSieveScreen extends AbstractContainerScreen { + private static final ResourceLocation BACKGROUND_TEXTURE = new ResourceLocation(ExDeorum.ID, "textures/gui/container/mechanical_sieve.png"); + + // Used by JEI and REI, these are bounds of the little grains texture between the mesh/input and the output slots + public static final int RECIPE_CLICK_AREA_POS_X = 51; + public static final int RECIPE_CLICK_AREA_POS_Y = 42; + public static final int RECIPE_CLICK_AREA_WIDTH = 21; + public static final int RECIPE_CLICK_AREA_HEIGHT = 14; + + private final MechanicalSieveMenu menu; + @Nullable + private RedstoneControlWidget redstoneControlWidget; + + public MechanicalSieveScreen(MechanicalSieveMenu menu, Inventory playerInventory, Component title) { + super(menu, playerInventory, title); + + this.menu = menu; + this.imageWidth = 176; + this.imageHeight = 173; + this.inventoryLabelY += 7; + } + + @Override + protected void init() { + super.init(); + + this.redstoneControlWidget = new RedstoneControlWidget(this, BACKGROUND_TEXTURE, this.leftPos + 176, this.topPos + 3); + addRenderableWidget(this.redstoneControlWidget); + } + + @Nullable + public RedstoneControlWidget getRedstoneControlWidget() { + return this.redstoneControlWidget; + } + + @Override + protected void renderBg(GuiGraphics graphics, float partialTick, int mX, int mY) { + int left = this.leftPos; + int top = this.topPos; + graphics.blit(BACKGROUND_TEXTURE, left, top, 0, 0, this.imageWidth, this.imageHeight); + + // energy bar + int energy = Mth.floor(54 * ((float) this.menu.prevSieveEnergy / EConfig.SERVER.mechanicalSieveEnergyStorage.get())); + graphics.blit(BACKGROUND_TEXTURE, left + 10, top + 22 + 54 - energy, this.imageWidth, 14 + 54 - energy, 12, energy); + + // progress arrow + int progress = Math.min(21, (int) (this.menu.sieve.getProgress() * 22)); + graphics.blit(BACKGROUND_TEXTURE, left + 51, top + 42, this.imageWidth, 0, progress, 14); + } + + @Override + public void render(GuiGraphics graphics, int mx, int my, float partialTicks) { + renderBackground(graphics); + super.render(graphics, mx, my, partialTicks); + renderTooltip(graphics, mx, my); + + int rx = mx - this.leftPos; + int ry = my - this.topPos; + + if (9 <= rx && rx < 23 && 21 <= ry && ry < 77) { + var energyTooltip = Component.translatable(TranslationKeys.ENERGY).append(Component.translatable(TranslationKeys.FRACTION_DISPLAY, this.menu.prevSieveEnergy, EConfig.SERVER.mechanicalSieveEnergyStorage.get())).append(" FE"); + graphics.renderTooltip(Minecraft.getInstance().font, energyTooltip, mx, my); + } + } +} diff --git a/src/main/java/thedarkcolour/exdeorum/client/screen/RedstoneControlWidget.java b/src/main/java/thedarkcolour/exdeorum/client/screen/RedstoneControlWidget.java new file mode 100644 index 00000000..35afd1aa --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/client/screen/RedstoneControlWidget.java @@ -0,0 +1,215 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.client.screen; + +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Renderable; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.client.gui.narration.NarrationElementOutput; +import net.minecraft.client.renderer.Rect2i; +import net.minecraft.client.resources.sounds.SimpleSoundInstance; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvents; +import thedarkcolour.exdeorum.data.TranslationKeys; + +import java.util.List; + +public class RedstoneControlWidget implements GuiEventListener, NarratableEntry, Renderable { + public static final int REDSTONE_MODE_IGNORED = 0; + public static final int REDSTONE_MODE_UNPOWERED = 1; + public static final int REDSTONE_MODE_POWERED = 2; + + private static final Component[] REDSTONE_MODES = new Component[] { + Component.translatable(TranslationKeys.REDSTONE_CONTROL_MODES[REDSTONE_MODE_IGNORED]).withStyle(ChatFormatting.YELLOW), + Component.translatable(TranslationKeys.REDSTONE_CONTROL_MODES[REDSTONE_MODE_UNPOWERED]).withStyle(ChatFormatting.GRAY), + Component.translatable(TranslationKeys.REDSTONE_CONTROL_MODES[REDSTONE_MODE_POWERED]).withStyle(ChatFormatting.WHITE), + }; + private static final Component REDSTONE_CONTROL_LABEL = Component.translatable(TranslationKeys.REDSTONE_CONTROL_LABEL); + + private final MechanicalSieveScreen screen; + private final ResourceLocation texture; + private final int posX; + private final int posY; + private final int tabU; + private final int tabV; + private final int tabWidth; + private final int tabHeight; + private final int expandedV; + private final int expandedU; + private final int expandedWidth; + private final int expandedHeight; + private final int buttonsPosX; + private final int buttonsPosY; + + // Percentage of the widget currently being displayed + private float percentage = 0.0f; + // Whether full widget is displayed + private boolean expanded = false; + // Last time (from currentTimeMillis) this button was clicked, used in animation lerp + private long lastClicked = -1L; + + public RedstoneControlWidget(MechanicalSieveScreen screen, ResourceLocation texture, int posX, int posY) { + this.screen = screen; + this.texture = texture; + this.posX = posX; + this.posY = posY; + + this.expandedU = 0; + this.expandedV = 189; + this.expandedWidth = 94; + this.expandedHeight = 67; + this.tabU = this.expandedWidth; + this.tabV = 189; + this.tabWidth = 26; + this.tabHeight = 28; + this.buttonsPosX = this.posX + 18; + this.buttonsPosY = this.posY + 44; + } + + @Override + public void render(GuiGraphics graphics, int mx, int my, float pPartialTick) { + if (this.lastClicked != -1L) { + // animation is 200 ms + this.percentage = (System.currentTimeMillis() - this.lastClicked) / 200.0f; + + if (this.percentage >= 1.0f) { + this.percentage = 0.0f; + this.expanded = !this.expanded; + this.lastClicked = -1L; + } else { + drawPartialConfig(graphics); + return; + } + } + + // Otherwise, render the full widget or the tab only + var font = Minecraft.getInstance().font; + + if (this.expanded) { + var redstoneMode = this.screen.getMenu().sieve.getRedstoneMode(); + graphics.blit(this.texture, this.posX, this.posY, this.expandedU, this.expandedV, this.expandedWidth, this.expandedHeight); + for (int i = 0; i < 3; ++i) { + graphics.blit(this.texture, this.buttonsPosX + (i * 19), this.buttonsPosY, (redstoneMode == i ? this.tabU + 16 : this.tabU), this.tabV + this.tabHeight, 16, 16); + } + graphics.blit(this.texture, this.buttonsPosX, this.buttonsPosY, this.tabU, this.tabV + this.tabHeight + 16, 52, 14); + + graphics.drawString(font, Component.translatable(TranslationKeys.REDSTONE_CONTROL_LABEL), this.posX + 16, this.posY + 10, 0xffffff); + // The label + graphics.drawString(font, Component.translatable(TranslationKeys.REDSTONE_CONTROL_MODE).append(REDSTONE_MODES[redstoneMode]), this.posX + 4, this.posY + 26, 0xffffff); + } else { + graphics.blit(this.texture, this.posX, this.posY, this.tabU, this.tabV, this.tabWidth, this.tabHeight); + + if (this.posX <= mx && mx < this.posX + this.tabWidth && this.posY <= my && my < this.posY + this.tabHeight) { + graphics.renderTooltip(font, REDSTONE_CONTROL_LABEL, mx, my); + } + } + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + // relative xy + int mx = (int) mouseX; + int my = (int) mouseY; + var rx = mx - this.posX; + var ry = my - this.posY; + + float percentage = this.expanded ? 1.0f - this.percentage : this.percentage; + + if (0 <= rx && rx < getWidth(percentage) && 0 <= ry && ry < getHeight(percentage)) { + if (this.expanded) { + if (this.buttonsPosY <= my && my < this.buttonsPosY + 16) { + for (int i = 0; i < 3; ++i) { + int buttonStartX = this.buttonsPosX + (i * 19); + + if (buttonStartX <= mx && mx < buttonStartX + 16) { + setRedstoneMode(i); + Minecraft.getInstance().getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.05f)); + return true; + } + } + } + } + if (this.lastClicked == -1L && rx < this.tabWidth && ry < this.tabHeight) { + this.lastClicked = System.currentTimeMillis(); + Minecraft.getInstance().getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0f)); + } + } + return false; + } + + private void setRedstoneMode(int redstoneMode) { + this.screen.getMenu().clickMenuButton(Minecraft.getInstance().player, redstoneMode); + Minecraft.getInstance().gameMode.handleInventoryButtonClick(this.screen.getMenu().containerId, redstoneMode); + } + + private void drawPartialConfig(GuiGraphics graphics) { + float percentage = this.expanded ? 1.0f - this.percentage : this.percentage; + // top left without edge + int width = getWidth(percentage) - 3; + int height = getHeight(percentage) - 3; + // edge + int edgeU = this.expandedU + this.expandedWidth - 3; + int edgeV = this.expandedV + this.expandedHeight - 3; + + // top left section (no edges) + graphics.blit(this.texture, this.posX, this.posY, this.expandedU, this.expandedV, width, height); + // bottom edge + graphics.blit(this.texture, this.posX, this.posY + height, this.expandedU, edgeV, width, 3); + // right edge + graphics.blit(this.texture, this.posX + width, this.posY, edgeU, this.expandedV, 3, height); + // bottom right corner + graphics.blit(this.texture, this.posX + width, this.posY + height, edgeU, edgeV, 3, 3); + } + + public int getWidth(float percentage) { + return this.tabWidth + Math.round((this.expandedWidth - this.tabWidth) * percentage); + } + + public int getHeight(float percentage) { + return this.tabHeight + Math.round((this.expandedHeight - this.tabHeight) * percentage); + } + + @Override + public void setFocused(boolean pFocused) { + + } + + @Override + public boolean isFocused() { + return false; + } + + @Override + public NarrationPriority narrationPriority() { + return NarrationPriority.NONE; + } + + @Override + public void updateNarration(NarrationElementOutput narration) { + } + + public List getJeiBounds() { + float percentage = this.expanded ? 1.0f - this.percentage : this.percentage; + return List.of(new Rect2i(this.posX, this.posY, getWidth(percentage), getHeight(percentage))); + } +} diff --git a/src/main/java/thedarkcolour/exdeorum/client/screen/package-info.java b/src/main/java/thedarkcolour/exdeorum/client/screen/package-info.java new file mode 100644 index 00000000..f1fe772a --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/client/screen/package-info.java @@ -0,0 +1,21 @@ +/* + * Ex Deorum + * Copyright (c) 2023 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +@net.minecraft.MethodsReturnNonnullByDefault +@javax.annotation.ParametersAreNonnullByDefault +package thedarkcolour.exdeorum.client.screen; diff --git a/src/main/java/thedarkcolour/exdeorum/client/ter/InfestedLeavesRenderer.java b/src/main/java/thedarkcolour/exdeorum/client/ter/InfestedLeavesRenderer.java index 00e9f8fe..1fbb9d1a 100644 --- a/src/main/java/thedarkcolour/exdeorum/client/ter/InfestedLeavesRenderer.java +++ b/src/main/java/thedarkcolour/exdeorum/client/ter/InfestedLeavesRenderer.java @@ -50,6 +50,7 @@ public class InfestedLeavesRenderer implements BlockEntityRenderer { - public static final Map MESH_TEXTURES = new IdentityHashMap<>(); - - public SieveRenderer(BlockEntityRendererProvider.Context ctx) {} +public class SieveRenderer implements BlockEntityRenderer { + public static final Map MESH_TEXTURES = new HashMap<>(); @Override - public void render(SieveBlockEntity sieve, float partialTicks, PoseStack stack, MultiBufferSource buffers, int light, int overlay) { + public void render(T sieve, float partialTicks, PoseStack stack, MultiBufferSource buffers, int light, int overlay) { var contents = sieve.getContents(); if (!contents.isEmpty() && contents.getItem() instanceof BlockItem blockItem) { var block = blockItem.getBlock(); - var percentage = (float) sieve.getProgress() / 100.0f; + var percentage = sieve.getProgress(); var face = RenderUtil.getTopFace(block); - face.renderFlatSpriteLerp(buffers, stack, percentage, 0xff, 0xff, 0xff, light, 1.0f, 13f, 15f); + face.renderFlatSpriteLerp(buffers, stack, percentage, 0xff, 0xff, 0xff, light, 1.0f, 15f, 13f); } var mesh = sieve.getMesh(); @@ -56,20 +53,20 @@ public class SieveRenderer implements BlockEntityRenderer { var builder = buffers.getBuffer(RenderType.cutoutMipped()); var meshItem = mesh.getItem(); - TextureAtlasSprite sprite; + TextureAtlasSprite meshSprite; if (MESH_TEXTURES.containsKey(meshItem)) { - sprite = MESH_TEXTURES.get(meshItem); + meshSprite = MESH_TEXTURES.get(meshItem); } else { ResourceLocation registryName = ForgeRegistries.ITEMS.getKey(meshItem); ResourceLocation textureLoc = registryName.withPrefix("item/mesh/"); - sprite = RenderUtil.blockAtlas.getSprite(textureLoc); - MESH_TEXTURES.put(meshItem, sprite); + meshSprite = RenderUtil.blockAtlas.getSprite(textureLoc); + MESH_TEXTURES.put(meshItem, meshSprite); } - RenderUtil.renderFlatSprite(builder, stack, 0.75f, 0xff, 0xff, 0xff, sprite, light, 1f); + RenderUtil.renderFlatSprite(builder, stack, 0.75f, 0xff, 0xff, 0xff, meshSprite, light, 1f); if (mesh.hasFoil()) { - RenderUtil.renderFlatSprite(buffers.getBuffer(RenderType.glint()), stack, 0.75f, 0xff, 0xff, 0xff, sprite, light, 1f); + RenderUtil.renderFlatSprite(buffers.getBuffer(RenderType.glint()), stack, 0.75f, 0xff, 0xff, 0xff, meshSprite, light, 1f); } } } diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/JeiSieveRecipeGroup.java b/src/main/java/thedarkcolour/exdeorum/compat/GroupedSieveRecipe.java similarity index 82% rename from src/main/java/thedarkcolour/exdeorum/compat/jei/JeiSieveRecipeGroup.java rename to src/main/java/thedarkcolour/exdeorum/compat/GroupedSieveRecipe.java index bee1ab03..d4af6e9e 100644 --- a/src/main/java/thedarkcolour/exdeorum/compat/jei/JeiSieveRecipeGroup.java +++ b/src/main/java/thedarkcolour/exdeorum/compat/GroupedSieveRecipe.java @@ -1,6 +1,6 @@ /* * Ex Deorum - * Copyright (c) 2023 thedarkcolour + * Copyright (c) 2024 thedarkcolour * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,13 +16,11 @@ * along with this program. If not, see . */ -package thedarkcolour.exdeorum.compat.jei; +package thedarkcolour.exdeorum.compat; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; -import mezz.jei.api.recipe.RecipeType; -import mezz.jei.api.registration.IRecipeRegistration; import net.minecraft.client.Minecraft; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.util.Mth; @@ -41,10 +39,11 @@ import java.util.Comparator; import java.util.List; import java.util.Objects; -record JeiSieveRecipeGroup(Ingredient ingredient, ItemStack mesh, List results) { +// Since no JEI code is used here, this can be reused for REI +public record GroupedSieveRecipe(Ingredient ingredient, ItemStack mesh, List results) { public static int maxSieveRows; - public static void addGroupedRecipes(IRecipeRegistration registration, RecipeType recipeType) { + public static ImmutableList getAllRecipesGrouped() { maxSieveRows = 1; // copy the list so we can do removals @@ -67,11 +66,11 @@ record JeiSieveRecipeGroup(Ingredient ingredient, ItemStack mesh, List r } } - ImmutableList.Builder jeiRecipes = new ImmutableList.Builder<>(); + ImmutableList.Builder jeiRecipes = new ImmutableList.Builder<>(); // Sort based on expected count of result var resultSorter = Comparator.comparingDouble(Result::expectedCount).reversed(); // Sort based on order of sieve tier - var meshSorter = Comparator.comparingInt(JeiSieveRecipeGroup::meshOrder); + var meshSorter = Comparator.comparingInt(GroupedSieveRecipe::meshOrder); // ingredients with common ingredients are grouped into lists (ex. dirt) for (var ingredient : ingredientGrouper.keySet()) { @@ -93,12 +92,12 @@ record JeiSieveRecipeGroup(Ingredient ingredient, ItemStack mesh, List r for (var recipe : meshRecipes) { int resultCount = recipe.resultAmount instanceof ConstantValue constant ? Math.round(constant.value) : 1; - results.add(new Result(new ItemStack(recipe.result, resultCount), recipe.resultAmount)); + results.add(new Result(new ItemStack(recipe.result, resultCount), recipe.resultAmount, recipe.byHandOnly)); } results.sort(resultSorter); - var jeiRecipe = new JeiSieveRecipeGroup(ingredient, new ItemStack(mesh), results); + var jeiRecipe = new GroupedSieveRecipe(ingredient, new ItemStack(mesh), results); jeiRecipes.add(jeiRecipe); var rows = Mth.ceil((float) meshRecipes.size() / 9f); @@ -107,10 +106,10 @@ record JeiSieveRecipeGroup(Ingredient ingredient, ItemStack mesh, List r } } } - - registration.addRecipes(recipeType, jeiRecipes.build()); + return jeiRecipes.build(); } + @SuppressWarnings("deprecation") private static int meshOrder(Item mesh) { if (mesh == EItems.STRING_MESH.get()) { return -5; @@ -129,14 +128,16 @@ record JeiSieveRecipeGroup(Ingredient ingredient, ItemStack mesh, List r } } - static final class Result { - final ItemStack item; - final NumberProvider provider; - final double expectedCount; + public static final class Result { + public final ItemStack item; + public final NumberProvider provider; + public final boolean byHandOnly; + private final double expectedCount; - Result(ItemStack item, NumberProvider provider) { + Result(ItemStack item, NumberProvider provider, boolean byHandOnly) { this.item = item; this.provider = provider; + this.byHandOnly = byHandOnly; this.expectedCount = RecipeUtil.getExpectedValue(this.provider); } diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/BarrelCompostCategory.java b/src/main/java/thedarkcolour/exdeorum/compat/jei/BarrelCompostCategory.java index 9f7977ce..53701e79 100644 --- a/src/main/java/thedarkcolour/exdeorum/compat/jei/BarrelCompostCategory.java +++ b/src/main/java/thedarkcolour/exdeorum/compat/jei/BarrelCompostCategory.java @@ -18,7 +18,6 @@ package thedarkcolour.exdeorum.compat.jei; -import com.mojang.blaze3d.platform.Lighting; import com.mojang.blaze3d.systems.RenderSystem; import mezz.jei.api.gui.builder.IRecipeLayoutBuilder; import mezz.jei.api.gui.drawable.IDrawable; @@ -28,16 +27,10 @@ import mezz.jei.api.recipe.IFocusGroup; import mezz.jei.api.recipe.RecipeIngredientRole; import mezz.jei.api.recipe.RecipeType; import mezz.jei.api.recipe.category.IRecipeCategory; -import net.minecraft.CrashReport; -import net.minecraft.CrashReportCategory; -import net.minecraft.ReportedException; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.network.chat.Component; -import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.ItemStack; -import org.joml.Matrix4f; import thedarkcolour.exdeorum.client.ClientHandler; import thedarkcolour.exdeorum.data.TranslationKeys; import thedarkcolour.exdeorum.recipe.barrel.BarrelCompostRecipe; @@ -86,7 +79,7 @@ class BarrelCompostCategory implements IRecipeCategory { @Override public void draw(BarrelCompostRecipe recipe, IRecipeSlotsView recipeSlotsView, GuiGraphics graphics, double mouseX, double mouseY) { - slot.draw(graphics); + this.slot.draw(graphics); var volume = recipe.getVolume(); var volumeLabel = Component.translatable(TranslationKeys.BARREL_COMPOST_RECIPE_VOLUME, volume); @@ -112,40 +105,10 @@ class BarrelCompostCategory implements IRecipeCategory { // From mezz.jei.library.render.ItemStackRenderer RenderSystem.enableDepthTest(); - // From GuiGraphics.renderFakeItem Minecraft mc = Minecraft.getInstance(); var model = mc.getModelManager().getModel(ClientHandler.OAK_BARREL_COMPOSTING); - var pose = guiGraphics.pose(); - pose.pushPose(); - pose.translate(8 + xOffset, 8 + yOffset, 150); - - try { - pose.mulPoseMatrix((new Matrix4f()).scaling(1.0F, -1.0F, 1.0F)); - pose.scale(16f, 16f, 16f); - boolean flag = !model.usesBlockLight(); - if (flag) { - Lighting.setupForFlatItems(); - } - - mc.getItemRenderer().render(oakBarrel, ItemDisplayContext.GUI, false, pose, guiGraphics.bufferSource(), 0xf000f0, OverlayTexture.NO_OVERLAY, model); - guiGraphics.flush(); - if (flag) { - Lighting.setupFor3DItems(); - } - } catch (Throwable throwable) { - CrashReport crashreport = CrashReport.forThrowable(throwable, "Rendering item"); - CrashReportCategory crashreportcategory = crashreport.addCategory("Item being rendered"); - crashreportcategory.setDetail("Item Type", () -> { - return String.valueOf(oakBarrel.getItem()); - }); - crashreportcategory.setDetail("Registry Name", () -> "exdeorum:oak_barrel"); - throw new ReportedException(crashreport); - } - - pose.popPose(); - - // From end of ItemStackRenderer - RenderSystem.disableBlend(); + // From GuiGraphics.renderFakeItem + ClientJeiUtil.renderItemAlternativeModel(guiGraphics, model, this.oakBarrel, xOffset, yOffset); // From end of DrawableIngredient RenderSystem.disableDepthTest(); } diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/ClientJeiUtil.java b/src/main/java/thedarkcolour/exdeorum/compat/jei/ClientJeiUtil.java index 6e1ed1e8..9394cb0b 100644 --- a/src/main/java/thedarkcolour/exdeorum/compat/jei/ClientJeiUtil.java +++ b/src/main/java/thedarkcolour/exdeorum/compat/jei/ClientJeiUtil.java @@ -24,17 +24,29 @@ import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.Tesselator; import com.mojang.math.Axis; +import mezz.jei.api.gui.ingredient.IRecipeSlotTooltipCallback; +import mezz.jei.api.gui.ingredient.IRecipeSlotView; +import mezz.jei.api.ingredients.IIngredientRenderer; +import net.minecraft.ChatFormatting; +import net.minecraft.CrashReport; +import net.minecraft.CrashReportCategory; +import net.minecraft.ReportedException; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.ItemBlockRenderTypes; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.Sheets; import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.client.resources.model.BakedModel; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; -import net.minecraft.world.inventory.InventoryMenu; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemDisplayContext; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.ColorResolver; import net.minecraft.world.level.LightLayer; @@ -45,11 +57,17 @@ import net.minecraft.world.level.lighting.LevelLightEngine; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.Fluids; import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4f; + +import java.util.ArrayList; +import java.util.List; class ClientJeiUtil { private static final FluidState EMPTY = Fluids.EMPTY.defaultFluidState(); private static final BlockState AIR = Blocks.AIR.defaultBlockState(); + private static final IIngredientRenderer ASTERISK_RENDERER = AsteriskRenderer.INSTANCE; + // https://github.com/way2muchnoise/JustEnoughResources/blob/89ee40ff068c8d6eb6ab103f76381445691cffc9/Common/src/main/java/jeresources/util/RenderHelper.java#L100 static void renderBlock(GuiGraphics guiGraphics, BlockState block, float x, float y, float z, float scale) { Minecraft mc = Minecraft.getInstance(); @@ -105,6 +123,53 @@ class ClientJeiUtil { poseStack.popPose(); } + static void renderItemWithAsterisk(GuiGraphics graphics, ItemStack stack, int xOffset, int yOffset) { + Minecraft mc = Minecraft.getInstance(); + BakedModel model = mc.getItemRenderer().getModel(stack, mc.level, null, 0); + renderItemAlternativeModel(graphics, model, stack, xOffset, yOffset); + graphics.pose().pushPose(); + graphics.pose().translate(0f, 0f, 200f); + // 0xff5555 is Minecraft's red text color. + graphics.drawString(mc.font, "*", xOffset + 19 - 2 - mc.font.width("*"), yOffset + 12, 0xff5555, true); + graphics.pose().popPose(); + } + + static void renderItemAlternativeModel(GuiGraphics graphics, BakedModel model, ItemStack stack, int xOffset, int yOffset) { + Minecraft mc = Minecraft.getInstance(); + + var pose = graphics.pose(); + pose.pushPose(); + pose.translate(8 + xOffset, 8 + yOffset, 150); + + try { + pose.mulPoseMatrix((new Matrix4f()).scaling(1.0F, -1.0F, 1.0F)); + pose.scale(16f, 16f, 16f); + boolean flag = !model.usesBlockLight(); + if (flag) { + Lighting.setupForFlatItems(); + } + + mc.getItemRenderer().render(stack, ItemDisplayContext.GUI, false, pose, graphics.bufferSource(), 0xf000f0, OverlayTexture.NO_OVERLAY, model); + graphics.flush(); + if (flag) { + Lighting.setupFor3DItems(); + } + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.forThrowable(throwable, "Rendering item"); + CrashReportCategory crashreportcategory = crashreport.addCategory("Item being rendered"); + crashreportcategory.setDetail("Item Type", () -> { + return String.valueOf(stack.getItem()); + }); + crashreportcategory.setDetail("Registry Name", () -> BuiltInRegistries.ITEM.getKey(stack.getItem()).toString()); + throw new ReportedException(crashreport); + } + + pose.popPose(); + + // From end of ItemStackRenderer + RenderSystem.disableBlend(); + } + private enum Dummy implements BlockAndTintGetter { INSTANCE; @@ -162,4 +227,32 @@ class ClientJeiUtil { return 0; } } + + enum AsteriskRenderer implements IIngredientRenderer { + INSTANCE; + + @Override + public void render(GuiGraphics graphics, ItemStack ingredient) { + // From mezz.jei.library.render.ItemStackRenderer + RenderSystem.enableDepthTest(); + ClientJeiUtil.renderItemWithAsterisk(graphics, ingredient, 0, 0); + // From end of DrawableIngredient + RenderSystem.disableDepthTest(); + } + + @Override + public List getTooltip(ItemStack ingredient, TooltipFlag tooltipFlag) { + // Copied from ItemStackRenderer + Minecraft minecraft = Minecraft.getInstance(); + Player player = minecraft.player; + try { + return ingredient.getTooltipLines(player, tooltipFlag); + } catch (RuntimeException | LinkageError e) { + List list = new ArrayList<>(); + MutableComponent crash = Component.translatable("jei.tooltip.error.crash"); + list.add(crash.withStyle(ChatFormatting.RED)); + return list; + } + } + } } diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/ExDeorumJeiPlugin.java b/src/main/java/thedarkcolour/exdeorum/compat/jei/ExDeorumJeiPlugin.java index 40f6af1b..368baec2 100644 --- a/src/main/java/thedarkcolour/exdeorum/compat/jei/ExDeorumJeiPlugin.java +++ b/src/main/java/thedarkcolour/exdeorum/compat/jei/ExDeorumJeiPlugin.java @@ -24,12 +24,15 @@ import mezz.jei.api.IModPlugin; import mezz.jei.api.JeiPlugin; import mezz.jei.api.constants.VanillaTypes; import mezz.jei.api.forge.ForgeTypes; +import mezz.jei.api.gui.handlers.IGuiClickableArea; +import mezz.jei.api.gui.handlers.IGuiContainerHandler; import mezz.jei.api.recipe.RecipeType; +import mezz.jei.api.registration.IGuiHandlerRegistration; import mezz.jei.api.registration.IRecipeCatalystRegistration; import mezz.jei.api.registration.IRecipeCategoryRegistration; import mezz.jei.api.registration.IRecipeRegistration; -import mezz.jei.api.registration.IVanillaCategoryExtensionRegistration; import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.Rect2i; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.Container; @@ -44,8 +47,9 @@ import net.minecraft.world.level.block.WallTorchBlock; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fml.ModList; import thedarkcolour.exdeorum.ExDeorum; -import thedarkcolour.exdeorum.block.SieveBlock; import thedarkcolour.exdeorum.blockentity.LavaCrucibleBlockEntity; +import thedarkcolour.exdeorum.client.screen.MechanicalSieveScreen; +import thedarkcolour.exdeorum.compat.GroupedSieveRecipe; import thedarkcolour.exdeorum.compat.ModIds; import thedarkcolour.exdeorum.data.TranslationKeys; import thedarkcolour.exdeorum.item.WateringCanItem; @@ -62,6 +66,7 @@ import thedarkcolour.exdeorum.registry.ERecipeTypes; import thedarkcolour.exdeorum.tag.EItemTags; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.function.Supplier; @@ -76,7 +81,7 @@ public class ExDeorumJeiPlugin implements IModPlugin { static final RecipeType LAVA_CRUCIBLE = RecipeType.create(ExDeorum.ID, "lava_crucible", CrucibleRecipe.class); static final RecipeType WATER_CRUCIBLE = RecipeType.create(ExDeorum.ID, "water_crucible", CrucibleRecipe.class); static final RecipeType CRUCIBLE_HEAT_SOURCES = RecipeType.create(ExDeorum.ID, "crucible_heat_sources", CrucibleHeatSourceRecipe.class); - static final RecipeType SIEVE = RecipeType.create(ExDeorum.ID, "sieve", JeiSieveRecipeGroup.class); + static final RecipeType SIEVE = RecipeType.create(ExDeorum.ID, "sieve", GroupedSieveRecipe.class); static final RecipeType HAMMER = RecipeType.create(ExDeorum.ID, "hammer", HammerRecipe.class); @Override @@ -114,7 +119,8 @@ public class ExDeorumJeiPlugin implements IModPlugin { EItems.CHERRY_SIEVE.get(), EItems.BAMBOO_SIEVE.get(), EItems.CRIMSON_SIEVE.get(), - EItems.WARPED_SIEVE.get() + EItems.WARPED_SIEVE.get(), + EItems.MECHANICAL_SIEVE.get() ); var lavaCrucibles = Lists.newArrayList( EItems.PORCELAIN_CRUCIBLE.get(), @@ -214,6 +220,7 @@ public class ExDeorumJeiPlugin implements IModPlugin { public void registerRecipes(IRecipeRegistration registration) { registration.addItemStackInfo(new ItemStack(EItems.SILK_WORM.get()), Component.translatable(TranslationKeys.SILK_WORM_JEI_INFO)); registration.addItemStackInfo(List.of(new ItemStack(EBlocks.OAK_SIEVE.get()), new ItemStack(EBlocks.SPRUCE_SIEVE.get()), new ItemStack(EBlocks.BIRCH_SIEVE.get()), new ItemStack(EBlocks.JUNGLE_SIEVE.get()), new ItemStack(EBlocks.ACACIA_SIEVE.get()), new ItemStack(EBlocks.DARK_OAK_SIEVE.get()), new ItemStack(EBlocks.MANGROVE_SIEVE.get()), new ItemStack(EBlocks.CHERRY_SIEVE.get()), new ItemStack(EBlocks.BAMBOO_SIEVE.get()), new ItemStack(EBlocks.CRIMSON_SIEVE.get()), new ItemStack(EBlocks.WARPED_SIEVE.get())), Component.translatable(TranslationKeys.SIEVE_JEI_INFO)); + registration.addItemStackInfo(List.of(new ItemStack(EItems.STRING_MESH.get()), new ItemStack(EItems.STRING_MESH.get()), new ItemStack(EItems.FLINT_MESH.get()), new ItemStack(EItems.IRON_MESH.get()), new ItemStack(EItems.GOLDEN_MESH.get()), new ItemStack(EItems.DIAMOND_MESH.get()), new ItemStack(EItems.NETHERITE_MESH.get())), Component.translatable(TranslationKeys.SIEVE_MESH_JEI_INFO)); registration.addItemStackInfo(List.of(WateringCanItem.getFull(EItems.WOODEN_WATERING_CAN), WateringCanItem.getFull(EItems.STONE_WATERING_CAN), WateringCanItem.getFull(EItems.IRON_WATERING_CAN), WateringCanItem.getFull(EItems.GOLDEN_WATERING_CAN), WateringCanItem.getFull(EItems.DIAMOND_WATERING_CAN), WateringCanItem.getFull(EItems.NETHERITE_WATERING_CAN)), Component.translatable(TranslationKeys.WATERING_CAN_JEI_INFO)); var witchWaterInfo = Component.translatable(TranslationKeys.WITCH_WATER_JEI_INFO); registration.addItemStackInfo(List.of(new ItemStack(EItems.WITCH_WATER_BUCKET.get()), new ItemStack(EItems.PORCELAIN_WITCH_WATER_BUCKET.get())), witchWaterInfo); @@ -223,6 +230,7 @@ public class ExDeorumJeiPlugin implements IModPlugin { registration.addItemStackInfo(new ItemStack(EItems.WARPED_NYLIUM_SPORES.get()), Component.translatable(TranslationKeys.WARPED_NYLIUM_SPORES_JEI_INFO)); registration.addItemStackInfo(new ItemStack(EItems.CRIMSON_NYLIUM_SPORES.get()), Component.translatable(TranslationKeys.CRIMSON_NYLIUM_SPORES_JEI_INFO)); registration.addItemStackInfo(new ItemStack(EItems.SCULK_CORE.get()), Component.translatable(TranslationKeys.SCULK_CORE_JEI_INFO)); + registration.addItemStackInfo(new ItemStack(EItems.MECHANICAL_SIEVE.get()), Component.translatable(TranslationKeys.MECHANICAL_SIEVE_JEI_INFO)); var toRemove = new ArrayList(); @@ -250,7 +258,7 @@ public class ExDeorumJeiPlugin implements IModPlugin { addRecipes(registration, LAVA_CRUCIBLE, ERecipeTypes.LAVA_CRUCIBLE); addRecipes(registration, WATER_CRUCIBLE, ERecipeTypes.WATER_CRUCIBLE); addRecipes(registration, HAMMER, ERecipeTypes.HAMMER); - JeiSieveRecipeGroup.addGroupedRecipes(registration, SIEVE); + registration.addRecipes(SIEVE, GroupedSieveRecipe.getAllRecipesGrouped()); addCrucibleHeatSources(registration); } @@ -296,8 +304,24 @@ public class ExDeorumJeiPlugin implements IModPlugin { } @Override - public void registerVanillaCategoryExtensions(IVanillaCategoryExtensionRegistration registration) { - IModPlugin.super.registerVanillaCategoryExtensions(registration); + public void registerGuiHandlers(IGuiHandlerRegistration registration) { + // see addRecipeClickArea for reference + registration.addGuiContainerHandler(MechanicalSieveScreen.class, new IGuiContainerHandler<>() { + @Override + public Collection getGuiClickableAreas(MechanicalSieveScreen containerScreen, double mouseX, double mouseY) { + IGuiClickableArea clickableArea = IGuiClickableArea.createBasic(MechanicalSieveScreen.RECIPE_CLICK_AREA_POS_X, MechanicalSieveScreen.RECIPE_CLICK_AREA_POS_Y, MechanicalSieveScreen.RECIPE_CLICK_AREA_WIDTH, MechanicalSieveScreen.RECIPE_CLICK_AREA_HEIGHT, SIEVE); + return List.of(clickableArea); + } + + @Override + public List getGuiExtraAreas(MechanicalSieveScreen containerScreen) { + var widget = containerScreen.getRedstoneControlWidget(); + if (widget != null) { + return widget.getJeiBounds(); + } + return List.of(); + } + }); } private static > void addRecipes(IRecipeRegistration registration, RecipeType category, Supplier> type) { @@ -353,7 +377,8 @@ public class ExDeorumJeiPlugin implements IModPlugin { EItems.CHERRY_SIEVE.get(), EItems.BAMBOO_SIEVE.get(), EItems.CRIMSON_SIEVE.get(), - EItems.WARPED_SIEVE.get() + EItems.WARPED_SIEVE.get(), + EItems.MECHANICAL_SIEVE.get() ); return sieves; diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/HammerCategory.java b/src/main/java/thedarkcolour/exdeorum/compat/jei/HammerCategory.java index 3ba11add..2483c4bd 100644 --- a/src/main/java/thedarkcolour/exdeorum/compat/jei/HammerCategory.java +++ b/src/main/java/thedarkcolour/exdeorum/compat/jei/HammerCategory.java @@ -46,6 +46,6 @@ class HammerCategory extends OneToOneCategory { @Override protected void addOutput(IRecipeSlotBuilder slot, HammerRecipe recipe) { slot.addItemStack(new ItemStack(recipe.result)); - SieveCategory.addTooltips(slot, recipe.resultAmount); + SieveCategory.addTooltips(slot, false, recipe.resultAmount); } } diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/SieveCategory.java b/src/main/java/thedarkcolour/exdeorum/compat/jei/SieveCategory.java index 4dd1b0b2..e24784f5 100644 --- a/src/main/java/thedarkcolour/exdeorum/compat/jei/SieveCategory.java +++ b/src/main/java/thedarkcolour/exdeorum/compat/jei/SieveCategory.java @@ -18,6 +18,8 @@ package thedarkcolour.exdeorum.compat.jei; +import com.google.common.collect.ImmutableList; +import mezz.jei.api.constants.VanillaTypes; import mezz.jei.api.gui.builder.IRecipeLayoutBuilder; import mezz.jei.api.gui.builder.IRecipeSlotBuilder; import mezz.jei.api.gui.drawable.IDrawable; @@ -36,14 +38,17 @@ import net.minecraft.world.level.storage.loot.providers.number.ConstantValue; import net.minecraft.world.level.storage.loot.providers.number.NumberProvider; import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator; import net.minecraftforge.common.util.Lazy; +import thedarkcolour.exdeorum.compat.GroupedSieveRecipe; import thedarkcolour.exdeorum.data.TranslationKeys; import thedarkcolour.exdeorum.recipe.RecipeUtil; import thedarkcolour.exdeorum.registry.EBlocks; import java.text.DecimalFormat; -class SieveCategory implements IRecipeCategory { +class SieveCategory implements IRecipeCategory { private static final DecimalFormat FORMATTER = new DecimalFormat(); + private static final Component BY_HAND_ONLY_LABEL = Component.translatable(TranslationKeys.SIEVE_RECIPE_BY_HAND_ONLY).withStyle(ChatFormatting.RED); + public static final int WIDTH = 162; public static final int ROW_START = 28; @@ -59,7 +64,7 @@ class SieveCategory implements IRecipeCategory { private final Component title; public SieveCategory(IGuiHelper helper) { - this.background = Lazy.of(() -> helper.createBlankDrawable(WIDTH, ROW_START + 18 * JeiSieveRecipeGroup.maxSieveRows)); + this.background = Lazy.of(() -> helper.createBlankDrawable(WIDTH, ROW_START + 18 * GroupedSieveRecipe.maxSieveRows)); this.slot = helper.getSlotDrawable(); this.row = helper.createDrawable(ExDeorumJeiPlugin.EX_DEORUM_JEI_TEXTURE, 0, 0, 162, 18); this.icon = helper.createDrawableItemStack(new ItemStack(EBlocks.OAK_SIEVE.get())); @@ -67,7 +72,7 @@ class SieveCategory implements IRecipeCategory { } @Override - public RecipeType getRecipeType() { + public RecipeType getRecipeType() { return ExDeorumJeiPlugin.SIEVE; } @@ -87,7 +92,7 @@ class SieveCategory implements IRecipeCategory { } @Override - public void setRecipe(IRecipeLayoutBuilder builder, JeiSieveRecipeGroup recipe, IFocusGroup focuses) { + public void setRecipe(IRecipeLayoutBuilder builder, GroupedSieveRecipe recipe, IFocusGroup focuses) { builder.addSlot(RecipeIngredientRole.INPUT, 59, 1).addIngredients(recipe.ingredient()); builder.addSlot(RecipeIngredientRole.CATALYST, 87, 1).addItemStack(recipe.mesh()); @@ -95,56 +100,63 @@ class SieveCategory implements IRecipeCategory { var result = recipe.results().get(i); var slot = builder.addSlot(RecipeIngredientRole.OUTPUT, 1 + (i % 9) * 18, 1 + ROW_START + 18 * (i / 9)).addItemStack(result.item); - addTooltips(slot, result.provider); + addTooltips(slot, result.byHandOnly, result.provider); } } - public static void addTooltips(IRecipeSlotBuilder slot, NumberProvider provider) { + public static void addTooltips(IRecipeSlotBuilder slot, boolean byHandOnly, NumberProvider provider) { + var tooltipLines = new ImmutableList.Builder(); + + if (byHandOnly) { + tooltipLines.add(BY_HAND_ONLY_LABEL); + slot.setCustomRenderer(VanillaTypes.ITEM_STACK, ClientJeiUtil.AsteriskRenderer.INSTANCE); + } if (provider instanceof BinomialDistributionGenerator binomial) { if (binomial.n instanceof ConstantValue constant && constant.value == 1) { var chance = FORMATTER.format(RecipeUtil.getExpectedValue(binomial.p) * 100); - slot.addTooltipCallback((slotView, tooltip) -> tooltip.add(Component.translatable(TranslationKeys.SIEVE_RECIPE_CHANCE, chance).withStyle(ChatFormatting.GRAY))); + tooltipLines.add(Component.translatable(TranslationKeys.SIEVE_RECIPE_CHANCE, chance).withStyle(ChatFormatting.GRAY)); } else { - addAvgOutput(slot, RecipeUtil.getExpectedValue(provider)); + addAvgOutput(tooltipLines, RecipeUtil.getExpectedValue(provider)); } - addMinMaxes(slot, ConstantValue.exactly(0), binomial.n); + addMinMaxes(tooltipLines, ConstantValue.exactly(0), binomial.n); } else if (provider.getClass() != ConstantValue.class) { var val = RecipeUtil.getExpectedValue(provider); if (val != -1.0) { - addAvgOutput(slot, val); + addAvgOutput(tooltipLines, val); if (provider instanceof UniformGenerator uniform) { - addMinMaxes(slot, uniform.min, uniform.max); + addMinMaxes(tooltipLines, uniform.min, uniform.max); } } } - } - - private static void addAvgOutput(IRecipeSlotBuilder slot, double avgValue) { - String avgOutput = FORMATTER.format(avgValue); - slot.addTooltipCallback((slotView, tooltip) -> tooltip.add(Component.translatable(TranslationKeys.SIEVE_RECIPE_AVERAGE_OUTPUT, avgOutput).withStyle(ChatFormatting.GRAY))); - } - - // when the player holds shift, they can see the min/max amounts of a drop - private static void addMinMaxes(IRecipeSlotBuilder slot, NumberProvider min, NumberProvider max) { - var minFormatted = FORMATTER.format(RecipeUtil.getExpectedValue(min)); - var maxFormatted = FORMATTER.format(RecipeUtil.getExpectedValue(max)); + var tooltipLinesList = tooltipLines.build(); slot.addTooltipCallback((slotView, tooltip) -> { - //if (Screen.hasShiftDown()) { - tooltip.add(Component.translatable(TranslationKeys.SIEVE_RECIPE_MIN_OUTPUT, minFormatted).withStyle(ChatFormatting.GRAY)); - tooltip.add(Component.translatable(TranslationKeys.SIEVE_RECIPE_MAX_OUTPUT, maxFormatted).withStyle(ChatFormatting.GRAY)); - //} + tooltip.addAll(tooltipLinesList); }); } + private static void addAvgOutput(ImmutableList.Builder tooltipLines, double avgValue) { + String avgOutput = FORMATTER.format(avgValue); + tooltipLines.add(Component.translatable(TranslationKeys.SIEVE_RECIPE_AVERAGE_OUTPUT, avgOutput).withStyle(ChatFormatting.GRAY)); + } + + // when the player holds shift, they can see the min/max amounts of a drop + private static void addMinMaxes(ImmutableList.Builder tooltipLines, NumberProvider min, NumberProvider max) { + var minFormatted = FORMATTER.format(RecipeUtil.getExpectedValue(min)); + var maxFormatted = FORMATTER.format(RecipeUtil.getExpectedValue(max)); + + tooltipLines.add(Component.translatable(TranslationKeys.SIEVE_RECIPE_MIN_OUTPUT, minFormatted).withStyle(ChatFormatting.GRAY)); + tooltipLines.add(Component.translatable(TranslationKeys.SIEVE_RECIPE_MAX_OUTPUT, maxFormatted).withStyle(ChatFormatting.GRAY)); + } + @Override - public void draw(JeiSieveRecipeGroup recipe, IRecipeSlotsView recipeSlotsView, GuiGraphics graphics, double mouseX, double mouseY) { + public void draw(GroupedSieveRecipe recipe, IRecipeSlotsView recipeSlotsView, GuiGraphics graphics, double mouseX, double mouseY) { this.slot.draw(graphics, 58, 0); this.slot.draw(graphics, 86, 0); - for (int i = 0; i < JeiSieveRecipeGroup.maxSieveRows; i++) { + for (int i = 0; i < GroupedSieveRecipe.maxSieveRows; i++) { this.row.draw(graphics, 0, 28 + i * 18); } } diff --git a/src/main/java/thedarkcolour/exdeorum/compat/top/ExDeorumInfoProvider.java b/src/main/java/thedarkcolour/exdeorum/compat/top/ExDeorumInfoProvider.java index 63e9996c..3da1be8b 100644 --- a/src/main/java/thedarkcolour/exdeorum/compat/top/ExDeorumInfoProvider.java +++ b/src/main/java/thedarkcolour/exdeorum/compat/top/ExDeorumInfoProvider.java @@ -81,7 +81,7 @@ public class ExDeorumInfoProvider implements IProbeInfoProvider { } } else if (te instanceof SieveBlockEntity sieve) { if (!sieve.getContents().isEmpty()) { - info.text(CompoundText.create().style(TextStyleClass.LABEL).text("Progress: ").style(TextStyleClass.WARNING).text((100 - sieve.getProgress()) + "%")); + info.text(CompoundText.create().style(TextStyleClass.LABEL).text("Progress: ").style(TextStyleClass.WARNING).text((Math.round(1000 * sieve.getProgress()) / 10) + "%")); } if (playerEntity.isShiftKeyDown()) { var mesh = sieve.getMesh(); diff --git a/src/main/java/thedarkcolour/exdeorum/config/EConfig.java b/src/main/java/thedarkcolour/exdeorum/config/EConfig.java index 46628e1f..48c65128 100644 --- a/src/main/java/thedarkcolour/exdeorum/config/EConfig.java +++ b/src/main/java/thedarkcolour/exdeorum/config/EConfig.java @@ -23,6 +23,7 @@ import net.minecraftforge.common.ForgeConfigSpec; import net.minecraftforge.common.ForgeConfigSpec.BooleanValue; import net.minecraftforge.common.ForgeConfigSpec.ConfigValue; import net.minecraftforge.common.ForgeConfigSpec.DoubleValue; +import net.minecraftforge.common.ForgeConfigSpec.IntValue; import org.apache.commons.lang3.text.WordUtils; import org.apache.commons.lang3.tuple.Pair; import thedarkcolour.exdeorum.compat.ModIds; @@ -117,6 +118,7 @@ public class EConfig { public final BooleanValue startingTorch; public final BooleanValue startingWateringCan; public final BooleanValue simultaneousSieveUsage; + public final IntValue simultaneousSieveUsageRange; public final BooleanValue automatedSieves; public final DoubleValue barrelProgressStep; public final BooleanValue witchWaterNetherrackGenerator; @@ -126,6 +128,10 @@ public class EConfig { public final BooleanValue limitMossSieveDrops; public final BooleanValue allowWaterBottleTransfer; public final BooleanValue allowWitchWaterEntityConversion; + public final IntValue mechanicalSieveEnergyStorage; + public final IntValue mechanicalSieveEnergyConsumption; + //public final IntValue mechanicalHammerEnergyStorage; + //public final IntValue mechanicalHammerEnergyConsumption; public Server(ForgeConfigSpec.Builder builder) { builder.comment("Server configuration for Ex Deorum").push("server"); @@ -137,8 +143,11 @@ public class EConfig { .comment("Whether players in a void world start out with a full wooden watering can.") .define("starting_watering_can", true); this.simultaneousSieveUsage = builder - .comment("Whether players can use multiple sieves in a 3x3 area at once.") + .comment("Whether players can use multiple sieves in a 3x3 or larger area at once.") .define("simultaneous_sieve_usage", true); + this.simultaneousSieveUsageRange = builder + .comment("The range from which simultaneous sieve usage can reach. 1 means a maximum of 3x3 sieves at once, 2 means a maximum of 5x5, 3 means maximum of 7x7 simultaneous sieves, and so on.") + .defineInRange("simultaneous_sieve_range", 2, 0, 6); this.automatedSieves = builder .comment("Whether machines/fake players can interact with the Sieve.") .define("automated_sieves", false); @@ -166,6 +175,18 @@ public class EConfig { this.allowWitchWaterEntityConversion = builder .comment("Whether the entity conversion mechanic of Witch Water is enabled. If enabled, when an entity steps into Witch Water, the following conversions may happen: Villager -> Zombie Villager, Cleric Villager -> Witch, Skeleton -> Wither Skeleton, Creeper -> Charged Creeper, Spider -> Cave Spider, Pig & Piglin -> Zombified Piglin, Squid -> Ghast, Mooshroom -> Brown Mooshroom, Axolotl -> Blue Axolotl, Rabbit -> Killer Rabbit, Pufferfish -> Guardian, Horse -> Skeleton/Zombie Horse") .define("allow_witch_water_entity_conversion", true); + this.mechanicalSieveEnergyStorage = builder + .comment("The maximum amount of FE the mechanical sieve can have in its energy storage.") + .defineInRange("mechanical_sieve_energy_storage", 40_000, 0, Integer.MAX_VALUE); + this.mechanicalSieveEnergyConsumption = builder + .comment("The amount of FE/t a tick consumed by the mechanical sieve when sifting a block.") + .defineInRange("mechanical_sieve_energy_consumption", 40, 0, Integer.MAX_VALUE); + //this.mechanicalHammerEnergyStorage = builder + // .comment("The maximum amount of FE the mechanical hammer can have in its energy storage.") + // .defineInRange("mechanical_hammer_energy_storage", 40_000, 0, Integer.MAX_VALUE); + //this.mechanicalHammerEnergyConsumption = builder + // .comment("The amount of FE/t a tick consumed by the mechanical hammer when sifting a block.") + // .defineInRange("mechanical_hammer_energy_consumption", 40, 0, Integer.MAX_VALUE); builder.pop(); } diff --git a/src/main/java/thedarkcolour/exdeorum/data/BlockLoot.java b/src/main/java/thedarkcolour/exdeorum/data/BlockLoot.java index a189fc49..7c5350b0 100644 --- a/src/main/java/thedarkcolour/exdeorum/data/BlockLoot.java +++ b/src/main/java/thedarkcolour/exdeorum/data/BlockLoot.java @@ -29,8 +29,10 @@ import net.minecraft.world.level.storage.loot.LootTable; import net.minecraft.world.level.storage.loot.entries.LootItem; import net.minecraft.world.level.storage.loot.providers.number.ConstantValue; import thedarkcolour.exdeorum.ExDeorum; -import thedarkcolour.exdeorum.loot.InfestedStringCount; +import thedarkcolour.exdeorum.loot.InfestedStringFunction; +import thedarkcolour.exdeorum.loot.MachineLootFunction; import thedarkcolour.exdeorum.registry.EBlocks; +import thedarkcolour.exdeorum.registry.EItems; import thedarkcolour.modkit.MKUtils; import java.util.ArrayList; @@ -52,10 +54,17 @@ class BlockLoot extends BlockLootSubProvider { } }); - add(EBlocks.INFESTED_LEAVES.get(), new LootTable.Builder() - .withPool(new LootPool.Builder() + add(EBlocks.INFESTED_LEAVES.get(), LootTable.lootTable() + .withPool(LootPool.lootPool() .setRolls(ConstantValue.exactly(1)) - .add(LootItem.lootTableItem(Items.STRING).apply(InfestedStringCount.infestedString())))); + .add(LootItem.lootTableItem(Items.STRING) + .apply(InfestedStringFunction.infestedString())))); + // see createSingleItemTable() for reference + add(EBlocks.MECHANICAL_SIEVE.get(), LootTable.lootTable() + .withPool(applyExplosionCondition(EItems.MECHANICAL_SIEVE.get(), LootPool.lootPool() + .setRolls(ConstantValue.exactly(1.0F)) + .add(LootItem.lootTableItem(EItems.MECHANICAL_SIEVE.get()) + .apply(MachineLootFunction.machineLoot()))))); } @Override diff --git a/src/main/java/thedarkcolour/exdeorum/data/English.java b/src/main/java/thedarkcolour/exdeorum/data/English.java index 81195058..298f1729 100644 --- a/src/main/java/thedarkcolour/exdeorum/data/English.java +++ b/src/main/java/thedarkcolour/exdeorum/data/English.java @@ -19,6 +19,7 @@ package thedarkcolour.exdeorum.data; import thedarkcolour.exdeorum.ExDeorum; +import thedarkcolour.exdeorum.client.screen.RedstoneControlWidget; import thedarkcolour.exdeorum.registry.EBlocks; import thedarkcolour.modkit.data.MKEnglishProvider; @@ -28,7 +29,9 @@ class English { english.add(TranslationKeys.MAIN_CREATIVE_TAB, "Ex Deorum"); english.add(TranslationKeys.VOID_WORLD_TYPE, "Void World"); - english.add(TranslationKeys.WATERING_CAN_FLUID_DISPLAY, ": %s / %s"); + english.add(TranslationKeys.FRACTION_DISPLAY, ": %s / %s"); + english.add(TranslationKeys.MECHANICAL_SIEVE_MESH_LABEL, "Mesh: "); + english.add(TranslationKeys.ENERGY, "Energy"); english.add(TranslationKeys.ROOT_ADVANCEMENT_TITLE, "Don't Look Down..."); english.add(TranslationKeys.ROOT_ADVANCEMENT_DESCRIPTION, "Spawn into a SkyBlock void world"); @@ -43,6 +46,7 @@ class English { english.add(TranslationKeys.SILK_WORM_JEI_INFO, "Silk worms have a 1 in 100 chance to drop from leaves harvested with a Crook. Using a silk worm on a tree's leaves will infest them, gradually spreading through the entire tree. 100% infested leaves can be harvested for string, but do not drop saplings."); english.add(TranslationKeys.SIEVE_JEI_INFO, "Sieves are used to sift for items from soft blocks like gravel and dirt. A mesh is required to use the sieve. Meshes can be enchanted with Fortune and Efficiency. Sieves in a 3x3 area can be used simultaneously."); + english.add(TranslationKeys.SIEVE_MESH_JEI_INFO, "Meshes are used in sieves. Different meshes yield different drops. Meshes can be enchanted with Fortune and Efficiency to increase likelihood of drops and sifting speed, respectively."); english.add(TranslationKeys.WATERING_CAN_JEI_INFO, "Watering cans speed up crop growth, tree growth, and grass spreading, among other things. They can be filled with water from barrels and wooden crucibles. Golden and above watering cans do not need to be refilled once full. Diamond watering cans water in a 3x3 area, and Netherite watering cans are usable by machinery."); english.add(TranslationKeys.WITCH_WATER_JEI_INFO, "Witch water is obtained by putting water in a barrel on top of mycelium. More mycelium speeds up the process. A barrel with witch water will grow mushrooms on nearby mycelium. Witch water and lava can make a netherrack generator, similar to a cobblestone generator."); english.add(TranslationKeys.MYCELIUM_SPORES_JEI_INFO, "Use on dirt to turn it into mycelium."); @@ -50,6 +54,8 @@ class English { english.add(TranslationKeys.WARPED_NYLIUM_SPORES_JEI_INFO, "Use on netherrack to turn it into a warped nylium block."); english.add(TranslationKeys.CRIMSON_NYLIUM_SPORES_JEI_INFO, "Use on netherrack to turn it into a crimson nylium block."); english.add(TranslationKeys.SCULK_CORE_JEI_INFO, "Use a sculk core on a Sculk Shrieker to enable it to spawn Wardens. Normally, Sculk Shriekers placed by players cannot spawn Wardens, so this item is useful for obtaining Sculk items in a SkyBlock world."); + english.add(TranslationKeys.MECHANICAL_SIEVE_JEI_INFO, "The Mechanical Sieve is a machine that, when supplied with a mesh and Forge Energy (FE), will sift blocks without a player having to do it themselves. It also supports three different modes of redstone control. Since Ex Deorum does not provide a way to generate FE, you will need another mod to provide power."); + english.add(TranslationKeys.BARREL_COMPOST_CATEGORY_TITLE, "Barrel Compost"); english.add(TranslationKeys.BARREL_COMPOST_RECIPE_VOLUME, "Compost: %s"); english.add(TranslationKeys.BARREL_MIXING_CATEGORY_TITLE, "Barrel Mixing"); @@ -64,6 +70,14 @@ class English { english.add(TranslationKeys.SIEVE_RECIPE_AVERAGE_OUTPUT, "Avg. Output: %s"); english.add(TranslationKeys.SIEVE_RECIPE_MIN_OUTPUT, "Min: %s"); english.add(TranslationKeys.SIEVE_RECIPE_MAX_OUTPUT, "Max: %s"); + english.add(TranslationKeys.SIEVE_RECIPE_BY_HAND_ONLY, "Does not drop from Mechanical Sieve"); + + english.add(TranslationKeys.MECHANICAL_SIEVE_SCREEN_TITLE, "Mechanical Sieve"); + english.add(TranslationKeys.REDSTONE_CONTROL_MODES[RedstoneControlWidget.REDSTONE_MODE_IGNORED], "Always"); + english.add(TranslationKeys.REDSTONE_CONTROL_MODES[RedstoneControlWidget.REDSTONE_MODE_UNPOWERED], "Unpowered"); + english.add(TranslationKeys.REDSTONE_CONTROL_MODES[RedstoneControlWidget.REDSTONE_MODE_POWERED], "Powered"); + english.add(TranslationKeys.REDSTONE_CONTROL_LABEL, "Redstone Mode"); + english.add(TranslationKeys.REDSTONE_CONTROL_MODE, "Mode: "); english.addBlock(EBlocks.VEXING_ARCHWOOD_CRUCIBLE, "Vexing Archwood Crucible"); english.addBlock(EBlocks.CASCADING_ARCHWOOD_CRUCIBLE, "Cascading Archwood Crucible"); diff --git a/src/main/java/thedarkcolour/exdeorum/data/ModCompatData.java b/src/main/java/thedarkcolour/exdeorum/data/ModCompatData.java index d4aebcf7..9b9fd76d 100644 --- a/src/main/java/thedarkcolour/exdeorum/data/ModCompatData.java +++ b/src/main/java/thedarkcolour/exdeorum/data/ModCompatData.java @@ -29,14 +29,14 @@ import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.RegistryObject; import thedarkcolour.exdeorum.compat.ModIds; -import java.util.IdentityHashMap; +import java.util.HashMap; import java.util.Map; // Mocks modded items so that data generation can reference modded items without needing those mods installed. public class ModCompatData { // Identity maps because keys are just constants from ModIds - private static final Map> itemRegistries = new IdentityHashMap<>(); - private static final Map> blockRegistries = new IdentityHashMap<>(); + private static final Map> itemRegistries = new HashMap<>(); + private static final Map> blockRegistries = new HashMap<>(); @SuppressWarnings("DataFlowIssue") private static RegistryObject item(String modid, String name) { diff --git a/src/main/java/thedarkcolour/exdeorum/data/TranslationKeys.java b/src/main/java/thedarkcolour/exdeorum/data/TranslationKeys.java index fc6ad624..8d685690 100644 --- a/src/main/java/thedarkcolour/exdeorum/data/TranslationKeys.java +++ b/src/main/java/thedarkcolour/exdeorum/data/TranslationKeys.java @@ -21,9 +21,13 @@ package thedarkcolour.exdeorum.data; import thedarkcolour.exdeorum.ExDeorum; public class TranslationKeys { + // Misc public static final String MAIN_CREATIVE_TAB = "itemGroup." + ExDeorum.ID + ".main"; public static final String VOID_WORLD_TYPE = "generator." + ExDeorum.ID + ".void_world"; - public static final String WATERING_CAN_FLUID_DISPLAY = "item." + ExDeorum.ID + ".watering_can_fluid_display"; + // ": %s / %s" + public static final String FRACTION_DISPLAY = "item." + ExDeorum.ID + ".watering_can_fluid_display"; + public static final String MECHANICAL_SIEVE_MESH_LABEL = "item." + ExDeorum.ID + ".mechanical_sieve.mesh_label"; + public static final String ENERGY = "gui." + ExDeorum.ID + ".energy_label"; // Advancements public static final String ROOT_ADVANCEMENT_TITLE = "advancements." + ExDeorum.ID + ".core.root.title"; @@ -40,6 +44,7 @@ public class TranslationKeys { // JEI descriptions public static final String SILK_WORM_JEI_INFO = "info." + ExDeorum.ID + ".silk_worm"; public static final String SIEVE_JEI_INFO = "info." + ExDeorum.ID + ".sieve"; + public static final String SIEVE_MESH_JEI_INFO = "info." + ExDeorum.ID + ".sieve_mesh"; public static final String WATERING_CAN_JEI_INFO = "info." + ExDeorum.ID + ".watering_can"; public static final String WITCH_WATER_JEI_INFO = "info." + ExDeorum.ID + ".witch_water"; public static final String MYCELIUM_SPORES_JEI_INFO = "info." + ExDeorum.ID + ".mycelium_spores"; @@ -47,6 +52,7 @@ public class TranslationKeys { public static final String WARPED_NYLIUM_SPORES_JEI_INFO = "info." + ExDeorum.ID + ".warped_nylium_spores"; public static final String CRIMSON_NYLIUM_SPORES_JEI_INFO = "info." + ExDeorum.ID + ".crimson_nylium_spores"; public static final String SCULK_CORE_JEI_INFO = "info." + ExDeorum.ID + ".sculk_core"; + public static final String MECHANICAL_SIEVE_JEI_INFO = "info." + ExDeorum.ID + ".mechanical_sieve"; // JEI recipe categories public static final String BARREL_COMPOST_CATEGORY_TITLE = "gui." + ExDeorum.ID + ".category.barrel_compost"; @@ -63,4 +69,15 @@ public class TranslationKeys { public static final String SIEVE_RECIPE_AVERAGE_OUTPUT = "gui." + ExDeorum.ID + ".category.sieve.average_output"; public static final String SIEVE_RECIPE_MIN_OUTPUT = "gui." + ExDeorum.ID + ".category.sieve.min_output"; public static final String SIEVE_RECIPE_MAX_OUTPUT = "gui." + ExDeorum.ID + ".category.sieve.max_output"; + public static final String SIEVE_RECIPE_BY_HAND_ONLY = "gui." + ExDeorum.ID + ".category.sieve.by_hand_only"; + + // Screens + public static final String MECHANICAL_SIEVE_SCREEN_TITLE = ExDeorum.ID + ".container.mechanical_sieve"; + public static final String[] REDSTONE_CONTROL_MODES = new String[] { + "gui." + ExDeorum.ID + ".redstone_control.always", + "gui." + ExDeorum.ID + ".redstone_control.unpowered", + "gui." + ExDeorum.ID + ".redstone_control.powered" + }; + public static final String REDSTONE_CONTROL_LABEL = "gui." + ExDeorum.ID + ".redstone_control.label"; + public static final String REDSTONE_CONTROL_MODE = "gui." + ExDeorum.ID + ".redstone_control.mode"; } diff --git a/src/main/java/thedarkcolour/exdeorum/data/package-info.java b/src/main/java/thedarkcolour/exdeorum/data/package-info.java index 62fb7d9f..7c7d168e 100644 --- a/src/main/java/thedarkcolour/exdeorum/data/package-info.java +++ b/src/main/java/thedarkcolour/exdeorum/data/package-info.java @@ -16,12 +16,6 @@ * along with this program. If not, see . */ -/** - * This package contains all data generation for Ex Deorum. It is not to be used outside of data generation. - *

- * Ex Deorum uses the ModKit library, which adds several utilities primarily for data generation - * without needing to be shaded or depended upon outside a development environment. - */ @net.minecraft.MethodsReturnNonnullByDefault @javax.annotation.ParametersAreNonnullByDefault package thedarkcolour.exdeorum.data; diff --git a/src/main/java/thedarkcolour/exdeorum/data/recipe/Recipes.java b/src/main/java/thedarkcolour/exdeorum/data/recipe/Recipes.java index 0cbd308a..d485e6f6 100644 --- a/src/main/java/thedarkcolour/exdeorum/data/recipe/Recipes.java +++ b/src/main/java/thedarkcolour/exdeorum/data/recipe/Recipes.java @@ -18,10 +18,10 @@ package thedarkcolour.exdeorum.data.recipe; +import mcjty.theoneprobe.apiimpl.elements.ElementItemStack; import net.minecraft.data.recipes.FinishedRecipe; import net.minecraft.data.recipes.RecipeCategory; import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.BlockTags; import net.minecraft.tags.ItemTags; import net.minecraft.tags.TagKey; import net.minecraft.world.item.Item; @@ -282,6 +282,16 @@ public class Recipes { recipe.pattern("WCW"); MKRecipeProvider.unlockedByHaving(recipe, EItems.WOOD_CHIPPINGS.get()); }); + recipes.shapedCrafting(RecipeCategory.MISC, EItems.MECHANICAL_SIEVE.get(), recipe -> { + recipe.define('#', Items.IRON_BLOCK); + recipe.define('G', Items.GLASS); + recipe.define('H', Items.HOPPER); + recipe.define('I', Items.IRON_BARS); + recipe.pattern("#G#"); + recipe.pattern("IHI"); + recipe.pattern("I I"); + MKRecipeProvider.unlockedByHaving(recipe, Items.HOPPER); + }); } private static void modUShaped(MKRecipeProvider recipes, String modid, RegistryObject sides, RegistryObject middle, RegistryObject result) { diff --git a/src/main/java/thedarkcolour/exdeorum/data/recipe/SieveRecipes.java b/src/main/java/thedarkcolour/exdeorum/data/recipe/SieveRecipes.java index 9c0305c9..9081c3d8 100644 --- a/src/main/java/thedarkcolour/exdeorum/data/recipe/SieveRecipes.java +++ b/src/main/java/thedarkcolour/exdeorum/data/recipe/SieveRecipes.java @@ -939,6 +939,7 @@ class SieveRecipes { void accept(BiConsumer addDrop, BiConsumer, NumberProvider> addTagDrop, AddConditionalTag addConditionalTag); } + @FunctionalInterface private interface AddConditionalTag { void accept(Either> result, NumberProvider resultAmount, ICondition condition); diff --git a/src/main/java/thedarkcolour/exdeorum/event/EventHandler.java b/src/main/java/thedarkcolour/exdeorum/event/EventHandler.java index 93c9befd..874b8b75 100644 --- a/src/main/java/thedarkcolour/exdeorum/event/EventHandler.java +++ b/src/main/java/thedarkcolour/exdeorum/event/EventHandler.java @@ -43,6 +43,7 @@ import net.minecraftforge.common.ForgeMod; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.AddReloadListenerEvent; import net.minecraftforge.event.OnDatapackSyncEvent; +import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.event.level.LevelEvent; import net.minecraftforge.event.server.ServerStoppingEvent; @@ -62,6 +63,7 @@ import thedarkcolour.exdeorum.compat.top.ExDeorumTopCompat; import thedarkcolour.exdeorum.config.EConfig; import thedarkcolour.exdeorum.item.WateringCanItem; import thedarkcolour.exdeorum.network.NetworkHandler; +import thedarkcolour.exdeorum.network.VisualUpdateTracker; import thedarkcolour.exdeorum.recipe.RecipeUtil; import thedarkcolour.exdeorum.registry.EFluids; import thedarkcolour.exdeorum.registry.EItems; @@ -85,6 +87,7 @@ public final class EventHandler { modBus.addListener(EventHandler::interModEnqueue); modBus.addListener(EventHandler::onCommonSetup); fmlBus.addListener(EventHandler::serverShutdown); + fmlBus.addListener(EventHandler::serverTick); if (ExDeorum.DEBUG) { fmlBus.addListener(EventHandler::handleDebugCommands); @@ -223,7 +226,7 @@ public final class EventHandler { } // Send messages to other mods - public static void interModEnqueue(InterModEnqueueEvent event) { + private static void interModEnqueue(InterModEnqueueEvent event) { if (ModList.get().isLoaded(ModIds.THE_ONE_PROBE)) { InterModComms.sendTo(ModIds.THE_ONE_PROBE, "getTheOneProbe", ExDeorumTopCompat::new); } @@ -238,4 +241,10 @@ public final class EventHandler { }, gameExecutor); }); } + + private static void serverTick(TickEvent.ServerTickEvent event) { + if (event.phase == TickEvent.Phase.END) { + VisualUpdateTracker.syncVisualUpdates(); + } + } } diff --git a/src/main/java/thedarkcolour/exdeorum/fluid/WitchWaterFluid.java b/src/main/java/thedarkcolour/exdeorum/fluid/WitchWaterFluid.java index c022c8e3..de30d5ef 100644 --- a/src/main/java/thedarkcolour/exdeorum/fluid/WitchWaterFluid.java +++ b/src/main/java/thedarkcolour/exdeorum/fluid/WitchWaterFluid.java @@ -78,6 +78,7 @@ public class WitchWaterFluid extends FluidType { return new Vector3f(32f / 255f, 12f / 255f, 64f / 255f); } + // todo return the correct value here @Override public int getTintColor() { return 0xFFFFFFFF; diff --git a/src/main/java/thedarkcolour/exdeorum/item/WateringCanItem.java b/src/main/java/thedarkcolour/exdeorum/item/WateringCanItem.java index 67c2136f..8592ab76 100644 --- a/src/main/java/thedarkcolour/exdeorum/item/WateringCanItem.java +++ b/src/main/java/thedarkcolour/exdeorum/item/WateringCanItem.java @@ -46,7 +46,6 @@ import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.BucketPickup; import net.minecraft.world.level.block.FarmBlock; import net.minecraft.world.level.block.LevelEvent; -import net.minecraft.world.level.block.SpreadingSnowyDirtBlock; import net.minecraft.world.level.block.SugarCaneBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.Fluids; @@ -132,9 +131,8 @@ public class WateringCanItem extends Item { @Override public void appendHoverText(ItemStack stack, @Nullable Level pLevel, List tooltip, TooltipFlag pIsAdvanced) { stack.getCapability(ForgeCapabilities.FLUID_HANDLER_ITEM).ifPresent(fluidHandler -> { - var water = Component.translatable(ForgeMod.WATER_TYPE.get().getDescriptionId()); - - tooltip.add(water.append(Component.translatable(TranslationKeys.WATERING_CAN_FLUID_DISPLAY, fluidHandler.getFluidInTank(0).getAmount(), capacity)).withStyle(ChatFormatting.GRAY)); + // use the block name which is guaranteed to have a vanilla translation + tooltip.add(Component.translatable("block.minecraft.water").append(Component.translatable(TranslationKeys.FRACTION_DISPLAY, fluidHandler.getFluidInTank(0).getAmount(), capacity)).withStyle(ChatFormatting.GRAY)); }); } diff --git a/src/main/java/thedarkcolour/exdeorum/item/package-info.java b/src/main/java/thedarkcolour/exdeorum/item/package-info.java new file mode 100644 index 00000000..fd802d4f --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/item/package-info.java @@ -0,0 +1,21 @@ +/* + * Ex Deorum + * Copyright (c) 2023 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +@net.minecraft.MethodsReturnNonnullByDefault +@javax.annotation.ParametersAreNonnullByDefault +package thedarkcolour.exdeorum.item; diff --git a/src/main/java/thedarkcolour/exdeorum/loot/InfestedStringCount.java b/src/main/java/thedarkcolour/exdeorum/loot/InfestedStringFunction.java similarity index 87% rename from src/main/java/thedarkcolour/exdeorum/loot/InfestedStringCount.java rename to src/main/java/thedarkcolour/exdeorum/loot/InfestedStringFunction.java index 08d98c57..db9fae36 100644 --- a/src/main/java/thedarkcolour/exdeorum/loot/InfestedStringCount.java +++ b/src/main/java/thedarkcolour/exdeorum/loot/InfestedStringFunction.java @@ -31,11 +31,11 @@ import thedarkcolour.exdeorum.blockentity.InfestedLeavesBlockEntity; import thedarkcolour.exdeorum.registry.ELootFunctions; // Sets the correct amount based on the progress of the infested leaves -public class InfestedStringCount extends LootItemConditionalFunction { +public class InfestedStringFunction extends LootItemConditionalFunction { // todo move to config public static final float STRING_CHANCE = 0.4f; - protected InfestedStringCount(LootItemCondition[] conditions) { + protected InfestedStringFunction(LootItemCondition[] conditions) { super(conditions); } @@ -73,13 +73,13 @@ public class InfestedStringCount extends LootItemConditionalFunction { } public static LootItemConditionalFunction.Builder infestedString() { - return LootItemConditionalFunction.simpleBuilder(InfestedStringCount::new); + return LootItemConditionalFunction.simpleBuilder(InfestedStringFunction::new); } - public static class LootSerializer extends LootItemConditionalFunction.Serializer { + public static class LootSerializer extends LootItemConditionalFunction.Serializer { @Override - public InfestedStringCount deserialize(JsonObject json, JsonDeserializationContext ctx, LootItemCondition[] conditions) { - return new InfestedStringCount(conditions); + public InfestedStringFunction deserialize(JsonObject json, JsonDeserializationContext ctx, LootItemCondition[] conditions) { + return new InfestedStringFunction(conditions); } } } diff --git a/src/main/java/thedarkcolour/exdeorum/loot/MachineLootFunction.java b/src/main/java/thedarkcolour/exdeorum/loot/MachineLootFunction.java new file mode 100644 index 00000000..c09bb79a --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/loot/MachineLootFunction.java @@ -0,0 +1,62 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.loot; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonObject; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.functions.LootItemConditionalFunction; +import net.minecraft.world.level.storage.loot.functions.LootItemFunctionType; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; +import thedarkcolour.exdeorum.registry.ELootFunctions; + +public class MachineLootFunction extends LootItemConditionalFunction { + protected MachineLootFunction(LootItemCondition[] conditions) { + super(conditions); + } + + @Override + protected ItemStack run(ItemStack stack, LootContext ctx) { + BlockEntity blockEntity = ctx.getParamOrNull(LootContextParams.BLOCK_ENTITY); + if (blockEntity != null) { + blockEntity.saveToItem(stack); + } + + return stack; + } + + @Override + public LootItemFunctionType getType() { + return ELootFunctions.MACHINE.get(); + } + + public static LootItemConditionalFunction.Builder machineLoot() { + return LootItemConditionalFunction.simpleBuilder(MachineLootFunction::new); + } + + public static class LootSerializer extends LootItemConditionalFunction.Serializer { + @Override + public MachineLootFunction deserialize(JsonObject json, JsonDeserializationContext ctx, LootItemCondition[] conditions) { + return new MachineLootFunction(conditions); + } + } +} diff --git a/src/main/java/thedarkcolour/exdeorum/menu/EContainerMenu.java b/src/main/java/thedarkcolour/exdeorum/menu/EContainerMenu.java new file mode 100644 index 00000000..f298089b --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/menu/EContainerMenu.java @@ -0,0 +1,31 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.menu; + +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.MenuType; + +public abstract class EContainerMenu extends AbstractContainerMenu { + protected EContainerMenu(MenuType pMenuType, int pContainerId) { + super(pMenuType, pContainerId); + } + + // When the server sends a menu property message, the client handles the synced property here. + public abstract void setClientProperty(int index, int value); +} diff --git a/src/main/java/thedarkcolour/exdeorum/menu/MechanicalSieveMenu.java b/src/main/java/thedarkcolour/exdeorum/menu/MechanicalSieveMenu.java new file mode 100644 index 00000000..056283cc --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/menu/MechanicalSieveMenu.java @@ -0,0 +1,178 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.menu; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.InventoryMenu; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; +import thedarkcolour.exdeorum.ExDeorum; +import thedarkcolour.exdeorum.blockentity.MechanicalSieveBlockEntity; +import thedarkcolour.exdeorum.network.NetworkHandler; +import thedarkcolour.exdeorum.registry.EMenus; + +public class MechanicalSieveMenu extends EContainerMenu { + private static final ResourceLocation EMPTY_SLOT_MESH = new ResourceLocation(ExDeorum.ID, "item/empty_slot_mesh"); + private static final int NUM_SLOTS = 22; // input + mesh, 20 output slots + private static final int PLAYER_SLOTS = 36; // hotbar + inventory + + public final MechanicalSieveBlockEntity sieve; + @Nullable + private final ServerPlayer player; + + public int prevSieveEnergy; + + public MechanicalSieveMenu(int containerId, Inventory playerInventory, FriendlyByteBuf data) { + this(containerId, playerInventory, (MechanicalSieveBlockEntity) playerInventory.player.level().getBlockEntity(data.readBlockPos())); + this.sieve.setRedstoneMode(data.readByte()); + } + + public MechanicalSieveMenu(int containerId, Inventory playerInventory, MechanicalSieveBlockEntity sieve) { + super(EMenus.MECHANICAL_SIEVE.get(), containerId); + + this.sieve = sieve; + + // input slot + addSlot(sieve.inventory.createSlot(0, 26, 30)); + // mesh slot + addSlot(sieve.inventory.createSlot(1, 26, 53).setBackground(InventoryMenu.BLOCK_ATLAS, EMPTY_SLOT_MESH)); + // output slots + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 5; ++c) { + addSlot(sieve.inventory.createSlot(2 + r * 5 + c, 80 + c * 18, 15 + r * 18)); + } + } + // Player slots + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + addSlot(new Slot(playerInventory, j + i * 9 + 9, 8 + j * 18, 7 + 84 + i * 18)); + } + } + for (int k = 0; k < 9; ++k) { + addSlot(new Slot(playerInventory, k, 8 + k * 18, 149)); + } + + if (playerInventory.player instanceof ServerPlayer serverPlayer) { + this.player = serverPlayer; + } else { + this.player = null; + } + } + + @Override + public void broadcastChanges() { + super.broadcastChanges(); + + if (this.player != null) { + if (this.prevSieveEnergy != this.sieve.energy.getEnergyStored()) { + this.prevSieveEnergy = this.sieve.energy.getEnergyStored(); + + NetworkHandler.sendMenuProperty(this.player, this.containerId, 0, this.prevSieveEnergy); + } + } + } + + @Override + public void broadcastFullState() { + super.broadcastFullState(); + + if (this.player != null) { + if (this.prevSieveEnergy != this.sieve.energy.getEnergyStored()) { + this.prevSieveEnergy = this.sieve.energy.getEnergyStored(); + + NetworkHandler.sendMenuProperty(this.player, this.containerId, 0, this.prevSieveEnergy); + } + } + } + + @Override + public void setClientProperty(int index, int value) { + if (index == 0) { + this.prevSieveEnergy = value; + } + } + + @Override + public ItemStack quickMoveStack(Player player, int clickedSlot) { + var stack = ItemStack.EMPTY; + var slot = this.slots.get(clickedSlot); + + if (slot.hasItem()) { + var clickedStack = slot.getItem(); + stack = clickedStack.copy(); + + if (clickedSlot > 1 && clickedSlot <= NUM_SLOTS) { // moving out of output slots + if (!moveItemStackTo(clickedStack, NUM_SLOTS, PLAYER_SLOTS + NUM_SLOTS, true)) { + return ItemStack.EMPTY; + } + } else if (clickedSlot < 2) { // moving out of input/mesh slot + if (!moveItemStackTo(clickedStack, NUM_SLOTS, NUM_SLOTS + PLAYER_SLOTS, false)) { + return ItemStack.EMPTY; + } + } else if (this.sieve.getLogic().isValidInput(clickedStack)) { // attempting to move into input slot + if (!moveItemStackTo(clickedStack, 0, 1, false)) { + return ItemStack.EMPTY; + } + } else if (this.sieve.getLogic().isValidMesh(clickedStack)) { // attempting to move into mesh slot + if (!moveItemStackTo(clickedStack, 1, 2, false)) { + return ItemStack.EMPTY; + } + } else if (clickedSlot < NUM_SLOTS + 27) { // attempting to move from inventory to hotbar + if (!moveItemStackTo(clickedStack, NUM_SLOTS + 27, NUM_SLOTS + PLAYER_SLOTS, false)) { + return ItemStack.EMPTY; + } + } else if (clickedSlot < NUM_SLOTS + PLAYER_SLOTS) { // attempting to move from hotbar to inventory + if (!moveItemStackTo(clickedStack, NUM_SLOTS, NUM_SLOTS + 27, false)) { + return ItemStack.EMPTY; + } + } + + if (clickedStack.isEmpty()) { + slot.set(ItemStack.EMPTY); + } + slot.setChanged(); + if (clickedStack.getCount() == stack.getCount()) { + return ItemStack.EMPTY; + } + slot.onTake(player, clickedStack); + broadcastChanges(); + } + + return stack; + } + + @Override + public boolean clickMenuButton(Player player, int id) { + if (0 <= id && id < 3) { + this.sieve.setRedstoneMode(id); + return false; + } + return false; + } + + @Override + public boolean stillValid(Player player) { + return this.sieve.stillValid(player); + } +} diff --git a/src/main/java/thedarkcolour/exdeorum/menu/package-info.java b/src/main/java/thedarkcolour/exdeorum/menu/package-info.java new file mode 100644 index 00000000..571d7361 --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/menu/package-info.java @@ -0,0 +1,21 @@ +/* + * Ex Deorum + * Copyright (c) 2023 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +@net.minecraft.MethodsReturnNonnullByDefault +@javax.annotation.ParametersAreNonnullByDefault +package thedarkcolour.exdeorum.menu; diff --git a/src/main/java/thedarkcolour/exdeorum/network/ClientMessageHandler.java b/src/main/java/thedarkcolour/exdeorum/network/ClientMessageHandler.java index 6fdbffd8..814b899f 100644 --- a/src/main/java/thedarkcolour/exdeorum/network/ClientMessageHandler.java +++ b/src/main/java/thedarkcolour/exdeorum/network/ClientMessageHandler.java @@ -19,8 +19,11 @@ package thedarkcolour.exdeorum.network; import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.world.entity.player.Player; +import thedarkcolour.exdeorum.blockentity.EBlockEntity; import thedarkcolour.exdeorum.client.ClientHandler; -import thedarkcolour.exdeorum.recipe.RecipeUtil; +import thedarkcolour.exdeorum.menu.EContainerMenu; public class ClientMessageHandler { public static boolean isInVoidWorld; @@ -36,7 +39,23 @@ public class ClientMessageHandler { } public static void reloadClientRecipeCache() { - //RecipeUtil.reload(Minecraft.getInstance().level.getRecipeManager()); ClientHandler.needsRecipeCacheRefresh = true; } + + @SuppressWarnings("DataFlowIssue") + public static void handleVisualUpdate(VisualUpdateMessage msg) { + ClientLevel level = Minecraft.getInstance().level; + if (level != null && level.getBlockEntity(msg.pos) instanceof EBlockEntity blockEntity) { + // payload should be nonnull on the client side + blockEntity.readVisualData(msg.payload); + } + } + + public static void handleMenuProperty(MenuPropertyMessage msg) { + Player player = Minecraft.getInstance().player; + + if (player != null && player.containerMenu instanceof EContainerMenu menu && menu.containerId == msg.containerId()) { + menu.setClientProperty(msg.index(), msg.value()); + } + } } diff --git a/src/main/java/thedarkcolour/exdeorum/network/MenuPropertyMessage.java b/src/main/java/thedarkcolour/exdeorum/network/MenuPropertyMessage.java new file mode 100644 index 00000000..3952c8c1 --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/network/MenuPropertyMessage.java @@ -0,0 +1,45 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.network; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.fml.DistExecutor; +import net.minecraftforge.network.NetworkEvent; + +import java.util.function.Supplier; + +// Like ClientboundContainerSetDataPacket except that the value is 32 bits instead of 16 bits +public record MenuPropertyMessage(int containerId, int index, int value) { + public static void encode(MenuPropertyMessage msg, FriendlyByteBuf buffer) { + buffer.writeByte(msg.containerId); + buffer.writeShort(msg.index); + buffer.writeVarInt(msg.value); + } + + public static MenuPropertyMessage decode(FriendlyByteBuf buffer) { + return new MenuPropertyMessage(buffer.readByte(), buffer.readShort(), buffer.readVarInt()); + } + + public static void handle(MenuPropertyMessage msg, Supplier ctxSupplier) { + NetworkHandler.handle(ctxSupplier, ctx -> { + DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> ClientMessageHandler.handleMenuProperty(msg)); + }); + } +} diff --git a/src/main/java/thedarkcolour/exdeorum/network/NetworkHandler.java b/src/main/java/thedarkcolour/exdeorum/network/NetworkHandler.java index 077d5799..47a7a5d9 100644 --- a/src/main/java/thedarkcolour/exdeorum/network/NetworkHandler.java +++ b/src/main/java/thedarkcolour/exdeorum/network/NetworkHandler.java @@ -25,6 +25,7 @@ import net.minecraftforge.network.NetworkEvent; import net.minecraftforge.network.NetworkRegistry; import net.minecraftforge.network.simple.SimpleChannel; import thedarkcolour.exdeorum.ExDeorum; +import thedarkcolour.exdeorum.blockentity.EBlockEntity; import java.util.function.Consumer; import java.util.function.Supplier; @@ -36,6 +37,8 @@ public final class NetworkHandler { public static void register() { CHANNEL.registerMessage(0, VoidWorldMessage.class, VoidWorldMessage::encode, VoidWorldMessage::decode, VoidWorldMessage::handle); CHANNEL.registerMessage(1, PlayerDataMessage.class, (msg, buffer) -> {}, buffer -> new PlayerDataMessage(), PlayerDataMessage::handle); + CHANNEL.registerMessage(2, VisualUpdateMessage.class, VisualUpdateMessage::encode, VisualUpdateMessage::decode, VisualUpdateMessage::handle); + CHANNEL.registerMessage(3, MenuPropertyMessage.class, MenuPropertyMessage::encode, MenuPropertyMessage::decode, MenuPropertyMessage::handle); } public static void sendVoidWorld(ServerPlayer pPlayer) { @@ -51,4 +54,8 @@ public final class NetworkHandler { ctx.enqueueWork(() -> handler.accept(ctx)); ctx.setPacketHandled(true); } + + public static void sendMenuProperty(ServerPlayer player, int containerId, int index, int prevSieveEnergy) { + CHANNEL.sendTo(new MenuPropertyMessage(containerId, index, prevSieveEnergy), player.connection.connection, NetworkDirection.PLAY_TO_CLIENT); + } } diff --git a/src/main/java/thedarkcolour/exdeorum/network/VisualUpdateMessage.java b/src/main/java/thedarkcolour/exdeorum/network/VisualUpdateMessage.java new file mode 100644 index 00000000..3173df6e --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/network/VisualUpdateMessage.java @@ -0,0 +1,73 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.network; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.fml.DistExecutor; +import net.minecraftforge.network.NetworkEvent; +import org.jetbrains.annotations.Nullable; +import thedarkcolour.exdeorum.blockentity.EBlockEntity; + +import java.util.function.Supplier; + +class VisualUpdateMessage { + final BlockEntityType blockEntityType; + final BlockPos pos; + // Null on the client side + @Nullable + private final EBlockEntity blockEntity; + // Null on the server side + @Nullable + final FriendlyByteBuf payload; + + public VisualUpdateMessage(BlockPos pos, @Nullable EBlockEntity blockEntity, @Nullable FriendlyByteBuf payload) { + this.pos = pos; + this.blockEntity = blockEntity; + // payload is saved on the client until it can be handled properly + this.payload = payload; + + // If the payload is null, we're on the server. Hence, the block entity is nonnull. + if (payload == null) { + this.blockEntityType = blockEntity.getType(); + } else { + this.blockEntityType = payload.readById(BuiltInRegistries.BLOCK_ENTITY_TYPE); + } + } + + public static void encode(VisualUpdateMessage msg, FriendlyByteBuf buffer) { + buffer.writeBlockPos(msg.pos); + buffer.writeId(BuiltInRegistries.BLOCK_ENTITY_TYPE, msg.blockEntityType); + // never null on the server side, where this packet is meant to be encoded + msg.blockEntity.writeVisualData(buffer); + } + + public static VisualUpdateMessage decode(FriendlyByteBuf buffer) { + return new VisualUpdateMessage(buffer.readBlockPos(), null, buffer); + } + + public static void handle(VisualUpdateMessage msg, Supplier ctxSupplier) { + NetworkHandler.handle(ctxSupplier, ctx -> { + DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> ClientMessageHandler.handleVisualUpdate(msg)); + }); + } +} diff --git a/src/main/java/thedarkcolour/exdeorum/network/VisualUpdateTracker.java b/src/main/java/thedarkcolour/exdeorum/network/VisualUpdateTracker.java new file mode 100644 index 00000000..f515c317 --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/network/VisualUpdateTracker.java @@ -0,0 +1,69 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.network; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraftforge.network.PacketDistributor; +import thedarkcolour.exdeorum.blockentity.EBlockEntity; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +// Syncs certain block entity data to the client for visual purposes +// Since some block entities might change their data multiple times a tick, this class keeps track of +// whether a block entity has updated and then pushes out the changes once at the end of each tick. +public class VisualUpdateTracker { + // WeakHashMap is faster than Guava mapmaker + private static final Map> UPDATES = new WeakHashMap<>(); + + public static void sendVisualUpdate(EBlockEntity blockEntity) { + var level = blockEntity.getLevel(); + + if (level != null && !level.isClientSide) { + var dimension = level.getChunkAt(blockEntity.getBlockPos()); + List updatesList; + if (!UPDATES.containsKey(dimension)) { + UPDATES.put(dimension, updatesList = new ArrayList<>()); + } else { + updatesList = UPDATES.get(dimension); + } + updatesList.add(blockEntity.getBlockPos()); + } + } + + public static void syncVisualUpdates() { + for (var entry : UPDATES.entrySet()) { + var pendingUpdates = entry.getValue(); + + for (var updatePos : pendingUpdates) { + var chunk = entry.getKey(); + + if (chunk.getBlockEntity(updatePos) instanceof EBlockEntity blockEntity) { + // packet uses strong reference + NetworkHandler.CHANNEL.send(PacketDistributor.TRACKING_CHUNK.with(entry::getKey), new VisualUpdateMessage(updatePos, blockEntity, null)); + } + } + + pendingUpdates.clear(); + } + } +} diff --git a/src/main/java/thedarkcolour/exdeorum/network/VoidWorldMessage.java b/src/main/java/thedarkcolour/exdeorum/network/VoidWorldMessage.java index c4ecc178..4e1f29d8 100644 --- a/src/main/java/thedarkcolour/exdeorum/network/VoidWorldMessage.java +++ b/src/main/java/thedarkcolour/exdeorum/network/VoidWorldMessage.java @@ -35,7 +35,7 @@ public class VoidWorldMessage { return new VoidWorldMessage(); } - public void handle(Supplier ctxSupplier) { + public static void handle(VoidWorldMessage msg, Supplier ctxSupplier) { NetworkHandler.handle(ctxSupplier, ctx -> { DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> ClientMessageHandler::disableVoidFogRendering); }); diff --git a/src/main/java/thedarkcolour/exdeorum/recipe/BarrelFluidMixingRecipeCache.java b/src/main/java/thedarkcolour/exdeorum/recipe/BarrelFluidMixingRecipeCache.java index 737a8683..196101e9 100644 --- a/src/main/java/thedarkcolour/exdeorum/recipe/BarrelFluidMixingRecipeCache.java +++ b/src/main/java/thedarkcolour/exdeorum/recipe/BarrelFluidMixingRecipeCache.java @@ -24,13 +24,14 @@ import org.jetbrains.annotations.Nullable; import thedarkcolour.exdeorum.recipe.barrel.BarrelFluidMixingRecipe; import thedarkcolour.exdeorum.registry.ERecipeTypes; -import java.util.IdentityHashMap; +import java.util.HashMap; +import java.util.Map; // for now, only simple recipes public class BarrelFluidMixingRecipeCache { private RecipeManager recipeManager; @Nullable - private IdentityHashMap> recipes; + private Map> recipes; public BarrelFluidMixingRecipeCache(RecipeManager recipeManager) { this.recipeManager = recipeManager; @@ -49,10 +50,10 @@ public class BarrelFluidMixingRecipeCache { } private void buildRecipes() { - this.recipes = new IdentityHashMap<>(); + var recipes = new HashMap>(); for (var recipe : this.recipeManager.byType(ERecipeTypes.BARREL_FLUID_MIXING.get()).values()) { - recipes.computeIfAbsent(recipe.baseFluid, key -> new IdentityHashMap<>()).put(recipe.additiveFluid, recipe); + recipes.computeIfAbsent(recipe.baseFluid, key -> new HashMap<>()).put(recipe.additiveFluid, recipe); } this.recipeManager = null; diff --git a/src/main/java/thedarkcolour/exdeorum/recipe/SieveRecipeCache.java b/src/main/java/thedarkcolour/exdeorum/recipe/SieveRecipeCache.java index 3990a4ad..17f8a6b3 100644 --- a/src/main/java/thedarkcolour/exdeorum/recipe/SieveRecipeCache.java +++ b/src/main/java/thedarkcolour/exdeorum/recipe/SieveRecipeCache.java @@ -28,13 +28,13 @@ import thedarkcolour.exdeorum.registry.ERecipeTypes; import java.util.ArrayList; import java.util.HashMap; -import java.util.IdentityHashMap; import java.util.List; +import java.util.Map; public class SieveRecipeCache { private RecipeManager recipeManager; @Nullable - private IdentityHashMap meshCaches; + private Map meshCaches; public SieveRecipeCache(RecipeManager recipeManager) { this.recipeManager = recipeManager; @@ -54,7 +54,7 @@ public class SieveRecipeCache { for (var recipe : recipeManager.byType(ERecipeTypes.SIEVE.get()).values()) { tempMap.computeIfAbsent(recipe.mesh, k -> new ArrayList<>()).add(recipe); } - this.meshCaches = new IdentityHashMap<>(); + this.meshCaches = new HashMap<>(); for (var mesh : tempMap.entrySet()) { this.meshCaches.put(mesh.getKey(), new MeshRecipeCache(mesh.getValue())); } @@ -64,14 +64,14 @@ public class SieveRecipeCache { // For now, there will be no complex recipes. What that means is, recipes may not use NBT information of the // block being sifted to decide what its drops are. Firstly, this would be complicated for me to code. Secondly, // conveying this information in JEI would be difficult (ex. Bottle drops from Sand, but only if the Sand has a - // certain NBT tag). Thirdly, I do not see anybody needing this use case, and if they do, they should contact + // certain enchantment). Thirdly, I do not see anybody needing this use case, and if they do, they should contact // me on GitHub or Discord so that I can get around to actually implementing it. private static class MeshRecipeCache { - private final IdentityHashMap> simpleRecipes; + private final Map> simpleRecipes; private MeshRecipeCache(List recipes) { - this.simpleRecipes = new IdentityHashMap<>(); - var temp = new IdentityHashMap>(); + this.simpleRecipes = new HashMap<>(); + var temp = new HashMap>(); for (var recipe : recipes) { for (var item : recipe.ingredient.getItems()) { diff --git a/src/main/java/thedarkcolour/exdeorum/recipe/SingleIngredientRecipeCache.java b/src/main/java/thedarkcolour/exdeorum/recipe/SingleIngredientRecipeCache.java index 1b540b46..1ae2bc44 100644 --- a/src/main/java/thedarkcolour/exdeorum/recipe/SingleIngredientRecipeCache.java +++ b/src/main/java/thedarkcolour/exdeorum/recipe/SingleIngredientRecipeCache.java @@ -26,15 +26,16 @@ import net.minecraft.world.item.crafting.RecipeType; import org.jetbrains.annotations.Nullable; import java.util.Collection; -import java.util.IdentityHashMap; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.function.Supplier; public class SingleIngredientRecipeCache { private final Supplier> recipeType; private RecipeManager recipeManager; @Nullable - private IdentityHashMap simpleRecipes; + private Map simpleRecipes; @Nullable private List complexRecipes; @Nullable @@ -98,7 +99,7 @@ public class SingleIngredientRecipeCache { * have been scanned, the {@link #recipeManager} is set to null, since it is no longer needed. */ private void buildRecipes() { - this.simpleRecipes = new IdentityHashMap<>(); + this.simpleRecipes = new HashMap<>(); var complexRecipes = ImmutableList.builder(); var allRecipes = this.recipeManager.byType(recipeType.get()).values(); diff --git a/src/main/java/thedarkcolour/exdeorum/recipe/barrel/package-info.java b/src/main/java/thedarkcolour/exdeorum/recipe/barrel/package-info.java new file mode 100644 index 00000000..6893e45d --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/recipe/barrel/package-info.java @@ -0,0 +1,21 @@ +/* + * Ex Deorum + * Copyright (c) 2023 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +@net.minecraft.MethodsReturnNonnullByDefault +@javax.annotation.ParametersAreNonnullByDefault +package thedarkcolour.exdeorum.recipe.barrel; diff --git a/src/main/java/thedarkcolour/exdeorum/recipe/crucible/package-info.java b/src/main/java/thedarkcolour/exdeorum/recipe/crucible/package-info.java new file mode 100644 index 00000000..ed0dae49 --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/recipe/crucible/package-info.java @@ -0,0 +1,21 @@ +/* + * Ex Deorum + * Copyright (c) 2023 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +@net.minecraft.MethodsReturnNonnullByDefault +@javax.annotation.ParametersAreNonnullByDefault +package thedarkcolour.exdeorum.recipe.crucible; diff --git a/src/main/java/thedarkcolour/exdeorum/recipe/hammer/package-info.java b/src/main/java/thedarkcolour/exdeorum/recipe/hammer/package-info.java index a8b6d472..a6070afc 100644 --- a/src/main/java/thedarkcolour/exdeorum/recipe/hammer/package-info.java +++ b/src/main/java/thedarkcolour/exdeorum/recipe/hammer/package-info.java @@ -16,12 +16,6 @@ * along with this program. If not, see . */ -/** - * This package contains all data generation for Ex Deorum. It is not to be used outside of data generation. - *

- * Ex Deorum uses the ModKit library, which adds several utilities primarily for data generation - * without needing to be shaded or depended upon outside a development environment. - */ @net.minecraft.MethodsReturnNonnullByDefault @javax.annotation.ParametersAreNonnullByDefault package thedarkcolour.exdeorum.recipe.hammer; diff --git a/src/main/java/thedarkcolour/exdeorum/recipe/package-info.java b/src/main/java/thedarkcolour/exdeorum/recipe/package-info.java index 308d7f38..5ca3d7d3 100644 --- a/src/main/java/thedarkcolour/exdeorum/recipe/package-info.java +++ b/src/main/java/thedarkcolour/exdeorum/recipe/package-info.java @@ -16,12 +16,6 @@ * along with this program. If not, see . */ -/** - * This package contains all data generation for Ex Deorum. It is not to be used outside of data generation. - *

- * Ex Deorum uses the ModKit library, which adds several utilities primarily for data generation - * without needing to be shaded or depended upon outside a development environment. - */ @net.minecraft.MethodsReturnNonnullByDefault @javax.annotation.ParametersAreNonnullByDefault package thedarkcolour.exdeorum.recipe; diff --git a/src/main/java/thedarkcolour/exdeorum/recipe/sieve/FinishedSieveRecipe.java b/src/main/java/thedarkcolour/exdeorum/recipe/sieve/FinishedSieveRecipe.java index 17da4389..b0def25e 100644 --- a/src/main/java/thedarkcolour/exdeorum/recipe/sieve/FinishedSieveRecipe.java +++ b/src/main/java/thedarkcolour/exdeorum/recipe/sieve/FinishedSieveRecipe.java @@ -49,19 +49,19 @@ public class FinishedSieveRecipe implements EFinishedRecipe { @Override public void serializeRecipeData(JsonObject object) { - object.add("ingredient", ingredient.toJson()); + object.add("ingredient", this.ingredient.toJson()); object.addProperty("mesh", ForgeRegistries.ITEMS.getKey(this.mesh).toString()); this.result.ifLeft(item -> { object.addProperty("result", ForgeRegistries.ITEMS.getKey(item).toString()); }).ifRight(tag -> { object.addProperty("result_tag", tag.location().toString()); }); - object.add("result_amount", LootDataType.PREDICATE.parser().toJsonTree(resultAmount)); + object.add("result_amount", LootDataType.PREDICATE.parser().toJsonTree(this.resultAmount)); } @Override public ResourceLocation getId() { - return id; + return this.id; } @Override diff --git a/src/main/java/thedarkcolour/exdeorum/recipe/sieve/SieveRecipe.java b/src/main/java/thedarkcolour/exdeorum/recipe/sieve/SieveRecipe.java index 4b72986e..c122186a 100644 --- a/src/main/java/thedarkcolour/exdeorum/recipe/sieve/SieveRecipe.java +++ b/src/main/java/thedarkcolour/exdeorum/recipe/sieve/SieveRecipe.java @@ -43,11 +43,13 @@ import thedarkcolour.exdeorum.registry.ERecipeTypes; public class SieveRecipe extends ProbabilityRecipe { public final Item mesh; + public final boolean byHandOnly; - public SieveRecipe(ResourceLocation id, Ingredient ingredient, Item mesh, Item result, NumberProvider resultAmount) { + public SieveRecipe(ResourceLocation id, Ingredient ingredient, Item mesh, Item result, NumberProvider resultAmount, boolean byHandOnly) { super(id, ingredient, result, resultAmount); this.mesh = mesh; + this.byHandOnly = byHandOnly; } @Override @@ -83,7 +85,8 @@ public class SieveRecipe extends ProbabilityRecipe { } NumberProvider resultAmount = RecipeUtil.readNumberProvider(json, "result_amount"); - return new SieveRecipe(id, ingredient, mesh, result, resultAmount); + boolean byHandOnly = json.has("by_hand_only") && json.get("by_hand_only").getAsBoolean(); + return new SieveRecipe(id, ingredient, mesh, result, resultAmount, byHandOnly); } @Override @@ -92,7 +95,7 @@ public class SieveRecipe extends ProbabilityRecipe { Item mesh = buffer.readById(BuiltInRegistries.ITEM); Item result = buffer.readById(BuiltInRegistries.ITEM); NumberProvider resultAmount = RecipeUtil.fromNetworkNumberProvider(buffer); - return new SieveRecipe(id, ingredient, mesh, result, resultAmount); + return new SieveRecipe(id, ingredient, mesh, result, resultAmount, buffer.readBoolean()); } @Override @@ -101,6 +104,7 @@ public class SieveRecipe extends ProbabilityRecipe { buffer.writeId(BuiltInRegistries.ITEM, recipe.mesh); buffer.writeId(BuiltInRegistries.ITEM, recipe.result); RecipeUtil.toNetworkNumberProvider(buffer, recipe.resultAmount); + buffer.writeBoolean(recipe.byHandOnly); } } } diff --git a/src/main/java/thedarkcolour/exdeorum/registry/EBlockEntities.java b/src/main/java/thedarkcolour/exdeorum/registry/EBlockEntities.java index f5a9b507..edcab94f 100644 --- a/src/main/java/thedarkcolour/exdeorum/registry/EBlockEntities.java +++ b/src/main/java/thedarkcolour/exdeorum/registry/EBlockEntities.java @@ -24,8 +24,10 @@ import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.RegistryObject; import thedarkcolour.exdeorum.ExDeorum; import thedarkcolour.exdeorum.blockentity.BarrelBlockEntity; +import thedarkcolour.exdeorum.blockentity.CompressedSieveBlockEntity; import thedarkcolour.exdeorum.blockentity.InfestedLeavesBlockEntity; import thedarkcolour.exdeorum.blockentity.LavaCrucibleBlockEntity; +import thedarkcolour.exdeorum.blockentity.MechanicalSieveBlockEntity; import thedarkcolour.exdeorum.blockentity.SieveBlockEntity; import thedarkcolour.exdeorum.blockentity.WaterCrucibleBlockEntity; @@ -154,4 +156,5 @@ public class EBlockEntities { EBlocks.MAPLE_SIEVE.get(), EBlocks.CRYSTALLIZED_SIEVE.get() ).build(null)); + public static final RegistryObject> MECHANICAL_SIEVE = BLOCK_ENTITIES.register("mechanical_sieve", () -> BlockEntityType.Builder.of(MechanicalSieveBlockEntity::new, EBlocks.MECHANICAL_SIEVE.get()).build(null)); } diff --git a/src/main/java/thedarkcolour/exdeorum/registry/EBlocks.java b/src/main/java/thedarkcolour/exdeorum/registry/EBlocks.java index fb3a65a4..e55b0cd4 100644 --- a/src/main/java/thedarkcolour/exdeorum/registry/EBlocks.java +++ b/src/main/java/thedarkcolour/exdeorum/registry/EBlocks.java @@ -119,6 +119,8 @@ public class EBlocks { public static final RegistryObject DUSK_SIEVE = registerSieve("dusk_sieve"); public static final RegistryObject MAPLE_SIEVE = registerSieve("maple_sieve"); public static final RegistryObject CRYSTALLIZED_SIEVE = registerSieve("crystallized_sieve", SoundType.GLASS); + // Mechanical Sieve (todo add properties) + public static final RegistryObject MECHANICAL_SIEVE = BLOCKS.register("mechanical_sieve", () -> new MechanicalSieveBlock(of())); // Lava Crucibles public static final RegistryObject PORCELAIN_CRUCIBLE = registerLavaCrucible("porcelain_crucible", true, SoundType.STONE); diff --git a/src/main/java/thedarkcolour/exdeorum/registry/EItems.java b/src/main/java/thedarkcolour/exdeorum/registry/EItems.java index 632eab0a..10e2004c 100644 --- a/src/main/java/thedarkcolour/exdeorum/registry/EItems.java +++ b/src/main/java/thedarkcolour/exdeorum/registry/EItems.java @@ -225,6 +225,8 @@ public class EItems { public static final RegistryObject DUSK_SIEVE = registerItemBlock(EBlocks.DUSK_SIEVE); public static final RegistryObject MAPLE_SIEVE = registerItemBlock(EBlocks.MAPLE_SIEVE); public static final RegistryObject CRYSTALLIZED_SIEVE = registerItemBlock(EBlocks.CRYSTALLIZED_SIEVE); + // Mechanical Sieves + public static final RegistryObject MECHANICAL_SIEVE = registerItemBlock(EBlocks.MECHANICAL_SIEVE); // Lava Crucibles public static final RegistryObject PORCELAIN_CRUCIBLE = registerItemBlock(EBlocks.PORCELAIN_CRUCIBLE); @@ -364,6 +366,7 @@ public class EItems { output.accept(MAPLE_SIEVE.get()); output.accept(CRYSTALLIZED_SIEVE.get()); } + output.accept(MECHANICAL_SIEVE.get()); output.accept(PORCELAIN_CRUCIBLE.get()); output.accept(WARPED_CRUCIBLE.get()); diff --git a/src/main/java/thedarkcolour/exdeorum/registry/ELootFunctions.java b/src/main/java/thedarkcolour/exdeorum/registry/ELootFunctions.java index 3a79232d..5912d288 100644 --- a/src/main/java/thedarkcolour/exdeorum/registry/ELootFunctions.java +++ b/src/main/java/thedarkcolour/exdeorum/registry/ELootFunctions.java @@ -23,9 +23,12 @@ import net.minecraft.world.level.storage.loot.functions.LootItemFunctionType; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.RegistryObject; import thedarkcolour.exdeorum.ExDeorum; -import thedarkcolour.exdeorum.loot.InfestedStringCount; +import thedarkcolour.exdeorum.loot.InfestedStringFunction; +import thedarkcolour.exdeorum.loot.MachineLootFunction; public class ELootFunctions { public static final DeferredRegister LOOT_FUNCTIONS = DeferredRegister.create(Registries.LOOT_FUNCTION_TYPE, ExDeorum.ID); - public static final RegistryObject INFESTED_STRING = LOOT_FUNCTIONS.register("infested_string", () -> new LootItemFunctionType(new InfestedStringCount.LootSerializer())); + + public static final RegistryObject INFESTED_STRING = LOOT_FUNCTIONS.register("infested_string", () -> new LootItemFunctionType(new InfestedStringFunction.LootSerializer())); + public static final RegistryObject MACHINE = LOOT_FUNCTIONS.register("machine", () -> new LootItemFunctionType(new MachineLootFunction.LootSerializer())); } diff --git a/src/main/java/thedarkcolour/exdeorum/registry/EMenus.java b/src/main/java/thedarkcolour/exdeorum/registry/EMenus.java new file mode 100644 index 00000000..a8750394 --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/registry/EMenus.java @@ -0,0 +1,34 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.registry; + +import net.minecraft.core.registries.Registries; +import net.minecraft.world.flag.FeatureFlags; +import net.minecraft.world.inventory.MenuType; +import net.minecraftforge.network.IContainerFactory; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.RegistryObject; +import thedarkcolour.exdeorum.ExDeorum; +import thedarkcolour.exdeorum.menu.MechanicalSieveMenu; + +public class EMenus { + public static final DeferredRegister> MENUS = DeferredRegister.create(Registries.MENU, ExDeorum.ID); + + public static final RegistryObject> MECHANICAL_SIEVE = MENUS.register("mechanical_sieve", () -> new MenuType<>((IContainerFactory) MechanicalSieveMenu::new, FeatureFlags.DEFAULT_FLAGS)); +} diff --git a/src/main/java/thedarkcolour/exdeorum/tag/EBiomeTags.java b/src/main/java/thedarkcolour/exdeorum/tag/EBiomeTags.java index fe61bc8d..2b5dd825 100644 --- a/src/main/java/thedarkcolour/exdeorum/tag/EBiomeTags.java +++ b/src/main/java/thedarkcolour/exdeorum/tag/EBiomeTags.java @@ -34,35 +34,35 @@ import java.util.Map; public class EBiomeTags { public static final Map, RegistryObject>> TREE_TAGS = new HashMap<>(); - // Vanilla - public static final TagKey OAK_TREE_BIOMES = addTreeTag("oak_tree_biomes", TreeFeatures.OAK_BEES_005.location()); - public static final TagKey SPRUCE_TREE_BIOMES = addTreeTag("spruce_tree_biomes", TreeFeatures.SPRUCE.location()); - public static final TagKey BIRCH_TREE_BIOMES = addTreeTag("birch_tree_biomes", TreeFeatures.BIRCH_BEES_002.location()); - public static final TagKey JUNGLE_TREE_BIOMES = addTreeTag("jungle_tree_biomes", TreeFeatures.JUNGLE_TREE_NO_VINE.location()); - public static final TagKey ACACIA_TREE_BIOMES = addTreeTag("acacia_tree_biomes", TreeFeatures.ACACIA.location()); - public static final TagKey CHERRY_TREE_BIOMES = addTreeTag("cherry_tree_biomes", TreeFeatures.CHERRY_BEES_005.location()); - public static final TagKey DARK_OAK_TREE_BIOMES = addTreeTag("dark_oak_tree_biomes", TreeFeatures.DARK_OAK.location()); - public static final TagKey MANGROVE_TREE_BIOMES = addTreeTag("mangrove_tree_biomes", TreeFeatures.MANGROVE.location()); + static { + // Vanilla + addTreeTag("oak_tree_biomes", TreeFeatures.OAK_BEES_005.location()); + addTreeTag("spruce_tree_biomes", TreeFeatures.SPRUCE.location()); + addTreeTag("birch_tree_biomes", TreeFeatures.BIRCH_BEES_002.location()); + addTreeTag("jungle_tree_biomes", TreeFeatures.JUNGLE_TREE_NO_VINE.location()); + addTreeTag("acacia_tree_biomes", TreeFeatures.ACACIA.location()); + addTreeTag("cherry_tree_biomes", TreeFeatures.CHERRY_BEES_005.location()); + addTreeTag("dark_oak_tree_biomes", TreeFeatures.DARK_OAK.location()); + addTreeTag("mangrove_tree_biomes", TreeFeatures.MANGROVE.location()); + // Bop tags + addTreeTag("flowering_oak_tree_biomes", new ResourceLocation(ModIds.BIOMES_O_PLENTY, "flowering_oak_tree_bees")); + addTreeTag("mahogany_tree_biomes", new ResourceLocation(ModIds.BIOMES_O_PLENTY, "mahogany_tree")); + addTreeTag("jacaranda_tree_biomes", new ResourceLocation(ModIds.BIOMES_O_PLENTY, "jacaranda_tree_bees")); + addTreeTag("palm_tree_biomes", new ResourceLocation(ModIds.BIOMES_O_PLENTY, "palm_tree")); + addTreeTag("willow_tree_biomes", new ResourceLocation(ModIds.BIOMES_O_PLENTY, "willow_tree")); + addTreeTag("dead_tree_biomes", new ResourceLocation(ModIds.BIOMES_O_PLENTY, "dead_tree_wasteland")); + addTreeTag("magic_tree_biomes", new ResourceLocation(ModIds.BIOMES_O_PLENTY, "magic_tree")); + addTreeTag("umbran_tree_biomes", new ResourceLocation(ModIds.BIOMES_O_PLENTY, "umbran_tree")); + } - // Bop tags - public static final TagKey FLOWERING_OAK_TREE_BIOMES = addTreeTag("flowering_oak_tree_biomes", new ResourceLocation(ModIds.BIOMES_O_PLENTY, "flowering_oak_tree_bees")); - public static final TagKey MAHOGANY_TREE_BIOMES = addTreeTag("mahogany_tree_biomes", new ResourceLocation(ModIds.BIOMES_O_PLENTY, "mahogany_tree")); - public static final TagKey JACARANDA_TREE_BIOMES = addTreeTag("jacaranda_tree_biomes", new ResourceLocation(ModIds.BIOMES_O_PLENTY, "jacaranda_tree_bees")); - public static final TagKey PALM_TREE_BIOMES = addTreeTag("palm_tree_biomes", new ResourceLocation(ModIds.BIOMES_O_PLENTY, "palm_tree")); - public static final TagKey WILLOW_TREE_BIOMES = addTreeTag("willow_tree_biomes", new ResourceLocation(ModIds.BIOMES_O_PLENTY, "willow_tree")); - public static final TagKey DEAD_TREE_BIOMES = addTreeTag("dead_tree_biomes", new ResourceLocation(ModIds.BIOMES_O_PLENTY, "dead_tree_wasteland")); - public static final TagKey MAGIC_TREE_BIOMES = addTreeTag("magic_tree_biomes", new ResourceLocation(ModIds.BIOMES_O_PLENTY, "magic_tree")); - public static final TagKey UMBRAN_TREE_BIOMES = addTreeTag("umbran_tree_biomes", new ResourceLocation(ModIds.BIOMES_O_PLENTY, "umbran_tree")); - - public static TagKey addTreeTag(String tagName, ResourceLocation id) { + private static void addTreeTag(String tagName, ResourceLocation id) { var tag = tag(tagName); if (TREE_TAGS.put(tag, RegistryObject.createOptional(id, Registries.CONFIGURED_FEATURE, ExDeorum.ID)) != null) { throw new IllegalStateException("Already added a tree tag under " + tag); } - return tag; } - public static TagKey tag(String name) { + private static TagKey tag(String name) { return TagKey.create(Registries.BIOME, new ResourceLocation(ExDeorum.ID, name)); } } diff --git a/src/main/resources/assets/exdeorum/blockstates/mechanical_sieve.json b/src/main/resources/assets/exdeorum/blockstates/mechanical_sieve.json new file mode 100644 index 00000000..388bdb16 --- /dev/null +++ b/src/main/resources/assets/exdeorum/blockstates/mechanical_sieve.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "exdeorum:block/mechanical_sieve" + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/exdeorum/models/block/mechanical_sieve.bbmodel b/src/main/resources/assets/exdeorum/models/block/mechanical_sieve.bbmodel new file mode 100644 index 00000000..3780c39a --- /dev/null +++ b/src/main/resources/assets/exdeorum/models/block/mechanical_sieve.bbmodel @@ -0,0 +1 @@ +{"meta":{"format_version":"4.9","model_format":"java_block","box_uv":false},"name":"mechanical_sieve","parent":"block/block","ambientocclusion":true,"front_gui_light":false,"visible_box":[1,1,0],"variable_placeholders":"","variable_placeholder_buttons":[],"unhandled_root_fields":{},"resolution":{"width":32,"height":32},"elements":[{"name":"cube","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[0,8,0],"to":[16,11,16],"autouv":0,"color":6,"origin":[0,0,0],"faces":{"north":{"uv":[0,10,32,16],"texture":1},"east":{"uv":[0,10,32,16],"texture":1},"south":{"uv":[0,10,32,16],"texture":1},"west":{"uv":[0,10,32,16],"texture":1},"up":{"uv":[0,0,32,32],"texture":2},"down":{"uv":[0,0,32,32],"texture":3}},"type":"cube","uuid":"873749a3-db29-f3c3-04d1-fffa055afac1"},{"name":"cube","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[0,11,14],"to":[16,16,16],"autouv":0,"color":6,"origin":[0,0,0],"faces":{"north":{"uv":[0,16,32,26],"texture":1},"east":{"uv":[0,0,4,10],"texture":1},"south":{"uv":[0,0,32,10],"texture":1},"west":{"uv":[28,0,32,10],"texture":1},"up":{"uv":[0,0,0,0],"texture":null},"down":{"uv":[0,0,0,0],"texture":null}},"type":"cube","uuid":"80b18281-5a54-1f3d-5254-f9bb95b2823d"},{"name":"cube","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[0,11,0],"to":[16,16,2],"autouv":0,"color":6,"origin":[0,0,0],"faces":{"north":{"uv":[0,0,32,10],"texture":1},"east":{"uv":[28,0,32,10],"texture":1},"south":{"uv":[0,16,32,26],"texture":1},"west":{"uv":[0,0,4,10],"texture":1},"up":{"uv":[0,0,0,0],"texture":null},"down":{"uv":[0,0,0,0],"texture":null}},"type":"cube","uuid":"74cdfb5d-2f29-430b-f364-c1b6e054c01d"},{"name":"cube","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[14,11,2],"to":[16,16,14],"autouv":0,"color":6,"origin":[0,0,0],"faces":{"north":{"uv":[0,0,0,0],"texture":null},"east":{"uv":[4,0,28,10],"texture":1},"south":{"uv":[0,0,0,0],"texture":null},"west":{"uv":[4,16,28,26],"texture":1},"up":{"uv":[0,0,0,0],"texture":null},"down":{"uv":[0,0,0,0],"texture":null}},"type":"cube","uuid":"7b50dd92-b51b-c945-6721-c158bb97d195"},{"name":"cube","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[0,11,2],"to":[2,16,14],"autouv":0,"color":6,"origin":[0,0,0],"faces":{"north":{"uv":[0,0,2,5],"texture":null},"east":{"uv":[4,16,28,26],"texture":1},"south":{"uv":[0,0,2,5],"texture":null},"west":{"uv":[4,0,28,10],"texture":1},"up":{"uv":[0,0,0,0],"texture":null},"down":{"uv":[0,0,2,12],"texture":null}},"type":"cube","uuid":"f78f4f46-3837-6be2-ea5a-50d956b59f43"},{"name":"cube","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[0,16,0],"to":[16,16,16],"autouv":1,"color":9,"origin":[0,0,0],"faces":{"north":{"uv":[0,0,0,0],"texture":null},"east":{"uv":[0,0,0,0],"texture":null},"south":{"uv":[0,0,0,0],"texture":null},"west":{"uv":[0,0,0,0],"texture":null},"up":{"uv":[0,0,32,32],"texture":0},"down":{"uv":[0,0,0,0],"texture":null}},"type":"cube","uuid":"13135bc9-6729-10c1-f047-49522065f6ea"},{"name":"cube","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[1,0,1],"to":[3,8,3],"autouv":0,"color":5,"origin":[0,0,0],"faces":{"north":{"uv":[0,0,2,8],"texture":4},"east":{"uv":[2,0,4,8],"texture":4},"south":{"uv":[4,0,6,8],"texture":4},"west":{"uv":[6,0,8,8],"texture":4},"up":{"uv":[0,0,0,0],"texture":null},"down":{"uv":[10,0,8,2],"texture":4}},"type":"cube","uuid":"dd170df8-ad97-d6f5-456f-b90fbef01fc0"},{"name":"cube","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[1,0,13],"to":[3,8,15],"autouv":0,"color":5,"origin":[0,0,0],"faces":{"north":{"uv":[0,0,2,8],"texture":4},"east":{"uv":[2,0,4,8],"texture":4},"south":{"uv":[4,0,6,8],"texture":4},"west":{"uv":[6,0,8,8],"texture":4},"up":{"uv":[0,0,0,0],"texture":null},"down":{"uv":[10,0,8,2],"texture":4}},"type":"cube","uuid":"98ff96a6-4795-1922-70f2-bea74ab08042"},{"name":"cube","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[13,0,1],"to":[15,8,3],"autouv":0,"color":5,"origin":[0,0,0],"faces":{"north":{"uv":[0,0,2,8],"texture":4},"east":{"uv":[2,0,4,8],"texture":4},"south":{"uv":[4,0,6,8],"texture":4},"west":{"uv":[6,0,8,8],"texture":4},"up":{"uv":[0,0,0,0],"texture":null},"down":{"uv":[10,0,8,2],"texture":4}},"type":"cube","uuid":"a9b7297d-bcc0-7d2c-20f1-d6108b51dbce"},{"name":"cube","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[13,0,13],"to":[15,8,15],"autouv":0,"color":5,"origin":[0,0,0],"faces":{"north":{"uv":[0,0,2,8],"texture":4},"east":{"uv":[2,0,4,8],"texture":4},"south":{"uv":[4,0,6,8],"texture":4},"west":{"uv":[6,0,8,8],"texture":4},"up":{"uv":[0,0,0,0],"texture":null},"down":{"uv":[10,0,8,2],"texture":4}},"type":"cube","uuid":"da8590e1-7d29-8fb4-191f-20a2b0022c30"}],"outliner":["873749a3-db29-f3c3-04d1-fffa055afac1","80b18281-5a54-1f3d-5254-f9bb95b2823d","7b50dd92-b51b-c945-6721-c158bb97d195","f78f4f46-3837-6be2-ea5a-50d956b59f43","74cdfb5d-2f29-430b-f364-c1b6e054c01d","13135bc9-6729-10c1-f047-49522065f6ea","dd170df8-ad97-d6f5-456f-b90fbef01fc0","98ff96a6-4795-1922-70f2-bea74ab08042","da8590e1-7d29-8fb4-191f-20a2b0022c30","a9b7297d-bcc0-7d2c-20f1-d6108b51dbce"],"textures":[{"path":"C:\\Users\\Max\\floobits\\share\\DJRoundTable\\Ex-Nihilo-Reborn\\src\\main\\resources\\assets\\exdeorum\\textures\\block\\mechanical_sieve_top.png","name":"mechanical_sieve_top.png","folder":"block","namespace":"exdeorum","id":"0","width":16,"height":16,"uv_width":16,"uv_height":16,"particle":true,"layers_enabled":false,"sync_to_project":"","render_mode":"default","render_sides":"auto","frame_time":1,"frame_order_type":"loop","frame_order":"","frame_interpolate":false,"visible":true,"internal":true,"saved":true,"uuid":"40b931c8-30ec-b0a3-59c4-f3a95beb9d5f","relative_path":"../../../textures/block/mechanical_sieve_top.png","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAgxJREFUOE+Vk8FvEkEUxr+3xcASlEijtU3UVBOpHBQX2uhBj5yWBEvXmBT+Bk6cuMJJEvgjoCEcxJtpw0Gs5UBiwk1rNRrbGsFENGF12V0cM0NaNWnBfpfZfe/tb7+ZN48KhQKzLAtcuq7D4XDAMAy4XC4Rs20bPE9EcLvd6Ha7Iu7xeEQd5XI5FggEIBGJBJdvehp6vw9JksQ7B3DQYDAASZL4yQ9dx5udnRFAURTMhpRDwKSH15++gbbbaLVaoGw2y8LhMC4otxA8P/PHxhhKpb3NznY+oNlsgvL5PFtaXMSZ6wv/ANrdDjsOmHn8lN0+ZaNer4MymQzTNA2Ymz0EjPuYG+OAJWmAF1tboGKxyEKhEE77rwkAt/cw6B+7FQ4Ik4FarQZKpVJMVVWcu3njv8/gUeMlm//yHtVqFZROp1kkEjkRgDu48nUf6xsboGQyyRKrq5g5QRe4g8uddyiVSqBkIsHi8TiM+QAm7f3gcDlgbvcVymtrI0AsFsPVe3fHnsHfneGAS5/folKpjABqNArmD050cHC3eKf4TSyXyyBtZUV0wRm8I/K73/u46PWI9SjxHNfH9Sd41miAoqrK7i8vi0nr9Xpwy7IoME0TPw0DjDH4fD54vV7IsixquPb39vB8cxP0QNOYmDjbhtPpFBPHp+/XcChiXLLLBdOy4Jiagj0cilXUmiZ+A17+9tFx+dCDAAAAAElFTkSuQmCC"},{"path":"C:\\Users\\Max\\floobits\\share\\DJRoundTable\\Ex-Nihilo-Reborn\\src\\main\\resources\\assets\\exdeorum\\textures\\block\\mechanical_sieve_side.png","name":"mechanical_sieve_side.png","folder":"block","namespace":"exdeorum","id":"1","width":16,"height":16,"uv_width":16,"uv_height":16,"particle":false,"layers_enabled":false,"sync_to_project":"","render_mode":"default","render_sides":"auto","frame_time":1,"frame_order_type":"loop","frame_order":"","frame_interpolate":false,"visible":true,"internal":true,"saved":true,"uuid":"24bd1ff8-0c80-35ed-34a2-dace0aa41ec5","relative_path":"../../../textures/block/mechanical_sieve_side.png","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAjFJREFUOE+lU01rWkEUPWOeH08EQZdpuoqIBhfpIrixQlxEIV0YSPf+AsGtayEEQQXxT0ggXQquEhDcCAFB8WlETVGiiS6iPp+aKXMhtraU0vZu5s29M+eed+4Zlkql+HK5hIjpdApJkqCqKkwmE+VWqxVEnTEGs9mMx8dHylssFjrHEokEd7vd0DFGBRE2ux3TlxfodDraCwABtFgswHQ6ajKbTtFQFLBkMskPDg7Q6XQQCoXQ6/XQaDRwcnJC3e7u7nB8fIxut4tKpQKPx0PsxL5UKoHF43Hu8/ngcrlwf3+PVquFQCBAnQuFAoLBIB56PTRbLbzb3SUGtVoNDocDxWIRLJ1O8w+Hh3jlHM1mkzoPh0PU63V4vV70+31i5HQ66ZcUwS4YRLlcRj6fB4tGo/yjz4fhaAS/34/BYABFUbC/vw9ZltFut3F0dERA1WoV7/f2IOn1mEwmuLq6Avt0esrDZ2ek8Hg8hlmWib6maZirKjjnsNlssFqtBCjOiPj68ICb21uwz+fnnJRerWA0Gklpofrrek05EbLJBG25hLSzg9V6TSud1TRsZnd5ecn/xgeZTIbubgC+XF9zMf/npyfywVvMZ7OND97yo9EI4XB4GyCXy3G73Q6DwbC5/POH0MVkNOJ5PEYkEtkGuLi44EIsIZoQ9C2EZYVL1cWCxjifzyEYxGKxbYBsNstnsxn0kkSq/zgF4TzxFgQ7MWax/gLwW95/KHx/Qf+I8N8A3wCZ2huN0EHuuQAAAABJRU5ErkJggg=="},{"path":"C:\\Users\\Max\\floobits\\share\\DJRoundTable\\Ex-Nihilo-Reborn\\src\\main\\resources\\assets\\exdeorum\\textures\\block\\mechanical_sieve_top_inner.png","name":"mechanical_sieve_top_inner.png","folder":"block","namespace":"exdeorum","id":"2","width":16,"height":16,"uv_width":16,"uv_height":16,"particle":false,"layers_enabled":false,"sync_to_project":"","render_mode":"default","render_sides":"auto","frame_time":1,"frame_order_type":"loop","frame_order":"","frame_interpolate":false,"visible":true,"internal":true,"saved":true,"uuid":"9d98f47b-0e7a-3415-e191-7c7030f4e84b","relative_path":"../../../textures/block/mechanical_sieve_top_inner.png","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAo9JREFUOE91U01LW0EUPZNETUJKJKn9CRVc+tFNJX7Uqkis70VTF5rf4MqVO6uLgJBs7EKx1mJE3LiuuKhVUxEq1WVTKILdSEWoJs3Xy9hzZdz1wvBm7rvn3HvP3FGpVEpXKhXQ8vk8PB4PisUivF6v+KrVKvhfKQW/34/Ly0vxBwIBiVPz8/O6paUFLqXkBy0UDiN/ewuXyyVnEpCoVCpBuVySpJDP43sud0/Q2tqKD2trCDY2PpD8b8PMvy4uYNk2jo+Poebm5nR7ezvSqRS+nZ4KjpndbreUTSuXy+Lj2XEcDA0NYXx8HNlsFmphYUE/6+hAMpnE15MTCeSqr6+H1lpALJ97gmmDAwOwYzHs7u5CzczM6Hg8jjezszjMZqU/ggkkiGaA9FGPl319sG0bB4eHUOl0Wre1teHt4qI4CKqrq0NDQwNqtZpk55l7I+aL3l7RYHt7G2pqakpHo1G8X13F/sGBBBJMDdg7jVdKIv6jr7enB69GRrC1tQU1PT2t+/v78W5lRVpgBWyD5VILgtgS2zBV9HR3IxKJ4OPODlQikdCTExNYWlrCl6MjyUwCLqOBITItUYOBwUGsr69DJSYn9ejoKJaXl+UWCCQJ+ybQlG7E5NkasdDZ+RyZjY17AsuyhOD07EyyGgKSsXQu+s1VxuwYIl0RbG5u3hNEh4dFRDNIzEYSY6zEaECfbdno6u5CJpOBio+NyS3kcj9QKBRwdfUb4fBjlMol3Py5QdOTJuHhQwo8CuD857m8iebmp/i0twc1HI1qThUDrq+v4ff5Hsb3b7EoZYdCIQSDQfh8Pomh8T183t+Heh2Py7hVqlW5f7JzYGqOIz6az+tFuVKBx+1G1XHkK7H/5uQOQ9hr4OroOUoAAAAASUVORK5CYII="},{"path":"C:\\Users\\Max\\floobits\\share\\DJRoundTable\\Ex-Nihilo-Reborn\\src\\main\\resources\\assets\\exdeorum\\textures\\block\\mechanical_sieve_bottom.png","name":"mechanical_sieve_bottom.png","folder":"block","namespace":"exdeorum","id":"3","width":16,"height":16,"uv_width":16,"uv_height":16,"particle":false,"layers_enabled":false,"sync_to_project":"","render_mode":"default","render_sides":"auto","frame_time":1,"frame_order_type":"loop","frame_order":"","frame_interpolate":false,"visible":true,"internal":true,"saved":true,"uuid":"e8768cd5-700e-9fe3-8b01-8db068fba8f0","relative_path":"../../../textures/block/mechanical_sieve_bottom.png","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAupJREFUOE89U8lOYlEQPY9BhoUmGsEEMbZDIxoXJk4Lo1EXonkacdoo7tyYmNgLf8EfEYy2xNhLXLXNyoUkDgFlEgQcoR1QGplep4rESl5ebqXuqXNO3RJEUZRUFRX4/PxEoViEQi7nf4VSiX/ZLCiUCgVkcjmUSiVUKhXXUo5CsFqt0vLyMmSCwAmK6poafLy/QyaT8Tmfz6NQKPBFQSaDQqFA5uMDOzs7ECYmJqS1tTVEo1GMj48jFovB7/djbGwMj4+POD09xcjICG5ubuDxeNDZ2Qm1Ws1n594ehKmpKWl1dRVmsxnhcBihUAijo6Pc2eVywWKxIB6LIRgKod5gYAY+nw+tra2wb22VJfxYX0dJkhAMBrnz09MTLi8v0d/fj7u7O2ZkMplYUoDYWSw4Pj7Gz93dsoTNzU14vV709vZyZ7fbjfb2dhSLRc4TOwqSSTWvr6/Myul0liVsbGygsbER4VAIkWiUu5FRDw8P0Ol0DESMBgYGkEgkcH19jaqqKuzv70NYWFiQVlZWUFlZibOzM3R1dSGbzeLt7Q0dHR0MQh+xIIOppqenB7e3t3A4HGUPyESiRx2en59xcXHBE6Fu5+fnLIdG6b+6gk6vR21tLXxeL1yHhxBsNpu0tLiIb01NjJ5OpzE4OMhAZJ7BYGA5BDQ8PMwTuL+/R3V1Nex2OwTb0pI0OzsLjVaLQCDAJv1NpdgLY309v8r3dBrd3d08ykQ8ju8mE/8d29tlgOnpaTwlkxgaGmJ0AmppaYFGo2HDCJTGSdIajEYolEq8vLyUXyIBiJOTbJ7n5ATJZBLGhgZIpRKSqRT6+voQiUT4kVFNJpNhaXq9vmzi/NycJIoimpqbuYO5rQ2lUomL6urqeP7xRIJZEDiZTU/718EBfh8dQZgURck6MwOtVsvGaTUavpTL5XgbJUliw2juJIlqKMiDP243hIX5eYk3rlD4WlUaWalY5ByFRq1GLp//WnVaeV7rXA7/AUDcnJe1lik0AAAAAElFTkSuQmCC"},{"path":"C:\\Users\\Max\\floobits\\share\\DJRoundTable\\Ex-Nihilo-Reborn\\src\\main\\resources\\assets\\exdeorum\\textures\\block\\mechanical_sieve_legs.png","name":"mechanical_sieve_legs.png","folder":"block","namespace":"exdeorum","id":"4","width":32,"height":32,"uv_width":32,"uv_height":32,"particle":false,"layers_enabled":false,"sync_to_project":"","render_mode":"default","render_sides":"auto","frame_time":1,"frame_order_type":"loop","frame_order":"","frame_interpolate":false,"visible":true,"internal":true,"saved":true,"uuid":"f9617dac-ea25-19f3-11f4-de0e5524d378","relative_path":"../../../textures/block/mechanical_sieve_legs.png","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAL9JREFUWEftk7EKgzAURW8MmKGZ7F85CC5O5uvqZAXBin/lFgeF+EoL7RSwSN/2XM6QBM49oHJ1TTj4/LKg73t1dO/MuXoLKAUQIcZ5nnHNMtyahk/gtdBeLoiRa/mnlnLO0bquMMYgxrZtWZZ/BcqypBACtNaIcRgGXoGqqsh7D2stYpymiVegKAoyaYp12xDjvet4BfI8J50kCPuOGB/jyCtw5t/95xvWdb+IioAUkAJSQApIASkgBaSAFJACT0FybiEIQR/wAAAAAElFTkSuQmCC"}]} \ No newline at end of file diff --git a/src/main/resources/assets/exdeorum/models/block/mechanical_sieve.json b/src/main/resources/assets/exdeorum/models/block/mechanical_sieve.json new file mode 100644 index 00000000..b92a6b8d --- /dev/null +++ b/src/main/resources/assets/exdeorum/models/block/mechanical_sieve.json @@ -0,0 +1,115 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "texture_size": [32, 32], + "render_type": "cutout_mipped", + "textures": { + "0": "exdeorum:block/mechanical_sieve_top", + "1": "exdeorum:block/mechanical_sieve_side", + "2": "exdeorum:block/mechanical_sieve_top_inner", + "3": "exdeorum:block/mechanical_sieve_bottom", + "4": "exdeorum:block/mechanical_sieve_legs", + "particle": "exdeorum:block/mechanical_sieve_top" + }, + "elements": [ + { + "from": [0, 8, 0], + "to": [16, 11, 16], + "faces": { + "north": {"uv": [0, 5, 16, 8], "texture": "#1"}, + "east": {"uv": [0, 5, 16, 8], "texture": "#1"}, + "south": {"uv": [0, 5, 16, 8], "texture": "#1"}, + "west": {"uv": [0, 5, 16, 8], "texture": "#1"}, + "up": {"uv": [0, 0, 16, 16], "texture": "#2"}, + "down": {"uv": [0, 0, 16, 16], "texture": "#3"} + } + }, + { + "from": [0, 11, 14], + "to": [16, 16, 16], + "faces": { + "north": {"uv": [0, 8, 16, 13], "texture": "#1"}, + "east": {"uv": [0, 0, 2, 5], "texture": "#1"}, + "south": {"uv": [0, 0, 16, 5], "texture": "#1"}, + "west": {"uv": [14, 0, 16, 5], "texture": "#1"} + } + }, + { + "from": [14, 11, 2], + "to": [16, 16, 14], + "faces": { + "east": {"uv": [2, 0, 14, 5], "texture": "#1"}, + "west": {"uv": [2, 8, 14, 13], "texture": "#1"} + } + }, + { + "from": [0, 11, 2], + "to": [2, 16, 14], + "faces": { + "east": {"uv": [2, 8, 14, 13], "texture": "#1"}, + "west": {"uv": [2, 0, 14, 5], "texture": "#1"} + } + }, + { + "from": [0, 11, 0], + "to": [16, 16, 2], + "faces": { + "north": {"uv": [0, 0, 16, 5], "texture": "#1"}, + "east": {"uv": [14, 0, 16, 5], "texture": "#1"}, + "south": {"uv": [0, 8, 16, 13], "texture": "#1"}, + "west": {"uv": [0, 0, 2, 5], "texture": "#1"} + } + }, + { + "from": [0, 16, 0], + "to": [16, 16, 16], + "faces": { + "up": {"uv": [0, 0, 16, 16], "texture": "#0"} + } + }, + { + "from": [1, 0, 1], + "to": [3, 8, 3], + "faces": { + "north": {"uv": [0, 0, 1, 4], "texture": "#4"}, + "east": {"uv": [1, 0, 2, 4], "texture": "#4"}, + "south": {"uv": [2, 0, 3, 4], "texture": "#4"}, + "west": {"uv": [3, 0, 4, 4], "texture": "#4"}, + "down": {"uv": [5, 0, 4, 1], "texture": "#4"} + } + }, + { + "from": [1, 0, 13], + "to": [3, 8, 15], + "faces": { + "north": {"uv": [0, 0, 1, 4], "texture": "#4"}, + "east": {"uv": [1, 0, 2, 4], "texture": "#4"}, + "south": {"uv": [2, 0, 3, 4], "texture": "#4"}, + "west": {"uv": [3, 0, 4, 4], "texture": "#4"}, + "down": {"uv": [5, 0, 4, 1], "texture": "#4"} + } + }, + { + "from": [13, 0, 13], + "to": [15, 8, 15], + "faces": { + "north": {"uv": [0, 0, 1, 4], "texture": "#4"}, + "east": {"uv": [1, 0, 2, 4], "texture": "#4"}, + "south": {"uv": [2, 0, 3, 4], "texture": "#4"}, + "west": {"uv": [3, 0, 4, 4], "texture": "#4"}, + "down": {"uv": [5, 0, 4, 1], "texture": "#4"} + } + }, + { + "from": [13, 0, 1], + "to": [15, 8, 3], + "faces": { + "north": {"uv": [0, 0, 1, 4], "texture": "#4"}, + "east": {"uv": [1, 0, 2, 4], "texture": "#4"}, + "south": {"uv": [2, 0, 3, 4], "texture": "#4"}, + "west": {"uv": [3, 0, 4, 4], "texture": "#4"}, + "down": {"uv": [5, 0, 4, 1], "texture": "#4"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/exdeorum/textures/gui/container/mechanical_sieve.png b/src/main/resources/assets/exdeorum/textures/gui/container/mechanical_sieve.png new file mode 100644 index 0000000000000000000000000000000000000000..801941e4d03a037e2ce12dc73846401df5304f6e GIT binary patch literal 3346 zcmds3i91yL8-LH3Gc#vq4CPu18A}Kg$u7*0kc!IM!c?Ou%9hG{$d)CQP^r+qFebTf zix?{Dmyk+|i=qvMmK!C0XLRpY)W<`jMXYkN2Vt|ydqN>^;VtI@T*+ZfY(N1>neNLx90~i>)Ko)5 zLttQ_r>Cd6xp`)0W(W!q6bPq47zIKpu$=-S6xfDB5DI)0yixE$!3zaX6g*H^jlwDv z+{xfZhMiQ1qd_bcVyF;Jg*{Z*MTICTL{eZ23V9fuVZdoZ=x~Y-C3GmF!wEVZ$KWUi zIDw);3I@p-?4>~x4H9X9(;$Hc8z>;4!X_$gqynD;{uJ<|fG-M(7-Z2QlMoCtFxZDd zItFPNq+)Q20hQ8lRT?S?VSPG|KQr5W~Qd5CMG5e7cRWSgj+1AX2DGs++e|VX}Bg0hcV!AI0gm=`uh3> z49I6d9s^=&psubyXU?2Z40aGggY7g3p}{5!7#SHE8XC@@KmRBbju66tcpBK)*eqJK z$lBW4%F3!t8tP@BP6os*sAWM73y#uZ!GZ)Yvn!W|J2G%v z1_~Ka$b@hVHdDdD!NK0%{x}1YFgU@0sfvnfB3NF29Tma>F3BKM06Y{p045Z)wzhWP zzI{_u(S0pzot&KF!cE-W-G2gjczA?|hkF4y6GPPjQ#p{4k>TRvGEXvN1w{$&A>L-? z0C_u?cskCvwY9Z3)vz&AwVtnRuBS-M;l-vGQN;2*a@C5}#7zEQKJbN?T1^}h3w8F5 z0ASThUc>@jtwq@MT^92^cKdxezAxs&{ou)gU>B zWCjMUtj;HGoDs~>WA`RzqOp_xsSBXi!uCIwO7)lr8d?7%fhJ1XvT3Sk+QLw->Z zU^7ZVp>rajO5*fIVkx5A5k$U9IeyoV^p}>h+O!IkIeLh(HVx-Yyjv!2-Eccq+V16@ z+!eO3pFCze=$>ITew8hFTA$NQ&Qh`rzmxvB#o}}6)fP)d<79hl*u*q}+YHg)F2;BI65PmoDA>FgiA>u7BkL_1}_k9p;;+qo4b`t`E?U zm?y*s1-(x~-E3@zXA#^yuJ4{bdmJ6_)~5tKK6hE4|DUT_j+rM5eT@hAOuOgSjwu=n zFJ1CmB^2U{egcE_-`gIPSAU-^prx>jHR^n-O3$4;SM9zY$t^JSui1DvBd?U{m&PoS zB{v;u_BGu*ntFqz@T6VnckBB@({1g-I0kV_Sy8D_SW{C|QlftF$T=aNv~#e%+<(eU ze{bF}H`Sd+O09nIT(qIDVfok6hu73(-#@;E+`zw??dYx!c#vPVwr8r>VY<9mntQFh zXpHOd?m1=Buca|#u*X`?D5kS2WKgM;*1(f+Wn@~U3@y1Z)+Z)~XUi;b6#PDNdLeSQ z=*W}y_IUa%dv#i%PyL0`Qh}%Y3?EoGpH<s z`kJ#seB8fK6x6FcUAgK_%*iZK zZzt*#0fpomq8B4&_@#gWdSkPSd)H}v-Cwzwq|BCfl~z}@KV0gK+w6*0-@lzKvn;;y z){@&ayp#Ql65p*R?v?MINGY-RM6mw z*zacDfU6|;Zt3VZc$4U5$2bSgr}z%qO)3^!Qu$@n#*1J$yNvdA8%uPma8wn0R{*b$ z6@Rl8XP#(Vk8GSUpmEp9fBY7Qe|25#wQj2WDBNj5zsKk_e#@FtJShLHc9%Ac=9a|Q zC_2lu{G(s3v-Re>{pveD_Fgn+5J_*LTF4iF%8-2}rx9I>Y1nECJn(kx#y&t^} zuOgC4Nm(+fPbLMlQJ0lAw#s*6ynJ%q(ne1zJnJ8a>S2a+v^{yUB0Ct_-J|rVf0lan zFw-S^X;bEqyt>jKCS>H~Tk_8q-VU<5 zS@OHlIOLZnb`!aQBhAC-^vp^MbG<#IH~#s;&hI%5q<$`mUu~v^lg0a`F*YLF zc^UPE(wi1+;RkVhi5sVbRNNLUn`!AYUC-Ys`CV=QK&JDH*WN$)G|7M~GtFhRJ;;Z| z|L_zEiz8}|+ghgp=($~Mq~s)H$H z_M!2S%{@zHe;_jXa(`9p3ShPPqmMQ+QdF!DZUu6!*yHX75y1J{Jhjt^2kfm;k@nQ8NDC43y|d; z&IH-?z8Rn4Jjmq`u%R!NA49f>JlA=K&t>0|x=XG!`AiSV^CMN^m8>T-TidrX%g_EU zf05K??d}R{{Ds!m6`wmL+A{ISs;aoy$U~p4No`i{%R#N=%h)N{K^?%jyx)3LB^`#J zG8S5pn#@~*#GKN^9lz=fkPhF^;jraeZ<5%Iu}2RlrM9!`bt$sJ$dC0ZR-vtTHz?f& z;+8~8e@}}RRWdRC#JiHo)&fc=@#WiPThWo zd&8pK BLOCKS = DeferredRegister.create(Registries.BLOCK, ID); - public static final DeferredRegister ITEMS = DeferredRegister.create(Registries.ITEM, ID); - - public static final RegistryObject TIN_ORE = BLOCKS.register("tin_ore", () -> new Block(BlockBehaviour.Properties.copy(Blocks.IRON_ORE))); - public static final RegistryObject TIN_ORE_ITEM = ITEMS.register("tin_ore", () -> new BlockItem(TIN_ORE.get(), new Item.Properties())); - - public OresTestMod() { - var modBus = FMLJavaModLoadingContext.get().getModEventBus(); - - BLOCKS.register(modBus); - ITEMS.register(modBus); - } -} diff --git a/src/test/resources/META-INF/mods.toml b/src/test/resources/META-INF/mods.toml deleted file mode 100644 index 328a033f..00000000 --- a/src/test/resources/META-INF/mods.toml +++ /dev/null @@ -1,13 +0,0 @@ -modLoader="javafml" -loaderVersion="[45,)" -license='MIT License' - -[[mods]] -modId="orestestmod" -version="${file.jarVersion}" -displayName="TestMod" -credits="" -authors="TheDarkColour" -description=''' -Blah -''' diff --git a/src/test/resources/data/forge/tags/blocks/ores/tin.json b/src/test/resources/data/forge/tags/blocks/ores/tin.json deleted file mode 100644 index edbb2203..00000000 --- a/src/test/resources/data/forge/tags/blocks/ores/tin.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "values": [ - "orestestmod:tin_ore" - ] -} \ No newline at end of file diff --git a/src/test/resources/data/forge/tags/items/ores/tin.json b/src/test/resources/data/forge/tags/items/ores/tin.json deleted file mode 100644 index edbb2203..00000000 --- a/src/test/resources/data/forge/tags/items/ores/tin.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "values": [ - "orestestmod:tin_ore" - ] -} \ No newline at end of file diff --git a/src/test/resources/pack.mcmeta b/src/test/resources/pack.mcmeta deleted file mode 100644 index 0ce61ede..00000000 --- a/src/test/resources/pack.mcmeta +++ /dev/null @@ -1,6 +0,0 @@ -{ - "pack": { - "description": "Ex Deorum Test resources", - "pack_format": 6 - } -}