diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d26d72..0608a4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# v1.3.0 +- Add Industrial Apiary [#13](https://github.com/GTModpackTeam/GTBeesMatrix/pull/13) + - Please report any unusual behavior. However, please note that the UI being null is by design. + +* * * + # v1.2.0 - Add Forestry Multifarm support for GregTech/GTFO saplings and GT fertilizer [#11](https://github.com/GTModpackTeam/GTBeesMatrix/pull/11) diff --git a/dependencies.gradle b/dependencies.gradle index 6a74ba3..bc75905 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -74,9 +74,9 @@ dependencies { } // Debug Binnies - compileOnly rfg.deobf("curse.maven:binnies-mods-patched-899182:5492997") // Binnie's Mods Patched 2.5.1.212 + compileOnly rfg.deobf("curse.maven:binnies-mods-patched-899182:7731146") // Binnie's Mods Patched 2.5.1.213 if (project.debug_all.toBoolean() || project.debug_binnies.toBoolean()) { - runtimeOnly rfg.deobf("curse.maven:binnies-mods-patched-899182:5492997") // Binnie's Mods Patched 2.5.1.212 + runtimeOnly rfg.deobf("curse.maven:binnies-mods-patched-899182:7731146") // Binnie's Mods Patched 2.5.1.213 } // Debug EnderIO diff --git a/src/main/java/com/github/gtexpert/gtbm/api/gui/GTBMGuiTextures.java b/src/main/java/com/github/gtexpert/gtbm/api/gui/GTBMGuiTextures.java index e347bd0..fb34a16 100644 --- a/src/main/java/com/github/gtexpert/gtbm/api/gui/GTBMGuiTextures.java +++ b/src/main/java/com/github/gtexpert/gtbm/api/gui/GTBMGuiTextures.java @@ -1,5 +1,7 @@ package com.github.gtexpert.gtbm.api.gui; +import net.minecraft.util.ResourceLocation; + import gregtech.api.gui.resources.TextureArea; public class GTBMGuiTextures { @@ -14,4 +16,16 @@ public class GTBMGuiTextures { .fullImage("textures/gui/icon/gtbm_logo_blinking_yellow.png"); public static final TextureArea GTBM_LOGO_BLINKING_RED = TextureArea .fullImage("textures/gui/icon/gtbm_logo_blinking_red.png"); + + // Bee status icon (from JEI) + public static final TextureArea BEE_INFO_ICON = new TextureArea( + new ResourceLocation("jei", "textures/gui/icons/info.png"), 0, 0, 1, 1); + + // Bee slot overlays (directly from Gendustry's hint icons) + public static final TextureArea QUEEN_OVERLAY = new TextureArea( + new ResourceLocation("gendustry", "textures/items/hints/queen.png"), 0, 0, 1, 1); + public static final TextureArea DRONE_OVERLAY = new TextureArea( + new ResourceLocation("gendustry", "textures/items/hints/drone.png"), 0, 0, 1, 1); + public static final TextureArea UPGRADE_OVERLAY = new TextureArea( + new ResourceLocation("gendustry", "textures/items/hints/upgrade.png"), 0, 0, 1, 1); } diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/binnies/botany/recipes/BotanyItemsRecipe.java b/src/main/java/com/github/gtexpert/gtbm/integration/binnies/botany/recipes/BotanyItemsRecipe.java index 177a8f5..63dc76a 100644 --- a/src/main/java/com/github/gtexpert/gtbm/integration/binnies/botany/recipes/BotanyItemsRecipe.java +++ b/src/main/java/com/github/gtexpert/gtbm/integration/binnies/botany/recipes/BotanyItemsRecipe.java @@ -9,15 +9,15 @@ import com.github.gtexpert.gtbm.api.util.Mods; import com.github.gtexpert.gtbm.integration.forestry.ForestryConfigHolder; -import com.github.gtexpert.gtbm.integration.forestry.ForestryUtility; import com.github.gtexpert.gtbm.integration.forestry.recipes.machines.CarpenterLoader; +import com.github.gtexpert.gtbm.integration.forestry.util.ForestryRecipeHelper; import forestry.api.recipes.RecipeManagers; public class BotanyItemsRecipe { public static void init() { - Enum recipeMode = ForestryUtility.recipeMode + Enum recipeMode = ForestryRecipeHelper.RecipeMode .safeValueOf(ForestryConfigHolder.gameMode); // Botanist Database @@ -25,7 +25,7 @@ public static void init() { Mods.Botany.getItem("database"), Mods.Botany.getItem("database")); - if (recipeMode == ForestryUtility.recipeMode.HARD) { + if (recipeMode == ForestryRecipeHelper.RecipeMode.HARD) { // Botanist Database CarpenterLoader.removeCarpenterRecipe(Mods.Botany.getItem("database")); RecipeManagers.carpenterManager.addRecipe( diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/binnies/extrabees/recipes/ExtraBeesItemsRecipe.java b/src/main/java/com/github/gtexpert/gtbm/integration/binnies/extrabees/recipes/ExtraBeesItemsRecipe.java index 419822d..6489d02 100644 --- a/src/main/java/com/github/gtexpert/gtbm/integration/binnies/extrabees/recipes/ExtraBeesItemsRecipe.java +++ b/src/main/java/com/github/gtexpert/gtbm/integration/binnies/extrabees/recipes/ExtraBeesItemsRecipe.java @@ -9,15 +9,15 @@ import com.github.gtexpert.gtbm.api.util.Mods; import com.github.gtexpert.gtbm.integration.forestry.ForestryConfigHolder; -import com.github.gtexpert.gtbm.integration.forestry.ForestryUtility; import com.github.gtexpert.gtbm.integration.forestry.recipes.machines.CarpenterLoader; +import com.github.gtexpert.gtbm.integration.forestry.util.ForestryRecipeHelper; import forestry.api.recipes.RecipeManagers; public class ExtraBeesItemsRecipe { public static void init() { - Enum recipeMode = ForestryUtility.recipeMode + Enum recipeMode = ForestryRecipeHelper.RecipeMode .safeValueOf(ForestryConfigHolder.gameMode); // Apiarist Database @@ -25,7 +25,7 @@ public static void init() { Mods.ExtraBees.getItem("dictionary"), Mods.ExtraBees.getItem("dictionary")); - if (recipeMode == ForestryUtility.recipeMode.HARD) { + if (recipeMode == ForestryRecipeHelper.RecipeMode.HARD) { // Apiarist Database CarpenterLoader.removeCarpenterRecipe(Mods.ExtraBees.getItem("dictionary")); RecipeManagers.carpenterManager.addRecipe( diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/binnies/extratrees/recipes/ExtraTreesItemsRecipe.java b/src/main/java/com/github/gtexpert/gtbm/integration/binnies/extratrees/recipes/ExtraTreesItemsRecipe.java index 1e4e41f..744eeba 100644 --- a/src/main/java/com/github/gtexpert/gtbm/integration/binnies/extratrees/recipes/ExtraTreesItemsRecipe.java +++ b/src/main/java/com/github/gtexpert/gtbm/integration/binnies/extratrees/recipes/ExtraTreesItemsRecipe.java @@ -9,15 +9,15 @@ import com.github.gtexpert.gtbm.api.util.Mods; import com.github.gtexpert.gtbm.integration.forestry.ForestryConfigHolder; -import com.github.gtexpert.gtbm.integration.forestry.ForestryUtility; import com.github.gtexpert.gtbm.integration.forestry.recipes.machines.CarpenterLoader; +import com.github.gtexpert.gtbm.integration.forestry.util.ForestryRecipeHelper; import forestry.api.recipes.RecipeManagers; public class ExtraTreesItemsRecipe { public static void init() { - Enum recipeMode = ForestryUtility.recipeMode + Enum recipeMode = ForestryRecipeHelper.RecipeMode .safeValueOf(ForestryConfigHolder.gameMode); // Arborist Database @@ -30,7 +30,7 @@ public static void init() { Mods.ExtraTrees.getItem("databaselepi"), Mods.ExtraTrees.getItem("databaselepi")); - if (recipeMode == ForestryUtility.recipeMode.HARD) { + if (recipeMode == ForestryRecipeHelper.RecipeMode.HARD) { // Arborist Database CarpenterLoader.removeCarpenterRecipe(Mods.ExtraTrees.getItem("databasetree")); RecipeManagers.carpenterManager.addRecipe( diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/binnies/genetics/recipes/GeneticsItemsRecipe.java b/src/main/java/com/github/gtexpert/gtbm/integration/binnies/genetics/recipes/GeneticsItemsRecipe.java index 5079e9e..62cbbcf 100644 --- a/src/main/java/com/github/gtexpert/gtbm/integration/binnies/genetics/recipes/GeneticsItemsRecipe.java +++ b/src/main/java/com/github/gtexpert/gtbm/integration/binnies/genetics/recipes/GeneticsItemsRecipe.java @@ -17,15 +17,15 @@ import com.github.gtexpert.gtbm.api.util.ModUtility; import com.github.gtexpert.gtbm.api.util.Mods; import com.github.gtexpert.gtbm.integration.forestry.ForestryConfigHolder; -import com.github.gtexpert.gtbm.integration.forestry.ForestryUtility; import com.github.gtexpert.gtbm.integration.forestry.recipes.machines.CarpenterLoader; +import com.github.gtexpert.gtbm.integration.forestry.util.ForestryRecipeHelper; import forestry.api.recipes.RecipeManagers; public class GeneticsItemsRecipe { public static void init() { - Enum recipeMode = ForestryUtility.recipeMode + Enum recipeMode = ForestryRecipeHelper.RecipeMode .safeValueOf(ForestryConfigHolder.gameMode); // Gene Database @@ -33,7 +33,7 @@ public static void init() { Mods.Genetics.getItem("geneticdatabase"), Mods.Genetics.getItem("geneticdatabase")); - if (recipeMode == ForestryUtility.recipeMode.HARD) { + if (recipeMode == ForestryRecipeHelper.RecipeMode.HARD) { // Reinforced Casing ModHandler.removeRecipeByName(Mods.Genetics.getResource("laboratory_casing")); RecipeMaps.ASSEMBLER_RECIPES.recipeBuilder() @@ -41,7 +41,7 @@ public static void init() { .inputs(Mods.Forestry.getItem("sturdy_machine")) .fluidInputs(Materials.Water.getFluid(1000)) .outputs(Mods.Genetics.getItem("misc")) - .EUt(VA[MV]).duration(ForestryUtility.timeCarpenter(75)).buildAndRegister(); + .EUt(VA[MV]).duration(ForestryRecipeHelper.timeCarpenter(75)).buildAndRegister(); RecipeManagers.carpenterManager.addRecipe( 75, Materials.Water.getFluid(5000), ItemStack.EMPTY, Mods.Genetics.getItem("misc"), diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/binnies/recipes/BinniesItemsRecipe.java b/src/main/java/com/github/gtexpert/gtbm/integration/binnies/recipes/BinniesItemsRecipe.java index 7867e89..5510d62 100644 --- a/src/main/java/com/github/gtexpert/gtbm/integration/binnies/recipes/BinniesItemsRecipe.java +++ b/src/main/java/com/github/gtexpert/gtbm/integration/binnies/recipes/BinniesItemsRecipe.java @@ -5,17 +5,17 @@ import com.github.gtexpert.gtbm.api.util.Mods; import com.github.gtexpert.gtbm.integration.forestry.ForestryConfigHolder; -import com.github.gtexpert.gtbm.integration.forestry.ForestryUtility; +import com.github.gtexpert.gtbm.integration.forestry.util.ForestryRecipeHelper; import forestry.api.recipes.RecipeManagers; public class BinniesItemsRecipe { public static void init() { - Enum recipeMode = ForestryUtility.recipeMode + Enum recipeMode = ForestryRecipeHelper.RecipeMode .safeValueOf(ForestryConfigHolder.gameMode); - if (recipeMode == ForestryUtility.recipeMode.HARD) { + if (recipeMode == ForestryRecipeHelper.RecipeMode.HARD) { // Registry ModHandler.removeRecipeByName(Mods.Genetics.getResource("registry")); RecipeManagers.carpenterManager.addRecipe( diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/forestry/ForestryConfigHolder.java b/src/main/java/com/github/gtexpert/gtbm/integration/forestry/ForestryConfigHolder.java index 2c573a6..e3547ff 100644 --- a/src/main/java/com/github/gtexpert/gtbm/integration/forestry/ForestryConfigHolder.java +++ b/src/main/java/com/github/gtexpert/gtbm/integration/forestry/ForestryConfigHolder.java @@ -16,15 +16,16 @@ public class ForestryConfigHolder { "valid: [NORMAL, HARD]" }) public static String gameMode = "NORMAL"; - @Config.Comment({ "If true, each will be uncraftable.", "default: false" }) - public static boolean Still = false, - Fabricator = false, - Centrifuge = false, - Bottler = false, - Fermenter = false, - Rainmaker = false, - Carpenter = false, - Moistener = false, - Raintank = false, - Squeezer = false; + @Config.Comment({ "If true, each will be uncraftable.", "default: true" }) + public static boolean still = true, + fabricator = true, + centrifuge = true, + bottler = true, + fermenter = true, + rainmaker = true, + carpenter = true, + moistener = true, + raintank = true, + squeezer = true, + farmBlock = true; } diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/forestry/ForestryModule.java b/src/main/java/com/github/gtexpert/gtbm/integration/forestry/ForestryModule.java index 864f1f5..1bf3ded 100644 --- a/src/main/java/com/github/gtexpert/gtbm/integration/forestry/ForestryModule.java +++ b/src/main/java/com/github/gtexpert/gtbm/integration/forestry/ForestryModule.java @@ -12,6 +12,7 @@ import com.github.gtexpert.gtbm.integration.forestry.loaders.FFMOreDictionaryLoader; import com.github.gtexpert.gtbm.integration.forestry.recipes.*; import com.github.gtexpert.gtbm.integration.forestry.recipes.machines.*; +import com.github.gtexpert.gtbm.integration.forestry.util.BeeHousingInfoProvider; import com.github.gtexpert.gtbm.module.Modules; @TModule( @@ -36,6 +37,11 @@ public void postInit(FMLPostInitializationEvent event) { CarpenterLoader.initMode(); CentrifugeLoader.init(); FabricatorLoader.init(); + + // Register TOP provider for bee housing health display + if (Mods.TheOneProbe.isModLoaded()) { + mcjty.theoneprobe.TheOneProbe.theOneProbeImp.registerProvider(new BeeHousingInfoProvider()); + } } @Override diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/forestry/recipes/FFMBlockRecipe.java b/src/main/java/com/github/gtexpert/gtbm/integration/forestry/recipes/FFMBlockRecipe.java index 7012389..232699a 100644 --- a/src/main/java/com/github/gtexpert/gtbm/integration/forestry/recipes/FFMBlockRecipe.java +++ b/src/main/java/com/github/gtexpert/gtbm/integration/forestry/recipes/FFMBlockRecipe.java @@ -1,7 +1,7 @@ package com.github.gtexpert.gtbm.integration.forestry.recipes; -import static com.github.gtexpert.gtbm.integration.forestry.ForestryUtility.feToEu; -import static com.github.gtexpert.gtbm.integration.forestry.ForestryUtility.timeCarpenter; +import static com.github.gtexpert.gtbm.integration.forestry.util.ForestryRecipeHelper.feToEu; +import static com.github.gtexpert.gtbm.integration.forestry.util.ForestryRecipeHelper.timeCarpenter; import static forestry.farming.ModuleFarming.getBlocks; import static gregtech.api.unification.ore.OrePrefix.*; @@ -19,6 +19,7 @@ import gregtech.common.items.MetaItems; import com.github.gtexpert.gtbm.api.util.Mods; +import com.github.gtexpert.gtbm.integration.forestry.ForestryConfigHolder; import forestry.core.ModuleCore; import forestry.core.items.EnumElectronTube; @@ -69,6 +70,8 @@ public static void blockCharcoal() { } public static void farm() { + if (!ForestryConfigHolder.farmBlock) return; + BlockRegistryFarming blocks = getBlocks(); ItemStack basic = blocks.farm.get(EnumFarmBlockType.PLAIN, 1); diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/forestry/recipes/FFMCraftingRecipe.java b/src/main/java/com/github/gtexpert/gtbm/integration/forestry/recipes/FFMCraftingRecipe.java index ac27db4..e7a448f 100644 --- a/src/main/java/com/github/gtexpert/gtbm/integration/forestry/recipes/FFMCraftingRecipe.java +++ b/src/main/java/com/github/gtexpert/gtbm/integration/forestry/recipes/FFMCraftingRecipe.java @@ -23,17 +23,17 @@ public static void init() { public static void recipeRemoval() { Map, String> recipes = new HashMap<>(); - recipes.put(() -> ForestryConfigHolder.Still, "still"); - recipes.put(() -> ForestryConfigHolder.Fabricator, "fabricator"); - recipes.put(() -> ForestryConfigHolder.Centrifuge, "centrifuge"); - recipes.put(() -> ForestryConfigHolder.Bottler, "bottler"); - recipes.put(() -> ForestryConfigHolder.Fermenter, "fermenter"); - recipes.put(() -> ForestryConfigHolder.Rainmaker, "Rainmaker"); - recipes.put(() -> ForestryConfigHolder.Carpenter, "carpenter"); - recipes.put(() -> ForestryConfigHolder.Moistener, "moistener"); - recipes.put(() -> ForestryConfigHolder.Raintank, "raintank"); - recipes.put(() -> ForestryConfigHolder.Squeezer, "squeezer"); - recipes.put(() -> ForestryConfigHolder.Fermenter, "fermenter"); + recipes.put(() -> ForestryConfigHolder.still, "still"); + recipes.put(() -> ForestryConfigHolder.fabricator, "fabricator"); + recipes.put(() -> ForestryConfigHolder.centrifuge, "centrifuge"); + recipes.put(() -> ForestryConfigHolder.bottler, "bottler"); + recipes.put(() -> ForestryConfigHolder.fermenter, "fermenter"); + recipes.put(() -> ForestryConfigHolder.rainmaker, "Rainmaker"); + recipes.put(() -> ForestryConfigHolder.carpenter, "carpenter"); + recipes.put(() -> ForestryConfigHolder.moistener, "moistener"); + recipes.put(() -> ForestryConfigHolder.raintank, "raintank"); + recipes.put(() -> ForestryConfigHolder.squeezer, "squeezer"); + recipes.put(() -> ForestryConfigHolder.fermenter, "fermenter"); recipes.forEach((config, name) -> { if (config.get()) { diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/forestry/recipes/machines/CarpenterLoader.java b/src/main/java/com/github/gtexpert/gtbm/integration/forestry/recipes/machines/CarpenterLoader.java index e36dd89..8e60a0a 100644 --- a/src/main/java/com/github/gtexpert/gtbm/integration/forestry/recipes/machines/CarpenterLoader.java +++ b/src/main/java/com/github/gtexpert/gtbm/integration/forestry/recipes/machines/CarpenterLoader.java @@ -23,7 +23,7 @@ import com.github.gtexpert.gtbm.api.util.ModUtility; import com.github.gtexpert.gtbm.api.util.Mods; import com.github.gtexpert.gtbm.integration.forestry.ForestryConfigHolder; -import com.github.gtexpert.gtbm.integration.forestry.ForestryUtility; +import com.github.gtexpert.gtbm.integration.forestry.util.ForestryRecipeHelper; import forestry.api.circuits.ICircuit; import forestry.api.recipes.RecipeManagers; @@ -46,10 +46,10 @@ public static void initBase() { } public static void initMode() { - Enum recipeMode = ForestryUtility.recipeMode + Enum recipeMode = ForestryRecipeHelper.RecipeMode .safeValueOf(ForestryConfigHolder.gameMode); - if (recipeMode == ForestryUtility.recipeMode.NORMAL) { + if (recipeMode == ForestryRecipeHelper.RecipeMode.NORMAL) { CoreNormal(); ApicultureNormal(); @@ -63,7 +63,7 @@ public static void initMode() { CarpenterLoader.registerCarpenterRecipe(recipeMode, Mods.Forestry.getItem("chipsets", 1, 3), 160, Materials.Electrum, MetaItems.PHENOLIC_BOARD); - } else if (recipeMode == ForestryUtility.recipeMode.HARD) { + } else if (recipeMode == ForestryRecipeHelper.RecipeMode.HARD) { CoreHard(); ApicultureHard(); ArboricultureHard(); @@ -133,7 +133,7 @@ private static void Core() { .fluidInputs(Materials.SeedOil.getFluid(250)) .circuitMeta(1) .outputs(Mods.Forestry.getItem("impregnated_casing")) - .EUt(50).duration(ForestryUtility.timeCarpenter(50)).buildAndRegister(); + .EUt(50).duration(ForestryRecipeHelper.timeCarpenter(50)).buildAndRegister(); // Escritoire RecipeMaps.ASSEMBLER_RECIPES.recipeBuilder() @@ -141,7 +141,7 @@ private static void Core() { .fluidInputs(Materials.SeedOil.getFluid(500)) .circuitMeta(10) .outputs(Mods.Forestry.getItem("escritoire")) - .EUt(50).duration(ForestryUtility.timeCarpenter(50)).buildAndRegister(); + .EUt(50).duration(ForestryRecipeHelper.timeCarpenter(50)).buildAndRegister(); // Impregnated Stick RecipeMaps.ASSEMBLER_RECIPES.recipeBuilder() @@ -149,7 +149,7 @@ private static void Core() { .fluidInputs(Materials.SeedOil.getFluid(100)) .circuitMeta(11) .outputs(Mods.Forestry.getItem("oak_stick")) - .EUt(10).duration(ForestryUtility.timeCarpenter(10)).buildAndRegister(); + .EUt(10).duration(ForestryRecipeHelper.timeCarpenter(10)).buildAndRegister(); // Bog Earth RecipeMaps.MIXER_RECIPES.recipeBuilder() @@ -159,7 +159,7 @@ private static void Core() { .fluidInputs(Materials.Water.getFluid(1000)) .circuitMeta(2) .outputs(Mods.Forestry.getItem("bog_earth", 8)) - .EUt(5).duration(ForestryUtility.timeCarpenter(5)).buildAndRegister(); + .EUt(5).duration(ForestryRecipeHelper.timeCarpenter(5)).buildAndRegister(); // Hardened Casing RecipeMaps.ASSEMBLER_RECIPES.recipeBuilder() @@ -167,52 +167,52 @@ private static void Core() { .inputs(Mods.Forestry.getItem("sturdy_machine")) .fluidInputs(Materials.Water.getFluid(1000)) .outputs(Mods.Forestry.getItem("hardened_machine")) - .EUt(75).duration(ForestryUtility.timeCarpenter(75)).buildAndRegister(); + .EUt(75).duration(ForestryRecipeHelper.timeCarpenter(75)).buildAndRegister(); // Ender Pearl RecipeMaps.CHEMICAL_RECIPES.recipeBuilder() .inputs(Mods.Forestry.getItem("crafting_material", 5, 1)) .outputs(new ItemStack(Items.ENDER_PEARL)) - .EUt(100).duration(ForestryUtility.timeCarpenter(100)).buildAndRegister(); + .EUt(100).duration(ForestryRecipeHelper.timeCarpenter(100)).buildAndRegister(); // Woven Silk RecipeMaps.ASSEMBLER_RECIPES.recipeBuilder() .inputs(Mods.Forestry.getItem("crafting_material", 9, 2)) .fluidInputs(Materials.Water.getFluid(500)) .outputs(Mods.Forestry.getItem("crafting_material", 1, 3)) - .EUt(10).duration(ForestryUtility.timeCarpenter(10)).buildAndRegister(); + .EUt(10).duration(ForestryRecipeHelper.timeCarpenter(10)).buildAndRegister(); // Tool RecipeMaps.PACKER_RECIPES.recipeBuilder() .inputs(Mods.Forestry.getItem("bronze_pickaxe")) .inputs(Mods.Forestry.getItem("carton")) .outputs(Mods.Forestry.getItem("kit_pickaxe")) - .EUt(20).duration(ForestryUtility.timeCarpenter(20)).buildAndRegister(); + .EUt(20).duration(ForestryRecipeHelper.timeCarpenter(20)).buildAndRegister(); RecipeMaps.PACKER_RECIPES.recipeBuilder() .inputs(Mods.Forestry.getItem("bronze_shovel")) .inputs(Mods.Forestry.getItem("carton")) .outputs(Mods.Forestry.getItem("kit_shovel")) - .EUt(20).duration(ForestryUtility.timeCarpenter(20)).buildAndRegister(); + .EUt(20).duration(ForestryRecipeHelper.timeCarpenter(20)).buildAndRegister(); RecipeMaps.ARC_FURNACE_RECIPES.recipeBuilder() .inputs(Mods.Forestry.getItem("broken_bronze_pickaxe")) .fluidInputs(Materials.Oxygen.getFluid(100)) .output(ingot, Materials.Bronze, 2) - .EUt(5).duration(ForestryUtility.timeCarpenter(5)).buildAndRegister(); + .EUt(5).duration(ForestryRecipeHelper.timeCarpenter(5)).buildAndRegister(); RecipeMaps.ARC_FURNACE_RECIPES.recipeBuilder() .inputs(Mods.Forestry.getItem("broken_bronze_shovel")) .fluidInputs(Materials.Oxygen.getFluid(100)) .output(ingot, Materials.Bronze) - .EUt(5).duration(ForestryUtility.timeCarpenter(5)).buildAndRegister(); + .EUt(5).duration(ForestryRecipeHelper.timeCarpenter(5)).buildAndRegister(); // Carton RecipeMaps.ASSEMBLER_RECIPES.recipeBuilder() .input(dust, Materials.Wood, 4) .fluidInputs(Materials.Water.getFluid(1000)) .outputs(Mods.Forestry.getItem("carton")) - .EUt(5).duration(ForestryUtility.timeCarpenter(5)).buildAndRegister(); + .EUt(5).duration(ForestryRecipeHelper.timeCarpenter(5)).buildAndRegister(); } private static void CoreNormal() { @@ -225,7 +225,7 @@ private static void CoreNormal() { .input(plate, Materials.RedAlloy, 2) .fluidInputs(Materials.Water.getFluid(2000)) .outputs(Mods.Forestry.getItem("portable_alyzer")) - .EUt(50).duration(ForestryUtility.timeCarpenter(50 * 4)).buildAndRegister(); + .EUt(50).duration(ForestryRecipeHelper.timeCarpenter(50 * 4)).buildAndRegister(); if (Mods.ForestryFactory.isModLoaded()) { RecipeManagers.carpenterManager.addRecipe( @@ -250,7 +250,7 @@ private static void CoreHard() { .input(plate, Materials.RedAlloy, 2) .fluidInputs(Materials.Water.getFluid(2000)) .outputs(Mods.Forestry.getItem("portable_alyzer")) - .EUt(50).duration(ForestryUtility.timeCarpenter(50 * 4)).buildAndRegister(); + .EUt(50).duration(ForestryRecipeHelper.timeCarpenter(50 * 4)).buildAndRegister(); if (Mods.ForestryFactory.isModLoaded()) { RecipeManagers.carpenterManager.addRecipe( @@ -275,7 +275,7 @@ private static void Apiculture() { .inputs(Mods.Forestry.getItem("pollen")) .fluidInputs(Fluids.FOR_HONEY.getFluid(500)) .outputs(Mods.Forestry.getItem("crafting_material", 1, 6)) - .EUt(50).duration(ForestryUtility.timeCarpenter(50)).buildAndRegister(); + .EUt(50).duration(ForestryRecipeHelper.timeCarpenter(50)).buildAndRegister(); // Candle RecipeMaps.ASSEMBLER_RECIPES.recipeBuilder() @@ -283,13 +283,13 @@ private static void Apiculture() { .inputs(Mods.Forestry.getItem("beeswax", 6)) .fluidInputs(Materials.Water.getFluid(600)) .outputs(Mods.Forestry.getItem("candle", 24)) - .EUt(30).duration(ForestryUtility.timeCarpenter(30)).buildAndRegister(); + .EUt(30).duration(ForestryRecipeHelper.timeCarpenter(30)).buildAndRegister(); RecipeMaps.ASSEMBLER_RECIPES.recipeBuilder() .inputs(Mods.Forestry.getItem("crafting_material", 1, 2)) .inputs(Mods.Forestry.getItem("beeswax", 6)) .fluidInputs(Materials.Water.getFluid(200)) .outputs(Mods.Forestry.getItem("candle", 6)) - .EUt(10).duration(ForestryUtility.timeCarpenter(10)).buildAndRegister(); + .EUt(10).duration(ForestryRecipeHelper.timeCarpenter(10)).buildAndRegister(); // Iodine Capsule RecipeMaps.ASSEMBLER_RECIPES.recipeBuilder() @@ -299,7 +299,7 @@ private static void Apiculture() { .input(dust, Materials.Gunpowder, 2) .fluidInputs(Materials.Water.getFluid(1000)) .outputs(Mods.Forestry.getItem("iodine_capsule")) - .EUt(5).duration(ForestryUtility.timeCarpenter(5)).buildAndRegister(); + .EUt(5).duration(ForestryRecipeHelper.timeCarpenter(5)).buildAndRegister(); // Dissipation Charge RecipeMaps.ASSEMBLER_RECIPES.recipeBuilder() @@ -309,7 +309,7 @@ private static void Apiculture() { .input(dust, Materials.Gunpowder, 2) .fluidInputs(Materials.Water.getFluid(1000)) .outputs(Mods.Forestry.getItem("crafting_material", 1, 4)) - .EUt(5).duration(ForestryUtility.timeCarpenter(5)).buildAndRegister(); + .EUt(5).duration(ForestryRecipeHelper.timeCarpenter(5)).buildAndRegister(); if (Mods.ForestryFactory.isModLoaded()) {} } @@ -330,7 +330,7 @@ private static void ApicultureHard() { .input("chestWood") .fluidInputs(Fluids.FOR_HONEY.getFluid(1000)) .outputs(Mods.Forestry.getItem("bee_chest")) - .EUt(60).duration(ForestryUtility.timeCarpenter(60)).buildAndRegister(); + .EUt(60).duration(ForestryRecipeHelper.timeCarpenter(60)).buildAndRegister(); // Apiariy ModHandler.removeRecipeByName(Mods.Forestry.getResource("apiary")); @@ -343,7 +343,7 @@ private static void ApicultureHard() { .input("fenceWood") .fluidInputs(Materials.SeedOil.getFluid(1000)) .outputs(Mods.Forestry.getItem("apiary")) - .EUt(60).duration(ForestryUtility.timeCarpenter(60)).buildAndRegister(); + .EUt(60).duration(ForestryRecipeHelper.timeCarpenter(60)).buildAndRegister(); // Alvearies ModHandler.removeRecipeByName(Mods.Forestry.getResource("alveary_swarmer")); @@ -362,7 +362,7 @@ private static void ApicultureHard() { .input(foil, Materials.RoseGold, 2) .fluidInputs(Fluids.FOR_HONEY.getFluid(5000)) .outputs(Mods.Forestry.getItem("alveary.swarmer")) - .EUt(60).duration(ForestryUtility.timeCarpenter(60)).buildAndRegister(); + .EUt(60).duration(ForestryRecipeHelper.timeCarpenter(60)).buildAndRegister(); // Fan RecipeMaps.ASSEMBLER_RECIPES.recipeBuilder() @@ -373,7 +373,7 @@ private static void ApicultureHard() { .input(MetaItems.ELECTRIC_MOTOR_MV, 2) .fluidInputs(Fluids.FOR_HONEY.getFluid(5000)) .outputs(Mods.Forestry.getItem("alveary.fan")) - .EUt(60).duration(ForestryUtility.timeCarpenter(60)).buildAndRegister(); + .EUt(60).duration(ForestryRecipeHelper.timeCarpenter(60)).buildAndRegister(); // Heater RecipeMaps.ASSEMBLER_RECIPES.recipeBuilder() @@ -384,7 +384,7 @@ private static void ApicultureHard() { .input(wireGtQuadruple, Materials.Cupronickel, 3) .fluidInputs(Fluids.FOR_HONEY.getFluid(5000)) .outputs(Mods.Forestry.getItem("alveary.heater")) - .EUt(60).duration(ForestryUtility.timeCarpenter(60)).buildAndRegister(); + .EUt(60).duration(ForestryRecipeHelper.timeCarpenter(60)).buildAndRegister(); // Hygroregulator RecipeMaps.ASSEMBLER_RECIPES.recipeBuilder() @@ -396,7 +396,7 @@ private static void ApicultureHard() { .input(plate, Materials.RedAlloy) .fluidInputs(Fluids.FOR_HONEY.getFluid(5000)) .outputs(Mods.Forestry.getItem("alveary.hygro")) - .EUt(60).duration(ForestryUtility.timeCarpenter(60)).buildAndRegister(); + .EUt(60).duration(ForestryRecipeHelper.timeCarpenter(60)).buildAndRegister(); // Stabiliser RecipeMaps.ASSEMBLER_RECIPES.recipeBuilder() @@ -407,7 +407,7 @@ private static void ApicultureHard() { .input(plate, Materials.CertusQuartz, 2) .fluidInputs(Fluids.FOR_HONEY.getFluid(5000)) .outputs(Mods.Forestry.getItem("alveary.stabiliser")) - .EUt(60).duration(ForestryUtility.timeCarpenter(60)).buildAndRegister(); + .EUt(60).duration(ForestryRecipeHelper.timeCarpenter(60)).buildAndRegister(); // Sieve RecipeMaps.ASSEMBLER_RECIPES.recipeBuilder() @@ -417,7 +417,7 @@ private static void ApicultureHard() { .inputs(GTUtility.copy(2, ModuleArboriculture.getItems().pollenFertile.getWildcard())) .fluidInputs(Fluids.FOR_HONEY.getFluid(5000)) .outputs(Mods.Forestry.getItem("alveary.sieve")) - .EUt(60).duration(ForestryUtility.timeCarpenter(60)).buildAndRegister(); + .EUt(60).duration(ForestryRecipeHelper.timeCarpenter(60)).buildAndRegister(); if (Mods.ForestryFactory.isModLoaded()) { // Apiarist's Chest @@ -515,7 +515,7 @@ private static void ArboricultureHard() { .input("chestWood") .fluidInputs(Materials.SeedOil.getFluid(1000)) .outputs(Mods.Forestry.getItem("tree_chest")) - .EUt(60).duration(ForestryUtility.timeCarpenter(60)).buildAndRegister(); + .EUt(60).duration(ForestryRecipeHelper.timeCarpenter(60)).buildAndRegister(); if (Mods.ForestryFactory.isModLoaded()) { RecipeManagers.carpenterManager.addRecipe( @@ -553,7 +553,7 @@ private static void Climatology() { .input(plate, Materials.Diamond) .fluidInputs(Materials.Water.getFluid(2000)) .outputs(Mods.Forestry.getItem("habitat_screen")) - .EUt(100).duration(ForestryUtility.timeCarpenter(100)).buildAndRegister(); + .EUt(100).duration(ForestryRecipeHelper.timeCarpenter(100)).buildAndRegister(); } private static void Factory() { @@ -580,7 +580,7 @@ private static void Factory() { .input(plate, Materials.Bronze) .fluidInputs(Materials.Water.getFluid(1000)) .outputs(Mods.Forestry.getItem("soldering_iron")) - .EUt(40).duration(ForestryUtility.timeCarpenter(40)).buildAndRegister(); + .EUt(40).duration(ForestryRecipeHelper.timeCarpenter(40)).buildAndRegister(); } private static void Mail() { @@ -594,7 +594,7 @@ private static void Mail() { .inputs(new ItemStack(Items.PAPER, 3)) .fluidInputs(Materials.SeedOil.getFluid(300)) .outputs(Mods.Forestry.getItem("stamps", 1, i)) - .EUt(10).duration(ForestryUtility.timeCarpenter(10)).buildAndRegister(); + .EUt(10).duration(ForestryRecipeHelper.timeCarpenter(10)).buildAndRegister(); } } @@ -609,7 +609,7 @@ private static void LepidopterologyHard() { .input("chestWood") .fluidInputs(Materials.SeedOil.getFluid(1000)) .outputs(Mods.Forestry.getItem("butterfly_chest")) - .EUt(60).duration(ForestryUtility.timeCarpenter(60)).buildAndRegister(); + .EUt(60).duration(ForestryRecipeHelper.timeCarpenter(60)).buildAndRegister(); if (Mods.ForestryFactory.isModLoaded()) { RecipeManagers.carpenterManager.addRecipe( @@ -646,7 +646,7 @@ private static void Storage() { .inputs(Mods.Forestry.getItem("crafting_material", 7, 3)) .fluidInputs(Materials.Water.getFluid(1000)) .outputs(backStackT2[i]) - .EUt(200).duration(ForestryUtility.timeCarpenter(200)).buildAndRegister(); + .EUt(200).duration(ForestryRecipeHelper.timeCarpenter(200)).buildAndRegister(); } } if (Mods.ForestryCrate.isModLoaded()) { @@ -655,19 +655,19 @@ private static void Storage() { .fluidInputs(Materials.Water.getFluid(1000)) .circuitMeta(10) .outputs(Mods.Forestry.getItem("crate")) - .EUt(20).duration(ForestryUtility.timeCarpenter(20)).buildAndRegister(); + .EUt(20).duration(ForestryRecipeHelper.timeCarpenter(20)).buildAndRegister(); } } public static void registerCarpenterRecipe(Enum recipeMode, ItemStack output, int EUt, Material material, MetaItem.MetaValueItem broad, String... circuit) { - if (recipeMode == ForestryUtility.recipeMode.NORMAL) { + if (recipeMode == ForestryRecipeHelper.RecipeMode.NORMAL) { RecipeMaps.CIRCUIT_ASSEMBLER_RECIPES.recipeBuilder() .input(plate, material, 6) .input(plate, Materials.RedAlloy, 2) .input(broad) .outputs(output) - .EUt(EUt).duration(ForestryUtility.timeCarpenter(EUt)).buildAndRegister(); + .EUt(EUt).duration(ForestryRecipeHelper.timeCarpenter(EUt)).buildAndRegister(); RecipeManagers.carpenterManager.addRecipe( EUt, Materials.SolderingAlloy.getFluid(72), ItemStack.EMPTY, output, @@ -683,7 +683,7 @@ public static void registerCarpenterRecipe(Enum recipeMode, ItemStack output, in 'T', new UnificationEntry(plate, Materials.RedAlloy).toString(), 'B', broad.getStackForm()); - } else if (recipeMode == ForestryUtility.recipeMode.HARD) { + } else if (recipeMode == ForestryRecipeHelper.RecipeMode.HARD) { if (circuit.length >= 2) { ModLog.logger.error("Circuit recipe for " + output.getDisplayName() + " has more than one circuit."); return; @@ -696,7 +696,7 @@ public static void registerCarpenterRecipe(Enum recipeMode, ItemStack output, in .input(circuit[0], 2) .input(broad) .outputs(output) - .EUt(EUt).duration(ForestryUtility.timeCarpenter(EUt)).buildAndRegister(); + .EUt(EUt).duration(ForestryRecipeHelper.timeCarpenter(EUt)).buildAndRegister(); RecipeManagers.carpenterManager.addRecipe( EUt, Materials.SolderingAlloy.getFluid(72), broad.getStackForm(), output, diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/forestry/recipes/machines/FabricatorLoader.java b/src/main/java/com/github/gtexpert/gtbm/integration/forestry/recipes/machines/FabricatorLoader.java index bdbc092..435c6d9 100644 --- a/src/main/java/com/github/gtexpert/gtbm/integration/forestry/recipes/machines/FabricatorLoader.java +++ b/src/main/java/com/github/gtexpert/gtbm/integration/forestry/recipes/machines/FabricatorLoader.java @@ -11,7 +11,7 @@ import gregtech.api.util.GTUtility; import com.github.gtexpert.gtbm.api.util.Mods; -import com.github.gtexpert.gtbm.integration.forestry.ForestryUtility; +import com.github.gtexpert.gtbm.integration.forestry.util.ForestryRecipeHelper; import binnie.extratrees.wood.EnumETLog; import forestry.api.arboriculture.*; @@ -57,7 +57,7 @@ public static void registerFireproofPlanksRecipe(ItemStack planks, ItemStack pla .inputs(Mods.Forestry.getItem("refractory_wax", 4)) .fluidInputs(Materials.Glass.getFluid(500)) .outputs(GTUtility.copy(5, planksFireproof)) - .EUt(10).duration(ForestryUtility.timeFabricator(10)).buildAndRegister(); + .EUt(10).duration(ForestryRecipeHelper.timeFabricator(10)).buildAndRegister(); } public static void registerFireproofLogsRecipe(ItemStack logs, ItemStack logsFireproof) { @@ -66,6 +66,6 @@ public static void registerFireproofLogsRecipe(ItemStack logs, ItemStack logsFir .inputs(Mods.Forestry.getItem("refractory_wax", 4)) .fluidInputs(Materials.Glass.getFluid(500)) .outputs(logsFireproof) - .EUt(10).duration(ForestryUtility.timeFabricator(10)).buildAndRegister(); + .EUt(10).duration(ForestryRecipeHelper.timeFabricator(10)).buildAndRegister(); } } diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/forestry/util/BeeHousingInfoProvider.java b/src/main/java/com/github/gtexpert/gtbm/integration/forestry/util/BeeHousingInfoProvider.java new file mode 100644 index 0000000..13e7e4f --- /dev/null +++ b/src/main/java/com/github/gtexpert/gtbm/integration/forestry/util/BeeHousingInfoProvider.java @@ -0,0 +1,65 @@ +package com.github.gtexpert.gtbm.integration.forestry.util; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; + +import org.jetbrains.annotations.NotNull; + +import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; + +import com.github.gtexpert.gtbm.api.ModValues; + +import forestry.api.apiculture.*; +import mcjty.theoneprobe.api.IProbeHitData; +import mcjty.theoneprobe.api.IProbeInfo; +import mcjty.theoneprobe.api.IProbeInfoProvider; +import mcjty.theoneprobe.api.ProbeMode; + +/** + * TOP provider that shows queen health for any GT MTE implementing IBeeHousing. + */ +public class BeeHousingInfoProvider implements IProbeInfoProvider { + + private static final int HEALTH_THRESHOLD_DIVISOR = 4; + private static final int HEALTH_COLOR_HIGH = 0xFF00D4CE; + private static final int HEALTH_COLOR_LOW = 0xFFBB1C28; + + @Override + public String getID() { + return ModValues.MODID + ":bee_housing_provider"; + } + + @Override + public void addProbeInfo(@NotNull ProbeMode mode, @NotNull IProbeInfo probeInfo, @NotNull EntityPlayer player, + @NotNull World world, @NotNull IBlockState blockState, @NotNull IProbeHitData data) { + TileEntity te = world.getTileEntity(data.getPos()); + if (!(te instanceof IGregTechTileEntity)) return; + + var mte = ((IGregTechTileEntity) te).getMetaTileEntity(); + if (!(mte instanceof IBeeHousing)) return; + + IBeeHousing housing = (IBeeHousing) mte; + IBeeHousingInventory inv = housing.getBeeInventory(); + if (inv.getQueen().isEmpty()) return; + + IBeeRoot beeRoot = BeeManager.beeRoot; + if (beeRoot == null) return; + + EnumBeeType type = beeRoot.getType(inv.getQueen()); + if (type == EnumBeeType.QUEEN) { + IBee bee = beeRoot.getMember(inv.getQueen()); + if (bee != null && bee.getMaxHealth() > 0) { + int health = bee.getHealth(); + int maxHealth = bee.getMaxHealth(); + int color = health > maxHealth / HEALTH_THRESHOLD_DIVISOR ? HEALTH_COLOR_HIGH : HEALTH_COLOR_LOW; + probeInfo.progress(health, maxHealth, probeInfo.defaultProgressStyle() + .suffix(" / " + maxHealth + " HP") + .filledColor(color) + .alternateFilledColor(color) + .borderColor(0xFF555555)); + } + } + } +} diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/forestry/util/BeeProductHelper.java b/src/main/java/com/github/gtexpert/gtbm/integration/forestry/util/BeeProductHelper.java new file mode 100644 index 0000000..f5a73ae --- /dev/null +++ b/src/main/java/com/github/gtexpert/gtbm/integration/forestry/util/BeeProductHelper.java @@ -0,0 +1,62 @@ +package com.github.gtexpert.gtbm.integration.forestry.util; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.items.IItemHandlerModifiable; + +import forestry.api.apiculture.EnumBeeType; +import forestry.api.apiculture.IBeeHousingInventory; +import forestry.api.apiculture.IBeeRoot; + +/** + * Reusable product distribution logic for bee housings. + * Handles automated bee re-insertion and output slot management. + */ +public class BeeProductHelper { + + /** + * Add a bee product to the housing's inventory. + * If automated, attempts to re-insert bees into queen/drone slots first. + * + * @param product the product to add + * @param beeRoot the bee root (nullable, skips automation if null) + * @param autoBreeding whether auto-breeding is enabled (princess auto-insertion) + * @param isAutomated whether automation upgrade is active (drone auto-insertion) + * @param beeInv the bee housing inventory (queen/drone access) + * @param importItems the import item handler (for drone merging) + * @param exportItems the export item handler (output slots) + * @return true if the product was fully inserted + */ + public static boolean addProduct(ItemStack product, IBeeRoot beeRoot, + boolean autoBreeding, boolean isAutomated, + IBeeHousingInventory beeInv, + IItemHandlerModifiable importItems, IItemHandlerModifiable exportItems) { + if (product.isEmpty()) return true; + + ItemStack remaining = product.copy(); + + if (beeRoot != null && beeRoot.isMember(remaining)) { + if (autoBreeding && (beeRoot.isMember(remaining, EnumBeeType.PRINCESS) || + beeRoot.isMember(remaining, EnumBeeType.QUEEN))) { + if (beeInv.getQueen().isEmpty()) { + beeInv.setQueen(remaining); + return true; + } + } + if ((autoBreeding || isAutomated) && beeRoot.isMember(remaining, EnumBeeType.DRONE)) { + if (beeInv.getDrone().isEmpty()) { + beeInv.setDrone(remaining); + return true; + } + ItemStack mergeResult = importItems.insertItem(1, remaining, false); + if (mergeResult.isEmpty()) return true; + remaining = mergeResult; + } + } + + for (int i = 0; i < exportItems.getSlots(); i++) { + remaining = exportItems.insertItem(i, remaining, false); + if (remaining.isEmpty()) return true; + } + return remaining.isEmpty(); + } +} diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/forestry/util/ForestryBeeHelper.java b/src/main/java/com/github/gtexpert/gtbm/integration/forestry/util/ForestryBeeHelper.java new file mode 100644 index 0000000..f5f1858 --- /dev/null +++ b/src/main/java/com/github/gtexpert/gtbm/integration/forestry/util/ForestryBeeHelper.java @@ -0,0 +1,25 @@ +package com.github.gtexpert.gtbm.integration.forestry.util; + +/** + * Utilities for Forestry bee work cycle timing and display. + */ +public class ForestryBeeHelper { + + /** Get ticks per bee work cycle from Forestry config. Default 550. */ + public static int getTicksPerWorkCycle() { + return forestry.apiculture.ModuleApiculture.ticksPerBeeWorkCycle; + } + + /** Effective ticks per health point, accounting for lifespan modifier. */ + public static int getEffectiveTicksPerHealth(float lifespanModifier) { + return Math.round(getTicksPerWorkCycle() * lifespanModifier); + } + + /** Format seconds as "Xm Ys" or "Xs". */ + public static String formatTime(int totalSeconds) { + if (totalSeconds >= 60) { + return String.format("%dm %ds", totalSeconds / 60, totalSeconds % 60); + } + return totalSeconds + "s"; + } +} diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/forestry/ForestryUtility.java b/src/main/java/com/github/gtexpert/gtbm/integration/forestry/util/ForestryRecipeHelper.java similarity index 54% rename from src/main/java/com/github/gtexpert/gtbm/integration/forestry/ForestryUtility.java rename to src/main/java/com/github/gtexpert/gtbm/integration/forestry/util/ForestryRecipeHelper.java index ef43c68..5661cf5 100644 --- a/src/main/java/com/github/gtexpert/gtbm/integration/forestry/ForestryUtility.java +++ b/src/main/java/com/github/gtexpert/gtbm/integration/forestry/util/ForestryRecipeHelper.java @@ -1,4 +1,4 @@ -package com.github.gtexpert.gtbm.integration.forestry; +package com.github.gtexpert.gtbm.integration.forestry.util; import org.jetbrains.annotations.NotNull; @@ -8,25 +8,41 @@ import forestry.api.core.ForestryAPI; -public class ForestryUtility { +/** + * Utilities for Forestry machine recipe timing and difficulty modes. + */ +public class ForestryRecipeHelper { public static float energyModifier = ForestryAPI.activeMode.getFloatSetting("energy.demand.modifier"); public static int feToEu = ConfigHolder.compat.energy.euToFeRatio; + /** + * Calculates the Carpenter recipe duration in ticks from EU/t. + * + * @param EUt the energy per tick + * @return duration in ticks + */ public static int timeCarpenter(int EUt) { return Math.round(EUt * 204 * energyModifier / (100 * feToEu)); } + /** + * Calculates the Thermionic Fabricator recipe duration in ticks from EU/t. + * + * @param EUt the energy per tick + * @return duration in ticks + */ public static int timeFabricator(int EUt) { return Math.round(200 * energyModifier / (EUt * feToEu)); } - public enum recipeMode { + /** Forestry recipe difficulty modes. Only NORMAL and HARD are actively supported. */ + public enum RecipeMode { - EASY("EASY"), // NO SUPPORT + EASY("EASY"), NORMAL("NORMAL"), HARD("HARD"), - OP("OP"); // NO SUPPORT + OP("OP"); private final String mode; @@ -34,18 +50,23 @@ public String getString() { return this.mode; } - recipeMode(final String mode) { + RecipeMode(final String mode) { this.mode = mode; } - public static recipeMode safeValueOf(@NotNull String name) { + /** + * Parses a recipe mode by name, falling back to NORMAL on invalid input. + * + * @param name the mode name to parse + * @return the matching recipe mode, or NORMAL if invalid + */ + public static RecipeMode safeValueOf(@NotNull String name) { if (name.isEmpty()) { ModLog.logger.error("Invalid recipe mode is empty! Set to default value.", new Throwable()); return NORMAL; } - try { - return recipeMode.valueOf(name); + return RecipeMode.valueOf(name); } catch (IllegalArgumentException e) { ModLog.logger.error("Invalid recipe mode! Set to default value. : {}", name, e, new Throwable()); return NORMAL; diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/GendustryConfigHolder.java b/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/GendustryConfigHolder.java index 2a1363e..6db54f6 100644 --- a/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/GendustryConfigHolder.java +++ b/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/GendustryConfigHolder.java @@ -11,15 +11,6 @@ category = "Gendustry") public class GendustryConfigHolder { - @Config.Comment({ "If true, each will be uncraftable.", "default: false" }) - public static boolean MutagenProducer = false, - Mutatron = false, - IndustrialApiary = false, - GeneticImprinter = false, - GeneticSampler = false, - AdvancedMutagen = false, - ProteinLiquifier = false, - DNAExtractor = false, - GeneticTransposer = false, - GeneticReplicator = false; + @Config.Comment({ "If true, each will be uncraftable.", "default: true" }) + public static boolean industrialApiary = true; } diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/metatileentities/GendustryMetaTileEntities.java b/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/metatileentities/GendustryMetaTileEntities.java index 905a9b0..e988e07 100644 --- a/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/metatileentities/GendustryMetaTileEntities.java +++ b/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/metatileentities/GendustryMetaTileEntities.java @@ -5,7 +5,6 @@ import static gregtech.common.metatileentities.MetaTileEntities.registerMetaTileEntities; import com.github.gtexpert.gtbm.client.GTBMTextures; -import com.github.gtexpert.gtbm.common.GTBMConfigHolder; import com.github.gtexpert.gtbm.integration.gendustry.GendustryRecipeMaps; public class GendustryMetaTileEntities { @@ -16,13 +15,11 @@ public static void init() { // INDUSTRIAL_APIARY 20000~20012 // TODO: IDの変更 - if (GTBMConfigHolder.featureFlag) { - registerMetaTileEntities(INDUSTRIAL_APIARY, 20000, "industrial_apiary", - (tier, voltageName) -> new MetaTileEntityIndustrialApiary( - gtbm(String.format("%s.%s", "industrial_apiary", voltageName)), - GendustryRecipeMaps.INDUSTRIAL_APIARY_RECIPES, - GTBMTextures.INDUSTRIAL_APIARY_OVERLAY, - tier, false, null)); - } + registerMetaTileEntities(INDUSTRIAL_APIARY, 20000, "industrial_apiary", + (tier, voltageName) -> new MetaTileEntityIndustrialApiary( + gtbm(String.format("%s.%s", "industrial_apiary", voltageName)), + GendustryRecipeMaps.INDUSTRIAL_APIARY_RECIPES, + GTBMTextures.INDUSTRIAL_APIARY_OVERLAY, + tier, false, null)); } } diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/metatileentities/IndustrialApiaryLogic.java b/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/metatileentities/IndustrialApiaryLogic.java index a9f7a72..f055d6c 100644 --- a/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/metatileentities/IndustrialApiaryLogic.java +++ b/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/metatileentities/IndustrialApiaryLogic.java @@ -2,6 +2,11 @@ import java.util.function.Supplier; +import net.bdew.gendustry.api.ApiaryModifiers; +import net.bdew.gendustry.api.items.IApiaryUpgrade; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + import org.jetbrains.annotations.NotNull; import gregtech.api.capability.IEnergyContainer; @@ -9,10 +14,306 @@ import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.recipes.RecipeMap; +import com.github.gtexpert.gtbm.integration.forestry.util.ForestryBeeHelper; + +import forestry.api.apiculture.*; +import forestry.core.errors.EnumErrorCode; + public class IndustrialApiaryLogic extends RecipeLogicEnergy { + private static final int BASE_EU_PER_TICK = 80; + private static final int PROGRESS_SYNC_TICKS = 20; + private static final int BREEDING_TIME_TICKS = 100; + + private IBeeRoot beeRoot; + private IBeekeepingLogic beekeepingLogic; + private boolean needsMovePrincess; + private int lastSyncedHealth = -1; + private int progressSyncCounter; + private int fxTickCounter; + public IndustrialApiaryLogic(@NotNull MetaTileEntity metaTileEntity, RecipeMap recipeMap, Supplier energyContainer) { super(metaTileEntity, recipeMap, energyContainer); } + + private MetaTileEntityIndustrialApiary getApiary() { + return (MetaTileEntityIndustrialApiary) metaTileEntity; + } + + public IBeeRoot getBeeRoot() { + if (beeRoot == null) { + beeRoot = BeeManager.beeRoot; + } + return beeRoot; + } + + private void initBeekeepingLogic() { + if (beekeepingLogic == null && metaTileEntity.getWorld() != null) { + IBeeRoot root = getBeeRoot(); + if (root != null) { + beekeepingLogic = root.createBeekeepingLogic(getApiary()); + } + } + } + + public void initBeekeepingLogicClient() { + initBeekeepingLogic(); + } + + public IBeekeepingLogic getBeekeepingLogic() { + return beekeepingLogic; + } + + public void updateModifiers() { + MetaTileEntityIndustrialApiary apiary = getApiary(); + ApiaryModifiers mods = apiary.getModifiers(); + if (mods == null) return; + + var upgradeInventory = apiary.getUpgradeInventory(); + if (upgradeInventory == null) return; + + mods.territory = 1; + mods.mutation = 1; + mods.lifespan = 1; + mods.production = 1; + mods.flowering = 1; + mods.geneticDecay = 1; + mods.energy = 1; + mods.temperature = 0; + mods.humidity = 0; + mods.isSealed = false; + mods.isSelfLighted = false; + mods.isSunlightSimulated = false; + mods.isAutomated = false; + mods.isCollectingPollen = false; + mods.biomeOverride = null; + + for (int i = 0; i < upgradeInventory.getSlots(); i++) { + ItemStack stack = upgradeInventory.getStackInSlot(i); + if (!stack.isEmpty() && stack.getItem() instanceof IApiaryUpgrade) { + ((IApiaryUpgrade) stack.getItem()).applyModifiers(mods, stack); + } + } + } + + public int getEUPerTick() { + ApiaryModifiers mods = getApiary().getModifiers(); + float energyMod = (mods != null) ? mods.energy : 1.0F; + return Math.max(1, Math.round(BASE_EU_PER_TICK * energyMod)); + } + + public int getEffectiveTicksPerHealth() { + ApiaryModifiers mods = getApiary().getModifiers(); + float lifespanMod = (mods != null) ? mods.lifespan : 1.0F; + return ForestryBeeHelper.getEffectiveTicksPerHealth(lifespanMod); + } + + public int getCycleTickCounter() { + if (maxProgressTime <= 0) return 0; + int effectiveTicks = getEffectiveTicksPerHealth(); + return effectiveTicks > 0 ? (progressTime % effectiveTicks) : 0; + } + + @Override + public void update() { + if (metaTileEntity.getWorld() == null) return; + + initBeekeepingLogic(); + + if (metaTileEntity.getWorld().isRemote) { + updateClient(); + return; + } + + updateServer(); + } + + private void updateClient() { + if (wasActiveAndNeedsUpdate) { + wasActiveAndNeedsUpdate = false; + setActive(false); + } + if (beekeepingLogic != null && isActive) { + fxTickCounter++; + if (fxTickCounter % 2 == 0 && beekeepingLogic.canDoBeeFX()) { + beekeepingLogic.doBeeFX(); + } + } + } + + private void updateServer() { + if (beekeepingLogic == null) { + if (isActive) setActive(false); + return; + } + + MetaTileEntityIndustrialApiary apiary = getApiary(); + needsMovePrincess = false; + + updateModifiers(); + + int euPerTick = getEUPerTick(); + boolean hasPower = getEnergyStored() >= euPerTick; + + // canWork() clears errorLogic internally, so power error must be set AFTER + boolean canWork = beekeepingLogic.canWork(); + apiary.getErrorLogic().setCondition(!hasPower, EnumErrorCode.NO_POWER); + + boolean isWorking = hasPower && workingEnabled && canWork; + + if (isWorking) { + beekeepingLogic.doWork(); + drawEnergy(euPerTick, false); + recipeEUt = euPerTick; + } else { + recipeEUt = 0; + } + + if (canWork && workingEnabled && !hasPower) { + hasNotEnoughEnergy = true; + } else if (hasNotEnoughEnergy && getEnergyInputPerSecond() > 19L * euPerTick) { + hasNotEnoughEnergy = false; + } + + if (needsMovePrincess && apiary.getQueen().isEmpty()) { + movePrincessToQueenSlot(); + } + + if (isActive != isWorking) { + setActive(isWorking); + apiary.syncBeeLogicToClient(); + } + if (wasActiveAndNeedsUpdate) { + wasActiveAndNeedsUpdate = false; + setActive(false); + } + + updateProgress(isWorking, apiary); + } + + private void updateProgress(boolean isWorking, MetaTileEntityIndustrialApiary apiary) { + if (isWorking && !apiary.getQueen().isEmpty()) { + if (maxProgressTime > 0 && progressTime >= maxProgressTime) { + progressTime = 0; + maxProgressTime = 0; + lastSyncedHealth = -1; + apiary.syncBeeLogicToClient(); + } + if (maxProgressTime <= 0) { + syncProgressFromBee(apiary); + } + + progressSyncCounter++; + if (progressSyncCounter >= PROGRESS_SYNC_TICKS && maxProgressTime > 0) { + progressSyncCounter = 0; + resyncIfHealthChanged(apiary); + } + + if (maxProgressTime > 0 && progressTime < maxProgressTime) { + progressTime++; + } + } else { + progressTime = 0; + maxProgressTime = 0; + lastSyncedHealth = -1; + progressSyncCounter = 0; + } + } + + private void syncProgressFromBee(MetaTileEntityIndustrialApiary apiary) { + IBeeRoot root = getBeeRoot(); + if (root == null) return; + + EnumBeeType type = root.getType(apiary.getQueen()); + if (type == EnumBeeType.QUEEN) { + IBee bee = root.getMember(apiary.getQueen()); + if (bee != null && bee.getMaxHealth() > 0 && bee.getHealth() > 0) { + maxProgressTime = getEffectiveTicksPerHealth(); + progressTime = 0; + lastSyncedHealth = bee.getHealth(); + } + } else if (type == EnumBeeType.PRINCESS) { + maxProgressTime = BREEDING_TIME_TICKS; + progressTime = beekeepingLogic.getBeeProgressPercent(); + lastSyncedHealth = -1; + } + } + + /** Resync progress when Forestry's probabilistic aging changes health. */ + private void resyncIfHealthChanged(MetaTileEntityIndustrialApiary apiary) { + IBeeRoot root = getBeeRoot(); + if (root == null || lastSyncedHealth < 0) return; + + IBee bee = root.getMember(apiary.getQueen()); + if (bee == null) return; + + int currentHealth = bee.getHealth(); + if (currentHealth != lastSyncedHealth) { + lastSyncedHealth = currentHealth; + progressTime = 0; + apiary.syncBeeLogicToClient(); + } + } + + public float getBeeProgress() { + if (maxProgressTime <= 0) return 0; + return (float) progressTime / maxProgressTime; + } + + public void setNeedsMovePrincess() { + needsMovePrincess = true; + } + + public void movePrincessToQueenSlot() { + IBeeRoot root = getBeeRoot(); + if (root == null) return; + MetaTileEntityIndustrialApiary apiary = getApiary(); + var output = getOutputInventory(); + for (int i = 0; i < output.getSlots(); i++) { + ItemStack stack = output.getStackInSlot(i); + if (!stack.isEmpty() && root.isMember(stack, EnumBeeType.PRINCESS)) { + apiary.setQueen(stack); + output.setStackInSlot(i, ItemStack.EMPTY); + return; + } + } + } + + @Override + protected void trySearchNewRecipe() {} + + @Override + protected void updateRecipeProgress() {} + + /** Ensure itemOutputs/fluidOutputs are non-null for parent serialization. */ + @Override + public NBTTagCompound serializeNBT() { + if (itemOutputs == null) { + itemOutputs = net.minecraft.util.NonNullList.create(); + } + if (fluidOutputs == null) { + fluidOutputs = java.util.Collections.emptyList(); + } + recipeEUt = getEUPerTick(); + + NBTTagCompound tag = super.serializeNBT(); + if (beekeepingLogic != null) { + NBTTagCompound beeTag = new NBTTagCompound(); + beekeepingLogic.writeToNBT(beeTag); + tag.setTag("BeekeepingLogic", beeTag); + } + return tag; + } + + @Override + public void deserializeNBT(@NotNull NBTTagCompound compound) { + super.deserializeNBT(compound); + if (compound.hasKey("BeekeepingLogic")) { + initBeekeepingLogic(); + if (beekeepingLogic != null) { + beekeepingLogic.readFromNBT(compound.getCompoundTag("BeekeepingLogic")); + } + } + } } diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/metatileentities/MetaTileEntityIndustrialApiary.java b/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/metatileentities/MetaTileEntityIndustrialApiary.java index b6ea8cb..f757196 100644 --- a/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/metatileentities/MetaTileEntityIndustrialApiary.java +++ b/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/metatileentities/MetaTileEntityIndustrialApiary.java @@ -1,17 +1,87 @@ package com.github.gtexpert.gtbm.integration.gendustry.metatileentities; +import java.util.Collections; import java.util.function.Function; +import javax.annotation.Nullable; + +import net.bdew.gendustry.api.ApiaryModifiers; +import net.bdew.gendustry.api.items.IApiaryUpgrade; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; +import net.minecraft.world.biome.Biome; +import net.minecraftforge.items.IItemHandlerModifiable; + +import com.mojang.authlib.GameProfile; +import gregtech.api.GTValues; +import gregtech.api.capability.impl.NotifiableItemStackHandler; +import gregtech.api.capability.impl.RecipeLogicEnergy; +import gregtech.api.gui.GuiTextures; +import gregtech.api.gui.ModularUI; +import gregtech.api.gui.widgets.*; import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.api.recipes.RecipeMap; import gregtech.client.renderer.ICubeRenderer; +import com.github.gtexpert.gtbm.api.gui.GTBMGuiTextures; import com.github.gtexpert.gtbm.common.metatileentities.GTBMSimpleMachineMetaTileEntity; +import com.github.gtexpert.gtbm.integration.forestry.util.BeeProductHelper; +import com.github.gtexpert.gtbm.integration.gendustry.util.ApiaryModifierBridge; +import com.github.gtexpert.gtbm.integration.gendustry.util.BeeClimateHelper; +import com.github.gtexpert.gtbm.integration.gendustry.util.WidgetBeeStatus; + +import forestry.api.apiculture.*; +import forestry.api.core.*; +import forestry.api.genetics.IIndividual; +import forestry.core.errors.ErrorLogic; + +public class MetaTileEntityIndustrialApiary extends GTBMSimpleMachineMetaTileEntity + implements IBeeHousing, IBeeHousingInventory { + + private static final int UPGRADE_SLOTS_BASE = 4; + private static final int UPGRADE_SLOTS_EXTENDED = 8; + private static final int OUTPUT_SLOT_COUNT = 9; + private static final int Y_OFFSET = 13; + private static final int BEE_LOGIC_SYNC_ID = 1000; -public class MetaTileEntityIndustrialApiary extends GTBMSimpleMachineMetaTileEntity { + private final ApiaryModifiers modifiers = new ApiaryModifiers(); + private final IErrorLogic errorLogic = new ErrorLogic(); + private final BeeClimateHelper climateHelper = new BeeClimateHelper(modifiers); + private final IBeeModifier beeModifier = new ApiaryModifierBridge(modifiers) { + + @Override + public boolean isHellish() { + return climateHelper.isHellish(getWorld(), getPos()); + } + }; + private final IBeeListener beeListener = new DefaultBeeListener() { + + @Override + public void onQueenDeath() { + if (autoBreeding) { + getLogic().setNeedsMovePrincess(); + } + } + + @Override + public boolean onPollenRetrieved(IIndividual pollen) { + if (!modifiers.isCollectingPollen) return false; + ItemStack stack = pollen.getGenome().getSpeciesRoot() + .getMemberStack(pollen, forestry.api.arboriculture.EnumGermlingType.POLLEN); + return addProduct(stack, true); + } + }; + private IItemHandlerModifiable upgradeInventory; + private GameProfile owner; + private boolean autoBreeding = false; public MetaTileEntityIndustrialApiary(ResourceLocation metaTileEntityId, RecipeMap recipeMap, ICubeRenderer renderer, int tier, boolean hasFrontFacing, @@ -25,7 +95,372 @@ public MetaTileEntity createMetaTileEntity(IGregTechTileEntity tileEntity) { hasFrontFacing(), getTankScalingFunction()); } - private IndustrialApiaryLogic getLogic() { + @Override + protected RecipeLogicEnergy createWorkable(RecipeMap recipeMap) { + return new IndustrialApiaryLogic(this, recipeMap, () -> energyContainer); + } + + // ---- Inventory ---- + + @Override + protected IItemHandlerModifiable createImportItemHandler() { + return new NotifiableItemStackHandler(this, 2, this, false) { + + @Override + public boolean isItemValid(int slot, @org.jetbrains.annotations.NotNull ItemStack stack) { + IBeeRoot beeRoot = getLogic().getBeeRoot(); + if (beeRoot == null) return true; + if (slot == 0) { + return beeRoot.isMember(stack, EnumBeeType.QUEEN) || beeRoot.isMember(stack, EnumBeeType.PRINCESS); + } + if (slot == 1) { + return beeRoot.isMember(stack, EnumBeeType.DRONE); + } + return false; + } + }; + } + + @Override + protected IItemHandlerModifiable createExportItemHandler() { + return new NotifiableItemStackHandler(this, OUTPUT_SLOT_COUNT, this, true); + } + + private int getUpgradeSlotCount() { + return getTier() >= GTValues.LuV ? UPGRADE_SLOTS_EXTENDED : UPGRADE_SLOTS_BASE; + } + + @Override + protected void initializeInventory() { + super.initializeInventory(); + this.upgradeInventory = new NotifiableItemStackHandler(this, getUpgradeSlotCount(), this, false) { + + @Override + public boolean isItemValid(int slot, @org.jetbrains.annotations.NotNull ItemStack stack) { + if (stack.isEmpty() || !(stack.getItem() instanceof IApiaryUpgrade)) return false; + return getMaxAdditionalUpgrades(stack) >= stack.getCount(); + } + + @Override + public int getSlotLimit(int slot) { + return 64; + } + }; + } + + public int getMaxAdditionalUpgrades(ItemStack stack) { + if (stack.isEmpty() || !(stack.getItem() instanceof IApiaryUpgrade)) return 0; + IApiaryUpgrade upgrade = (IApiaryUpgrade) stack.getItem(); + long thisId = upgrade.getStackingId(stack); + int existing = 0; + for (int i = 0; i < upgradeInventory.getSlots(); i++) { + ItemStack slotStack = upgradeInventory.getStackInSlot(i); + if (!slotStack.isEmpty() && slotStack.getItem() instanceof IApiaryUpgrade) { + if (((IApiaryUpgrade) slotStack.getItem()).getStackingId(slotStack) == thisId) { + existing += slotStack.getCount(); + } + } + } + return upgrade.getMaxNumber(stack) - existing; + } + + // ---- NBT ---- + + @Override + public NBTTagCompound writeToNBT(NBTTagCompound data) { + super.writeToNBT(data); + NBTTagCompound upgradeTag = new NBTTagCompound(); + for (int i = 0; i < upgradeInventory.getSlots(); i++) { + ItemStack stack = upgradeInventory.getStackInSlot(i); + if (!stack.isEmpty()) { + upgradeTag.setTag("Slot" + i, stack.writeToNBT(new NBTTagCompound())); + } + } + data.setTag("UpgradeInventory", upgradeTag); + + if (owner != null) { + NBTTagCompound ownerTag = new NBTTagCompound(); + ownerTag.setString("Name", owner.getName()); + ownerTag.setString("UUID", owner.getId().toString()); + data.setTag("Owner", ownerTag); + } + + data.setBoolean("AutoBreeding", autoBreeding); + return data; + } + + @Override + public void readFromNBT(NBTTagCompound data) { + super.readFromNBT(data); + if (data.hasKey("UpgradeInventory")) { + NBTTagCompound upgradeTag = data.getCompoundTag("UpgradeInventory"); + for (int i = 0; i < upgradeInventory.getSlots(); i++) { + String key = "Slot" + i; + if (upgradeTag.hasKey(key)) { + upgradeInventory.setStackInSlot(i, new ItemStack(upgradeTag.getCompoundTag(key))); + } + } + } + if (data.hasKey("Owner")) { + NBTTagCompound ownerTag = data.getCompoundTag("Owner"); + owner = new GameProfile( + java.util.UUID.fromString(ownerTag.getString("UUID")), + ownerTag.getString("Name")); + } + if (data.hasKey("AutoBreeding")) { + autoBreeding = data.getBoolean("AutoBreeding"); + } + } + + // ---- GUI ---- + + @Override + protected ModularUI createUI(EntityPlayer player) { + if (owner == null) { + owner = player.getGameProfile(); + } + IBeekeepingLogic logic = getBeekeepingLogic(); + if (logic != null && !getWorld().isRemote) { + logic.clearCachedValues(); + } + + ModularUI.Builder builder = ModularUI.builder(GuiTextures.BACKGROUND, 176, 166 + Y_OFFSET); + builder.widget(new LabelWidget(5, 5, getMetaFullName())); + + builder.widget(new SlotWidget(importItems, 0, 7, 18, true, true, true) + .setBackgroundTexture(GuiTextures.SLOT)); + builder.widget(new ImageWidget(8, 19, 16, 16, GTBMGuiTextures.QUEEN_OVERLAY)); + builder.widget(new SlotWidget(importItems, 1, 7, 36, true, true, true) + .setBackgroundTexture(GuiTextures.SLOT)); + builder.widget(new ImageWidget(8, 37, 16, 16, GTBMGuiTextures.DRONE_OVERLAY)); + + builder.widget(new WidgetBeeStatus(8, 56, this, getLogic().getBeeRoot(), getModifiers(), + getLogic()::getEUPerTick)); + + builder.widget(new ProgressWidget(getLogic()::getBeeProgress, 60, 16, 20, 20, + GuiTextures.PROGRESS_BAR_ARROW, ProgressWidget.MoveType.HORIZONTAL)); + + int upgradeSlots = getUpgradeSlotCount(); + for (int i = 0; i < upgradeSlots; i++) { + int col = i % 4; + int row = i / 4; + builder.widget(new SlotWidget(upgradeInventory, i, 34 + col * 18, 36 + row * 18, + true, true, true).setBackgroundTexture(GuiTextures.SLOT)); + builder.widget(new ImageWidget(35 + col * 18, 37 + row * 18, 16, 16, + GTBMGuiTextures.UPGRADE_OVERLAY)); + } + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + builder.widget(new SlotWidget(exportItems, j + i * 3, 116 + j * 18, 18 + i * 18, + true, false, false).setBackgroundTexture(GuiTextures.SLOT)); + } + } + + builder.widget(new ImageWidget(79, 42 + Y_OFFSET, 18, 18, GuiTextures.INDICATOR_NO_ENERGY) + .setIgnoreColor(true).setPredicate(workable::isHasNotEnoughEnergy)); + builder.widget(new ToggleButtonWidget(7, 62 + Y_OFFSET, 18, 18, + GuiTextures.BUTTON_ITEM_OUTPUT, this::isAutoOutputItems, this::setAutoOutputItems) + .setTooltipText("gregtech.gui.item_auto_output.tooltip").shouldUseBaseBackground()); + builder.widget(new CycleButtonWidget(25, 62 + Y_OFFSET, 18, 18, + workable.getAvailableOverclockingTiers(), workable::getOverclockTier, workable::setOverclockTier) + .setTooltipHoverString("gregtech.gui.overclock.description") + .setButtonTexture(GuiTextures.BUTTON_OVERCLOCK)); + builder.widget(new ToggleButtonWidget(43, 62 + Y_OFFSET, 18, 18, + GuiTextures.BUTTON_ITEM_OUTPUT, this::isAutoBreeding, this::setAutoBreeding) + .setTooltipText("gtbm.gui.auto_breeding.tooltip").shouldUseBaseBackground()); + builder.widget(new SlotWidget(chargerInventory, 0, 79, 62 + Y_OFFSET, true, true, true) + .setBackgroundTexture(GuiTextures.SLOT, GuiTextures.CHARGER_OVERLAY) + .setTooltipText("gregtech.gui.charger_slot.tooltip", + GTValues.VNF[getTier()], GTValues.VNF[getTier()])); + builder.widget(new ImageWidget(152, 63 + Y_OFFSET, 17, 17, + GTValues.XMAS.get() ? GTBMGuiTextures.GTBM_LOGO_XMAS : GTBMGuiTextures.GTBM_LOGO) + .setIgnoreColor(true)); + builder.bindPlayerInventory(player.inventory, GuiTextures.SLOT, Y_OFFSET); + + return builder.build(getHolder(), player); + } + + // ---- IBeeHousing ---- + + @Override + public Iterable getBeeModifiers() { + return Collections.singletonList(beeModifier); + } + + @Override + public Iterable getBeeListeners() { + return Collections.singletonList(beeListener); + } + + @Override + public IBeeHousingInventory getBeeInventory() { + return this; + } + + @Override + public IBeekeepingLogic getBeekeepingLogic() { + return getLogic().getBeekeepingLogic(); + } + + @Override + public int getBlockLightValue() { + return getWorld() != null ? getWorld().getLightFromNeighbors(getPos().offset(EnumFacing.UP)) : 0; + } + + @Override + public boolean canBlockSeeTheSky() { + return getWorld() != null && getWorld().canBlockSeeSky(getPos().offset(EnumFacing.UP, 2)); + } + + @Override + public boolean isRaining() { + return getWorld() != null && getWorld().isRainingAt(getPos().up()); + } + + @Override + @Nullable + public GameProfile getOwner() { + return owner; + } + + @Override + public Vec3d getBeeFXCoordinates() { + return new Vec3d(getPos().getX() + 0.5, getPos().getY() + 0.5, getPos().getZ() + 0.5); + } + + @Override + public BlockPos getCoordinates() { + return getPos(); + } + + @Override + public World getWorldObj() { + return getWorld(); + } + + // ---- IClimateProvider ---- + + @Override + public Biome getBiome() { + return climateHelper.getBiome(getWorld(), getPos()); + } + + @Override + public EnumTemperature getTemperature() { + return climateHelper.getTemperature(getWorld(), getPos()); + } + + @Override + public EnumHumidity getHumidity() { + return climateHelper.getHumidity(getWorld(), getPos()); + } + + @Override + public IErrorLogic getErrorLogic() { + return errorLogic; + } + + // ---- IBeeHousingInventory ---- + + @Override + public ItemStack getQueen() { + return importItems.getStackInSlot(0); + } + + @Override + public ItemStack getDrone() { + return importItems.getStackInSlot(1); + } + + @Override + public void setQueen(ItemStack stack) { + importItems.setStackInSlot(0, stack); + } + + @Override + public void setDrone(ItemStack stack) { + importItems.setStackInSlot(1, stack); + } + + @Override + public boolean addProduct(ItemStack product, boolean all) { + return BeeProductHelper.addProduct(product, getLogic().getBeeRoot(), + autoBreeding, modifiers.isAutomated, this, importItems, exportItems); + } + + // ---- Bee FX sync ---- + + public void syncBeeLogicToClient() { + IBeekeepingLogic logic = getBeekeepingLogic(); + if (logic != null && getWorld() != null && !getWorld().isRemote) { + writeCustomData(BEE_LOGIC_SYNC_ID, buf -> logic.writeData(buf)); + } + } + + @Override + public void writeInitialSyncData(@org.jetbrains.annotations.NotNull net.minecraft.network.PacketBuffer buf) { + super.writeInitialSyncData(buf); + IBeekeepingLogic logic = getBeekeepingLogic(); + if (logic != null) { + buf.writeBoolean(true); + logic.writeData(buf); + } else { + buf.writeBoolean(false); + } + } + + @Override + public void receiveInitialSyncData(@org.jetbrains.annotations.NotNull net.minecraft.network.PacketBuffer buf) { + super.receiveInitialSyncData(buf); + if (buf.readBoolean()) { + readBeeLogicData(buf); + } + } + + @Override + public void receiveCustomData(int dataId, + @org.jetbrains.annotations.NotNull net.minecraft.network.PacketBuffer buf) { + super.receiveCustomData(dataId, buf); + if (dataId == BEE_LOGIC_SYNC_ID) { + readBeeLogicData(buf); + } + } + + private void readBeeLogicData(net.minecraft.network.PacketBuffer buf) { + getLogic().initBeekeepingLogicClient(); + IBeekeepingLogic logic = getBeekeepingLogic(); + try { + if (logic != null) { + logic.readData(buf); + } else { + buf.readBytes(buf.readableBytes()); + } + } catch (java.io.IOException ignored) {} + } + + // ---- Accessors ---- + + protected IndustrialApiaryLogic getLogic() { return (IndustrialApiaryLogic) workable; } + + public IItemHandlerModifiable getUpgradeInventory() { + return upgradeInventory; + } + + public ApiaryModifiers getModifiers() { + return modifiers; + } + + public boolean isAutoBreeding() { + return autoBreeding; + } + + public void setAutoBreeding(boolean autoBreeding) { + this.autoBreeding = autoBreeding; + if (autoBreeding && getQueen().isEmpty()) { + getLogic().movePrincessToQueenSlot(); + } + } } diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/recipes/GendustryBlocksRecipe.java b/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/recipes/GendustryBlocksRecipe.java index d1e2d18..adc04a4 100644 --- a/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/recipes/GendustryBlocksRecipe.java +++ b/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/recipes/GendustryBlocksRecipe.java @@ -1,6 +1,31 @@ package com.github.gtexpert.gtbm.integration.gendustry.recipes; +import static gregtech.loaders.recipe.CraftingComponent.*; + +import gregtech.api.GTValues; +import gregtech.api.recipes.ModHandler; +import gregtech.loaders.recipe.MetaTileEntityLoader; + +import com.github.gtexpert.gtbm.api.util.Mods; +import com.github.gtexpert.gtbm.integration.gendustry.metatileentities.GendustryMetaTileEntities; + public class GendustryBlocksRecipe { - public static void init() {} + public static void init() { + machines(); + } + + private static void machines() { + // Industrial Apiary + MetaTileEntityLoader.registerMachineRecipe(true, GendustryMetaTileEntities.INDUSTRIAL_APIARY, + "ACA", "RHR", "ASA", + 'A', Mods.Forestry.getItem("alveary.plain"), + 'C', Mods.Forestry.getItem("chipsets", 1, 1), + 'R', ROBOT_ARM, + 'H', HULL, + 'S', Mods.Forestry.getItem("alveary.sieve")); + ModHandler.addShapelessRecipe("gendustry_to_gtbm_industrial_apiary", + GendustryMetaTileEntities.INDUSTRIAL_APIARY[GTValues.HV].getStackForm(), + Mods.Gendustry.getItem("industrial_apiary")); + } } diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/recipes/GendustryCraftingRecipe.java b/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/recipes/GendustryCraftingRecipe.java index c3bce41..fdcc6d0 100644 --- a/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/recipes/GendustryCraftingRecipe.java +++ b/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/recipes/GendustryCraftingRecipe.java @@ -23,16 +23,7 @@ public static void init() { public static void recipeRemoval() { Map, String> recipes = new HashMap<>(); - recipes.put(() -> GendustryConfigHolder.MutagenProducer, "mutagen_producer"); - recipes.put(() -> GendustryConfigHolder.Mutatron, "mutatron"); - recipes.put(() -> GendustryConfigHolder.IndustrialApiary, "industrial_apiary"); - recipes.put(() -> GendustryConfigHolder.GeneticImprinter, "genetic_imprinter"); - recipes.put(() -> GendustryConfigHolder.GeneticSampler, "sampler"); - recipes.put(() -> GendustryConfigHolder.AdvancedMutagen, "mutatron_advanced"); - recipes.put(() -> GendustryConfigHolder.ProteinLiquifier, "protein_liquifier"); - recipes.put(() -> GendustryConfigHolder.DNAExtractor, "dna_extractor"); - recipes.put(() -> GendustryConfigHolder.GeneticTransposer, "transposer"); - recipes.put(() -> GendustryConfigHolder.GeneticReplicator, "replicator"); + recipes.put(() -> GendustryConfigHolder.industrialApiary, "industrial_apiary"); recipes.forEach((config, name) -> { if (config.get()) { diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/recipes/GendustryItemsRecipe.java b/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/recipes/GendustryItemsRecipe.java index 36f73d9..e3fbb26 100644 --- a/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/recipes/GendustryItemsRecipe.java +++ b/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/recipes/GendustryItemsRecipe.java @@ -1,6 +1,94 @@ package com.github.gtexpert.gtbm.integration.gendustry.recipes; +import static gregtech.api.unification.ore.OrePrefix.*; + +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemStack; + +import gregtech.api.recipes.ModHandler; +import gregtech.api.unification.material.Materials; +import gregtech.api.unification.stack.UnificationEntry; + +import com.github.gtexpert.gtbm.api.util.Mods; + public class GendustryItemsRecipe { - public static void init() {} + public static void init() { + components(); + } + + private static void components() { + // Mutagen Tank + ModHandler.removeRecipeByOutput(Mods.Gendustry.getItem("mutagen_tank")); + ModHandler.addShapedRecipe(true, "gendustry_mutagen_tank", + Mods.Gendustry.getItem("mutagen_tank"), + "TPT", "TPT", "TPT", + 'T', new UnificationEntry(ingot, Materials.Tin), + 'P', new ItemStack(Blocks.GLASS_PANE)); + + // Bee Receptacle + ModHandler.removeRecipeByOutput(Mods.Gendustry.getItem("bee_receptacle")); + ModHandler.addShapedRecipe(true, "gendustry_bee_receptacle", + Mods.Gendustry.getItem("bee_receptacle"), + "BBB", "BPB", "RZR", + 'B', new UnificationEntry(ingot, Materials.Bronze), + 'P', new ItemStack(Blocks.GLASS_PANE), + 'R', new UnificationEntry(dust, Materials.Redstone), + 'Z', new ItemStack(Blocks.LIGHT_WEIGHTED_PRESSURE_PLATE)); + + // Power Module + ModHandler.removeRecipeByOutput(Mods.Gendustry.getItem("power_module")); + ModHandler.addShapedRecipe(true, "gendustry_power_module", + Mods.Gendustry.getItem("power_module"), + "AGA", "SRS", "AGA", + 'A', new UnificationEntry(gear, Materials.Bronze), + 'G', new UnificationEntry(ingot, Materials.Gold), + 'S', new ItemStack(Blocks.PISTON), + 'R', new UnificationEntry(dust, Materials.Redstone)); + + // Genetics Processor + ModHandler.removeRecipeByOutput(Mods.Gendustry.getItem("genetics_processor")); + ModHandler.addShapedRecipe(true, "gendustry_genetics_processor", + Mods.Gendustry.getItem("genetics_processor"), + "DQD", "RYR", "DQD", + 'D', new UnificationEntry(gem, Materials.Diamond), + 'Q', new UnificationEntry(gem, Materials.NetherQuartz), + 'R', new UnificationEntry(dust, Materials.Redstone), + 'Y', new UnificationEntry(gem, Materials.EnderPearl)); + + // Environmental Processor + ModHandler.removeRecipeByOutput(Mods.Gendustry.getItem("env_processor")); + ModHandler.addShapedRecipe(true, "gendustry_env_processor", + Mods.Gendustry.getItem("env_processor"), + "DLD", "LGL", "DLD", + 'D', new UnificationEntry(gem, Materials.Diamond), + 'L', new UnificationEntry(gem, Materials.Lapis), + 'G', new UnificationEntry(ingot, Materials.Gold)); + + // Upgrade Frame + ModHandler.removeRecipeByOutput(Mods.Gendustry.getItem("upgrade_frame")); + ModHandler.addShapedRecipe(true, "gendustry_upgrade_frame", + Mods.Gendustry.getItem("upgrade_frame"), + "TgT", "R R", "TgT", + 'T', new UnificationEntry(ingot, Materials.Tin), + 'g', new UnificationEntry(nugget, Materials.Gold), + 'R', new UnificationEntry(dust, Materials.Redstone)); + + // Climate Module + ModHandler.removeRecipeByOutput(Mods.Gendustry.getItem("climate_module")); + ModHandler.addShapedRecipe(true, "gendustry_climate_module", + Mods.Gendustry.getItem("climate_module"), + "BRB", "BAB", "BRB", + 'B', new UnificationEntry(ingot, Materials.Bronze), + 'R', new UnificationEntry(dust, Materials.Redstone), + 'A', new UnificationEntry(gear, Materials.Bronze)); + + // Labware (output x16) + ModHandler.removeRecipeByOutput(Mods.Gendustry.getItem("labware")); + ModHandler.addShapedRecipe(true, "gendustry_labware", + Mods.Gendustry.getItem("labware", 16), + "P P", "P P", " D ", + 'P', new ItemStack(Blocks.GLASS_PANE), + 'D', new UnificationEntry(gem, Materials.Diamond)); + } } diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/util/ApiaryModifierBridge.java b/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/util/ApiaryModifierBridge.java new file mode 100644 index 0000000..196dc1f --- /dev/null +++ b/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/util/ApiaryModifierBridge.java @@ -0,0 +1,74 @@ +package com.github.gtexpert.gtbm.integration.gendustry.util; + +import javax.annotation.Nullable; + +import net.bdew.gendustry.api.ApiaryModifiers; + +import forestry.api.apiculture.DefaultBeeModifier; +import forestry.api.apiculture.IBeeGenome; + +/** + * IBeeModifier implementation that delegates to ApiaryModifiers. + * Reusable bridge between Gendustry's upgrade system and Forestry's modifier interface. + * Override {@link #isHellish()} in subclass since it requires world/pos context. + */ +public class ApiaryModifierBridge extends DefaultBeeModifier { + + private static final float MAX_TERRITORY = 5; + + protected final ApiaryModifiers modifiers; + + /** + * Creates a new bridge wrapping the given modifiers. + * + * @param modifiers the Gendustry apiary modifiers to delegate to + */ + public ApiaryModifierBridge(ApiaryModifiers modifiers) { + this.modifiers = modifiers; + } + + @Override + public float getTerritoryModifier(IBeeGenome genome, float currentModifier) { + return Math.min(modifiers.territory, MAX_TERRITORY); + } + + @Override + public float getMutationModifier(IBeeGenome genome, IBeeGenome mate, float currentModifier) { + return modifiers.mutation; + } + + @Override + public float getLifespanModifier(IBeeGenome genome, @Nullable IBeeGenome mate, float currentModifier) { + return modifiers.lifespan; + } + + @Override + public float getProductionModifier(IBeeGenome genome, float currentModifier) { + return modifiers.production; + } + + @Override + public float getFloweringModifier(IBeeGenome genome, float currentModifier) { + return modifiers.flowering; + } + + @Override + public float getGeneticDecay(IBeeGenome genome, float currentModifier) { + return modifiers.geneticDecay; + } + + @Override + public boolean isSealed() { + return modifiers.isSealed; + } + + @Override + public boolean isSelfLighted() { + return modifiers.isSelfLighted; + } + + @Override + public boolean isSunlightSimulated() { + return modifiers.isSunlightSimulated; + } +} diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/util/BeeClimateHelper.java b/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/util/BeeClimateHelper.java new file mode 100644 index 0000000..f219caf --- /dev/null +++ b/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/util/BeeClimateHelper.java @@ -0,0 +1,96 @@ +package com.github.gtexpert.gtbm.integration.gendustry.util; + +import javax.annotation.Nullable; + +import net.bdew.gendustry.api.ApiaryModifiers; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.biome.Biome; + +import forestry.api.core.BiomeHelper; +import forestry.api.core.EnumHumidity; +import forestry.api.core.EnumTemperature; + +/** + * Reusable climate calculation helper for bee housings. + * Combines biome data with ApiaryModifiers for temperature/humidity. + */ +public class BeeClimateHelper { + + private final ApiaryModifiers modifiers; + + /** + * Creates a climate helper using the given apiary modifiers. + * + * @param modifiers the Gendustry apiary modifiers (for biome override, temperature/humidity offsets) + */ + public BeeClimateHelper(ApiaryModifiers modifiers) { + this.modifiers = modifiers; + } + + /** + * Returns the effective biome, respecting the modifier's biome override. + * + * @param world the world (may be null) + * @param pos the block position + * @return the overridden or actual biome, or null if world is null and no override exists + */ + @Nullable + public Biome getEffectiveBiome(@Nullable World world, BlockPos pos) { + if (modifiers.biomeOverride != null) return modifiers.biomeOverride; + return world != null ? world.getBiome(pos) : null; + } + + /** + * Returns the effective biome, requiring a non-null world. + * + * @param world the world (must not be null) + * @param pos the block position + * @return the overridden or actual biome + */ + public Biome getBiome(World world, BlockPos pos) { + if (modifiers.biomeOverride != null) { + return modifiers.biomeOverride; + } + return world.getBiome(pos); + } + + /** + * Computes the effective temperature, applying modifier offsets. + * + * @param world the world (may be null; defaults to NORMAL) + * @param pos the block position + * @return the computed temperature enum value + */ + public EnumTemperature getTemperature(@Nullable World world, BlockPos pos) { + Biome biome = getEffectiveBiome(world, pos); + if (biome == null) return EnumTemperature.NORMAL; + if (BiomeHelper.isBiomeHellish(biome)) return EnumTemperature.HELLISH; + return EnumTemperature.getFromValue(biome.getTemperature(pos) + modifiers.temperature); + } + + /** + * Computes the effective humidity, applying modifier offsets. + * + * @param world the world (may be null; defaults to NORMAL) + * @param pos the block position + * @return the computed humidity enum value + */ + public EnumHumidity getHumidity(@Nullable World world, BlockPos pos) { + Biome biome = getEffectiveBiome(world, pos); + if (biome == null) return EnumHumidity.NORMAL; + return EnumHumidity.getFromValue(biome.getRainfall() + modifiers.humidity); + } + + /** + * Checks whether the effective biome is hellish (Nether-like). + * + * @param world the world (may be null) + * @param pos the block position + * @return true if the effective biome is hellish + */ + public boolean isHellish(@Nullable World world, BlockPos pos) { + Biome biome = getEffectiveBiome(world, pos); + return biome != null && BiomeHelper.isBiomeHellish(biome); + } +} diff --git a/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/util/WidgetBeeStatus.java b/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/util/WidgetBeeStatus.java new file mode 100644 index 0000000..04612d5 --- /dev/null +++ b/src/main/java/com/github/gtexpert/gtbm/integration/gendustry/util/WidgetBeeStatus.java @@ -0,0 +1,265 @@ +package com.github.gtexpert.gtbm.integration.gendustry.util; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.IntSupplier; + +import net.bdew.gendustry.api.ApiaryModifiers; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.BufferBuilder; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.resources.I18n; +import net.minecraft.item.ItemStack; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import gregtech.api.gui.IRenderContext; +import gregtech.api.gui.Widget; +import gregtech.api.util.Position; +import gregtech.api.util.Size; + +import forestry.api.apiculture.*; +import forestry.api.core.*; + +/** + * Gendustry-style bee status widget. + * Displays Forestry error icons (cycled) and shows climate/bee stats on hover. + */ +public class WidgetBeeStatus extends Widget { + + private static final int SYNC_TOOLTIP = 0; + private static final int SYNC_ERRORS = 1; + private static final int BREEDING_TIME_TICKS = 100; + private static final char LINE_SEPARATOR = '\t'; + + private final IBeeHousing housing; + private final IBeeRoot beeRoot; + private final ApiaryModifiers modifiers; + private final IntSupplier euPerTickSupplier; + + // Client-side synced data + private List clientTooltip = new ArrayList<>(); + private List clientErrorIds = new ArrayList<>(); + + // Server-side change tracking + private int lastErrorHash = Integer.MIN_VALUE; + private int syncTickCounter; + + /** + * Creates a bee status widget at the given GUI position. + * + * @param x the x position in the GUI + * @param y the y position in the GUI + * @param housing the bee housing to monitor + * @param beeRoot the Forestry bee root for bee type lookups + * @param modifiers the Gendustry apiary modifiers for stat display + * @param euPerTickSupplier supplier for the current EU/t consumption + */ + public WidgetBeeStatus(int x, int y, IBeeHousing housing, IBeeRoot beeRoot, ApiaryModifiers modifiers, + IntSupplier euPerTickSupplier) { + super(new Position(x, y), new Size(16, 16)); + this.housing = housing; + this.beeRoot = beeRoot; + this.modifiers = modifiers; + this.euPerTickSupplier = euPerTickSupplier; + } + + // ---- Server-side: sync ---- + + @Override + public void detectAndSendChanges() { + if (housing.getWorldObj() == null) return; + + // Sync error IDs (only on change) + IErrorLogic errorLogic = housing.getErrorLogic(); + var errorStates = errorLogic.getErrorStates(); + int errorHash = errorStates.hashCode(); + if (errorHash != lastErrorHash) { + lastErrorHash = errorHash; + writeUpdateInfo(SYNC_ERRORS, buf -> { + buf.writeVarInt(errorStates.size()); + for (IErrorState error : errorStates) { + buf.writeShort(error.getID()); + } + }); + } + + // Sync tooltip (active: every 1s, idle: every 2s) + syncTickCounter++; + boolean isActive = housing.getBeekeepingLogic() != null && !housing.getBeeInventory().getQueen().isEmpty(); + if (syncTickCounter >= (isActive ? 20 : 40)) { + syncTickCounter = 0; + List tooltip = buildTooltipLines(); + writeUpdateInfo(SYNC_TOOLTIP, buf -> { + buf.writeVarInt(tooltip.size()); + for (String line : tooltip) { + buf.writeString(line); + } + }); + } + } + + private List buildTooltipLines() { + List lines = new ArrayList<>(); + + // Error descriptions + for (IErrorState error : housing.getErrorLogic().getErrorStates()) { + lines.add(TextFormatting.RED + error.getUnlocalizedDescription()); + } + + // Energy, climate + addLine(lines, "gtbm.bee.label.energy", String.valueOf(euPerTickSupplier.getAsInt())); + addLine(lines, "gtbm.bee.label.temperature", housing.getTemperature().getName().toLowerCase()); + addLine(lines, "gtbm.bee.label.humidity", housing.getHumidity().getName().toLowerCase()); + + // Bee stats + IBeeHousingInventory inv = housing.getBeeInventory(); + if (beeRoot != null && !inv.getQueen().isEmpty()) { + IBee bee = beeRoot.getMember(inv.getQueen()); + if (bee != null) { + addBeeStats(lines, bee, inv); + } + } + + // Upgrade note (check if any modifier is non-default) + if (hasActiveUpgrades(modifiers)) { + addLine(lines, "gtbm.bee.label.upgrade_note", ""); + } + + return lines; + } + + private static boolean hasActiveUpgrades(ApiaryModifiers mods) { + return mods.energy != 1 || mods.lifespan != 1 || mods.production != 1 || + mods.mutation != 1 || mods.territory != 1 || mods.flowering != 1 || + mods.geneticDecay != 1 || mods.humidity != 0 || mods.temperature != 0 || + mods.isSealed || mods.isSelfLighted || mods.isSunlightSimulated || + mods.isAutomated || mods.isCollectingPollen || mods.biomeOverride != null; + } + + private void addBeeStats(List lines, IBee bee, IBeeHousingInventory inv) { + EnumBeeType type = beeRoot.getType(inv.getQueen()); + + if (type == EnumBeeType.PRINCESS) { + addLine(lines, "gtbm.bee.label.breeding", (BREEDING_TIME_TICKS / 20) + "s"); + } + + if (bee.isAnalyzed()) { + IBeeGenome genome = bee.getGenome(); + addLine(lines, "gtbm.bee.label.production", + String.format("%.0f%%", 100F * modifiers.production * genome.getSpeed())); + addLine(lines, "gtbm.bee.label.flowering", + String.format("%.0f%%", modifiers.flowering * genome.getFlowering())); + addLine(lines, "gtbm.bee.label.lifespan", + String.format("%.0f%%", 100F * modifiers.lifespan * genome.getLifespan())); + var t = genome.getTerritory(); + addLine(lines, "gtbm.bee.label.territory", String.format("%.0f x %.0f x %.0f", + t.getX() * modifiers.territory, t.getY() * modifiers.territory, + t.getZ() * modifiers.territory)); + } else { + addLine(lines, "gtbm.bee.label.not_analyzed", ""); + } + } + + private static void addLine(List lines, String key, String value) { + lines.add(key + LINE_SEPARATOR + value); + } + + // ---- Client-side: receive ---- + + @Override + public void readUpdateInfo(int id, PacketBuffer buf) { + if (id == SYNC_ERRORS) { + int count = buf.readVarInt(); + clientErrorIds = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + clientErrorIds.add(buf.readShort()); + } + } else if (id == SYNC_TOOLTIP) { + int count = buf.readVarInt(); + clientTooltip = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + clientTooltip.add(buf.readString(512)); + } + } + } + + // ---- Client-side: render icon ---- + + @Override + @SideOnly(Side.CLIENT) + public void drawInBackground(int mouseX, int mouseY, float partialTicks, IRenderContext context) { + Position pos = getPosition(); + + if (!clientErrorIds.isEmpty()) { + // Errors: cycle through Forestry error icons + Minecraft mc = Minecraft.getMinecraft(); + mc.getTextureManager().bindTexture(ForestryAPI.textureManager.getGuiTextureMap()); + GlStateManager.color(1, 1, 1, 1); + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, + GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); + + int index = (int) ((mc.world.getTotalWorldTime() / 40) % clientErrorIds.size()); + IErrorState errorState = ForestryAPI.errorStateRegistry.getErrorState(clientErrorIds.get(index)); + if (errorState != null) { + TextureAtlasSprite sprite = errorState.getSprite(); + if (sprite != null) { + drawAtlasSprite(pos.x, pos.y, 16, 16, sprite); + } + } + } else if (!clientTooltip.isEmpty()) { + // Working, no errors: show JEI info icon + com.github.gtexpert.gtbm.api.gui.GTBMGuiTextures.BEE_INFO_ICON + .draw(pos.x, pos.y, 16, 16); + } + } + + @SideOnly(Side.CLIENT) + private static void drawAtlasSprite(int x, int y, int width, int height, TextureAtlasSprite sprite) { + Tessellator tessellator = Tessellator.getInstance(); + BufferBuilder buffer = tessellator.getBuffer(); + buffer.begin(7, DefaultVertexFormats.POSITION_TEX); + buffer.pos(x, y + height, 0).tex(sprite.getMinU(), sprite.getMaxV()).endVertex(); + buffer.pos(x + width, y + height, 0).tex(sprite.getMaxU(), sprite.getMaxV()).endVertex(); + buffer.pos(x + width, y, 0).tex(sprite.getMaxU(), sprite.getMinV()).endVertex(); + buffer.pos(x, y, 0).tex(sprite.getMinU(), sprite.getMinV()).endVertex(); + tessellator.draw(); + } + + // ---- Client-side: render tooltip ---- + + @Override + @SideOnly(Side.CLIENT) + public void drawInForeground(int mouseX, int mouseY) { + if (!isMouseOverElement(mouseX, mouseY) || clientTooltip.isEmpty()) return; + + List tooltip = new ArrayList<>(clientTooltip.size()); + for (String line : clientTooltip) { + int tab = line.indexOf(LINE_SEPARATOR); + if (tab >= 0) { + String key = line.substring(0, tab); + String value = line.substring(tab + 1); + if (key.contains("temperature") || key.contains("humidity")) { + tooltip.add(I18n.format(key, I18n.format("for.gui." + value))); + } else { + tooltip.add(I18n.format(key, value)); + } + } else if (line.startsWith(TextFormatting.RED.toString())) { + tooltip.add(TextFormatting.RED + I18n.format( + line.substring(TextFormatting.RED.toString().length()))); + } else { + tooltip.add(line); + } + } + + if (!tooltip.isEmpty()) { + drawHoveringText(ItemStack.EMPTY, tooltip, 200, mouseX, mouseY); + } + } +} diff --git a/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_front.png b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_front.png new file mode 100644 index 0000000..71b4efe Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_front.png differ diff --git a/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_front_active.png b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_front_active.png new file mode 100644 index 0000000..9e46f30 Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_front_active.png differ diff --git a/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_front_active_emissive.png b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_front_active_emissive.png new file mode 100644 index 0000000..fbc8bab Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_front_active_emissive.png differ diff --git a/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_front_emissive.png b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_front_emissive.png new file mode 100644 index 0000000..2673ecb Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_front_emissive.png differ diff --git a/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_front_paused.png b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_front_paused.png new file mode 100644 index 0000000..8f139ba Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_front_paused.png differ diff --git a/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_front_paused_emissive.png b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_front_paused_emissive.png new file mode 100644 index 0000000..577f081 Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_front_paused_emissive.png differ diff --git a/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_top.png b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_top.png new file mode 100644 index 0000000..ad768c8 Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_top.png differ diff --git a/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_top_active.png b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_top_active.png new file mode 100644 index 0000000..e27a2f7 Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_top_active.png differ diff --git a/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_top_active.png.mcmeta b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_top_active.png.mcmeta new file mode 100644 index 0000000..dc9736f --- /dev/null +++ b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_top_active.png.mcmeta @@ -0,0 +1,10 @@ +{ + "animation":{ + "frametime":1, + "frames": [ + 1, + 0, + 1 + ] + } +} \ No newline at end of file diff --git a/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_top_active_emissive.png b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_top_active_emissive.png new file mode 100644 index 0000000..c601e3d Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_top_active_emissive.png differ diff --git a/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_top_active_emissive.png.mcmeta b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_top_active_emissive.png.mcmeta new file mode 100644 index 0000000..dc9736f --- /dev/null +++ b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_top_active_emissive.png.mcmeta @@ -0,0 +1,10 @@ +{ + "animation":{ + "frametime":1, + "frames": [ + 1, + 0, + 1 + ] + } +} \ No newline at end of file diff --git a/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_top_emissive.png b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_top_emissive.png new file mode 100644 index 0000000..c737d59 Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/blocks/machines/industrial_apiary/overlay_top_emissive.png differ diff --git a/src/main/resources/assets/gtbm/lang/en_us.lang b/src/main/resources/assets/gtbm/lang/en_us.lang index ebe2d93..6cb1e97 100644 --- a/src/main/resources/assets/gtbm/lang/en_us.lang +++ b/src/main/resources/assets/gtbm/lang/en_us.lang @@ -1 +1,37 @@ gtbm.tooltip.warn.disabled_item=§l§dThis item is disabled!! + +# Industrial Apiary +gtbm.machine.industrial_apiary.lv.name=Basic Industrial Apiary +gtbm.machine.industrial_apiary.lv.tooltip=BEES GOES BRRRR/n§dAuthor:§f @tier940 +gtbm.machine.industrial_apiary.mv.name=Advanced Industrial Apiary +gtbm.machine.industrial_apiary.mv.tooltip=BEES GOES BRRRR/n§dAuthor:§f @tier940 +gtbm.machine.industrial_apiary.hv.name=Advanced Industrial Apiary II +gtbm.machine.industrial_apiary.hv.tooltip=BEES GOES BRRRR/n§dAuthor:§f @tier940 +gtbm.machine.industrial_apiary.ev.name=Advanced Industrial Apiary III +gtbm.machine.industrial_apiary.ev.tooltip=BEES GOES BRRRR/n§dAuthor:§f @tier940 +gtbm.machine.industrial_apiary.iv.name=Elite Industrial Apiary +gtbm.machine.industrial_apiary.iv.tooltip=BEES GOES BRRRR/n§dAuthor:§f @tier940 +gtbm.machine.industrial_apiary.luv.name=Elite Industrial Apiary II +gtbm.machine.industrial_apiary.luv.tooltip=BEES GOES BRRRR/n§dAuthor:§f @tier940 +gtbm.machine.industrial_apiary.zpm.name=Elite Industrial Apiary III +gtbm.machine.industrial_apiary.zpm.tooltip=BEES GOES BRRRR/n§dAuthor:§f @tier940 +gtbm.machine.industrial_apiary.uv.name=Ultimate Industrial Apiary +gtbm.machine.industrial_apiary.uv.tooltip=BEES GOES BRRRR/n§dAuthor:§f @tier940 + +# Bee Status Widget +gtbm.bee.label.energy=Energy Required: %s EU/t +gtbm.bee.label.temperature=Temperature: %s +gtbm.bee.label.humidity=Humidity: %s +gtbm.bee.label.production=Production Modifier: %s +gtbm.bee.label.flowering=Flowering Chance: %s +gtbm.bee.label.lifespan=Lifespan Modifier: %s +gtbm.bee.label.territory=Territory: %s +gtbm.bee.label.health=Queen Health: %s +gtbm.bee.label.cycle=Next Product: %s +gtbm.bee.label.breeding=Breeding Progress: %s +gtbm.bee.label.not_analyzed=§o§eInsert analyzed bee to see more info +gtbm.bee.label.upgrade_note=§o§7Upgrade changes apply from next cycle + +# GUI +gtbm.gui.auto_breeding.tooltip.enabled=Auto-Breeding: ON/nAutomatically puts the princess back after queen death +gtbm.gui.auto_breeding.tooltip.disabled=Auto-Breeding: OFF/nPrincess will remain in the output slot diff --git a/src/main/resources/assets/gtbm/lang/ja_jp.lang b/src/main/resources/assets/gtbm/lang/ja_jp.lang index 3dc5900..e2307b4 100644 --- a/src/main/resources/assets/gtbm/lang/ja_jp.lang +++ b/src/main/resources/assets/gtbm/lang/ja_jp.lang @@ -1 +1,37 @@ gtbm.tooltip.warn.disabled_item=§l§dこのアイテムは無効化されています!! + +# Industrial Apiary +gtbm.machine.industrial_apiary.lv.name=基本型工業用養蜂箱 +gtbm.machine.industrial_apiary.lv.tooltip=ミツバチ殿様のおな~り~/n§d作者:§f @tier940 +gtbm.machine.industrial_apiary.mv.name=発展型工業用養蜂箱 +gtbm.machine.industrial_apiary.mv.tooltip=ミツバチ殿様のおな~り~/n§d作者:§f @tier940 +gtbm.machine.industrial_apiary.hv.name=発展型工業用養蜂箱 II +gtbm.machine.industrial_apiary.hv.tooltip=ミツバチ殿様のおな~り~/n§d作者:§f @tier940 +gtbm.machine.industrial_apiary.ev.name=発展型工業用養蜂箱 III +gtbm.machine.industrial_apiary.ev.tooltip=ミツバチ殿様のおな~り~/n§d作者:§f @tier940 +gtbm.machine.industrial_apiary.iv.name=精鋭型工業用養蜂箱 +gtbm.machine.industrial_apiary.iv.tooltip=ミツバチ殿様のおな~り~/n§d作者:§f @tier940 +gtbm.machine.industrial_apiary.luv.name=精鋭型工業用養蜂箱 II +gtbm.machine.industrial_apiary.luv.tooltip=ミツバチ殿様のおな~り~/n§d作者:§f @tier940 +gtbm.machine.industrial_apiary.zpm.name=精鋭型工業用養蜂箱 III +gtbm.machine.industrial_apiary.zpm.tooltip=ミツバチ殿様のおな~り~/n§d作者:§f @tier940 +gtbm.machine.industrial_apiary.uv.name=究極型工業用養蜂箱 +gtbm.machine.industrial_apiary.uv.tooltip=ミツバチ殿様のおな~り~/n§d作者:§f @tier940 + +# Bee Status Widget +gtbm.bee.label.energy=消費電力: %s EU/t +gtbm.bee.label.temperature=気温: %s +gtbm.bee.label.humidity=湿度: %s +gtbm.bee.label.production=生産速度: %s +gtbm.bee.label.flowering=受粉確率: %s +gtbm.bee.label.lifespan=寿命補正: %s +gtbm.bee.label.territory=活動範囲: %s +gtbm.bee.label.health=女王の体力: %s +gtbm.bee.label.cycle=次の産出まで: %s +gtbm.bee.label.breeding=交配進行度: %s +gtbm.bee.label.not_analyzed=§o§e解析済みの蜂を入れるとより詳しい情報が表示されます +gtbm.bee.label.upgrade_note=§o§7アップグレードの変更は次のサイクルから適用されます + +# GUI +gtbm.gui.auto_breeding.tooltip.enabled=自動交配: ON/nクイーン死亡後にプリンセスを自動的に入力スロットに戻します +gtbm.gui.auto_breeding.tooltip.disabled=自動交配: OFF/nプリンセスはアウトプットに残ります