diff --git a/src/main/java/gregicality/multiblocks/api/capability/impl/GCYMHeatingCoilRecipeLogic.java b/src/main/java/gregicality/multiblocks/api/capability/impl/GCYMHeatingCoilRecipeLogic.java new file mode 100644 index 00000000..b81cb839 --- /dev/null +++ b/src/main/java/gregicality/multiblocks/api/capability/impl/GCYMHeatingCoilRecipeLogic.java @@ -0,0 +1,33 @@ +package gregicality.multiblocks.api.capability.impl; + +import org.jetbrains.annotations.NotNull; + +import gregtech.api.capability.IHeatingCoil; +import gregtech.api.metatileentity.multiblock.RecipeMapMultiblockController; +import gregtech.api.recipes.logic.OverclockingLogic; +import gregtech.api.recipes.recipeproperties.IRecipePropertyStorage; +import gregtech.api.recipes.recipeproperties.TemperatureProperty; + +public class GCYMHeatingCoilRecipeLogic extends GCYMMultiblockRecipeLogic { + + public GCYMHeatingCoilRecipeLogic(RecipeMapMultiblockController metaTileEntity) { + super(metaTileEntity); + if (!(metaTileEntity instanceof IHeatingCoil)) { + throw new IllegalArgumentException("MetaTileEntity must be instanceof IHeatingCoil"); + } + } + + protected void modifyOverclockPre(@NotNull int[] values, @NotNull IRecipePropertyStorage storage) { + super.modifyOverclockPre(values, storage); + values[0] = OverclockingLogic.applyCoilEUtDiscount(values[0], + ((IHeatingCoil) this.metaTileEntity).getCurrentTemperature(), + (Integer) storage.getRecipePropertyValue(TemperatureProperty.getInstance(), 0)); + } + + protected @NotNull int[] runOverclockingLogic(@NotNull IRecipePropertyStorage propertyStorage, int recipeEUt, + long maxVoltage, int duration, int amountOC) { + return OverclockingLogic.heatingCoilOverclockingLogic(Math.abs(recipeEUt), maxVoltage, duration, amountOC, + ((IHeatingCoil) this.metaTileEntity).getCurrentTemperature(), + (Integer) propertyStorage.getRecipePropertyValue(TemperatureProperty.getInstance(), 0)); + } +} diff --git a/src/main/java/gregicality/multiblocks/api/metatileentity/GCYMMultiShapeMultiblockController.java b/src/main/java/gregicality/multiblocks/api/metatileentity/GCYMMultiShapeMultiblockController.java new file mode 100644 index 00000000..0520f0ce --- /dev/null +++ b/src/main/java/gregicality/multiblocks/api/metatileentity/GCYMMultiShapeMultiblockController.java @@ -0,0 +1,44 @@ +package gregicality.multiblocks.api.metatileentity; + +import net.minecraft.util.ResourceLocation; + +import org.jetbrains.annotations.NotNull; + +import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; +import gregtech.api.pattern.BlockPattern; +import gregtech.api.recipes.RecipeMap; + +public abstract class GCYMMultiShapeMultiblockController extends GCYMRecipeMapMultiblockController { + + public GCYMMultiShapeMultiblockController(ResourceLocation metaTileEntityId, RecipeMap[] recipeMaps) { + super(metaTileEntityId, recipeMaps); + } + + @Override + protected @NotNull BlockPattern createStructurePattern() { + if (this.getWorld() != null && this.getPos() != null) { + if (this.getWorld().getTileEntity(this.getPos()) instanceof IGregTechTileEntity gtte) { + if (gtte.getMetaTileEntity() instanceof GCYMMultiShapeMultiblockController controller) { + return getStructurePattern(controller.getRecipeMapIndex()); + } + } + } + return getStructurePattern(0); + } + + protected abstract @NotNull BlockPattern getStructurePattern(int index); + + @Override + public void checkStructurePattern() { + if (!this.isStructureFormed()) { + this.reinitializeStructurePattern(); + } + super.checkStructurePattern(); + } + + @Override + public void setRecipeMapIndex(int index) { + super.setRecipeMapIndex(index); + this.reinitializeStructurePattern(); + } +} diff --git a/src/main/java/gregicality/multiblocks/api/recipeproperties/LFFRecipeTypeProperty.java b/src/main/java/gregicality/multiblocks/api/recipeproperties/LFFRecipeTypeProperty.java new file mode 100644 index 00000000..f090ed37 --- /dev/null +++ b/src/main/java/gregicality/multiblocks/api/recipeproperties/LFFRecipeTypeProperty.java @@ -0,0 +1,33 @@ +package gregicality.multiblocks.api.recipeproperties; + +import net.minecraft.client.Minecraft; + +import gregtech.api.recipes.recipeproperties.RecipeProperty; + +import gregicality.multiblocks.api.recipes.LinearForgingFurnaceRecipeType; + +public class LFFRecipeTypeProperty extends RecipeProperty { + + public static final String KEY = "lff_recipe_type"; + + private static LFFRecipeTypeProperty INSTANCE; + + protected LFFRecipeTypeProperty() { + super(KEY, LinearForgingFurnaceRecipeType.class); + } + + public static LFFRecipeTypeProperty getInstance() { + if (INSTANCE == null) { + INSTANCE = new LFFRecipeTypeProperty(); + } + return INSTANCE; + } + + @Override + public void drawInfo(Minecraft minecraft, int x, int y, int color, Object value) {} + + @Override + public int getInfoHeight(Object value) { + return 0; + } +} diff --git a/src/main/java/gregicality/multiblocks/api/recipes/GCYMRecipeMaps.java b/src/main/java/gregicality/multiblocks/api/recipes/GCYMRecipeMaps.java index 8f9d3593..cb194918 100644 --- a/src/main/java/gregicality/multiblocks/api/recipes/GCYMRecipeMaps.java +++ b/src/main/java/gregicality/multiblocks/api/recipes/GCYMRecipeMaps.java @@ -1,7 +1,12 @@ package gregicality.multiblocks.api.recipes; +import java.util.List; + +import com.google.common.collect.Lists; + import gregtech.api.gui.GuiTextures; import gregtech.api.recipes.RecipeMap; +import gregtech.api.recipes.RecipeMaps; import gregtech.api.recipes.builders.BlastRecipeBuilder; import gregtech.core.sound.GTSoundEvents; @@ -17,5 +22,42 @@ public final class GCYMRecipeMaps { .setSlotOverlay(true, true, true, GuiTextures.FURNACE_OVERLAY_2) .setSound(GTSoundEvents.FURNACE); + public static final RecipeMap[] LINEAR_FORGING_RECIPES = new RecipeMap[] { + RecipeMaps.BLAST_RECIPES, + ALLOY_BLAST_RECIPES, + new RecipeMap<>("lff_dual", 9, 3, 3, 1, new BlastRecipeBuilder(), false) + .setSlotOverlay(false, false, false, GuiTextures.FURNACE_OVERLAY_1) + .setSlotOverlay(false, true, false, GuiTextures.FURNACE_OVERLAY_2) + .setSlotOverlay(true, true, false, GuiTextures.FURNACE_OVERLAY_2) + .setSound(GTSoundEvents.FURNACE), + RecipeMaps.VACUUM_RECIPES, + new RecipeMap<>("lff_blast_cooled", 6, 3, 3, 2, new BlastRecipeBuilder(), false), + new RecipeMap<>("lff_alloy_cooled", 12, 1, 4, 2, new BlastRecipeBuilder(), false) + .setSlotOverlay(false, false, false, GuiTextures.FURNACE_OVERLAY_1) + .setSlotOverlay(false, false, true, GuiTextures.FURNACE_OVERLAY_1) + .setSlotOverlay(false, true, false, GuiTextures.FURNACE_OVERLAY_2) + .setSlotOverlay(true, true, false, GuiTextures.FURNACE_OVERLAY_2) + .setSound(GTSoundEvents.FURNACE), + new RecipeMap<>("lff_dual_cooled", 12, 3, 4, 2, new BlastRecipeBuilder(), false) + .setSlotOverlay(false, false, false, GuiTextures.FURNACE_OVERLAY_1) + .setSlotOverlay(false, true, false, GuiTextures.FURNACE_OVERLAY_2) + .setSlotOverlay(true, true, false, GuiTextures.FURNACE_OVERLAY_2) + .setSound(GTSoundEvents.FURNACE), + new RecipeMap<>("lff_blast_forging_cooled", 6, 3, 3, 2, new BlastRecipeBuilder(), false), + new RecipeMap<>("lff_alloy_forging_cooled", 12, 1, 4, 2, new BlastRecipeBuilder(), false) + .setSlotOverlay(false, false, false, GuiTextures.FURNACE_OVERLAY_1) + .setSlotOverlay(false, false, true, GuiTextures.FURNACE_OVERLAY_1) + .setSlotOverlay(false, true, false, GuiTextures.FURNACE_OVERLAY_2) + .setSlotOverlay(true, true, false, GuiTextures.FURNACE_OVERLAY_2) + .setSound(GTSoundEvents.FURNACE), + new RecipeMap<>("lff_dual_forging_cooled", 12, 3, 4, 2, new BlastRecipeBuilder(), false) + .setSlotOverlay(false, false, false, GuiTextures.FURNACE_OVERLAY_1) + .setSlotOverlay(false, true, false, GuiTextures.FURNACE_OVERLAY_2) + .setSlotOverlay(true, true, false, GuiTextures.FURNACE_OVERLAY_2) + .setSound(GTSoundEvents.FURNACE) + }; + + static final List> LFF_RECIPES_AS_LIST = Lists.newArrayList(GCYMRecipeMaps.LINEAR_FORGING_RECIPES); + private GCYMRecipeMaps() {} } diff --git a/src/main/java/gregicality/multiblocks/api/recipes/LinearForgingFurnaceRecipeType.java b/src/main/java/gregicality/multiblocks/api/recipes/LinearForgingFurnaceRecipeType.java new file mode 100644 index 00000000..cb40c847 --- /dev/null +++ b/src/main/java/gregicality/multiblocks/api/recipes/LinearForgingFurnaceRecipeType.java @@ -0,0 +1,12 @@ +package gregicality.multiblocks.api.recipes; + +public enum LinearForgingFurnaceRecipeType { + + NONE, + BLAST, + ALLOY, + BLAST_COOLED, + ALLOY_COOLED, + BLAST_FORGING_COOLED, + ALLOY_FORGING_COOLED; +} diff --git a/src/main/java/gregicality/multiblocks/api/render/GCYMTextures.java b/src/main/java/gregicality/multiblocks/api/render/GCYMTextures.java index c3a286ce..53ebdf92 100644 --- a/src/main/java/gregicality/multiblocks/api/render/GCYMTextures.java +++ b/src/main/java/gregicality/multiblocks/api/render/GCYMTextures.java @@ -41,6 +41,7 @@ public final class GCYMTextures { public static OrientedOverlayRenderer MEGA_BLAST_FURNACE_OVERLAY; public static OrientedOverlayRenderer MEGA_VACUUM_FREEZER_OVERLAY; public static OrientedOverlayRenderer STEAM_ENGINE_OVERLAY; + public static OrientedOverlayRenderer LINEAR_FORGING_FURNACE_OVERLAY; // Hatches public static OrientedOverlayRenderer PARALLEL_HATCH_MK1_OVERLAY; @@ -63,6 +64,7 @@ public final class GCYMTextures { public static SimpleOverlayRenderer ENGRAVER_CASING; public static SimpleOverlayRenderer ATOMIC_CASING; public static SimpleOverlayRenderer STEAM_CASING; + public static SimpleOverlayRenderer FORGING_CASING; private GCYMTextures() {} @@ -97,6 +99,7 @@ public static void preInit() { MEGA_BLAST_FURNACE_OVERLAY = new OrientedOverlayRenderer("multiblock/mega_blast_furnace"); MEGA_VACUUM_FREEZER_OVERLAY = new OrientedOverlayRenderer("multiblock/mega_vacuum_freezer"); STEAM_ENGINE_OVERLAY = new OrientedOverlayRenderer("multiblock/steam_engine"); + LINEAR_FORGING_FURNACE_OVERLAY = new OrientedOverlayRenderer("multiblock/linear_forging_furnace"); // Hatches PARALLEL_HATCH_MK1_OVERLAY = new OrientedOverlayRenderer("hatches/parallel_hatch_mk1"); @@ -119,5 +122,6 @@ public static void preInit() { ENGRAVER_CASING = new SimpleOverlayRenderer("casings/large_multiblock_casing/engraver_casing"); ATOMIC_CASING = new SimpleOverlayRenderer("casings/large_multiblock_casing/atomic_casing"); STEAM_CASING = new SimpleOverlayRenderer("casings/large_multiblock_casing/steam_casing"); + FORGING_CASING = new SimpleOverlayRenderer("casings/large_multiblock_casing/forging_casing"); } } diff --git a/src/main/java/gregicality/multiblocks/api/unification/GCYMFirstDegreeMaterials.java b/src/main/java/gregicality/multiblocks/api/unification/GCYMFirstDegreeMaterials.java index 72e2949f..16bd2be9 100644 --- a/src/main/java/gregicality/multiblocks/api/unification/GCYMFirstDegreeMaterials.java +++ b/src/main/java/gregicality/multiblocks/api/unification/GCYMFirstDegreeMaterials.java @@ -94,5 +94,13 @@ public static void init() { .components(Molybdenum, 1, Silicon, 2) .blastTemp(2300, BlastProperty.GasTier.MID, GTValues.VA[GTValues.EV], 800) .build(); + + CobaltAlloy = new Material.Builder(3010, gcymId("cobalt_alloy")) + .ingot().fluid() + .color(0x6594B2).iconSet(MaterialIconSet.METALLIC) + .flags(GENERATE_PLATE, GENERATE_GEAR) + .components(Cobalt, 3, Iridium, 3, Aluminium, 1, Tungsten, 1) + .blastTemp(5388, BlastProperty.GasTier.HIGH, GTValues.VA[GTValues.IV], 1000) + .build(); } } diff --git a/src/main/java/gregicality/multiblocks/api/unification/GCYMMaterialFlags.java b/src/main/java/gregicality/multiblocks/api/unification/GCYMMaterialFlags.java index 4128b58f..7177294b 100644 --- a/src/main/java/gregicality/multiblocks/api/unification/GCYMMaterialFlags.java +++ b/src/main/java/gregicality/multiblocks/api/unification/GCYMMaterialFlags.java @@ -20,5 +20,17 @@ public final class GCYMMaterialFlags { .requireFlags(NO_ALLOY_BLAST_RECIPES) .build(); + /** + * Use to disable forging recipes from generating where this material is an input. Not compatible with ore entries. + */ + public static final MaterialFlag NO_FORGING_RECIPES_IN = new MaterialFlag.Builder("no_forging_recipes_in") + .build(); + + /** + * Use to disable forging recipes from forging this material as an output. + */ + public static final MaterialFlag NO_FORGING_OUT = new MaterialFlag.Builder("no_forging_out") + .build(); + private GCYMMaterialFlags() {} } diff --git a/src/main/java/gregicality/multiblocks/api/unification/GCYMMaterials.java b/src/main/java/gregicality/multiblocks/api/unification/GCYMMaterials.java index b2e6a791..460e1711 100644 --- a/src/main/java/gregicality/multiblocks/api/unification/GCYMMaterials.java +++ b/src/main/java/gregicality/multiblocks/api/unification/GCYMMaterials.java @@ -17,6 +17,7 @@ public final class GCYMMaterials { public static Material TitaniumCarbide; public static Material TantalumCarbide; public static Material MolybdenumDisilicide; + public static Material CobaltAlloy; /* * Second Degree Materials 3020-3039 diff --git a/src/main/java/gregicality/multiblocks/common/CommonProxy.java b/src/main/java/gregicality/multiblocks/common/CommonProxy.java index 0e927db1..d37525d0 100644 --- a/src/main/java/gregicality/multiblocks/common/CommonProxy.java +++ b/src/main/java/gregicality/multiblocks/common/CommonProxy.java @@ -12,6 +12,7 @@ import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.fml.client.event.ConfigChangedEvent; import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.eventhandler.EventPriority; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.registries.IForgeRegistry; @@ -21,6 +22,7 @@ import gregicality.multiblocks.api.utils.GCYMLog; import gregicality.multiblocks.common.block.GCYMMetaBlocks; import gregicality.multiblocks.loaders.recipe.GCYMRecipeLoader; +import gregicality.multiblocks.loaders.recipe.LinearForgingFurnaceLoader; @Mod.EventBusSubscriber(modid = GregicalityMultiblocks.MODID) public class CommonProxy { @@ -67,4 +69,9 @@ public static void registerRecipes(RegistryEvent.Register event) { // anything here is safe to call removals in GCYMRecipeLoader.init(); } + + @SubscribeEvent(priority = EventPriority.LOWEST) + public static void recipesLate(RegistryEvent.Register event) { + LinearForgingFurnaceLoader.registerLate(); + } } diff --git a/src/main/java/gregicality/multiblocks/common/GCYMConfigHolder.java b/src/main/java/gregicality/multiblocks/common/GCYMConfigHolder.java index d7132e23..21732d0e 100644 --- a/src/main/java/gregicality/multiblocks/common/GCYMConfigHolder.java +++ b/src/main/java/gregicality/multiblocks/common/GCYMConfigHolder.java @@ -11,10 +11,49 @@ public class GCYMConfigHolder { @Config.Name("Global Multiblock Options") public static GlobalMultiblocks globalMultiblocks = new GlobalMultiblocks(); + @Config.Comment("Config settings applying to the Linear Forging Furnace") + @Config.Name("Linear Forging Furnace Settings") + public static LinearForgingFurnaceSettings linearForgingFurnaceSettings = new LinearForgingFurnaceSettings(); + public static class GlobalMultiblocks { @Config.Comment({ "Makes nearly every GCYM Multiblock require blocks which set their maximum voltages.", "Default: false" }) public boolean enableTieredCasings = false; } + + public static class LinearForgingFurnaceSettings { + + @Config.Comment({ "Sets the multiplier to duration applied for cooling recipes.", + "Default: 1" }) + @Config.RequiresMcRestart + @Config.RangeDouble( + min = 0.1, + max = 2.0) + public double coolingDurationModifier = 1.0; + + @Config.Comment({ "Sets the flat temperature penalty applied for cooling recipes.", + "Default: 0" }) + @Config.RequiresMcRestart + @Config.RangeInt( + min = 0, + max = 2000) + public int coolingTemperaturePenalty = 0; + + @Config.Comment({ "Sets the multiplier to duration applied for forging recipes.", + "Default: 1.05" }) + @Config.RequiresMcRestart + @Config.RangeDouble( + min = 0.1, + max = 2.0) + public double forgingDurationModifier = 1.05; + + @Config.Comment({ "Sets the flat temperature penalty applied for forging recipes.", + "Default: 200" }) + @Config.RequiresMcRestart + @Config.RangeInt( + min = 0, + max = 2000) + public int forgingTemperaturePenalty = 200; + } } diff --git a/src/main/java/gregicality/multiblocks/common/block/blocks/BlockLargeMultiblockCasing.java b/src/main/java/gregicality/multiblocks/common/block/blocks/BlockLargeMultiblockCasing.java index 14e6a0f9..a247de68 100644 --- a/src/main/java/gregicality/multiblocks/common/block/blocks/BlockLargeMultiblockCasing.java +++ b/src/main/java/gregicality/multiblocks/common/block/blocks/BlockLargeMultiblockCasing.java @@ -44,7 +44,8 @@ public enum CasingType implements IStringSerializable { MIXER_CASING("mixer_casing"), ENGRAVER_CASING("engraver_casing"), ATOMIC_CASING("atomic_casing"), - STEAM_CASING("steam_casing"); + STEAM_CASING("steam_casing"), + FORGING_CASING("forging_casing"); private final String name; diff --git a/src/main/java/gregicality/multiblocks/common/metatileentities/GCYMMetaTileEntities.java b/src/main/java/gregicality/multiblocks/common/metatileentities/GCYMMetaTileEntities.java index 6958eb0f..61621600 100644 --- a/src/main/java/gregicality/multiblocks/common/metatileentities/GCYMMetaTileEntities.java +++ b/src/main/java/gregicality/multiblocks/common/metatileentities/GCYMMetaTileEntities.java @@ -6,6 +6,7 @@ import gregtech.api.GTValues; import gregtech.api.GregTechAPI; +import gregicality.multiblocks.common.metatileentities.multiblock.advanced.MetaTileEntityLinearForgingFurnace; import gregicality.multiblocks.common.metatileentities.multiblock.generator.MetaTileEntitySteamEngine; import gregicality.multiblocks.common.metatileentities.multiblock.standard.*; import gregicality.multiblocks.common.metatileentities.multiblockpart.MetaTileEntityParallelHatch; @@ -41,6 +42,7 @@ public final class GCYMMetaTileEntities { public static MetaTileEntityMegaVacuumFreezer MEGA_VACUUM_FREEZER; public static MetaTileEntitySteamEngine STEAM_ENGINE; public static MetaTileEntityLargeCircuitAssembler LARGE_CIRCUIT_ASSEMBLER; + public static MetaTileEntityLinearForgingFurnace LINEAR_FORGING_FURNACE; public static MetaTileEntityParallelHatch[] PARALLEL_HATCH = new MetaTileEntityParallelHatch[4]; public static MetaTileEntityTieredHatch[] TIERED_HATCH = new MetaTileEntityTieredHatch[GTValues.V.length]; @@ -88,6 +90,8 @@ public static void init() { STEAM_ENGINE = registerMetaTileEntity(2027, new MetaTileEntitySteamEngine(gcymId("steam_engine"))); LARGE_CIRCUIT_ASSEMBLER = registerMetaTileEntity(2028, new MetaTileEntityLargeCircuitAssembler(gcymId("large_circuit_assembler"))); + LINEAR_FORGING_FURNACE = registerMetaTileEntity(2029, + new MetaTileEntityLinearForgingFurnace(gcymId("linear_forging_furnace"))); // Hatches for (int i = 0; i < PARALLEL_HATCH.length; i++) { diff --git a/src/main/java/gregicality/multiblocks/common/metatileentities/multiblock/advanced/MetaTileEntityLinearForgingFurnace.java b/src/main/java/gregicality/multiblocks/common/metatileentities/multiblock/advanced/MetaTileEntityLinearForgingFurnace.java new file mode 100644 index 00000000..8cca96b7 --- /dev/null +++ b/src/main/java/gregicality/multiblocks/common/metatileentities/multiblock/advanced/MetaTileEntityLinearForgingFurnace.java @@ -0,0 +1,1212 @@ +package gregicality.multiblocks.common.metatileentities.multiblock.advanced; + +import java.util.*; +import java.util.function.Consumer; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.resources.I18n; +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextFormatting; +import net.minecraft.world.World; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import gregtech.api.GTValues; +import gregtech.api.block.IHeatingCoilBlockStats; +import gregtech.api.block.VariantActiveBlock; +import gregtech.api.capability.GregtechDataCodes; +import gregtech.api.capability.IHeatingCoil; +import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; +import gregtech.api.metatileentity.multiblock.IMultiblockPart; +import gregtech.api.metatileentity.multiblock.MultiblockAbility; +import gregtech.api.metatileentity.multiblock.MultiblockDisplayText; +import gregtech.api.pattern.*; +import gregtech.api.recipes.Recipe; +import gregtech.api.recipes.recipeproperties.TemperatureProperty; +import gregtech.api.util.GTUtility; +import gregtech.api.util.RelativeDirection; +import gregtech.api.util.TextComponentUtil; +import gregtech.api.util.TextFormattingUtil; +import gregtech.api.util.function.TriConsumer; +import gregtech.client.renderer.ICubeRenderer; +import gregtech.client.renderer.texture.cube.OrientedOverlayRenderer; +import gregtech.common.ConfigHolder; +import gregtech.common.blocks.*; +import gregtech.common.metatileentities.MetaTileEntities; + +import gregicality.multiblocks.api.capability.impl.GCYMHeatingCoilRecipeLogic; +import gregicality.multiblocks.api.metatileentity.GCYMMultiShapeMultiblockController; +import gregicality.multiblocks.api.recipeproperties.LFFRecipeTypeProperty; +import gregicality.multiblocks.api.recipes.GCYMRecipeMaps; +import gregicality.multiblocks.api.recipes.LinearForgingFurnaceRecipeType; +import gregicality.multiblocks.api.render.GCYMTextures; +import gregicality.multiblocks.common.block.GCYMMetaBlocks; +import gregicality.multiblocks.common.block.blocks.BlockLargeMultiblockCasing; +import gregicality.multiblocks.common.block.blocks.BlockUniqueCasing; +import gregicality.multiblocks.common.metatileentities.GCYMMetaTileEntities; + +public class MetaTileEntityLinearForgingFurnace extends GCYMMultiShapeMultiblockController implements IHeatingCoil { + + private int blastFurnaceTemperature; + private int rowCount; + private int inDisplayWorld = -1; + + private LinearForgingFurnaceRecipeType lastRecipeType = LinearForgingFurnaceRecipeType.NONE; + + public MetaTileEntityLinearForgingFurnace(ResourceLocation metaTileEntityId) { + super(metaTileEntityId, GCYMRecipeMaps.LINEAR_FORGING_RECIPES); + this.recipeMapWorkable = new GCYMHeatingCoilRecipeLogic(this); + } + + @Override + public MetaTileEntity createMetaTileEntity(IGregTechTileEntity tileEntity) { + return new MetaTileEntityLinearForgingFurnace(this.metaTileEntityId).inDisplayWorld(this.inDisplayWorld); + } + + @Override + protected void addDisplayText(List textList) { + MultiblockDisplayText.builder(textList, isStructureFormed()) + .setWorkingStatus(recipeMapWorkable.isWorkingEnabled(), recipeMapWorkable.isActive()) + .addEnergyUsageLine(getEnergyContainer()) + .addEnergyTierLine(GTUtility.getTierByVoltage(recipeMapWorkable.getMaxVoltage())) + .addCustom(tl -> { + // Coil heat capacity line + if (isStructureFormed()) { + ITextComponent heatString = TextComponentUtil.stringWithColor( + TextFormatting.RED, + TextFormattingUtil.formatNumbers(blastFurnaceTemperature) + "K"); + + tl.add(TextComponentUtil.translationWithColor( + TextFormatting.GRAY, + "gregtech.multiblock.blast_furnace.max_temperature", + heatString)); + } + }) + .addParallelsLine(recipeMapWorkable.getParallelLimit()) + .addWorkingStatusLine() + .addProgressLine(recipeMapWorkable.getProgressPercent()); + } + + @Override + protected void formStructure(PatternMatchContext context) { + super.formStructure(context); + Object type = context.get("CoilType"); + this.rowCount = calculateRowCount(context); + if (type instanceof IHeatingCoilBlockStats) { + this.blastFurnaceTemperature = ((IHeatingCoilBlockStats) type).getCoilTemperature(); + } else { + this.blastFurnaceTemperature = BlockWireCoil.CoilType.CUPRONICKEL.getCoilTemperature(); + } + + this.blastFurnaceTemperature += 100 * + Math.max(0, GTUtility.getTierByVoltage(getEnergyContainer().getInputVoltage()) - GTValues.MV); + } + + protected int calculateRowCount(PatternMatchContext context) { + int VAs = ((LinkedList) context.get("VABlock")).size(); + return switch (this.getRecipeMapIndex()) { + default -> // Blast + (VAs - 36) / 12; + case 1 -> // Alloy + (VAs - 70) / 14; + case 2 -> // Dual + (VAs - 106) / 26; + case 3 -> // Freezer + (VAs - 2) / 3; + case 4 -> // Cooled Blast + (VAs - 38) / 15; + case 5 -> // Cooled Alloy + (VAs - 72) / 17; + case 6 -> // Cooled Dual + (VAs - 108) / 29; + case 7 -> // Cooled Blast Forging + (VAs - 38) / 17; + case 8 -> // Cooled Alloy Forging + (VAs - 72) / 19; + case 9 -> // Cooled Dual Forging + (VAs - 108) / 31; + }; + } + + @Override + protected @NotNull BlockPattern getStructurePattern(int index) { + TraceabilityPredicate casing = states(getCasingState()).setMinLayerLimited(10); + TraceabilityPredicate auto = autoAbilities(true, true, false, false, false, false, false); + return (switch (index) { + default -> // Blast + FactoryBlockPattern.start(RelativeDirection.RIGHT, RelativeDirection.UP, RelativeDirection.FRONT) + .aisle( + "XXX EFCFFCFE ZZZ", + "XSX EICVVCIE ZZZ", + "XXX EFCFFCFE ZZZ") + .aisle( + "XXX ECCCCCCE ZZZ", + "XPPPP######PPPPZ", + "XXX ECCCCCCE ZZZ") + .setRepeatable(1, 16) + .aisle( + "XXX EFCFFCFE ZZZ", + "XMX EICVVCIE ZZZ", + "XXX EFCFFCFE ZZZ"); + case 1 -> // Alloy + FactoryBlockPattern.start(RelativeDirection.RIGHT, RelativeDirection.UP, RelativeDirection.FRONT) + .aisle( + " ", + " AICIVICIA ", + " AICIVICIA ", + " AICIVICIA ", + " ") + .aisle( + " AFCFVFCFA ", + "XXX A#######A ZZZ", + "XSX A#######A ZZZ", + "XXX A#######A ZZZ", + " AFCFVFCFA ") + .aisle( + " ACCCVCCCA ", + "XXX A#######A ZZZ", + "XPPPP#######PPPPZ", + "XXX A#######A ZZZ", + " ACCCVCCCA ") + .setRepeatable(1, 16) + .aisle( + " AFCFVFCFA ", + "XXX A#######A ZZZ", + "XMX A#######A ZZZ", + "XXX A#######A ZZZ", + " AFCFVFCFA ") + .aisle( + " ", + " AICIVICIA ", + " AICIVICIA ", + " AICIVICIA ", + " "); + case 2 -> // Dual + FactoryBlockPattern.start(RelativeDirection.RIGHT, RelativeDirection.UP, RelativeDirection.FRONT) + .aisle( + " ", + " ", + " ", + " ", + " ", + " AICIVICIA ", + " AICIVICIA ", + " AICIVICIA ", + " ") + .aisle( + "EEE EFCFFCFE EEE", + "EEE EICVVCIE EEE", + "EEE EFCFFCFE EEE", + " ", + " AFCFVFCFA ", + "XXX A#######A ZZZ", + "XSX A#######A ZZZ", + "XXX A#######A ZZZ", + " AFCFVFCFA ") + .aisle( + "EEE ECCCCCCE EEE", + "EPPPP######PPPPPE", + "EPE ECCCCCCE EPE", + " P P ", + " P ACCCVCCCA P ", + "XPX A#######A ZPZ", + "XPPPP#######PPPPZ", + "XXX A#######A ZZZ", + " ACCCVCCCA ") + .setRepeatable(1) + .aisle( + "EEE ECCCCCCE EEE", + "EPPPP######PPPPPE", + "EEE ECCCCCCE EEE", + " ", + " ACCCVCCCA ", + "XXX A#######A ZZZ", + "XPPPP#######PPPPZ", + "XXX A#######A ZZZ", + " ACCCVCCCA ") + .setRepeatable(0, 14) + .aisle( + "EEE ECCCCCCE EEE", + "EPPPP######PPPPPE", + "EPE ECCCCCCE EPE", + " P P ", + " P ACCCVCCCA P ", + "XPX A#######A ZPZ", + "XPPPP#######PPPPZ", + "XXX A#######A ZZZ", + " ACCCVCCCA ") + .setRepeatable(0, 1) + .aisle( + "EEE EFCFFCFE EEE", + "EEE EICVVCIE EEE", + "EEE EFCFFCFE EEE", + " ", + " AFCFVFCFA ", + "XXX A#######A ZZZ", + "XMX A#######A ZZZ", + "XXX A#######A ZZZ", + " AFCFVFCFA ") + .aisle( + " ", + " ", + " ", + " ", + " ", + " AICIVICIA ", + " AICIVICIA ", + " AICIVICIA ", + " "); + case 3 -> // Freezer + FactoryBlockPattern.start(RelativeDirection.RIGHT, RelativeDirection.UP, RelativeDirection.FRONT) + .aisle( + "XXX HHH ZZZ", + "XSX HHH ZZZ", + "XXX HHH ZZZ", + " BVB ", + " BBB ") + .aisle( + "XXX HHH ZZZ", + "XPPPP#PPPPZ", + "XXX HPH ZZZ", + " VPV ", + " BVB ") + .setRepeatable(1, 16) + .aisle( + "XXX HHH ZZZ", + "XMX HHH ZZZ", + "XXX HHH ZZZ", + " BVB ", + " BBB "); + case 4 -> // Cooled Blast + FactoryBlockPattern.start(RelativeDirection.RIGHT, RelativeDirection.UP, RelativeDirection.FRONT) + .aisle( + "XXX EFCFFCFE HHH ZZZ", + "XSX EICVVCIE HHH ZZZ", + "XXX EFCFFCFE HHH ZZZ", + " BVB ", + " BBB ") + .aisle( + "XXX ECCCCCCE HHH ZZZ", + "XPPPP######PPP#PPPPZ", + "XXX ECCCCCCE HPH ZZZ", + " VPV ", + " BVB ") + .setRepeatable(1, 16) + .aisle( + "XXX EFCFFCFE HHH ZZZ", + "XMX EICVVCIE HHH ZZZ", + "XXX EFCFFCFE HHH ZZZ", + " BVB ", + " BBB "); + case 5 -> // Cooled Alloy + FactoryBlockPattern.start(RelativeDirection.RIGHT, RelativeDirection.UP, RelativeDirection.FRONT) + .aisle( + " ", + " AICIVICIA ", + " AICIVICIA ", + " AICIVICIA ", + " ", + " ") + .aisle( + " AFCFVFCFA ", + "XXX A#######A HHH ZZZ", + "XSX A#######A HHH ZZZ", + "XXX A#######A HHH ZZZ", + " AFCFVFCFA BVB ", + " BBB ") + .aisle( + " ACCCVCCCA ", + "XXX A#######A HHH ZZZ", + "XPPPP#######PPP#PPPPZ", + "XXX A#######A HPH ZZZ", + " ACCCVCCCA VPV ", + " BVB ") + .setRepeatable(1, 16) + .aisle( + " AFCFVFCFA ", + "XXX A#######A HHH ZZZ", + "XMX A#######A HHH ZZZ", + "XXX A#######A HHH ZZZ", + " AFCFVFCFA BVB ", + " BBB ") + .aisle( + " ", + " AICIVICIA ", + " AICIVICIA ", + " AICIVICIA ", + " ", + " "); + case 6 -> // Cooled Dual + FactoryBlockPattern.start(RelativeDirection.RIGHT, RelativeDirection.UP, RelativeDirection.FRONT) + .aisle( + " ", + " ", + " ", + " ", + " ", + " AICIVICIA ", + " AICIVICIA ", + " AICIVICIA ", + " ", + " ") + .aisle( + "EEE EFCFFCFE EEE ", + "EEE EICVVCIE EEE ", + "EEE EFCFFCFE EEE ", + " ", + " AFCFVFCFA ", + "XXX A#######A HHH ZZZ", + "XSX A#######A HHH ZZZ", + "XXX A#######A HHH ZZZ", + " AFCFVFCFA BVB ", + " BBB ") + .aisle( + "EEE ECCCCCCE EEE ", + "EPPPP######PPPPPE ", + "EPE ECCCCCCE EPE ", + " P P ", + " P ACCCVCCCA P ", + "XPX A#######A HPH ZZZ", + "XPPPP#######PPP#PPPPZ", + "XXX A#######A HPH ZZZ", + " ACCCVCCCA VPV ", + " BVB ") + .setRepeatable(1) + .aisle( + "EEE ECCCCCCE EEE ", + "EPPPP######PPPPPE ", + "EEE ECCCCCCE EEE ", + " ", + " ACCCVCCCA ", + "XXX A#######A HHH ZZZ", + "XPPPP#######PPP#PPPPZ", + "XXX A#######A HPH ZZZ", + " ACCCVCCCA VPV ", + " BVB ") + .setRepeatable(0, 14) + .aisle( + "EEE ECCCCCCE EEE ", + "EPPPP######PPPPPE ", + "EPE ECCCCCCE EPE ", + " P P ", + " P ACCCVCCCA P ", + "XPX A#######A HPH ZZZ", + "XPPPP#######PPP#PPPPZ", + "XXX A#######A HPH ZZZ", + " ACCCVCCCA VPV ", + " BVB ") + .setRepeatable(0, 1) + .aisle( + "EEE EFCFFCFE EEE ", + "EEE EICVVCIE EEE ", + "EEE EFCFFCFE EEE ", + " ", + " AFCFVFCFA ", + "XXX A#######A HHH ZZZ", + "XMX A#######A HHH ZZZ", + "XXX A#######A HHH ZZZ", + " AFCFVFCFA BVB ", + " BBB ") + .aisle( + " ", + " ", + " ", + " ", + " ", + " AICIVICIA ", + " AICIVICIA ", + " AICIVICIA ", + " ", + " "); + case 7 -> // Cooled Blast Forging + FactoryBlockPattern.start(RelativeDirection.RIGHT, RelativeDirection.UP, RelativeDirection.FRONT) + .aisle( + " WWWW ", + " WWWW ", + " WWWW ", + " ", + " ", + "XXX EFCFFCFE HHH ZZZ", + "XSX EICVVCIE HHH ZZZ", + "XXX EFCFFCFE HHH ZZZ", + " BVB ", + " BBB ") + .aisle( + " WDDW ", + " WGGF ", + " WIPW ", + " P ", + " P ", + "XXX ECCCCCCE HPH ZZZ", + "XPPPP######PPP#PPPPZ", + "XXX ECCCCCCE HPH ZZZ", + " VPV ", + " BVB ") + .setRepeatable(1) + .aisle( + " WDDW ", + " WGGF ", + " WIWW ", + " ", + " ", + "XXX ECCCCCCE HHH ZZZ", + "XPPPP######PPP#PPPPZ", + "XXX ECCCCCCE HPH ZZZ", + " VPV ", + " BVB ") + .setRepeatable(0, 14) + .aisle( + " WDDW ", + " WGGF ", + " WIPW ", + " P ", + " P ", + "XXX ECCCCCCE HPH ZZZ", + "XPPPP######PPP#PPPPZ", + "XXX ECCCCCCE HPH ZZZ", + " VPV ", + " BVB ") + .setRepeatable(0, 1) + .aisle( + " WWWW ", + " WWWW ", + " WWWW ", + " ", + " ", + "XXX EFCFFCFE HHH ZZZ", + "XMX EICVVCIE HHH ZZZ", + "XXX EFCFFCFE HHH ZZZ", + " BVB ", + " BBB "); + case 8 -> // Cooled Alloy Forging + FactoryBlockPattern.start(RelativeDirection.RIGHT, RelativeDirection.UP, RelativeDirection.FRONT) + .aisle( + " ", + " ", + " ", + " ", + " ", + " AICIVICIA ", + " AICIVICIA ", + " AICIVICIA ", + " ", + " ") + .aisle( + " WWWW ", + " WWWW ", + " WWWW ", + " ", + " AFCFVFCFA ", + "XXX A#######A HHH ZZZ", + "XSX A#######A HHH ZZZ", + "XXX A#######A HHH ZZZ", + " AFCFVFCFA BVB ", + " BBB ") + .aisle( + " WDDW ", + " WGGF ", + " WIPW ", + " P ", + " ACCCVCCCA P ", + "XXX A#######A HPH ZZZ", + "XPPPP#######PPP#PPPPZ", + "XXX A#######A HPH ZZZ", + " ACCCVCCCA VPV ", + " BVB ") + .setRepeatable(1) + .aisle( + " WDDW ", + " WGGF ", + " WIWW ", + " ", + " ACCCVCCCA ", + "XXX A#######A HHH ZZZ", + "XPPPP#######PPP#PPPPZ", + "XXX A#######A HPH ZZZ", + " ACCCVCCCA VPV ", + " BVB ") + .setRepeatable(0, 14) + .aisle( + " WDDW ", + " WGGF ", + " WIPW ", + " P ", + " ACCCVCCCA P ", + "XXX A#######A HPH ZZZ", + "XPPPP#######PPP#PPPPZ", + "XXX A#######A HPH ZZZ", + " ACCCVCCCA VPV ", + " BVB ") + .setRepeatable(0, 1) + .aisle( + " WWWW ", + " WWWW ", + " WWWW ", + " ", + " AFCFVFCFA ", + "XXX A#######A HHH ZZZ", + "XMX A#######A HHH ZZZ", + "XXX A#######A HHH ZZZ", + " AFCFVFCFA BVB ", + " BBB ") + .aisle( + " ", + " ", + " ", + " ", + " ", + " AICIVICIA ", + " AICIVICIA ", + " AICIVICIA ", + " ", + " "); + case 9 -> // Cooled Dual Forging + FactoryBlockPattern.start(RelativeDirection.RIGHT, RelativeDirection.UP, RelativeDirection.FRONT) + .aisle( + " ", + " ", + " ", + " ", + " ", + " AICIVICIA ", + " AICIVICIA ", + " AICIVICIA ", + " ", + " ") + .aisle( + "EEE EFCFFCFE WWWW ", + "EEE EICVVCIE WWWW ", + "EEE EFCFFCFE WWWW ", + " ", + " AFCFVFCFA ", + "XXX A#######A HHH ZZZ", + "XSX A#######A HHH ZZZ", + "XXX A#######A HHH ZZZ", + " AFCFVFCFA BVB ", + " BBB ") + .aisle( + "EEE ECCCCCCE WDDW ", + "EPPPP######PPPGGF ", + "EPE ECCCCCCE WIPW ", + " P P ", + " P ACCCVCCCA P ", + "XPX A#######A HPH ZZZ", + "XPPPP#######PPP#PPPPZ", + "XXX A#######A HPH ZZZ", + " ACCCVCCCA VPV ", + " BVB ") + .setRepeatable(1) + .aisle( + "EEE ECCCCCCE WDDW ", + "EPPPP######PPPGGF ", + "EEE ECCCCCCE WIWW ", + " ", + " ACCCVCCCA ", + "XXX A#######A HHH ZZZ", + "XPPPP#######PPP#PPPPZ", + "XXX A#######A HPH ZZZ", + " ACCCVCCCA VPV ", + " BVB ") + .setRepeatable(0, 14) + .aisle( + "EEE ECCCCCCE WDDW ", + "EPPPP######PPPGGF ", + "EPE ECCCCCCE WIPW ", + " P P ", + " P ACCCVCCCA P ", + "XPX A#######A HPH ZZZ", + "XPPPP#######PPP#PPPPZ", + "XXX A#######A HPH ZZZ", + " ACCCVCCCA VPV ", + " BVB ") + .setRepeatable(0, 1) + .aisle( + "EEE EFCFFCFE WWWW ", + "EEE EICVVCIE WWWW ", + "EEE EFCFFCFE WWWW ", + " ", + " AFCFVFCFA ", + "XXX A#######A HHH ZZZ", + "XMX A#######A HHH ZZZ", + "XXX A#######A HHH ZZZ", + " AFCFVFCFA BVB ", + " BBB ") + .aisle( + " ", + " ", + " ", + " ", + " ", + " AICIVICIA ", + " AICIVICIA ", + " AICIVICIA ", + " ", + " "); + }) + .where('S', selfPredicate()) + .where('M', abilities(MultiblockAbility.MUFFLER_HATCH)) + .where('W', casing) + .where('X', casing.or(auto) + .or(abilities(MultiblockAbility.IMPORT_FLUIDS, MultiblockAbility.IMPORT_ITEMS))) + .where('Z', casing.or(auto) + .or(abilities(MultiblockAbility.EXPORT_FLUIDS, MultiblockAbility.EXPORT_ITEMS))) + .where('C', heatingCoils()) + .where('E', states(getCasingStateEBF())) + .where('A', states(getCasingStateABS())) + .where('H', states(getCasingStateVacuum())) + .where('B', states(getCasingStateStainless())) + .where('D', states(getCasingStateGlass())) + .where('G', states(getGearboxState())) + .where('F', states(getFireboxState())) + .where('I', states(getIntakeState())) + .where('P', states(getPipeState())) + .where('V', states(getVentState())) + .where('#', air()) + .where(' ', any()) + .build(); + } + + @Override + public List getMatchingShapes() { + ArrayList shapeInfo = new ArrayList<>(); + MultiblockShapeInfo.Builder builder = MultiblockShapeInfo.builder() + .where('M', MetaTileEntities.MUFFLER_HATCH[GTValues.HV], EnumFacing.NORTH) + .where('X', getCasingState()) + .where('C', MetaBlocks.WIRE_COIL.getDefaultState()) + .where('E', getCasingStateEBF()) + .where('A', getCasingStateABS()) + .where('H', getCasingStateVacuum()) + .where('B', getCasingStateStainless()) + .where('D', getCasingStateGlass()) + .where('G', getGearboxState()) + .where('F', getFireboxState()) + .where('I', getIntakeState()) + .where('P', getPipeState()) + .where('V', getVentState()) + .where(' ', Blocks.AIR.getDefaultState()) + .where('Z', MetaTileEntities.ITEM_IMPORT_BUS[GTValues.HV], EnumFacing.SOUTH) + .where('O', MetaTileEntities.ITEM_EXPORT_BUS[GTValues.HV], EnumFacing.SOUTH) + .where('N', MetaTileEntities.FLUID_IMPORT_HATCH[GTValues.HV], EnumFacing.SOUTH) + .where('T', MetaTileEntities.FLUID_EXPORT_HATCH[GTValues.HV], EnumFacing.SOUTH) + .where('Y', MetaTileEntities.ENERGY_INPUT_HATCH[GTValues.EV], EnumFacing.SOUTH) + .where('L', getCasingState()) + .where('U', + () -> ConfigHolder.machines.enableMaintenance ? MetaTileEntities.MAINTENANCE_HATCH : + getCasingState(), + EnumFacing.SOUTH); + MetaTileEntityLinearForgingFurnace fakeController = (MetaTileEntityLinearForgingFurnace) GCYMMetaTileEntities.LINEAR_FORGING_FURNACE + .createMetaTileEntity(null); + // Blast + fakeController.inDisplayWorld(0); + doRowCopies(shapeInfo, builder, + a -> a.where('S', fakeController, EnumFacing.SOUTH) + .aisle("XXX EFCFFCFE XXX", "XMX EICVVCIE XXX", "XXX EFCFFCFE XXX"), + (a, i, j) -> a.aisle("XXX ECCCCCCE XXX", "XPPPP PPPPX", "XXX ECCCCCCE XXX"), + a -> a.aisle("XXX EFCFFCFE XXX", "YSX EICVVCIE ULX", "ZXN EFCFFCFE OXT")); + // Alloy + fakeController.inDisplayWorld(1); + doRowCopies(shapeInfo, builder, + a -> a.where('S', fakeController, EnumFacing.SOUTH) + .aisle(" ", " AICIVICIA ", " AICIVICIA ", " AICIVICIA ", + " ") + .aisle(" AFCFVFCFA ", "XXX A#######A XXX", "XMX A#######A XXX", "XXX A#######A XXX", + " AFCFVFCFA "), + (a, i, j) -> a.aisle(" ACCCVCCCA ", "XXX A#######A XXX", "XPPPP#######PPPPX", "XXX A#######A XXX", + " ACCCVCCCA "), + a -> a.aisle(" AFCFVFCFA ", "XXX A#######A XXX", "YSX A#######A ULX", "ZXN A#######A OXT", + " AFCFVFCFA ") + .aisle(" ", " AICIVICIA ", " AICIVICIA ", " AICIVICIA ", + " ")); + // Dual + fakeController.inDisplayWorld(2); + doRowCopies(shapeInfo, builder, + a -> a.where('S', fakeController, EnumFacing.SOUTH) + .aisle(" ", " ", " ", " ", + " ", " AICIVICIA ", " AICIVICIA ", " AICIVICIA ", + " ") + .aisle("EEE EFCFFCFE EEE", "EEE EICVVCIE EEE", "EEE EFCFFCFE EEE", " ", + " AFCFVFCFA ", "XXX A#######A XXX", "XMX A#######A XXX", "XXX A#######A XXX", + " AFCFVFCFA "), + (a, i, j) -> { + if (j == 0 || j == i - 1) { + a.aisle("EEE ECCCCCCE EEE", "EPPPP######PPPPPE", "EPE ECCCCCCE EPE", " P P ", + " P ACCCVCCCA P ", "XPX A#######A XPX", "XPPPP#######PPPPX", "XXX A#######A XXX", + " ACCCVCCCA "); + } else { + a.aisle("EEE ECCCCCCE EEE", "EPPPP######PPPPPE", "EEE ECCCCCCE EEE", " ", + " ACCCVCCCA ", "XXX A#######A XXX", "XPPPP#######PPPPX", "XXX A#######A XXX", + " ACCCVCCCA "); + } + }, + a -> a.aisle("EEE EFCFFCFE EEE", "EEE EICVVCIE EEE", "EEE EFCFFCFE EEE", " ", + " AFCFVFCFA ", "XXX A#######A XXX", "YSX A#######A ULX", "ZXN A#######A OXT", + " AFCFVFCFA ") + .aisle(" ", " ", " ", " ", + " ", " AICIVICIA ", " AICIVICIA ", " AICIVICIA ", + " ")); + // Freezer + fakeController.inDisplayWorld(3); + doRowCopies(shapeInfo, builder, + a -> a.where('S', fakeController, EnumFacing.SOUTH) + .aisle("XXX HHH XXX", "XMX HHH XXX", "XXX HHH XXX", " BVB ", " BBB "), + (a, i, j) -> a.aisle("XXX HHH XXX", "XPPPP#PPPPX", "XXX HPH XXX", " VPV ", " BVB "), + a -> a.aisle("XXX HHH XXX", "YSX HHH ULX", "ZXN HHH OXT", " BVB ", " BBB ")); + // Cooled Blast + fakeController.inDisplayWorld(4); + doRowCopies(shapeInfo, builder, + a -> a.where('S', fakeController, EnumFacing.SOUTH) + .aisle("XXX EFCFFCFE HHH XXX", "XMX EICVVCIE HHH XXX", "XXX EFCFFCFE HHH XXX", + " BVB ", " BBB "), + (a, i, j) -> a.aisle("XXX ECCCCCCE HHH XXX", "XPPPP PPP PPPPX", "XXX ECCCCCCE HPH XXX", + " VPV ", " BVB "), + a -> a.aisle("XXX EFCFFCFE HHH XXX", "YSX EICVVCIE HHH ULX", "ZXN EFCFFCFE HHH OXT", + " BVB ", " BBB ")); + // Cooled Alloy + fakeController.inDisplayWorld(5); + doRowCopies(shapeInfo, builder, + a -> a.where('S', fakeController, EnumFacing.SOUTH) + .aisle(" ", " AICIVICIA ", " AICIVICIA ", + " AICIVICIA ", " ", " ") + .aisle(" AFCFVFCFA ", "XXX A#######A HHH XXX", "XMX A#######A HHH XXX", + "XXX A#######A HHH XXX", " AFCFVFCFA BVB ", " BBB "), + (a, i, j) -> a.aisle(" ACCCVCCCA ", "XXX A#######A HHH XXX", "XPPPP#######PPP#PPPPX", + "XXX A#######A HPH XXX", " ACCCVCCCA VPV ", " BVB "), + a -> a.aisle(" AFCFVFCFA ", "XXX A#######A HHH XXX", "YSX A#######A HHH ULX", + "ZXN A#######A HHH OXT", " AFCFVFCFA BVB ", " BBB ") + .aisle(" ", " AICIVICIA ", " AICIVICIA ", + " AICIVICIA ", " ", " ")); + // Cooled Dual + fakeController.inDisplayWorld(6); + doRowCopies(shapeInfo, builder, + a -> a.where('S', fakeController, EnumFacing.SOUTH) + .aisle(" ", " ", " ", + " ", " ", " AICIVICIA ", + " AICIVICIA ", " AICIVICIA ", " ", + " ") + .aisle("EEE EFCFFCFE EEE ", "EEE EICVVCIE EEE ", "EEE EFCFFCFE EEE ", + " ", " AFCFVFCFA ", "XXX A#######A HHH XXX", + "XMX A#######A HHH XXX", "XXX A#######A HHH XXX", " AFCFVFCFA BVB ", + " BBB "), + (a, i, j) -> { + if (j == 0 || j == i - 1) { + a.aisle("EEE ECCCCCCE EEE ", "EPPPP######PPPPPE ", "EPE ECCCCCCE EPE ", + " P P ", " P ACCCVCCCA P ", "XPX A#######A HPH XXX", + "XPPPP#######PPP#PPPPX", "XXX A#######A HPH XXX", " ACCCVCCCA VPV ", + " BVB "); + } else { + a.aisle("EEE ECCCCCCE EEE ", "EPPPP######PPPPPE ", "EEE ECCCCCCE EEE ", + " ", " ACCCVCCCA ", "XXX A#######A HHH XXX", + "XPPPP#######PPP#PPPPX", "XXX A#######A HPH XXX", " ACCCVCCCA VPV ", + " BVB "); + } + }, + a -> a.aisle("EEE EFCFFCFE EEE ", "EEE EICVVCIE EEE ", "EEE EFCFFCFE EEE ", + " ", " AFCFVFCFA ", "XXX A#######A HHH XXX", + "YSX A#######A HHH ULX", "ZXN A#######A HHH OXT", " AFCFVFCFA BVB ", + " BBB ") + .aisle(" ", " ", " ", + " ", " ", " AICIVICIA ", + " AICIVICIA ", " AICIVICIA ", " ", + " ")); + // Cooled Blast Forging + fakeController.inDisplayWorld(7); + doRowCopies(shapeInfo, builder, + a -> a.where('S', fakeController, EnumFacing.SOUTH) + .aisle(" XXXX ", " XXXX ", " XXXX ", + " ", " ", "XXX EFCFFCFE HHH XXX", + "XMX EICVVCIE HHH XXX", "XXX EFCFFCFE HHH XXX", " BVB ", + " BBB "), + (a, i, j) -> { + if (j == 0 || j == i - 1) { + a.aisle(" XDDX ", " XGGF ", " XIPX ", + " P ", " P ", "XXX ECCCCCCE HPH XXX", + "XPPPP######PPP#PPPPX", "XXX ECCCCCCE HPH XXX", " VPV ", + " BVB "); + } else { + a.aisle(" XDDX ", " XGGF ", " XIXX ", + " ", " ", "XXX ECCCCCCE HHH XXX", + "XPPPP######PPP#PPPPX", "XXX ECCCCCCE HPH XXX", " VPV ", + " BVB "); + } + }, + a -> a.aisle(" XXXX ", " XXXX ", " XXXX ", + " ", " ", "XXX EFCFFCFE HHH XXX", "YSX EICVVCIE HHH ULX", + "ZXN EFCFFCFE HHH OXT", " BVB ", " BBB ")); + // Cooled Alloy Forging + fakeController.inDisplayWorld(8); + doRowCopies(shapeInfo, builder, + a -> a.where('S', fakeController, EnumFacing.SOUTH) + .aisle(" ", " ", " ", + " ", " ", " AICIVICIA ", + " AICIVICIA ", " AICIVICIA ", " ", + " ") + .aisle(" XXXX ", " XXXX ", " XXXX ", + " ", " AFCFVFCFA ", "XXX A#######A HHH XXX", + "XMX A#######A HHH XXX", "XXX A#######A HHH XXX", " AFCFVFCFA BVB ", + " BBB "), + (a, i, j) -> { + if (j == 0 || j == i - 1) { + a.aisle(" XDDX ", " XGGF ", " XIPX ", + " P ", " ACCCVCCCA P ", "XXX A#######A HPH XXX", + "XPPPP#######PPP#PPPPX", "XXX A#######A HPH XXX", " ACCCVCCCA VPV ", + " BVB "); + } else { + a.aisle(" XDDX ", " XGGF ", " XIXX ", + " ", " ACCCVCCCA ", "XXX A#######A HHH XXX", + "XPPPP#######PPP#PPPPX", "XXX A#######A HPH XXX", " ACCCVCCCA VPV ", + " BVB "); + } + }, + a -> a.aisle(" XXXX ", " XXXX ", " XXXX ", + " ", " AFCFVFCFA ", "XXX A#######A HHH XXX", + "YSX A#######A HHH ULX", "ZXN A#######A HHH OXT", " AFCFVFCFA BVB ", + " BBB ") + .aisle(" ", " ", " ", + " ", " ", " AICIVICIA ", + " AICIVICIA ", " AICIVICIA ", " ", + " ")); + // Cooled Dual Forging + fakeController.inDisplayWorld(9); + doRowCopies(shapeInfo, builder, + a -> a.where('S', fakeController, EnumFacing.SOUTH) + .aisle(" ", " ", " ", + " ", " ", " AICIVICIA ", + " AICIVICIA ", " AICIVICIA ", " ", + " ") + .aisle("EEE EFCFFCFE XXXX ", "EEE EICVVCIE XXXX ", "EEE EFCFFCFE XXXX ", + " ", " AFCFVFCFA ", "XXX A#######A HHH XXX", + "XMX A#######A HHH XXX", "XXX A#######A HHH XXX", " AFCFVFCFA BVB ", + " BBB "), + (a, i, j) -> { + if (j == 0 || j == i - 1) { + a.aisle("EEE ECCCCCCE XDDX ", "EPPPP######PPPGGF ", "EPE ECCCCCCE XIPX ", + " P P ", " P ACCCVCCCA P ", "XPX A#######A HPH XXX", + "XPPPP#######PPP#PPPPX", "XXX A#######A HPH XXX", " ACCCVCCCA VPV ", + " BVB "); + } else { + a.aisle("EEE ECCCCCCE XDDX ", "EPPPP######PPPGGF ", "EEE ECCCCCCE XIXX ", + " ", " ACCCVCCCA ", "XXX A#######A HHH XXX", + "XPPPP#######PPP#PPPPX", "XXX A#######A HPH XXX", " ACCCVCCCA VPV ", + " BVB "); + } + }, + a -> a.aisle("EEE EFCFFCFE XXXX ", "EEE EICVVCIE XXXX ", "EEE EFCFFCFE XXXX ", + " ", " AFCFVFCFA ", "XXX A#######A HHH XXX", + "YSX A#######A HHH ULX", "ZXN A#######A HHH OXT", " AFCFVFCFA BVB ", + " BBB ") + .aisle(" ", " ", " ", + " ", " ", " AICIVICIA ", + " AICIVICIA ", " AICIVICIA ", " ", + " ")); + return shapeInfo; + } + + public static void doRowCopies(ArrayList shapeInfo, MultiblockShapeInfo.Builder builder, + Consumer preActions, + TriConsumer copyActions, + Consumer postActions) { + int z = -1; + for (int i = 1; i <= 16; i *= 2) { // 1, 2, 4, 8, 16 + MultiblockShapeInfo.Builder copy = builder.shallowCopy(); + if (z != -1) { + copy.where('L', GCYMMetaTileEntities.PARALLEL_HATCH[z], EnumFacing.SOUTH); + } + z++; + + preActions.accept(copy); + for (int j = 0; j < i; j++) { + copyActions.accept(copy, i, j); + } + postActions.accept(copy); + shapeInfo.add(copy.build()); + } + } + + private static IBlockState getCasingState() { + return GCYMMetaBlocks.LARGE_MULTIBLOCK_CASING + .getState(BlockLargeMultiblockCasing.CasingType.FORGING_CASING); + } + + private static IBlockState getCasingStateEBF() { + return MetaBlocks.METAL_CASING.getState(BlockMetalCasing.MetalCasingType.INVAR_HEATPROOF); + } + + private static IBlockState getCasingStateABS() { + return GCYMMetaBlocks.LARGE_MULTIBLOCK_CASING + .getState(BlockLargeMultiblockCasing.CasingType.HIGH_TEMPERATURE_CASING); + } + + private static IBlockState getCasingStateVacuum() { + return MetaBlocks.METAL_CASING.getState(BlockMetalCasing.MetalCasingType.ALUMINIUM_FROSTPROOF); + } + + private static IBlockState getCasingStateStainless() { + return MetaBlocks.METAL_CASING.getState(BlockMetalCasing.MetalCasingType.STAINLESS_CLEAN); + } + + private static IBlockState getCasingStateGlass() { + return MetaBlocks.TRANSPARENT_CASING.getState(BlockGlassCasing.CasingType.TEMPERED_GLASS); + } + + private static IBlockState getGearboxState() { + return MetaBlocks.TURBINE_CASING.getState(BlockTurbineCasing.TurbineCasingType.TUNGSTENSTEEL_GEARBOX); + } + + private static IBlockState getFireboxState() { + return MetaBlocks.BOILER_FIREBOX_CASING.getState(BlockFireboxCasing.FireboxCasingType.TUNGSTENSTEEL_FIREBOX); + } + + private static IBlockState getIntakeState() { + return MetaBlocks.MULTIBLOCK_CASING + .getState(BlockMultiblockCasing.MultiblockCasingType.EXTREME_ENGINE_INTAKE_CASING); + } + + private static IBlockState getPipeState() { + return MetaBlocks.BOILER_CASING.getState(BlockBoilerCasing.BoilerCasingType.TUNGSTENSTEEL_PIPE); + } + + private static IBlockState getVentState() { + return GCYMMetaBlocks.UNIQUE_CASING.getState(BlockUniqueCasing.UniqueCasingType.HEAT_VENT); + } + + @Override + public void update() { + super.update(); + if (!this.getWorld().isRemote) { + if (this.lastRecipeType != this.getRecipeType() && this.isActive()) + this.replaceVariantBlocksActive(true); + } + } + + @Override + public void invalidateStructure() { + super.invalidateStructure(); + this.blastFurnaceTemperature = 0; + this.rowCount = 0; + this.lastRecipeType = LinearForgingFurnaceRecipeType.NONE; + } + + @Override + protected void replaceVariantBlocksActive(boolean isActive) { + if (this.variantActiveBlocks != null && !this.variantActiveBlocks.isEmpty()) { + int id = this.getWorld().provider.getDimension(); + this.writeCustomData(GregtechDataCodes.VARIANT_RENDER_UPDATE, (buf) -> { + buf.writeInt(id); + buf.writeBoolean(isActive); + buf.writeInt(this.getRecipeMapIndex()); + buf.writeInt(this.getRecipeType().ordinal()); + + buf.writeInt(this.variantActiveBlocks.size()); + Iterator var4 = this.variantActiveBlocks.iterator(); + + while (var4.hasNext()) { + BlockPos blockPos = var4.next(); + VariantActiveBlock.setBlockActive(id, blockPos, isActive && + shouldVABlockBeActive(blockPos, this.getRecipeMapIndex(), this.getRecipeType())); + buf.writeBlockPos(blockPos); + } + + }); + } + } + + @Override + public void receiveCustomData(int dataId, PacketBuffer buf) { + if (dataId == GregtechDataCodes.VARIANT_RENDER_UPDATE) { + int minZ = Integer.MAX_VALUE; + int minY = Integer.MAX_VALUE; + int minX = Integer.MAX_VALUE; + int maxZ = Integer.MIN_VALUE; + int maxY = Integer.MIN_VALUE; + int maxX = Integer.MIN_VALUE; + int id = buf.readInt(); + boolean isActive = buf.readBoolean(); + int recipeMapIndex = buf.readInt(); + LinearForgingFurnaceRecipeType recipeType = LinearForgingFurnaceRecipeType.values()[buf.readInt()]; + + int size = buf.readInt(); + + for (int i = 0; i < size; ++i) { + BlockPos blockPos = buf.readBlockPos(); + VariantActiveBlock.setBlockActive(id, blockPos, + isActive && shouldVABlockBeActive(blockPos, recipeMapIndex, recipeType)); + minX = Math.min(minX, blockPos.getX()); + minY = Math.min(minY, blockPos.getY()); + minZ = Math.min(minZ, blockPos.getZ()); + maxX = Math.max(maxX, blockPos.getX()); + maxY = Math.max(maxY, blockPos.getY()); + maxZ = Math.max(maxZ, blockPos.getZ()); + } + + if (this.getWorld().provider.getDimension() == id) { + this.getWorld().markBlockRangeForRenderUpdate(new BlockPos(minX, minY, minZ), + new BlockPos(maxX, maxY, maxZ)); + } + return; + } + + super.receiveCustomData(dataId, buf); + } + + protected boolean shouldVABlockBeActive(BlockPos pos, int recipeMapIndex, + LinearForgingFurnaceRecipeType recipeType) { + if (recipeMapIndex != 2 && recipeMapIndex != 6 && recipeMapIndex != 9) return true; + return switch (recipeType) { + default -> true; + case BLAST -> {// bottom half + EnumFacing relativeDown = RelativeDirection.DOWN + .getRelativeFacing(this.getFrontFacing(), this.getUpwardsFacing(), this.isFlipped()); + BlockPos comparingPos = this.getPos().offset(relativeDown, 3); + + int x = comparingPos.getX() - this.getPos().getX(); + int y = comparingPos.getY() - this.getPos().getY(); + int z = comparingPos.getZ() - this.getPos().getZ(); + yield ((pos.getX() - comparingPos.getX()) * x > 0) || ((pos.getY() - comparingPos.getY()) * y > 0) || + ((pos.getZ() - comparingPos.getZ()) * z > 0); + } + case ALLOY -> {// top half + EnumFacing relativeDown = RelativeDirection.DOWN + .getRelativeFacing(this.getFrontFacing(), this.getUpwardsFacing(), this.isFlipped()); + BlockPos comparingPos = this.getPos().offset(relativeDown, 3); + + int x = comparingPos.getX() - this.getPos().getX(); + int y = comparingPos.getY() - this.getPos().getY(); + int z = comparingPos.getZ() - this.getPos().getZ(); + yield ((pos.getX() - comparingPos.getX()) * x < 0) || ((pos.getY() - comparingPos.getY()) * y < 0) || + ((pos.getZ() - comparingPos.getZ()) * z < 0); + } + case BLAST_COOLED, BLAST_FORGING_COOLED -> {// bottom half + EnumFacing relativeDown = RelativeDirection.DOWN + .getRelativeFacing(this.getFrontFacing(), this.getUpwardsFacing(), this.isFlipped()); + EnumFacing relativeLeft = RelativeDirection.LEFT + .getRelativeFacing(this.getFrontFacing(), this.getUpwardsFacing(), this.isFlipped()); + + BlockPos comparingPos = this.getPos().offset(relativeDown, 3); + int x = comparingPos.getX() - this.getPos().getX(); + int y = comparingPos.getY() - this.getPos().getY(); + int z = comparingPos.getZ() - this.getPos().getZ(); + boolean flag; + flag = ((pos.getX() - comparingPos.getX()) * x > 0) || ((pos.getY() - comparingPos.getY()) * y > 0) || + ((pos.getZ() - comparingPos.getZ()) * z > 0); + + comparingPos = this.getPos().offset(relativeLeft, 11); + x = comparingPos.getX() - this.getPos().getX(); + y = comparingPos.getY() - this.getPos().getY(); + z = comparingPos.getZ() - this.getPos().getZ(); + flag |= ((pos.getX() - comparingPos.getX()) * x > 0) || ((pos.getY() - comparingPos.getY()) * y > 0) || + ((pos.getZ() - comparingPos.getZ()) * z > 0); + yield flag; + } + case ALLOY_COOLED, ALLOY_FORGING_COOLED -> {// top half + EnumFacing relativeDown = RelativeDirection.DOWN + .getRelativeFacing(this.getFrontFacing(), this.getUpwardsFacing(), this.isFlipped()); + EnumFacing relativeLeft = RelativeDirection.LEFT + .getRelativeFacing(this.getFrontFacing(), this.getUpwardsFacing(), this.isFlipped()); + + BlockPos comparingPos = this.getPos().offset(relativeDown, 3); + int x = comparingPos.getX() - this.getPos().getX(); + int y = comparingPos.getY() - this.getPos().getY(); + int z = comparingPos.getZ() - this.getPos().getZ(); + boolean flag; + flag = ((pos.getX() - comparingPos.getX()) * x < 0) || ((pos.getY() - comparingPos.getY()) * y < 0) || + ((pos.getZ() - comparingPos.getZ()) * z < 0); + + comparingPos = this.getPos().offset(relativeLeft, 11); + x = comparingPos.getX() - this.getPos().getX(); + y = comparingPos.getY() - this.getPos().getY(); + z = comparingPos.getZ() - this.getPos().getZ(); + flag |= ((pos.getX() - comparingPos.getX()) * x > 0) || ((pos.getY() - comparingPos.getY()) * y > 0) || + ((pos.getZ() - comparingPos.getZ()) * z > 0); + yield flag; + } + }; + } + + protected LinearForgingFurnaceRecipeType getRecipeType() { + Recipe recipe = this.getRecipeMapWorkable().getPreviousRecipe(); + LinearForgingFurnaceRecipeType type = LinearForgingFurnaceRecipeType.NONE; + if (recipe != null) { + type = recipe.getProperty(LFFRecipeTypeProperty.getInstance(), type); + } + if (type == LinearForgingFurnaceRecipeType.NONE) { + type = this.lastRecipeType; + } else this.lastRecipeType = type; + return type; + } + + @Override + public boolean checkRecipe(@NotNull Recipe recipe, boolean consumeIfSuccess) { + return this.blastFurnaceTemperature >= recipe.getProperty(TemperatureProperty.getInstance(), 0); + } + + @Override + public void addInformation(ItemStack stack, @Nullable World player, @NotNull List tooltip, + boolean advanced) { + super.addInformation(stack, player, tooltip, advanced); + tooltip.add(I18n.format("gregtech.machine.electric_blast_furnace.tooltip.1")); + tooltip.add(I18n.format("gregtech.machine.electric_blast_furnace.tooltip.2")); + tooltip.add(I18n.format("gregtech.machine.electric_blast_furnace.tooltip.3")); + } + + @Override + public ICubeRenderer getBaseTexture(IMultiblockPart iMultiblockPart) { + return GCYMTextures.FORGING_CASING; + } + + @Override + protected @NotNull OrientedOverlayRenderer getFrontOverlay() { + return GCYMTextures.LINEAR_FORGING_FURNACE_OVERLAY; + } + + @Override + public boolean hasMufflerMechanics() { + return this.getRecipeMapIndex() != 3; // recipe map #4 is just vacuum + } + + @Override + public boolean canBeDistinct() { + return true; + } + + @Override + public int getCurrentTemperature() { + return this.blastFurnaceTemperature; + } + + @Override + public NBTTagCompound writeToNBT(NBTTagCompound data) { + super.writeToNBT(data); + data.setInteger("LastRecipeType", this.getRecipeType().ordinal()); + return data; + } + + @Override + public void readFromNBT(NBTTagCompound data) { + super.readFromNBT(data); + this.lastRecipeType = LinearForgingFurnaceRecipeType.values()[data.getInteger("LastRecipeType")]; + } + + @Override + public int getMaxParallel() { + return Math.min(super.getMaxParallel(), this.rowCount * this.rowCount); + } + + protected MetaTileEntityLinearForgingFurnace inDisplayWorld(int recipeMapIndex) { + this.inDisplayWorld = recipeMapIndex; + return this; + } + + @Override + public int getRecipeMapIndex() { + return this.inDisplayWorld == -1 ? super.getRecipeMapIndex() : this.inDisplayWorld; + } +} diff --git a/src/main/java/gregicality/multiblocks/loaders/recipe/GCYMCasingLoader.java b/src/main/java/gregicality/multiblocks/loaders/recipe/GCYMCasingLoader.java index d78d3483..549c1938 100644 --- a/src/main/java/gregicality/multiblocks/loaders/recipe/GCYMCasingLoader.java +++ b/src/main/java/gregicality/multiblocks/loaders/recipe/GCYMCasingLoader.java @@ -8,6 +8,7 @@ import gregtech.api.unification.material.Materials; import gregtech.api.unification.ore.OrePrefix; import gregtech.api.unification.stack.UnificationEntry; +import gregtech.common.ConfigHolder; import gregtech.common.items.MetaItems; import gregicality.multiblocks.api.unification.GCYMMaterials; @@ -20,6 +21,7 @@ public final class GCYMCasingLoader { private GCYMCasingLoader() {} public static void init() { + final int numCasings = ConfigHolder.recipes.casingsPerCraft; // Multiblock Casings ModHandler.addShapedRecipe(true, "casing_large_macerator", GCYMMetaBlocks.LARGE_MULTIBLOCK_CASING @@ -87,6 +89,11 @@ public static void init() { .getItemVariant(BlockLargeMultiblockCasing.CasingType.STEAM_CASING, 2), "PhP", "PFP", "PwP", 'P', new UnificationEntry(OrePrefix.plate, Materials.Brass), 'F', new UnificationEntry(OrePrefix.frameGt, Materials.Brass)); + ModHandler.addShapedRecipe(true, "casing_forging", + GCYMMetaBlocks.LARGE_MULTIBLOCK_CASING + .getItemVariant(BlockLargeMultiblockCasing.CasingType.FORGING_CASING, numCasings), + "PhP", "PFP", "PwP", 'P', new UnificationEntry(OrePrefix.plate, GCYMMaterials.CobaltAlloy), 'F', + new UnificationEntry(OrePrefix.frameGt, Materials.Aluminium)); RecipeMaps.ASSEMBLER_RECIPES.recipeBuilder() .input(OrePrefix.plate, GCYMMaterials.Zeron100, 6) @@ -193,6 +200,14 @@ public static void init() { .getItemVariant(BlockLargeMultiblockCasing.CasingType.STEAM_CASING, 2)) .duration(50).EUt(16).buildAndRegister(); + RecipeMaps.ASSEMBLER_RECIPES.recipeBuilder() + .input(OrePrefix.plate, GCYMMaterials.CobaltAlloy, 6) + .input(OrePrefix.frameGt, Materials.Aluminium) + .notConsumable(new IntCircuitIngredient(6)) + .outputs(GCYMMetaBlocks.LARGE_MULTIBLOCK_CASING + .getItemVariant(BlockLargeMultiblockCasing.CasingType.FORGING_CASING, numCasings)) + .duration(50).EUt(16).buildAndRegister(); + // Unique Casings ModHandler.addShapedRecipe(true, "casing_crushing_wheels", GCYMMetaBlocks.UNIQUE_CASING.getItemVariant(BlockUniqueCasing.UniqueCasingType.CRUSHING_WHEELS, 2), diff --git a/src/main/java/gregicality/multiblocks/loaders/recipe/GCYMMetaTileEntityLoader.java b/src/main/java/gregicality/multiblocks/loaders/recipe/GCYMMetaTileEntityLoader.java index 487bd164..3907d597 100644 --- a/src/main/java/gregicality/multiblocks/loaders/recipe/GCYMMetaTileEntityLoader.java +++ b/src/main/java/gregicality/multiblocks/loaders/recipe/GCYMMetaTileEntityLoader.java @@ -283,6 +283,16 @@ public static void init() { 'S', MetaTileEntities.CIRCUIT_ASSEMBLER[LuV].getStackForm(), 'W', new UnificationEntry(cableGtSingle, NiobiumTitanium)); + ModHandler.addShapedRecipe(true, "linear_forging_furnace", + GCYMMetaTileEntities.LINEAR_FORGING_FURNACE.getStackForm(), + "CFC", "EGA", "CMC", + 'C', new UnificationEntry(circuit, MarkerMaterials.Tier.LuV), + 'G', new UnificationEntry(gear, CobaltAlloy), + 'F', MetaTileEntities.VACUUM_FREEZER.getStackForm(), + 'E', MetaTileEntities.ELECTRIC_BLAST_FURNACE.getStackForm(), + 'A', GCYMMetaTileEntities.ALLOY_BLAST_SMELTER.getStackForm(), + 'M', GCYMMetaTileEntities.LARGE_BENDER.getStackForm()); + // Parallel Hatches ModHandler.addShapedRecipe(true, "parallel_hatch_iv", GCYMMetaTileEntities.PARALLEL_HATCH[0].getStackForm(), "SCE", "CHC", "WCW", diff --git a/src/main/java/gregicality/multiblocks/loaders/recipe/LinearForgingFurnaceLoader.java b/src/main/java/gregicality/multiblocks/loaders/recipe/LinearForgingFurnaceLoader.java new file mode 100644 index 00000000..49fe6a33 --- /dev/null +++ b/src/main/java/gregicality/multiblocks/loaders/recipe/LinearForgingFurnaceLoader.java @@ -0,0 +1,659 @@ +package gregicality.multiblocks.loaders.recipe; + +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import com.google.common.collect.ImmutableList; + +import gregtech.api.GTValues; +import gregtech.api.fluids.store.FluidStorageKeys; +import gregtech.api.recipes.Recipe; +import gregtech.api.recipes.RecipeBuilder; +import gregtech.api.recipes.RecipeMap; +import gregtech.api.recipes.RecipeMaps; +import gregtech.api.recipes.builders.BlastRecipeBuilder; +import gregtech.api.recipes.category.GTRecipeCategory; +import gregtech.api.recipes.chance.output.impl.ChancedFluidOutput; +import gregtech.api.recipes.chance.output.impl.ChancedItemOutput; +import gregtech.api.recipes.ingredients.GTRecipeFluidInput; +import gregtech.api.recipes.ingredients.GTRecipeInput; +import gregtech.api.recipes.ingredients.GTRecipeItemInput; +import gregtech.api.recipes.recipeproperties.IRecipePropertyStorage; +import gregtech.api.recipes.recipeproperties.TemperatureProperty; +import gregtech.api.unification.OreDictUnifier; +import gregtech.api.unification.material.Materials; +import gregtech.api.unification.ore.OrePrefix; +import gregtech.common.items.MetaItems; + +import gregicality.multiblocks.api.recipeproperties.LFFRecipeTypeProperty; +import gregicality.multiblocks.api.recipes.GCYMRecipeMaps; +import gregicality.multiblocks.api.recipes.LinearForgingFurnaceRecipeType; +import gregicality.multiblocks.api.unification.GCYMMaterialFlags; +import gregicality.multiblocks.api.utils.GCYMLog; +import gregicality.multiblocks.common.GCYMConfigHolder; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; + +public class LinearForgingFurnaceLoader { + + private final static int MAXIMUM_SEARCH_MULTIPLIER = 8; + + private static final Map> PREFIX_MODIFIERS = new Object2ObjectOpenHashMap<>(); + + private static final List> EMPTY_BUILDER_LIST = new ObjectArrayList<>(); + + private static int count = 0; + + private static void populateReferences() { + // TODO create unique metaitems to separate out the forging recipes + PREFIX_MODIFIERS.put(OrePrefix.nugget, ImmutableList.of( + MetaItems.SHAPE_MOLD_NUGGET.getStackForm(), MetaItems.SHAPE_EXTRUDER_INGOT.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.ingot, ImmutableList.of( + MetaItems.SHAPE_MOLD_INGOT.getStackForm(), MetaItems.SHAPE_EXTRUDER_INGOT.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.block, ImmutableList.of( + MetaItems.SHAPE_MOLD_BLOCK.getStackForm(), MetaItems.SHAPE_EXTRUDER_INGOT.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.plate, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_PLATE.getStackForm(), MetaItems.SHAPE_MOLD_INGOT.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.plateDouble, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_PLATE.getStackForm(), MetaItems.SHAPE_MOLD_PLATE.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.plateDense, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_PLATE.getStackForm(), MetaItems.SHAPE_MOLD_BLOCK.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.stick, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_ROD.getStackForm(), MetaItems.SHAPE_MOLD_INGOT.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.stickLong, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_ROD_LONG.getStackForm(), MetaItems.SHAPE_MOLD_INGOT.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.frameGt, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_ROD.getStackForm(), MetaItems.SHAPE_MOLD_BLOCK.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.bolt, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_BOLT.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.spring, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_ROD_LONG.getStackForm(), MetaItems.SHAPE_MOLD_CYLINDER.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.springSmall, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_ROD.getStackForm(), MetaItems.SHAPE_MOLD_CYLINDER.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.round, ImmutableList.of( + MetaItems.SHAPE_MOLD_NUGGET.getStackForm(), MetaItems.SHAPE_MOLD_BALL.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.gear, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_GEAR.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.gearSmall, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_GEAR_SMALL.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.rotor, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_ROTOR.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.turbineBlade, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_ROD_LONG.getStackForm(), MetaItems.SHAPE_EXTRUDER_PLATE.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.foil, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_FOIL.getStackForm())); + // this prefix exists but is never used, it's here just in case. + PREFIX_MODIFIERS.put(OrePrefix.pipeTinyItem, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_PIPE_TINY.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.pipeTinyFluid, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_PIPE_TINY.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.pipeSmallItem, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_PIPE_SMALL.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.pipeSmallFluid, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_PIPE_SMALL.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.pipeNormalItem, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_PIPE_NORMAL.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.pipeNormalFluid, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_PIPE_NORMAL.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.pipeLargeItem, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_PIPE_LARGE.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.pipeLargeFluid, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_PIPE_LARGE.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.pipeHugeItem, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_PIPE_HUGE.getStackForm())); + PREFIX_MODIFIERS.put(OrePrefix.pipeHugeFluid, ImmutableList.of( + MetaItems.SHAPE_EXTRUDER_PIPE_HUGE.getStackForm())); + } + + private LinearForgingFurnaceLoader() {} + + public static void registerLate() { + GCYMLog.logger.info("Registering Linear Forging Furnace recipes..."); + populateReferences(); + long time = System.currentTimeMillis(); + registerCooled(); + GCYMLog.logger.debug(String.format("Registered %s cooled recipes in %s milliseconds.", count, + System.currentTimeMillis() - time)); + count = 0; + time = System.currentTimeMillis(); + registerForgingCooled(); + GCYMLog.logger.debug(String.format("Registered %s cooled forging recipes in %s milliseconds.", count, + System.currentTimeMillis() - time)); + time = System.currentTimeMillis(); + assembleCompositeMaps(); + GCYMLog.logger.debug(String.format("Assembled composite maps in %s milliseconds.", + System.currentTimeMillis() - time)); + } + + private static void registerCooled() { + RecipeMap map = (RecipeMap) GCYMRecipeMaps.LINEAR_FORGING_RECIPES[4]; + GTRecipeCategory category = GTRecipeCategory.getByName(map.getUnlocalizedName()); + if (category == null) + throw new IllegalStateException("Component recipemap " + map.getUnlocalizedName() + " had no a category!"); + for (Recipe blastRecipe : RecipeMaps.BLAST_RECIPES.getRecipeList()) { + var recipe = produceCooledRecipe(blastRecipe, map, category); + if (recipe != null) { + count++; + recipe.buildAndRegister(); + } + } + + map = (RecipeMap) GCYMRecipeMaps.LINEAR_FORGING_RECIPES[5]; + category = GTRecipeCategory.getByName(map.getUnlocalizedName()); + if (category == null) + throw new IllegalStateException("Component recipemap " + map.getUnlocalizedName() + " had no a category!"); + for (Recipe blastRecipe : GCYMRecipeMaps.ALLOY_BLAST_RECIPES.getRecipeList()) { + var recipe = produceCooledRecipe(blastRecipe, map, category); + if (recipe != null) { + count++; + recipe.buildAndRegister(); + } + } + } + + private static @Nullable BlastRecipeBuilder produceCooledRecipe(Recipe blastRecipe, + RecipeMap targetMap, + @NotNull GTRecipeCategory mapCategory) { + List compositeOutputs = deepCopyIS(blastRecipe.getOutputs()); + List compositeFluidOutputs = deepCopyFS(blastRecipe.getFluidOutputs()); + Recipe freezerRecipe = null; + + int blastRepetitions = 0; + int freezerRepetitions = 0; + + // look for a matching freezer recipe, up to the maximum search multiplier + ItemStack moldStack = new ItemStack(MetaItems.SHAPE_MOLD_INGOT.getMetaItem(), 1, + MetaItems.SHAPE_MOLD_INGOT.getMetaValue()); + FluidStack heliumStack = Materials.Helium.getFluid(FluidStorageKeys.LIQUID, 50000); + compositeOutputs.add(moldStack); + compositeFluidOutputs.add(heliumStack); + while (blastRepetitions <= MAXIMUM_SEARCH_MULTIPLIER) { + blastRepetitions++; + freezerRecipe = RecipeMaps.VACUUM_RECIPES + .findRecipe(Long.MAX_VALUE, compositeOutputs, compositeFluidOutputs); + if (freezerRecipe == null) { + freezerRecipe = RecipeMaps.FLUID_SOLIDFICATION_RECIPES + .findRecipe(Long.MAX_VALUE, compositeOutputs, compositeFluidOutputs); + } + if (freezerRecipe == null) { + double mult = (double) (blastRepetitions + 1) / blastRepetitions; + compositeOutputs.forEach(a -> a.setCount((int) (a.getCount() * mult))); + compositeFluidOutputs.forEach(a -> a.amount = (int) (a.amount * mult)); + } else break; + } + compositeOutputs.remove(moldStack); + compositeFluidOutputs.remove(heliumStack); + if (freezerRecipe == null) { + GTRecipeCategory category = GTRecipeCategory.getByName(targetMap.getUnlocalizedName()); + if (category != null) + return new BlastRecipeBuilder(blastRecipe, targetMap).category(category); + return null; + } + + List compositeInputs = deepCopyRI(freezerRecipe.getInputs()); + List compositeFluidInputs = deepCopyRI(freezerRecipe.getFluidInputs()); + + // check if there's any small ratio of blast to freezer repetitions, otherwise do a procedural cancel + AtomicReference ratio = new AtomicReference<>(-1d); + AtomicInteger targetFreezerRepetitions = new AtomicInteger(); + + List finalCompositeOutputs = compositeOutputs; + compositeInputs.forEach(a -> finalCompositeOutputs.forEach(b -> { + if (ratio.get() == -1 && a.acceptsStack(b)) { + double stackRatio = (double) b.getCount() / a.getAmount(); + Pair simplestRatio = simplestRatio(stackRatio, b.getCount()); + // prevent excessively large ratios + if (simplestRatio.getLeft() * simplestRatio.getRight() > + MAXIMUM_SEARCH_MULTIPLIER * MAXIMUM_SEARCH_MULTIPLIER / 2) + return; + ratio.set(stackRatio); + targetFreezerRepetitions.set(Math.max(b.getCount(), targetFreezerRepetitions.get())); + } + })); + List finalCompositeFluidOutputs = compositeFluidOutputs; + compositeFluidInputs.forEach(a -> finalCompositeFluidOutputs.forEach(b -> { + if (ratio.get() == -1 && a.acceptsFluid(b)) { + double stackRatio = (double) b.amount / a.getAmount(); + Pair simplestRatio = simplestRatio(stackRatio, b.amount); + // prevent excessively large ratios + if (simplestRatio.getLeft() * simplestRatio.getRight() > + MAXIMUM_SEARCH_MULTIPLIER * MAXIMUM_SEARCH_MULTIPLIER / 2) + return; + ratio.set(stackRatio); + targetFreezerRepetitions.set(Math.max(b.amount, targetFreezerRepetitions.get())); + } + })); + + if (ratio.get() != -1) { + // smart cancellation of freezer inputs with blast outputs based on identified ratio. + Pair simplestRatio = simplestRatio(ratio.get(), targetFreezerRepetitions.get()); + blastRepetitions *= simplestRatio.getRight(); + freezerRepetitions = simplestRatio.getLeft(); + List constructingOutputs = deepCopyIS(compositeOutputs); + List constructingFluidOutputs = deepCopyFS(compositeFluidOutputs); + + List constructingInputs = deepCopyRI(compositeInputs); + List constructingFluidInputs = deepCopyRI(compositeFluidInputs); + + int finalFreezerRepetitions = freezerRepetitions; + int finalBlastRepetitions = blastRepetitions; + constructingOutputs.forEach(b -> b.setCount(b.getCount() * finalBlastRepetitions)); + constructingInputs.forEach(a -> { + a.withAmount(a.getAmount() * finalFreezerRepetitions); + constructingOutputs.forEach(b -> { + if (a.acceptsStack(b)) { + int dif = a.getAmount() - b.getCount(); + a.withAmount(Math.max(0, dif)); + b.setCount(Math.max(0, -dif)); + } + }); + }); + constructingFluidOutputs.forEach(b -> b.amount *= finalBlastRepetitions); + constructingFluidInputs.forEach(a -> { + a.withAmount(a.getAmount() * finalFreezerRepetitions); + constructingFluidOutputs.forEach(b -> { + if (a.acceptsFluid(b)) { + int dif = a.getAmount() - b.amount; + a.withAmount(Math.max(0, dif)); + b.amount = Math.max(0, -dif); + } + }); + }); + + compositeOutputs = constructingOutputs; + compositeFluidOutputs = constructingFluidOutputs; + + compositeInputs = constructingInputs; + compositeFluidInputs = constructingFluidInputs; + } else { + // fallback procedural cancellation of freezer inputs with blast outputs + AtomicBoolean noNegativeOutputs = new AtomicBoolean(true); + AtomicBoolean noZeroedOutput = new AtomicBoolean(true); + while (noNegativeOutputs.get() && noZeroedOutput.get()) { + List constructingOutputs = deepCopyIS(compositeOutputs); + List constructingFluidOutputs = deepCopyFS(compositeFluidOutputs); + + List constructingInputs = deepCopyRI(compositeInputs); + List constructingFluidInputs = deepCopyRI(compositeFluidInputs); + + constructingInputs.forEach(a -> constructingOutputs.forEach(b -> { + if (a.acceptsStack(b)) { + int difference = b.getCount() - a.getAmount(); + if (difference == 0) noZeroedOutput.set(false); + a.withAmount(0); + if (difference < 0) noNegativeOutputs.set(false); + b.setCount(difference); + } + })); + constructingFluidInputs.forEach(a -> constructingFluidOutputs.forEach(b -> { + if (a.acceptsFluid(b)) { + int difference = b.amount - a.getAmount(); + if (difference == 0) noZeroedOutput.set(false); + a.withAmount(0); + if (difference < 0) noNegativeOutputs.set(false); + b.amount = difference; + } + })); + + if (noNegativeOutputs.get()) { + freezerRepetitions++; + compositeOutputs = constructingOutputs; + compositeFluidOutputs = constructingFluidOutputs; + + compositeInputs = constructingInputs; + compositeFluidInputs = constructingFluidInputs; + + if (noZeroedOutput.get()) { + freezerRecipe.getOutputs().forEach(a -> { + for (var b : constructingOutputs) { + if (ItemStack.areItemStacksEqual(a, b)) + a.setCount(a.getCount() + b.getCount()); + } + }); + freezerRecipe.getFluidOutputs().forEach(a -> { + for (var b : constructingFluidOutputs) { + if (a.isFluidEqual(b)) + a.amount += b.amount; + } + }); + freezerRecipe.getInputs().forEach(a -> { + for (var b : constructingInputs) { + if (a.equals(b.copyWithAmount(a.getAmount()))) + a.withAmount(a.getAmount() + b.getAmount()); + } + }); + freezerRecipe.getFluidInputs().forEach(a -> { + for (var b : constructingFluidInputs) { + if (a.equals(b.copyWithAmount(a.getAmount()))) + a.withAmount(a.getAmount() + b.getAmount()); + } + }); + } + } + } + } + + // do final recipe balancing + int finalBlastRepetitions = blastRepetitions; + compositeInputs.addAll(blastRecipe.getInputs().stream() + .map(a -> a.copyWithAmount(a.getAmount() * finalBlastRepetitions)).collect(Collectors.toList())); + compositeFluidInputs.addAll(blastRecipe.getFluidInputs().stream() + .map(a -> a.copyWithAmount(a.getAmount() * finalBlastRepetitions)).collect(Collectors.toList())); + int finalFreezerRepetitions = freezerRepetitions; + compositeOutputs.addAll(freezerRecipe.getOutputs().stream().map(a -> { + var b = a.copy(); + b.setCount(b.getCount() * finalFreezerRepetitions); + return b; + }).collect(Collectors.toList())); + compositeFluidOutputs.addAll(freezerRecipe.getFluidOutputs().stream().map(a -> { + var b = a.copy(); + b.amount = b.amount * finalFreezerRepetitions; + return b; + }).collect(Collectors.toList())); + List chancedOutputs = new ObjectArrayList<>(); + blastRecipe.getChancedOutputs().getChancedEntries().forEach(a -> { + var stack = a.getIngredient().copy(); + stack.setCount(stack.getCount() * finalBlastRepetitions); + chancedOutputs.add(new ChancedItemOutput(stack, a.getChance(), a.getChanceBoost())); + }); + freezerRecipe.getChancedOutputs().getChancedEntries().forEach(a -> { + var stack = a.getIngredient().copy(); + stack.setCount(stack.getCount() * finalFreezerRepetitions); + chancedOutputs.add(new ChancedItemOutput(stack, a.getChance(), a.getChanceBoost())); + }); + List chancedFluidOutputs = new ObjectArrayList<>(); + blastRecipe.getChancedFluidOutputs().getChancedEntries().forEach(a -> { + var stack = a.getIngredient().copy(); + stack.amount *= finalBlastRepetitions; + chancedFluidOutputs.add(new ChancedFluidOutput(stack, a.getChance(), a.getChanceBoost())); + }); + freezerRecipe.getChancedFluidOutputs().getChancedEntries().forEach(a -> { + var stack = a.getIngredient().copy(); + stack.amount *= finalFreezerRepetitions; + chancedFluidOutputs.add(new ChancedFluidOutput(stack, a.getChance(), a.getChanceBoost())); + }); + + // freezer/blast OCs to reach new voltage are perfect, that's one of the benefits. + double newEU = (double) blastRecipe.getEUt() * blastRecipe.getDuration() * blastRepetitions + + (double) freezerRecipe.getEUt() * freezerRecipe.getDuration() * freezerRepetitions; + int newEUt = Math.max(blastRecipe.getEUt(), freezerRecipe.getEUt()); + + // builder from the blast recipe to copy all properties we don't later override. + var builder = new BlastRecipeBuilder(blastRecipe, targetMap) + .clearInputs().inputs(finalizedRI(compositeInputs).toArray(new GTRecipeInput[] {})) + .clearFluidInputs().fluidInputs(finalizedRI(compositeFluidInputs)) + .clearOutputs().outputs(finalizedIS(compositeOutputs)) + .clearFluidOutputs().fluidOutputs(finalizedFS(compositeFluidOutputs)) + .clearChancedOutput().chancedOutputs(chancedOutputs) + .clearChancedFluidOutputs().chancedFluidOutputs(chancedFluidOutputs) + .category(mapCategory) + .EUt(newEUt) + .duration((int) (Math.ceil(newEU / newEUt) * + GCYMConfigHolder.linearForgingFurnaceSettings.coolingDurationModifier)); + + int temp = blastRecipe.getProperty(TemperatureProperty.getInstance(), 0); + builder.applyProperty(TemperatureProperty.getInstance(), null); + builder.applyProperty(TemperatureProperty.getInstance(), temp + + GCYMConfigHolder.linearForgingFurnaceSettings.coolingTemperaturePenalty); + return builder; + } + + private static void registerForgingCooled() { + RecipeMap map = (RecipeMap) GCYMRecipeMaps.LINEAR_FORGING_RECIPES[7]; + GTRecipeCategory category = GTRecipeCategory.getByName(map.getUnlocalizedName()); + if (category == null) + throw new IllegalStateException("Component recipemap " + map.getUnlocalizedName() + " had no a category!"); + for (Recipe blastRecipe : RecipeMaps.BLAST_RECIPES.getRecipeList()) { + for (RecipeBuilder recipeBuilder : produceForgingCooledRecipes(blastRecipe, map, category)) { + count++; + recipeBuilder.buildAndRegister(); + } + } + + map = (RecipeMap) GCYMRecipeMaps.LINEAR_FORGING_RECIPES[8]; + category = GTRecipeCategory.getByName(map.getUnlocalizedName()); + if (category == null) + throw new IllegalStateException("Component recipemap " + map.getUnlocalizedName() + " had no a category!"); + for (Recipe blastRecipe : GCYMRecipeMaps.ALLOY_BLAST_RECIPES.getRecipeList()) { + for (RecipeBuilder recipeBuilder : produceForgingCooledRecipes(blastRecipe, map, category)) { + count++; + recipeBuilder.buildAndRegister(); + } + } + } + + private static List> produceForgingCooledRecipes(Recipe blastRecipe, + RecipeMap targetMap, + @NotNull GTRecipeCategory mapCategory) { + for (GTRecipeInput input : blastRecipe.getInputs()) { + var stacks = input.getInputStacks(); + if (stacks != null) { + var mat = OreDictUnifier.getMaterial(stacks[0]); + if (mat != null) { + if (mat.material.hasFlag(GCYMMaterialFlags.NO_FORGING_RECIPES_IN)) return EMPTY_BUILDER_LIST; + } + } + } + // also check fluid inputs? How to even do that? + + RecipeBuilder baseBuilder = produceCooledRecipe(blastRecipe, targetMap, mapCategory); + if (baseBuilder == null) return EMPTY_BUILDER_LIST; + baseBuilder.duration((int) (baseBuilder.getDuration() * + GCYMConfigHolder.linearForgingFurnaceSettings.forgingDurationModifier)); + int temp = baseBuilder.copy().build().getResult().getProperty(TemperatureProperty.getInstance(), 0); + baseBuilder.applyProperty(TemperatureProperty.getInstance(), null); + baseBuilder.applyProperty(TemperatureProperty.getInstance(), temp + + GCYMConfigHolder.linearForgingFurnaceSettings.forgingTemperaturePenalty); + List> builders = new ObjectArrayList<>(); + for (var pair : PREFIX_MODIFIERS.entrySet()) { + var builder = attemptForgingCooledRecipe(pair.getKey(), baseBuilder.copy()); + if (builder != null) { + for (ItemStack stack : pair.getValue()) { + builder.notConsumable(stack); + } + builders.add(builder); + } + } + + return builders; + } + + private static @Nullable RecipeBuilder attemptForgingCooledRecipe(OrePrefix prefix, + RecipeBuilder baseBuilder) { + Set multipliers = new ObjectOpenHashSet<>(); + for (var stack : baseBuilder.getOutputs()) { + var mat = OreDictUnifier.getMaterial(stack); + var stackPrefix = OreDictUnifier.getPrefix(stack); + if (mat != null && stackPrefix != null) { + if (mat.material.hasFlag(GCYMMaterialFlags.NO_FORGING_OUT)) continue; + + long cost = prefix.getMaterialAmount(mat.material); + long provided = stackPrefix.getMaterialAmount(mat.material) * stack.getCount(); + multipliers.add(findMultiplier(cost, provided)); + } + } + Optional ia = multipliers.stream().reduce(LinearForgingFurnaceLoader::lcm); + // noinspection SimplifyOptionalCallChains + if (!ia.isPresent()) return null; + int inputMultiplier = Math.max(ia.get(), 1); + + RecipeBuilder internalBaseBuilder = baseBuilder.copy(); + + internalBaseBuilder.duration(baseBuilder.getDuration() * inputMultiplier); + + internalBaseBuilder.clearInputs().inputs(finalizedRI(baseBuilder.getInputs().stream() + .map(a -> a.copyWithAmount(a.getAmount() * inputMultiplier)) + .collect(Collectors.toList())).toArray(new GTRecipeInput[] {})); + internalBaseBuilder.clearFluidInputs().fluidInputs(finalizedRI(baseBuilder.getFluidInputs().stream() + .map(a -> a.copyWithAmount(a.getAmount() * inputMultiplier)) + .collect(Collectors.toList()))); + internalBaseBuilder.clearFluidOutputs().fluidOutputs(baseBuilder.getFluidOutputs().stream() + .map(a -> { + FluidStack b = a.copy(); + b.amount *= inputMultiplier; + return b; + }).collect(Collectors.toList())); + internalBaseBuilder.clearChancedOutput().chancedOutputs(baseBuilder.getChancedOutputs().stream() + .map(a -> { + var stack = a.getIngredient().copy(); + stack.setCount(stack.getCount() * inputMultiplier); + return new ChancedItemOutput(stack, a.getChance(), a.getChanceBoost()); + }).collect(Collectors.toList())); + internalBaseBuilder.clearChancedFluidOutputs().chancedFluidOutputs(baseBuilder.getChancedFluidOutputs().stream() + .map(a -> { + var stack = a.getIngredient().copy(); + stack.amount *= inputMultiplier; + return new ChancedFluidOutput(stack, a.getChance(), a.getChanceBoost()); + }).collect(Collectors.toList())); + + internalBaseBuilder.clearOutputs(); + boolean changed = false; + for (ItemStack stack : baseBuilder.getOutputs()) { + var mat = OreDictUnifier.getMaterial(stack); + var stackPrefix = OreDictUnifier.getPrefix(stack); + if (mat != null && stackPrefix != null) { + boolean flag = mat.material.hasFlag(GCYMMaterialFlags.NO_FORGING_OUT); + var newStack = flag ? ItemStack.EMPTY : OreDictUnifier.get(prefix, mat.material); + if (newStack == ItemStack.EMPTY) internalBaseBuilder.output(stack.getItem(), stack.getCount()); + else { + internalBaseBuilder.output(prefix, mat.material, (int) (inputMultiplier * stack.getCount() * + stackPrefix.getMaterialAmount(mat.material) / prefix.getMaterialAmount(mat.material))); + changed = true; + } + } + } + return changed ? internalBaseBuilder : null; + } + + public static void assembleCompositeMaps() { + for (Recipe recipe : RecipeMaps.BLAST_RECIPES.getRecipeList()) { + assignRecipeType(recipe, LinearForgingFurnaceRecipeType.BLAST); + buildToMap(recipe, GCYMRecipeMaps.LINEAR_FORGING_RECIPES[2]); + } + for (Recipe recipe : GCYMRecipeMaps.ALLOY_BLAST_RECIPES.getRecipeList()) { + assignRecipeType(recipe, LinearForgingFurnaceRecipeType.ALLOY); + buildToMap(recipe, GCYMRecipeMaps.LINEAR_FORGING_RECIPES[2]); + } + + for (Recipe recipe : GCYMRecipeMaps.LINEAR_FORGING_RECIPES[4].getRecipeList()) { + assignRecipeType(recipe, LinearForgingFurnaceRecipeType.BLAST_COOLED); + buildToMap(recipe, GCYMRecipeMaps.LINEAR_FORGING_RECIPES[6]); + } + for (Recipe recipe : GCYMRecipeMaps.LINEAR_FORGING_RECIPES[5].getRecipeList()) { + assignRecipeType(recipe, LinearForgingFurnaceRecipeType.ALLOY_COOLED); + buildToMap(recipe, GCYMRecipeMaps.LINEAR_FORGING_RECIPES[6]); + } + + for (Recipe recipe : GCYMRecipeMaps.LINEAR_FORGING_RECIPES[7].getRecipeList()) { + assignRecipeType(recipe, LinearForgingFurnaceRecipeType.BLAST_FORGING_COOLED); + buildToMap(recipe, GCYMRecipeMaps.LINEAR_FORGING_RECIPES[9]); + } + for (Recipe recipe : GCYMRecipeMaps.LINEAR_FORGING_RECIPES[8].getRecipeList()) { + assignRecipeType(recipe, LinearForgingFurnaceRecipeType.ALLOY_FORGING_COOLED); + buildToMap(recipe, GCYMRecipeMaps.LINEAR_FORGING_RECIPES[9]); + } + } + + private static void assignRecipeType(Recipe recipe, LinearForgingFurnaceRecipeType type) { + IRecipePropertyStorage storage = recipe.getRecipePropertyStorage(); + storage.freeze(false); // is this illegal? + storage.store(LFFRecipeTypeProperty.getInstance(), type); + storage.freeze(true); + } + + private static void buildToMap(Recipe recipe, RecipeMap map) { + GTRecipeCategory category = GTRecipeCategory.getByName(map.getUnlocalizedName()); + if (category != null) + new RecipeBuilder<>(recipe, map).category(category).buildAndRegister(); + } + + private static List deepCopyRI(List list) { + return list.stream().map(a -> a.copyWithAmount(a.getAmount())).collect(Collectors.toList()); + } + + private static List finalizedRI(List list) { + return list.stream().peek(a -> { + if (a instanceof GTRecipeItemInput itemInput) { + for (var stack : itemInput.getInputStacks()) { + if (stack.getItem() != MetaItems.SHAPE_MOLD_INGOT.getMetaItem()) + stack.setCount(a.getAmount()); + else { + a.withAmount(0); + } + } + } else if (a instanceof GTRecipeFluidInput fluidInput) { + fluidInput.getInputFluidStack().amount = a.getAmount(); + } + }).filter(a -> a.getAmount() != 0).collect(Collectors.toList()); + } + + private static List deepCopyIS(List list) { + return list.stream().map(ItemStack::copy).collect(Collectors.toList()); + } + + private static List finalizedIS(List list) { + return list.stream().filter(a -> a.getCount() != 0).collect(Collectors.toList()); + } + + private static List deepCopyFS(List list) { + return list.stream().map(FluidStack::copy).collect(Collectors.toList()); + } + + private static List finalizedFS(List list) { + return list.stream().filter(a -> a.amount != 0).collect(Collectors.toList()); + } + + private static Pair simplestRatio(double ratio, int denominatorMult) { + int a = (int) (ratio * denominatorMult); + int b = denominatorMult; + int gcd = gcd(a, b); + a /= gcd; + b /= gcd; + return new ImmutablePair<>(a, b); + } + + /** + * Finds the smallest whole multiplier that allows the provided quantity to satisfy the cost with a whole output. + */ + private static int findMultiplier(long cost, long provided) { + long ratio = provided * GTValues.M / cost; + long frac = ratio % GTValues.M; + // case whole number + if (frac == 0) return 1; + long denominator = GTValues.M * GTValues.M / frac; + long numerator = ratio * denominator / GTValues.M; + long gcd = gcd(numerator, denominator); + return (int) (denominator / gcd); + } + + private static int gcd(int a, int b) { + if (b == 0) + return a; + else + return gcd(b, a % b); + } + + private static long gcd(long a, long b) { + if (b == 0) + return a; + else + return gcd(b, a % b); + } + + private static int lcm(int a, int b) { + return a * b / gcd(a, b); + } +} diff --git a/src/main/resources/assets/gcym/blockstates/large_multiblock_casing.json b/src/main/resources/assets/gcym/blockstates/large_multiblock_casing.json index 7b7a6726..5aa392e1 100644 --- a/src/main/resources/assets/gcym/blockstates/large_multiblock_casing.json +++ b/src/main/resources/assets/gcym/blockstates/large_multiblock_casing.json @@ -20,6 +20,12 @@ "all": "gregtech:blocks/casings/large_multiblock_casing/blast_casing" } }, + "forging_casing": { + "model": "minecraft:cube_all", + "textures": { + "all": "gregtech:blocks/casings/large_multiblock_casing/forging_casing" + } + }, "assembler_casing": { "model": "minecraft:cube_all", "textures": { diff --git a/src/main/resources/assets/gcym/lang/en_us.lang b/src/main/resources/assets/gcym/lang/en_us.lang index 9cd67656..337491f0 100644 --- a/src/main/resources/assets/gcym/lang/en_us.lang +++ b/src/main/resources/assets/gcym/lang/en_us.lang @@ -32,7 +32,9 @@ gcym.machine.mega_vacuum_freezer.name=Bulk Blast Chiller gcym.machine.steam_engine.name=Industrial Steam Engine gcym.machine.steam_engine.tooltip.1=Produces up to %s§7 power. gcym.machine.large_circuit_assembler.name=Large Circuit Assembler +gcym.machine.linear_forging_furnace.name=Linear Forging Furnace +gcym.machine.linear_forging_furnace.tooltip=Parallelization cannot exceed the square of the repeatable layer count. gcym.tooltip.parallel_enabled=Can parallelize with §bParallel Control Hatches gcym.tooltip.tiered_hatch_enabled=Requires §bVoltage Control Units§7 to run. @@ -70,7 +72,7 @@ gcym.multiblock.mega_blast_furnace.description=A massive Electric Blast Furnace gcym.multiblock.mega_vacuum_freezer.description=A massive Vacuum Freezer for extreme volume cooling. gcym.multiblock.steam_engine.description=A large Steam Engine for effective power production. gcym.multiblock.large_circuit_assembler.description=A multiblock Circuit Assembler for fabricating circuits. - +gcym.multiblock.linear_forging_furnace.description=A modular multiblock that can support Blasting, Alloy Blasting, Cooling, and Metalworking all in one. # hatches @@ -125,9 +127,17 @@ tile.large_multiblock_casing.mixer_casing.name=Reaction-Safe Mixing Casing tile.large_multiblock_casing.engraver_casing.name=Laser-Safe Engraving Casing tile.large_multiblock_casing.atomic_casing.name=Atomic Casing tile.large_multiblock_casing.steam_casing.name=Industrial Steam Casing +tile.large_multiblock_casing.forging_casing.name=Thermal Forging Casing # RecipeMaps recipemap.alloy_blast_smelter.name=Alloy Blast Smelting +recipemap.lff_dual.name=Universal Blast Smelting +recipemap.lff_blast_cooled.name=Cooled Blast Furnace +recipemap.lff_alloy_cooled.name=Cooled Alloy Blast Smelting +recipemap.lff_dual_cooled.name=Cooled Universal Blast Smelting +recipemap.lff_blast_forging_cooled.name=Cooled Blast Forging +recipemap.lff_alloy_forging_cooled.name=Cooled Alloy Blast Forging +recipemap.lff_dual_forging_cooled.name=Cooled Universal Blast Forging # Materials gcym.material.stellite_100=Stellite-100 @@ -140,6 +150,7 @@ gcym.material.zeron_100=Zeron-100 gcym.material.titanium_carbide=Titanium Carbide gcym.material.tantalum_carbide=Tantalum Carbide gcym.material.molybdenum_disilicide=Molybdenum Disilicide +gcym.material.cobalt_alloy=Cobalt Thermal Alloy gcym.material.hsla_steel=HSLA Steel gcym.material.titanium_tungsten_carbide=Titanium Tungsten Carbide diff --git a/src/main/resources/assets/gregtech/textures/blocks/casings/large_multiblock_casing/forging_casing.png b/src/main/resources/assets/gregtech/textures/blocks/casings/large_multiblock_casing/forging_casing.png new file mode 100644 index 00000000..dbfd94de Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/blocks/casings/large_multiblock_casing/forging_casing.png differ diff --git a/src/main/resources/assets/gregtech/textures/blocks/multiblock/linear_forging_furnace/overlay_front.png b/src/main/resources/assets/gregtech/textures/blocks/multiblock/linear_forging_furnace/overlay_front.png new file mode 100755 index 00000000..72bbef85 Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/blocks/multiblock/linear_forging_furnace/overlay_front.png differ diff --git a/src/main/resources/assets/gregtech/textures/blocks/multiblock/linear_forging_furnace/overlay_front_active.png b/src/main/resources/assets/gregtech/textures/blocks/multiblock/linear_forging_furnace/overlay_front_active.png new file mode 100755 index 00000000..94a4416b Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/blocks/multiblock/linear_forging_furnace/overlay_front_active.png differ diff --git a/src/main/resources/assets/gregtech/textures/blocks/multiblock/linear_forging_furnace/overlay_front_active_emissive.png b/src/main/resources/assets/gregtech/textures/blocks/multiblock/linear_forging_furnace/overlay_front_active_emissive.png new file mode 100644 index 00000000..822fa52e Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/blocks/multiblock/linear_forging_furnace/overlay_front_active_emissive.png differ diff --git a/src/main/resources/assets/gregtech/textures/blocks/multiblock/linear_forging_furnace/overlay_front_emissive.png b/src/main/resources/assets/gregtech/textures/blocks/multiblock/linear_forging_furnace/overlay_front_emissive.png new file mode 100644 index 00000000..00bcc3b5 Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/blocks/multiblock/linear_forging_furnace/overlay_front_emissive.png differ diff --git a/src/main/resources/assets/gregtech/textures/blocks/multiblock/linear_forging_furnace/overlay_front_paused.png b/src/main/resources/assets/gregtech/textures/blocks/multiblock/linear_forging_furnace/overlay_front_paused.png new file mode 100644 index 00000000..8f139ba6 Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/blocks/multiblock/linear_forging_furnace/overlay_front_paused.png differ diff --git a/src/main/resources/assets/gregtech/textures/blocks/multiblock/linear_forging_furnace/overlay_front_paused_emissive.png b/src/main/resources/assets/gregtech/textures/blocks/multiblock/linear_forging_furnace/overlay_front_paused_emissive.png new file mode 100644 index 00000000..577f0815 Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/blocks/multiblock/linear_forging_furnace/overlay_front_paused_emissive.png differ