aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/gregtech/common/tileentities
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/gregtech/common/tileentities')
-rw-r--r--src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_ChestBuffer.java137
-rw-r--r--src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_Filter.java144
-rw-r--r--src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_ItemDistributor.java204
-rw-r--r--src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_RecipeFilter.java326
-rw-r--r--src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_Regulator.java228
-rw-r--r--src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_SuperBuffer.java105
-rw-r--r--src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_TypeFilter.java213
-rw-r--r--src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler.java530
-rw-r--r--src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler_Bronze.java260
-rw-r--r--src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler_Lava.java474
-rw-r--r--src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler_Solar.java311
-rw-r--r--src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler_Solar_Steel.java71
-rw-r--r--src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler_Steel.java108
-rw-r--r--src/main/java/gregtech/common/tileentities/casings/functional/Conveyor.java16
-rw-r--r--src/main/java/gregtech/common/tileentities/casings/functional/Emitter.java16
-rw-r--r--src/main/java/gregtech/common/tileentities/casings/functional/FieldGenerator.java16
-rw-r--r--src/main/java/gregtech/common/tileentities/casings/functional/Motor.java16
-rw-r--r--src/main/java/gregtech/common/tileentities/casings/functional/Piston.java16
-rw-r--r--src/main/java/gregtech/common/tileentities/casings/functional/Pump.java16
-rw-r--r--src/main/java/gregtech/common/tileentities/casings/functional/RobotArm.java16
-rw-r--r--src/main/java/gregtech/common/tileentities/casings/functional/Sensor.java16
-rw-r--r--src/main/java/gregtech/common/tileentities/casings/upgrade/Ampere.java47
-rw-r--r--src/main/java/gregtech/common/tileentities/casings/upgrade/Cleanroom.java26
-rw-r--r--src/main/java/gregtech/common/tileentities/casings/upgrade/Heater.java21
-rw-r--r--src/main/java/gregtech/common/tileentities/casings/upgrade/Insulator.java22
-rw-r--r--src/main/java/gregtech/common/tileentities/casings/upgrade/Inventory.java123
-rw-r--r--src/main/java/gregtech/common/tileentities/casings/upgrade/Laser.java26
-rw-r--r--src/main/java/gregtech/common/tileentities/casings/upgrade/Tank.java61
-rw-r--r--src/main/java/gregtech/common/tileentities/casings/upgrade/Wireless.java26
-rw-r--r--src/main/java/gregtech/common/tileentities/debug/GT_MetaTileEntity_AdvDebugStructureWriter.java437
-rw-r--r--src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_DieselGenerator.java278
-rw-r--r--src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_GasTurbine.java207
-rw-r--r--src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_LightningRod.java169
-rw-r--r--src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_MagicEnergyConverter.java173
-rw-r--r--src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_MagicalEnergyAbsorber.java805
-rw-r--r--src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_NaquadahReactor.java201
-rw-r--r--src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_PlasmaGenerator.java142
-rw-r--r--src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_SteamTurbine.java238
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_BasicHull_Bronze.java45
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_BasicHull_BronzeBricks.java53
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_BasicHull_Steel.java47
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_BasicHull_SteelBricks.java53
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_CraftingInput_ME.java1040
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_CraftingInput_Slave.java262
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_InputBus_ME.java887
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_Input_ME.java949
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_OutputBus_ME.java380
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_Output_ME.java443
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/IDualInputHatch.java21
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/IDualInputInventory.java11
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/IRecipeProcessingAwareHatch.java26
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/ISmartInputHatch.java15
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_AdvSeismicProspector.java309
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_BetterJukebox.java702
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Boxinator.java229
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Charger.java110
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_IndustrialApiary.java1555
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Massfabricator.java241
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_MicrowaveEnergyTransmitter.java495
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Miner.java407
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_MonsterRepellent.java182
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_PotionBrewer.java225
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Pump.java846
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Replicator.java130
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_RockBreaker.java169
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Scanner.java425
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Teleporter.java601
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_TurboCharger.java146
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_WorldAccelerator.java470
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/long_distance/GT_MetaTileEntity_LongDistancePipelineBase.java415
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/long_distance/GT_MetaTileEntity_LongDistancePipelineFluid.java125
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/long_distance/GT_MetaTileEntity_LongDistancePipelineItem.java214
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_AbstractMultiFurnace.java48
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_AssemblyLine.java495
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_BrickedBlastFurnace.java155
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_Charcoal_Pit.java312
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_Cleanroom.java515
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ConcreteBackfiller1.java53
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ConcreteBackfiller2.java54
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ConcreteBackfillerBase.java265
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_DieselEngine.java416
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_DistillationTower.java369
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_DrillerBase.java952
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ElectricBlastFurnace.java401
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ExtremeDieselEngine.java237
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_FusionComputer.java675
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_FusionComputer1.java103
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_FusionComputer2.java103
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_FusionComputer3.java103
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_HeatExchanger.java400
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ImplosionCompressor.java163
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_IndustrialElectromagneticSeparator.java467
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_IndustrialExtractor.java293
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_IndustrialLaserEngraver.java615
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_IntegratedOreFactory.java829
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeBoiler.java504
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeBoiler_Bronze.java96
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeBoiler_Steel.java96
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeBoiler_Titanium.java96
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeBoiler_TungstenSteel.java96
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeChemicalReactor.java298
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeFluidExtractor.java419
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine.java492
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine_Gas.java220
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine_GasAdvanced.java219
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine_HPSteam.java232
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine_Plasma.java391
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine_Steam.java273
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_MultiAutoclave.java425
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_MultiCanner.java314
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_MultiFurnace.java377
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_MultiLathe.java377
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_NanoForge.java467
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilCracker.java423
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrill1.java53
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrill2.java55
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrill3.java55
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrill4.java55
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrillBase.java489
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrillInfinite.java87
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlant1.java60
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlant2.java60
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlant3.java60
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlant4.java60
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlantBase.java794
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PCBFactory.java1290
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PlasmaForge.java1263
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PrimitiveBlastFurnace.java521
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ProcessingArray.java550
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PyrolyseOven.java263
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_TranscendentPlasmaMixer.java389
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_VacuumFreezer.java163
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_WormholeGenerator.java1119
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/compressor/GT_MetaTileEntity_BlackHoleCompressor.java539
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/compressor/GT_MetaTileEntity_HIPCompressor.java525
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/compressor/GT_MetaTileEntity_HeatSensor.java188
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/compressor/GT_MetaTileEntity_IndustrialCompressor.java244
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/compressor/GT_MetaTileEntity_NeutroniumCompressor.java261
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/drone/DroneConnection.java143
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/drone/GT_MetaTileEntity_DroneCentre.java917
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/drone/GT_MetaTileEntity_Hatch_DroneDownLink.java329
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_Hatch_DegasifierControlHatch.java122
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_LensHousing.java52
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_LensIndicator.java117
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationPlant.java740
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitBase.java805
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitClarifier.java333
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitDegasifier.java832
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitFlocculation.java489
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitOzonation.java299
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitParticleExtractor.java566
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitPhAdjustment.java612
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitPlasmaHeater.java566
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitUVTreatment.java526
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_pHSensor.java192
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/purification/LinkedPurificationUnit.java125
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/purification/PurificationPlantStructureString.java15
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/purification/PurificationUnitStatus.java14
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/purification/PurifiedWaterHelpers.java42
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multi/purification/UVTreatmentLensCycle.java39
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multiblock/AdvChemicalProcessor.java508
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multiblock/CokeOven.java157
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multiblock/DistillationTower.java176
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multiblock/LaserEngraver.java305
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multiblock/LayeredCokeBattery.java300
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multiblock/Macerator.java132
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multiblock/logic/AdvChemicalProcessorProcessingLogic.java12
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multiblock/logic/CokeOvenProcessingLogic.java69
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multiblock/logic/DistillationTowerProcessingLogic.java7
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multiblock/logic/LaserEngraverProcessingLogic.java7
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multiblock/logic/LayeredCokeBatteryProcessingLogic.java7
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/multiblock/logic/MaceratorProcessingLogic.java7
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_AlloySmelter_Bronze.java139
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_AlloySmelter_Steel.java139
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Compressor_Bronze.java134
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Compressor_Steel.java134
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Extractor_Bronze.java134
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Extractor_Steel.java134
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_ForgeHammer_Bronze.java202
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_ForgeHammer_Steel.java202
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Furnace_Bronze.java161
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Furnace_Steel.java159
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Macerator_Bronze.java189
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Macerator_Steel.java188
-rw-r--r--src/main/java/gregtech/common/tileentities/render/TileDrone.java24
-rw-r--r--src/main/java/gregtech/common/tileentities/render/TileLaser.java116
-rw-r--r--src/main/java/gregtech/common/tileentities/render/TileWormhole.java87
-rw-r--r--src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_DigitalChestBase.java559
-rw-r--r--src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_DigitalTankBase.java694
-rw-r--r--src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_Locker.java283
-rw-r--r--src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_QuantumChest.java102
-rw-r--r--src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_QuantumTank.java55
-rw-r--r--src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_SuperChest.java30
-rw-r--r--src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_SuperTank.java54
194 files changed, 54931 insertions, 0 deletions
diff --git a/src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_ChestBuffer.java b/src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_ChestBuffer.java
new file mode 100644
index 0000000000..67c38fad9e
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_ChestBuffer.java
@@ -0,0 +1,137 @@
+package gregtech.common.tileentities.automation;
+
+import static gregtech.api.enums.Textures.BlockIcons.AUTOMATION_CHESTBUFFER;
+import static gregtech.api.enums.Textures.BlockIcons.AUTOMATION_CHESTBUFFER_GLOW;
+
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Buffer;
+import gregtech.api.render.TextureFactory;
+
+public class GT_MetaTileEntity_ChestBuffer extends GT_MetaTileEntity_Buffer {
+
+ private static final int[] tickRate = { 400, 200, 100, 20, 4, 1, 1, 1, 1, 1, 1, 1, 1 };
+ private static final int[] maxStacks = { 1, 1, 1, 1, 1, 1, 2, 4, 8, 16, 32, 64, 128 };
+
+ public GT_MetaTileEntity_ChestBuffer(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 28,
+ new String[] { "Buffers up to 27 Item Stacks", "Use Screwdriver to regulate output stack size",
+ getTickRateDesc(aTier) });
+ }
+
+ public GT_MetaTileEntity_ChestBuffer(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount,
+ String aDescription) {
+ super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription);
+ }
+
+ public GT_MetaTileEntity_ChestBuffer(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount,
+ String[] aDescription) {
+ super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription);
+ }
+
+ public GT_MetaTileEntity_ChestBuffer(String aName, int aTier, int aInvSlotCount, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_ChestBuffer(String aName, int aTier, int aInvSlotCount, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_ChestBuffer(
+ this.mName,
+ this.mTier,
+ this.mInventory.length,
+ this.mDescriptionArray,
+ this.mTextures);
+ }
+
+ @Override
+ public ITexture getOverlayIcon() {
+ return TextureFactory.of(
+ TextureFactory.of(AUTOMATION_CHESTBUFFER),
+ TextureFactory.builder()
+ .addIcon(AUTOMATION_CHESTBUFFER_GLOW)
+ .glow()
+ .build());
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return aIndex < this.mInventory.length - 1;
+ }
+
+ @Override
+ protected void moveItems(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) {
+ if (aTimer % tickRate[mTier] > 0) return;
+
+ // mSuccess will be negative if the call is caused by the %200 aTimer, always try to push. Otherwise it will be
+ // positive.
+ // For the first 6 ticks after a successful move (49->44), push every tick. Then go to every 5 ticks.
+ if ((mSuccess <= 0) || (mSuccess > 43) || ((mSuccess % 5) == 0)) {
+ super.moveItems(aBaseMetaTileEntity, aTimer, Math.min(MAX, maxStacks[mTier]));
+ }
+
+ if (mSuccess < 0) {
+ mSuccess = 0;
+ }
+ }
+
+ protected static String getTickRateDesc(int tier) {
+ int tickRate = getTickRate(tier);
+ String timeStr = "";
+ String numStr = "";
+ if (maxStacks[tier] > 1) {
+ numStr = maxStacks[tier] + " items";
+ } else {
+ numStr = "1 item";
+ }
+ if (tickRate < 20) timeStr = "1/" + 20 / tickRate + " ";
+ else if (tickRate > 20) {
+ timeStr = (tickRate / 20) + "th ";
+ }
+ return "Moves " + numStr + " every " + timeStr + "second";
+ }
+
+ protected static int getTickRate(int tier) {
+ if (tier > 9) return 1;
+ return tickRate[tier];
+ }
+
+ protected static int getMaxStacks(int tier) {
+ // Included higher tiers on the off chance they actually work without blowing things up lmao
+ return tier > 9 ? MAX : Math.min(maxStacks[tier], MAX);
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ super.addUIWidgets(builder, buildContext);
+ addSortStacksButton(builder);
+ addEmitRedstoneIfFullButton(builder);
+ addInvertRedstoneButton(builder);
+ addStockingModeButton(builder);
+ builder.widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_ARROW_22_RED.apply(69, true))
+ .setPos(98, 60)
+ .setSize(51, 22));
+ addMainUI(builder);
+ }
+
+ protected void addMainUI(ModularWindow.Builder builder) {
+ addInventorySlots(builder);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_Filter.java b/src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_Filter.java
new file mode 100644
index 0000000000..b6f1d53604
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_Filter.java
@@ -0,0 +1,144 @@
+package gregtech.common.tileentities.automation;
+
+import static gregtech.api.enums.Textures.BlockIcons.AUTOMATION_FILTER;
+import static gregtech.api.enums.Textures.BlockIcons.AUTOMATION_FILTER_GLOW;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotGroup;
+
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_FilterBase;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_Filter extends GT_MetaTileEntity_FilterBase {
+
+ private static final int NUM_FILTER_SLOTS = 9;
+ private static final String IGNORE_NBT_TOOLTIP = "GT5U.machines.ignore_nbt.tooltip";
+ private boolean ignoreNbt = false;
+
+ public GT_MetaTileEntity_Filter(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 19,
+ new String[] { "Filters up to 9 different Items", "Use Screwdriver to regulate output stack size" });
+ }
+
+ public GT_MetaTileEntity_Filter(String aName, int aTier, int aInvSlotCount, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Filter(String aName, int aTier, int aInvSlotCount, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Filter(
+ this.mName,
+ this.mTier,
+ this.mInventory.length,
+ this.mDescriptionArray,
+ this.mTextures);
+ }
+
+ @Override
+ public ITexture getOverlayIcon() {
+ return TextureFactory.of(
+ TextureFactory.of(AUTOMATION_FILTER),
+ TextureFactory.builder()
+ .addIcon(AUTOMATION_FILTER_GLOW)
+ .glow()
+ .build());
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setBoolean("bIgnoreNBT", this.ignoreNbt);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ this.ignoreNbt = aNBT.getBoolean("bIgnoreNBT");
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ if (!super.allowPutStack(aBaseMetaTileEntity, aIndex, side, aStack)) {
+ return false;
+ }
+ if (this.invertFilter) {
+ for (int i = 0; i < NUM_FILTER_SLOTS; i++) {
+ if (GT_Utility.areStacksEqual(this.mInventory[FILTER_SLOT_INDEX + i], aStack, this.ignoreNbt)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return GT_Utility.areStacksEqual(this.mInventory[(FILTER_SLOT_INDEX + aIndex)], aStack, this.ignoreNbt);
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ super.addUIWidgets(builder, buildContext);
+ addAllowNbtButton(builder);
+ builder.widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_ARROW_24_WHITE.apply(9, false))
+ .setPos(6, 19)
+ .setSize(9, 24))
+ .widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_ARROW_24_BLUE.apply(24, true))
+ .setPos(71, 19)
+ .setSize(24, 24))
+ .widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_ARROW_24_RED.apply(19, true))
+ .setPos(152, 19)
+ .setSize(19, 24))
+ .widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_SLOTS_HOLO_3BY3)
+ .setPos(16, 4)
+ .setSize(54, 54))
+ .widget(
+ SlotGroup.ofItemHandler(inventoryHandler, 3)
+ .startFromSlot(FILTER_SLOT_INDEX)
+ .endAtSlot(FILTER_SLOT_INDEX + NUM_FILTER_SLOTS - 1)
+ .phantom(true)
+ .applyForWidget(
+ widget -> widget.disableShiftInsert()
+ .setBackground(GT_UITextures.TRANSPARENT))
+ .build()
+ .setPos(16, 4))
+ .widget(
+ SlotGroup.ofItemHandler(inventoryHandler, 3)
+ .startFromSlot(0)
+ .endAtSlot(NUM_INVENTORY_SLOTS - 1)
+ .build()
+ .setPos(97, 4));
+ }
+
+ private void addAllowNbtButton(ModularWindow.Builder builder) {
+ builder.widget(
+ createToggleButton(
+ () -> ignoreNbt,
+ val -> ignoreNbt = val,
+ GT_UITextures.OVERLAY_BUTTON_NBT,
+ () -> mTooltipCache.getData(IGNORE_NBT_TOOLTIP)));
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_ItemDistributor.java b/src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_ItemDistributor.java
new file mode 100644
index 0000000000..58b7fa57df
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_ItemDistributor.java
@@ -0,0 +1,204 @@
+package gregtech.common.tileentities.automation;
+
+import static gregtech.api.enums.Textures.BlockIcons.AUTOMATION_ITEMDISTRIBUTOR;
+import static gregtech.api.enums.Textures.BlockIcons.AUTOMATION_ITEMDISTRIBUTOR_GLOW;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+
+import gregtech.api.enums.Textures;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Buffer;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_ItemDistributor extends GT_MetaTileEntity_Buffer {
+
+ private byte[] itemsPerSide = new byte[6];
+ private ForgeDirection currentSide = ForgeDirection.DOWN;
+ private byte currentSideItemCount = 0;
+
+ public GT_MetaTileEntity_ItemDistributor(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 28,
+ new String[] { "Distributes Items between different Machine Sides", "Default Items per Machine Side: 0",
+ "Use Screwdriver to increase/decrease Items per Side" });
+ }
+
+ public GT_MetaTileEntity_ItemDistributor(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount,
+ String aDescription) {
+ super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription);
+ }
+
+ public GT_MetaTileEntity_ItemDistributor(String aName, int aTier, int aInvSlotCount, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_ItemDistributor(String aName, int aTier, int aInvSlotCount, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_ItemDistributor(
+ this.mName,
+ this.mTier,
+ this.mInventory.length,
+ this.mDescriptionArray,
+ this.mTextures);
+ }
+
+ @Override
+ public ITexture getOverlayIcon() {
+ return TextureFactory.of(
+ TextureFactory.of(AUTOMATION_ITEMDISTRIBUTOR),
+ TextureFactory.builder()
+ .addIcon(AUTOMATION_ITEMDISTRIBUTOR_GLOW)
+ .glow()
+ .build());
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return side == aBaseMetaTileEntity.getFrontFacing();
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side == aFacing) {
+ return mTextures[0][colorIndex + 1];
+ } else {
+ return mTextures[1][colorIndex + 1];
+ }
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ ITexture[][][] returnTextures = new ITexture[2][17][];
+ ITexture baseIcon = getOverlayIcon(), pipeIcon = TextureFactory.of(Textures.BlockIcons.OVERLAY_PIPE_OUT);
+ for (int i = 0; i < 17; i++) {
+ returnTextures[0][i] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i], baseIcon };
+ returnTextures[1][i] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i], pipeIcon, baseIcon };
+ }
+ return returnTextures;
+ }
+
+ @Override
+ public boolean isInputFacing(ForgeDirection side) {
+ return getBaseMetaTileEntity().getFrontFacing() == side || itemsPerSide[side.ordinal()] == 0;
+ }
+
+ @Override
+ public boolean isOutputFacing(ForgeDirection side) {
+ return getBaseMetaTileEntity().getFrontFacing() != side && itemsPerSide[side.ordinal()] > 0;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return aIndex < this.mInventory.length - 1;
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ itemsPerSide = aNBT.getByteArray("mItemsPerSide");
+ if (itemsPerSide.length != 6) {
+ itemsPerSide = new byte[6];
+ }
+ currentSide = ForgeDirection.getOrientation(aNBT.getByte("mCurrentSide"));
+ currentSideItemCount = aNBT.getByte("mCurrentSideItemCount");
+ }
+
+ @Override
+ protected void moveItems(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) {
+ int currentSideOrdinal = currentSide.ordinal();
+ fillStacksIntoFirstSlots();
+ int movedItems;
+ TileEntity adjacentTileEntity = aBaseMetaTileEntity.getTileEntityAtSide(currentSide);
+ int inspectedSides = 0;
+ while (itemsPerSide[currentSideOrdinal] == 0) {
+ currentSideOrdinal = ((currentSideOrdinal + 1) % 6);
+ currentSide = ForgeDirection.getOrientation(currentSideOrdinal);
+ currentSideItemCount = 0;
+ adjacentTileEntity = aBaseMetaTileEntity.getTileEntityAtSide(currentSide);
+ inspectedSides += 1;
+ if (inspectedSides == 6) {
+ return;
+ }
+ }
+ movedItems = GT_Utility.moveOneItemStack(
+ aBaseMetaTileEntity,
+ adjacentTileEntity,
+ currentSide,
+ currentSide.getOpposite(),
+ null,
+ false,
+ (byte) 64,
+ (byte) 1,
+ (byte) (itemsPerSide[currentSideOrdinal] - currentSideItemCount),
+ (byte) 1);
+ currentSideItemCount += movedItems;
+ if (currentSideItemCount >= itemsPerSide[currentSideOrdinal]) {
+ currentSideOrdinal = ((currentSideOrdinal + 1) % 6);
+ currentSide = ForgeDirection.getOrientation(currentSideOrdinal);
+ currentSideItemCount = 0;
+ }
+ if (movedItems > 0 || aBaseMetaTileEntity.hasInventoryBeenModified()) {
+ mSuccess = 50;
+ }
+ fillStacksIntoFirstSlots();
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ final int ordinalSide = side.ordinal();
+ // Adjust items per side by 1 or -1, constrained to the cyclic interval [0, 127]
+ itemsPerSide[ordinalSide] += aPlayer.isSneaking() ? -1 : 1;
+ itemsPerSide[ordinalSide] = (byte) ((itemsPerSide[ordinalSide] + 128) % 128);
+ GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("211", "Items per side: ") + itemsPerSide[ordinalSide]);
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setByteArray("mItemsPerSide", itemsPerSide);
+ aNBT.setByte("mCurrentSide", (byte) currentSide.ordinal());
+ aNBT.setByte("mCurrentSideItemCount", currentSideItemCount);
+ }
+
+ @Override
+ public void setItemNBT(NBTTagCompound aNBT) {
+ super.setItemNBT(aNBT);
+ aNBT.setByteArray("mItemsPerSide", itemsPerSide);
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ super.addUIWidgets(builder, buildContext);
+ addEmitRedstoneIfFullButton(builder);
+ addInvertRedstoneButton(builder);
+ builder.widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_ARROW_22_RED.apply(87, true))
+ .setPos(62, 60)
+ .setSize(87, 22));
+ addInventorySlots(builder);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_RecipeFilter.java b/src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_RecipeFilter.java
new file mode 100644
index 0000000000..d446009ac7
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_RecipeFilter.java
@@ -0,0 +1,326 @@
+package gregtech.common.tileentities.automation;
+
+import static gregtech.api.enums.Textures.BlockIcons.AUTOMATION_RECIPEFILTER;
+import static gregtech.api.enums.Textures.BlockIcons.AUTOMATION_RECIPEFILTER_GLOW;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraft.network.PacketBuffer;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraftforge.common.util.Constants;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.gtnewhorizons.modularui.api.drawable.Text;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.internal.network.NetworkUtils;
+import com.gtnewhorizons.modularui.common.internal.wrapper.BaseSlot;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+
+import codechicken.nei.recipe.RecipeCatalysts;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.interfaces.tileentity.RecipeMapWorkable;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_SpecialFilter;
+import gregtech.api.multitileentity.MultiTileEntityItem;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.blocks.GT_Item_Machines;
+import gregtech.loaders.preload.GT_Loader_MultiTileEntities;
+
+public class GT_MetaTileEntity_RecipeFilter extends GT_MetaTileEntity_SpecialFilter {
+
+ private static final String TT_machineType = "GT5U.MBTT.MachineType";
+ private static final String REPRESENTATION_SLOT_TOOLTIP = "GT5U.recipe_filter.representation_slot.tooltip";
+ private static final String EMPTY_REPRESENTATION_SLOT_TOOLTIP = "GT5U.recipe_filter.empty_representation_slot.tooltip";
+ public RecipeMap<?> mRecipeMap;
+ private List<ItemStack> filteredMachines = new ArrayList<>();
+ public int mRotationIndex = 0;
+
+ public GT_MetaTileEntity_RecipeFilter(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ new String[] { "Filters 1 Recipe Type", "Use Screwdriver to regulate output stack size" });
+ }
+
+ public GT_MetaTileEntity_RecipeFilter(String aName, int aTier, int aInvSlotCount, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_RecipeFilter(String aName, int aTier, int aInvSlotCount, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ private static RecipeMap<?> getItemStackMachineRecipeMap(ItemStack stack) {
+ if (stack != null) {
+ IMetaTileEntity metaTileEntity = GT_Item_Machines.getMetaTileEntity(stack);
+ if (metaTileEntity != null) {
+ return getMetaTileEntityRecipeMap(metaTileEntity);
+ } else if (stack.getItem() instanceof MultiTileEntityItem) {
+ return getMuTeRecipeMap(stack);
+ }
+ }
+ return null;
+ }
+
+ private static RecipeMap<?> getMetaTileEntityRecipeMap(IMetaTileEntity metaTileEntity) {
+ if (metaTileEntity instanceof RecipeMapWorkable recipeMapWorkable) {
+ return recipeMapWorkable.getRecipeMap();
+ }
+ return null;
+ }
+
+ private static RecipeMap<?> getMuTeRecipeMap(@NotNull ItemStack stack) {
+ final TileEntity tileEntity = GT_Loader_MultiTileEntities.MACHINE_REGISTRY.getReferenceTileEntity(stack);
+ if (tileEntity instanceof RecipeMapWorkable recipeMapWorkable) {
+ return recipeMapWorkable.getRecipeMap();
+ }
+ return null;
+ }
+
+ private static List<ItemStack> getFilteredMachines(RecipeMap<?> recipeMap) {
+ return RecipeCatalysts.getRecipeCatalysts(
+ recipeMap.getFrontend()
+ .getUIProperties().neiTransferRectId)
+ .stream()
+ .map(positionedStack -> positionedStack.item)
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPreTick(aBaseMetaTileEntity, aTick);
+ if ((!getBaseMetaTileEntity().isServerSide()) || ((aTick % 8L != 0L) && mRotationIndex != -1)) return;
+ if (this.filteredMachines.isEmpty()) {
+ return;
+ }
+ this.mInventory[FILTER_SLOT_INDEX] = GT_Utility.copyAmount(
+ 1,
+ this.filteredMachines.get(this.mRotationIndex = (this.mRotationIndex + 1) % this.filteredMachines.size()));
+ if (this.mInventory[FILTER_SLOT_INDEX] == null) return;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_RecipeFilter(
+ this.mName,
+ this.mTier,
+ this.mInventory.length,
+ this.mDescriptionArray,
+ this.mTextures);
+ }
+
+ @Override
+ public ITexture getOverlayIcon() {
+ return TextureFactory.of(
+ TextureFactory.of(AUTOMATION_RECIPEFILTER),
+ TextureFactory.builder()
+ .addIcon(AUTOMATION_RECIPEFILTER_GLOW)
+ .glow()
+ .build());
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ if (mRecipeMap != null) {
+ aNBT.setString("mRecipeMap", this.mRecipeMap.unlocalizedName);
+ }
+ NBTTagList tagList = new NBTTagList();
+ for (ItemStack filteredMachine : filteredMachines) {
+ tagList.appendTag(filteredMachine.writeToNBT(new NBTTagCompound()));
+ }
+ aNBT.setTag("filteredMachines", tagList);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ this.mRecipeMap = RecipeMap.getFromOldIdentifier(aNBT.getString("mRecipeMap"));
+ filteredMachines.clear();
+ NBTTagList tagList = aNBT.getTagList("filteredMachines", Constants.NBT.TAG_COMPOUND);
+ for (int i = 0; i < tagList.tagCount(); i++) {
+ ItemStack readStack = ItemStack.loadItemStackFromNBT(tagList.getCompoundTagAt(i));
+ if (readStack != null) {
+ filteredMachines.add(readStack);
+ }
+ }
+ }
+
+ @Override
+ protected boolean isStackAllowed(ItemStack aStack) {
+ return mRecipeMap != null && mRecipeMap.containsInput(aStack);
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ super.addUIWidgets(builder, buildContext);
+ builder.widget(
+ new FakeSyncWidget.StringSyncer(
+ () -> this.mRecipeMap == null ? "" : this.mRecipeMap.unlocalizedName,
+ id -> this.mRecipeMap = RecipeMap.ALL_RECIPE_MAPS.get(id)));
+ }
+
+ @Override
+ protected List<Text> getEmptySlotTooltip() {
+ return Collections.singletonList(Text.localised(EMPTY_REPRESENTATION_SLOT_TOOLTIP));
+ }
+
+ @Override
+ public Function<List<String>, List<String>> getItemStackReplacementTooltip() {
+ if (mRecipeMap != null) {
+ List<String> tooltip = assembleItemStackReplacementTooltip(mRecipeMap);
+ return list -> tooltip;
+ }
+ return super.getItemStackReplacementTooltip();
+ }
+
+ @NotNull
+ private List<String> assembleItemStackReplacementTooltip(RecipeMap<?> recipeMap) {
+ List<String> tooltip = new ArrayList<>();
+ tooltip.add(
+ StatCollector.translateToLocal(TT_machineType) + ": "
+ + EnumChatFormatting.YELLOW
+ + StatCollector.translateToLocal(recipeMap.unlocalizedName)
+ + EnumChatFormatting.RESET);
+ int recipeSize = recipeMap.getAllRecipes()
+ .size();
+ if (recipeSize > 0) {
+ tooltip.add("Filter size: §e" + recipeSize + "§r");
+ }
+ tooltip.addAll(mTooltipCache.getData(REPRESENTATION_SLOT_TOOLTIP).text);
+ return tooltip;
+ }
+
+ @Override
+ protected SlotWidget createFilterIconSlot(BaseSlot slot) {
+ return new RecipeFilterIconSlotWidget(slot);
+ }
+
+ private class RecipeFilterIconSlotWidget extends FilterIconSlotWidget {
+
+ private static final int SYNC_RECIPEMAP_C2S = 98;
+ private static final int REQUEST_FILTERED_MACHINES_S2C = 99;
+
+ public RecipeFilterIconSlotWidget(BaseSlot slot) {
+ super(slot);
+ }
+
+ @Override
+ protected void phantomClick(ClickData clickData, ItemStack cursorStack) {}
+
+ // region client
+
+ @Override
+ public ClickResult onClick(int buttonId, boolean doubleClick) {
+ updateAndSendRecipeMapToServer(
+ getContext().getCursor()
+ .getItemStack());
+ return ClickResult.SUCCESS;
+ }
+
+ @Override
+ public boolean handleDragAndDrop(ItemStack draggedStack, int button) {
+ updateAndSendRecipeMapToServer(draggedStack);
+ draggedStack.stackSize = 0;
+ return true;
+ }
+
+ private void updateAndSendRecipeMapToServer(ItemStack stack) {
+ mRecipeMap = getItemStackMachineRecipeMap(stack);
+ updateAndSendRecipeMapToServer(mRecipeMap);
+ }
+
+ private void updateAndSendRecipeMapToServer(RecipeMap<?> recipeMap) {
+ if (recipeMap != null) {
+ filteredMachines = getFilteredMachines(recipeMap);
+ } else {
+ filteredMachines = new ArrayList<>();
+ mInventory[FILTER_SLOT_INDEX] = null;
+ }
+ mRotationIndex = -1;
+ syncToServer(SYNC_RECIPEMAP_C2S, buffer -> {
+ NetworkUtils.writeStringSafe(buffer, recipeMap != null ? recipeMap.unlocalizedName : null);
+ buffer.writeVarIntToBuffer(filteredMachines.size());
+ for (ItemStack filteredMachine : filteredMachines) {
+ NetworkUtils.writeItemStack(buffer, filteredMachine);
+ }
+ });
+ }
+
+ @Override
+ public void readOnClient(int id, PacketBuffer buf) {
+ if (id != REQUEST_FILTERED_MACHINES_S2C) {
+ super.readOnClient(id, buf);
+ return;
+ }
+
+ String recipeMapName = NetworkUtils.readStringSafe(buf);
+ mRecipeMap = recipeMapName != null ? RecipeMap.ALL_RECIPE_MAPS.get(recipeMapName) : null;
+ if (mRecipeMap != null) {
+ updateAndSendRecipeMapToServer(mRecipeMap);
+ }
+ }
+
+ // endregion
+
+ // region server
+
+ @Override
+ public void readOnServer(int id, PacketBuffer buf) throws IOException {
+ if (id != SYNC_RECIPEMAP_C2S) {
+ super.readOnServer(id, buf);
+ return;
+ }
+
+ String recipeMapName = NetworkUtils.readStringSafe(buf);
+ mRecipeMap = recipeMapName != null ? RecipeMap.getFromOldIdentifier(recipeMapName) : null;
+ mRotationIndex = -1;
+ mInventory[FILTER_SLOT_INDEX] = null;
+ filteredMachines.clear();
+
+ if (mRecipeMap != null) {
+ int filteredMachineSize = buf.readVarIntFromBuffer();
+ filteredMachineSize = Math.min(filteredMachineSize, 256); // Prevent storing too many items
+ for (int i = 0; i < filteredMachineSize; i++) {
+ ItemStack stack = NetworkUtils.readItemStack(buf);
+ if (stack != null) {
+ filteredMachines.add(stack);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void detectAndSendChanges(boolean init) {
+ super.detectAndSendChanges(init);
+ if (init && mRecipeMap != null && filteredMachines.isEmpty()) {
+ // backward compatibility: This machine used to store only mRecipeMap, not filteredMachines
+ syncToClient(
+ REQUEST_FILTERED_MACHINES_S2C,
+ buffer -> NetworkUtils.writeStringSafe(buffer, mRecipeMap.unlocalizedName));
+ }
+ }
+
+ // endregion
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_Regulator.java b/src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_Regulator.java
new file mode 100644
index 0000000000..08d3d32512
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_Regulator.java
@@ -0,0 +1,228 @@
+package gregtech.common.tileentities.automation;
+
+import static gregtech.api.enums.Textures.BlockIcons.AUTOMATION_REGULATOR;
+import static gregtech.api.enums.Textures.BlockIcons.AUTOMATION_REGULATOR_GLOW;
+
+import java.util.Collections;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.internal.wrapper.BaseSlot;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotGroup;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Buffer;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_Regulator extends GT_MetaTileEntity_Buffer {
+
+ public int[] mTargetSlots = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ private boolean charge = false, decharge = false;
+
+ public GT_MetaTileEntity_Regulator(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 20,
+ new String[] { "Filters up to 9 different Items", "Allows Item-specific output stack size",
+ "Allows Item-specific output slot" });
+ }
+
+ public GT_MetaTileEntity_Regulator(String aName, int aTier, int aInvSlotCount, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Regulator(String aName, int aTier, int aInvSlotCount, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Regulator(
+ this.mName,
+ this.mTier,
+ this.mInventory.length,
+ this.mDescriptionArray,
+ this.mTextures);
+ }
+
+ @Override
+ public ITexture getOverlayIcon() {
+ return TextureFactory.of(
+ TextureFactory.of(AUTOMATION_REGULATOR),
+ TextureFactory.builder()
+ .addIcon(AUTOMATION_REGULATOR_GLOW)
+ .glow()
+ .build());
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return aIndex < 9 || aIndex == rechargerSlotStartIndex();
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setInteger("mTargetSlot1", this.mTargetSlots[0]);
+ aNBT.setInteger("mTargetSlot2", this.mTargetSlots[1]);
+ aNBT.setInteger("mTargetSlot3", this.mTargetSlots[2]);
+ aNBT.setInteger("mTargetSlot4", this.mTargetSlots[3]);
+ aNBT.setInteger("mTargetSlot5", this.mTargetSlots[4]);
+ aNBT.setInteger("mTargetSlot6", this.mTargetSlots[5]);
+ aNBT.setInteger("mTargetSlot7", this.mTargetSlots[6]);
+ aNBT.setInteger("mTargetSlot8", this.mTargetSlots[7]);
+ aNBT.setInteger("mTargetSlot9", this.mTargetSlots[8]);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ this.mTargetSlots[0] = aNBT.getInteger("mTargetSlot1");
+ this.mTargetSlots[1] = aNBT.getInteger("mTargetSlot2");
+ this.mTargetSlots[2] = aNBT.getInteger("mTargetSlot3");
+ this.mTargetSlots[3] = aNBT.getInteger("mTargetSlot4");
+ this.mTargetSlots[4] = aNBT.getInteger("mTargetSlot5");
+ this.mTargetSlots[5] = aNBT.getInteger("mTargetSlot6");
+ this.mTargetSlots[6] = aNBT.getInteger("mTargetSlot7");
+ this.mTargetSlots[7] = aNBT.getInteger("mTargetSlot8");
+ this.mTargetSlots[8] = aNBT.getInteger("mTargetSlot9");
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ // Regulation per Screwdriver is overridden by GUI regulation.
+ }
+
+ @Override
+ public void moveItems(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) {
+ for (int i = 0, tCosts; i < 9; i++) {
+ if (this.mInventory[(i + 9)] != null) {
+ tCosts = GT_Utility.moveOneItemStackIntoSlot(
+ getBaseMetaTileEntity(),
+ getBaseMetaTileEntity().getTileEntityAtSide(getBaseMetaTileEntity().getBackFacing()),
+ getBaseMetaTileEntity().getBackFacing(),
+ this.mTargetSlots[i],
+ Collections.singletonList(this.mInventory[(i + 9)]),
+ false,
+ (byte) this.mInventory[(i + 9)].stackSize,
+ (byte) this.mInventory[(i + 9)].stackSize,
+ (byte) 64,
+ (byte) 1) * 3;
+ if (tCosts > 0) {
+ this.mSuccess = 50;
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return super.allowPutStack(aBaseMetaTileEntity, aIndex, side, aStack) && aIndex >= 0
+ && aIndex <= 8
+ && GT_Utility.areStacksEqual(aStack, this.mInventory[(aIndex + 9)]);
+ }
+
+ @Override
+ public int rechargerSlotStartIndex() {
+ return 19;
+ }
+
+ @Override
+ public int dechargerSlotStartIndex() {
+ return 19;
+ }
+
+ @Override
+ public int rechargerSlotCount() {
+ return charge ? 1 : 0;
+ }
+
+ @Override
+ public int dechargerSlotCount() {
+ return decharge ? 1 : 0;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ if (aBaseMetaTileEntity.isServerSide()) {
+ charge = aBaseMetaTileEntity.getStoredEU() / 2 > aBaseMetaTileEntity.getEUCapacity() / 3;
+ decharge = aBaseMetaTileEntity.getStoredEU() < aBaseMetaTileEntity.getEUCapacity() / 3;
+ }
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ super.addUIWidgets(builder, buildContext);
+ builder.widget(createChargerSlot(43, 62));
+ builder.widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_ARROW_22_RED.apply(84, true))
+ .setPos(65, 60)
+ .setSize(84, 22))
+ .widget(
+ SlotGroup.ofItemHandler(inventoryHandler, 3)
+ .startFromSlot(0)
+ .endAtSlot(8)
+ .build()
+ .setPos(7, 5))
+ .widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_SLOTS_HOLO_3BY3)
+ .setPos(62, 5)
+ .setSize(54, 54))
+ .widget(
+ SlotGroup.ofItemHandler(inventoryHandler, 3)
+ .phantom(true)
+ .startFromSlot(9)
+ .endAtSlot(17)
+ .applyForWidget(
+ widget -> widget.setControlsAmount(true)
+ .setBackground(GT_UITextures.TRANSPARENT))
+ .build()
+ .setPos(62, 5))
+ .widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_SLOTS_HOLO_3BY3)
+ .setPos(117, 5)
+ .setSize(54, 54));
+
+ int xBase = 117, yBase = 5;
+ for (int i = 0; i < mTargetSlots.length; i++) {
+ final int index = i;
+ int xPos = xBase + (i % 3) * 18, yPos = yBase + (i / 3) * 18;
+ builder.widget(new SlotWidget(BaseSlot.empty()) {
+
+ @Override
+ protected void phantomClick(ClickData clickData, ItemStack cursorStack) {
+ mTargetSlots[index] = Math.min(
+ 99,
+ Math.max(
+ 0,
+ mTargetSlots[index] + (clickData.mouseButton == 0 ? -1 : 1) * (clickData.shift ? 16 : 1)));
+ }
+ }.setBackground(GT_UITextures.TRANSPARENT)
+ .setPos(xPos, yPos))
+ .widget(
+ TextWidget.dynamicString(() -> String.valueOf(mTargetSlots[index]))
+ .setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setPos(xPos + 2 + (i % 3 == 0 ? 1 : 0), yPos + 3 + (i / 3 == 0 ? 1 : 0)));
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_SuperBuffer.java b/src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_SuperBuffer.java
new file mode 100644
index 0000000000..9a1d2d7dcf
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_SuperBuffer.java
@@ -0,0 +1,105 @@
+package gregtech.common.tileentities.automation;
+
+import static gregtech.api.enums.Textures.BlockIcons.AUTOMATION_SUPERBUFFER;
+import static gregtech.api.enums.Textures.BlockIcons.AUTOMATION_SUPERBUFFER_GLOW;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.minecraft.item.ItemStack;
+
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_SuperBuffer extends GT_MetaTileEntity_ChestBuffer {
+
+ public GT_MetaTileEntity_SuperBuffer(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 257,
+ new String[] { "Buffers up to 256 Item Stacks", "Use Screwdriver to regulate output stack size",
+ getTickRateDesc(aTier) });
+ }
+
+ public GT_MetaTileEntity_SuperBuffer(String aName, int aTier, int aInvSlotCount, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_SuperBuffer(String aName, int aTier, int aInvSlotCount, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_SuperBuffer(
+ this.mName,
+ this.mTier,
+ this.mInventory.length,
+ this.mDescriptionArray,
+ this.mTextures);
+ }
+
+ @Override
+ public ITexture getOverlayIcon() {
+ return TextureFactory.of(
+ TextureFactory.of(AUTOMATION_SUPERBUFFER),
+ TextureFactory.builder()
+ .addIcon(AUTOMATION_SUPERBUFFER_GLOW)
+ .glow()
+ .build());
+ }
+
+ @Override
+ protected void fillStacksIntoFirstSlots() {
+ // no order, this is super buffer
+ HashMap<GT_Utility.ItemId, Integer> slots = new HashMap<>(mInventory.length);
+ HashMap<GT_Utility.ItemId, ItemStack> stacks = new HashMap<>(mInventory.length);
+ List<Integer> validSlots = new ArrayList<>(mInventory.length);
+ // List<String> order = new ArrayList<>(mInventory.length);
+ for (int i = 0; i < mInventory.length - 1; i++) {
+ if (!isValidSlot(i)) continue;
+ validSlots.add(i);
+ ItemStack s = mInventory[i];
+ if (s == null) continue;
+ GT_Utility.ItemId sID = GT_Utility.ItemId.createNoCopy(s);
+ slots.merge(sID, s.stackSize, Integer::sum);
+ if (!stacks.containsKey(sID)) stacks.put(sID, s);
+ // order.add(sID);
+ mInventory[i] = null;
+ }
+ int i = 0;
+ for (Map.Entry<GT_Utility.ItemId, Integer> entry : slots.entrySet()) {
+ do {
+ int slot = validSlots.get(i);
+ mInventory[slot] = stacks.get(entry.getKey())
+ .copy();
+ int toSet = Math.min(entry.getValue(), mInventory[slot].getMaxStackSize());
+ mInventory[slot].stackSize = toSet;
+ entry.setValue(entry.getValue() - toSet);
+ i++;
+ } while (entry.getValue() > 0);
+ }
+ }
+
+ @Override
+ protected void addMainUI(ModularWindow.Builder builder) {
+ builder.widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_SUPER_BUFFER)
+ .setPos(61, 4)
+ .setSize(54, 54));
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_TypeFilter.java b/src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_TypeFilter.java
new file mode 100644
index 0000000000..be4a2226a1
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/automation/GT_MetaTileEntity_TypeFilter.java
@@ -0,0 +1,213 @@
+package gregtech.common.tileentities.automation;
+
+import static gregtech.api.enums.GT_Values.W;
+import static gregtech.api.enums.Textures.BlockIcons.AUTOMATION_TYPEFILTER;
+import static gregtech.api.enums.Textures.BlockIcons.AUTOMATION_TYPEFILTER_GLOW;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+
+import com.google.common.collect.ImmutableList;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.internal.wrapper.BaseSlot;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_SpecialFilter;
+import gregtech.api.objects.ItemData;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_OreDictUnificator;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_TypeFilter extends GT_MetaTileEntity_SpecialFilter {
+
+ private static final String REPRESENTATION_SLOT_TOOLTIP = "GT5U.type_filter.representation_slot.tooltip";
+ public int mRotationIndex = 0;
+ public OrePrefixes mPrefix = OrePrefixes.ore;
+
+ public static ImmutableList<OrePrefixes> OREBLOCK_PREFIXES = ImmutableList.of(
+ OrePrefixes.oreBlackgranite,
+ OrePrefixes.oreDense,
+ OrePrefixes.oreEnd,
+ OrePrefixes.oreEndstone,
+ OrePrefixes.oreNether,
+ OrePrefixes.oreNetherrack,
+ OrePrefixes.oreNormal,
+ OrePrefixes.orePoor,
+ OrePrefixes.oreRedgranite,
+ OrePrefixes.oreRich,
+ OrePrefixes.oreSmall,
+ OrePrefixes.oreBasalt,
+ OrePrefixes.oreMarble);
+
+ public GT_MetaTileEntity_TypeFilter(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ new String[] { "Filters 1 Item Type", "Use Screwdriver to regulate output stack size" });
+ }
+
+ public GT_MetaTileEntity_TypeFilter(String aName, int aTier, int aInvSlotCount, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_TypeFilter(String aName, int aTier, int aInvSlotCount, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_TypeFilter(
+ this.mName,
+ this.mTier,
+ this.mInventory.length,
+ this.mDescriptionArray,
+ this.mTextures);
+ }
+
+ @Override
+ public ITexture getOverlayIcon() {
+ return TextureFactory.of(
+ TextureFactory.of(AUTOMATION_TYPEFILTER),
+ TextureFactory.builder()
+ .addIcon(AUTOMATION_TYPEFILTER_GLOW)
+ .glow()
+ .build());
+ }
+
+ public void clickTypeIcon(boolean aRightClick, ItemStack aHandStack) {
+ if (getBaseMetaTileEntity().isServerSide()) {
+ if (aHandStack != null) {
+ copyHeldItemPrefix(aHandStack);
+ } else {
+ cyclePrefix(aRightClick);
+ }
+ }
+ }
+
+ private void copyHeldItemPrefix(ItemStack handStack) {
+ ItemData data = GT_OreDictUnificator.getAssociation(handStack);
+ if (data != null && data.hasValidPrefixData()) {
+ this.mPrefix = data.mPrefix;
+ this.mRotationIndex = -1;
+ }
+ }
+
+ private void cyclePrefix(boolean aRightClick) {
+ for (int i = 0; i < OrePrefixes.values().length; i++) {
+ if (this.mPrefix == OrePrefixes.values()[i]) {
+ for (this.mPrefix = null; this.mPrefix == null; this.mPrefix = OrePrefixes.values()[i]) {
+ if (aRightClick) {
+ do {
+ i--;
+ if (i < 0) {
+ i = OrePrefixes.values().length - 1;
+ }
+ } while (OrePrefixes.values()[i].mPrefixedItems.isEmpty());
+ } else {
+ do {
+ i++;
+ if (i >= OrePrefixes.values().length) {
+ i = 0;
+ }
+ } while (OrePrefixes.values()[i].mPrefixedItems.isEmpty());
+ }
+ if (!OrePrefixes.values()[i].mPrefixedItems.isEmpty()
+ && OrePrefixes.values()[i].mPrefixInto == OrePrefixes.values()[i])
+ mPrefix = OrePrefixes.values()[i];
+ }
+ }
+ this.mRotationIndex = -1;
+ }
+ }
+
+ @Override
+ public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPreTick(aBaseMetaTileEntity, aTick);
+ if ((!getBaseMetaTileEntity().isServerSide()) || ((aTick % 8L != 0L) && mRotationIndex != -1)) return;
+ if (this.mPrefix.mPrefixedItems.isEmpty()) {
+ this.mInventory[FILTER_SLOT_INDEX] = null;
+ return;
+ }
+ this.mInventory[FILTER_SLOT_INDEX] = GT_Utility.copyAmount(
+ 1,
+ this.mPrefix.mPrefixedItems
+ .get(this.mRotationIndex = (this.mRotationIndex + 1) % this.mPrefix.mPrefixedItems.size()));
+ if (this.mInventory[FILTER_SLOT_INDEX] == null) return;
+ if (this.mInventory[FILTER_SLOT_INDEX].getItemDamage() == W) this.mInventory[9].setItemDamage(0);
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setString("mPrefix", this.mPrefix.toString());
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ this.mPrefix = OrePrefixes.getPrefix(aNBT.getString("mPrefix"), this.mPrefix);
+ }
+
+ @Override
+ protected boolean isStackAllowed(ItemStack aStack) {
+ if (this.mPrefix == OrePrefixes.ore) {
+ ItemData data = GT_OreDictUnificator.getItemData(aStack);
+ if (data != null && data.mPrefix != null && OREBLOCK_PREFIXES.contains(data.mPrefix)) {
+ return true;
+ }
+ }
+ return this.mPrefix.contains(aStack);
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ super.addUIWidgets(builder, buildContext);
+ builder.widget(
+ new FakeSyncWidget.StringSyncer(
+ () -> this.mPrefix.toString(),
+ (prefix) -> this.mPrefix = OrePrefixes.getPrefix(prefix, this.mPrefix)));
+ }
+
+ @Override
+ protected Function<List<String>, List<String>> getItemStackReplacementTooltip() {
+ return (itemTooltip) -> {
+ List<String> replacementTooltip = new ArrayList<>();
+ replacementTooltip.add("Filter set to " + mPrefix.mRegularLocalName);
+ replacementTooltip.add("Ore prefix: §e" + mPrefix + "§r");
+ replacementTooltip.add("Filter size: §e" + mPrefix.mPrefixedItems.size() + "§r");
+ replacementTooltip.addAll(mTooltipCache.getData(REPRESENTATION_SLOT_TOOLTIP).text);
+ return replacementTooltip;
+ };
+ }
+
+ @Override
+ protected SlotWidget createFilterIconSlot(BaseSlot slot) {
+ return new TypeFilterIconSlotWidget(slot);
+ }
+
+ private class TypeFilterIconSlotWidget extends FilterIconSlotWidget {
+
+ public TypeFilterIconSlotWidget(BaseSlot slot) {
+ super(slot);
+ }
+
+ @Override
+ protected void phantomClick(ClickData clickData, ItemStack cursorStack) {
+ clickTypeIcon(clickData.mouseButton != 0, cursorStack);
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler.java b/src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler.java
new file mode 100644
index 0000000000..6964b13165
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler.java
@@ -0,0 +1,530 @@
+package gregtech.common.tileentities.boilers;
+
+import static gregtech.api.objects.XSTR.XSTR_INSTANCE;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.init.Items;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.IFluidHandler;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.drawable.UITexture;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.api.widget.Widget;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.ProgressBar;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.ParticleFX;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.SteamVariant;
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.gui.modularui.GUITextureSet;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.modularui.IGetTitleColor;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicTank;
+import gregtech.api.objects.GT_ItemStack;
+import gregtech.api.util.GT_Log;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.WorldSpawnedEventBuilder.ParticleEventBuilder;
+import gregtech.common.GT_Pollution;
+
+public abstract class GT_MetaTileEntity_Boiler extends GT_MetaTileEntity_BasicTank
+ implements IGetTitleColor, IAddUIWidgets {
+
+ public static final byte SOUND_EVENT_LET_OFF_EXCESS_STEAM = 1;
+ public int mTemperature = 20;
+ public int mProcessingEnergy = 0;
+ public int mLossTimer = 0;
+ public FluidStack mSteam = null;
+ public boolean mHadNoWater = false;
+ private int mExcessWater = 0;
+
+ public GT_MetaTileEntity_Boiler(int aID, String aName, String aNameRegional, String aDescription,
+ ITexture... aTextures) {
+ super(aID, aName, aNameRegional, 0, 4, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Boiler(int aID, String aName, String aNameRegional, String[] aDescription,
+ ITexture... aTextures) {
+ super(aID, aName, aNameRegional, 0, 4, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Boiler(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 4, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Boiler(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 4, aDescription, aTextures);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ ITexture[] tmp;
+ if ((sideDirection.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0) { // Horizontal
+ if (sideDirection != facingDirection) tmp = mTextures[2][colorIndex + 1];
+ else tmp = mTextures[(byte) (active ? 4 : 3)][colorIndex + 1];
+ } else {
+ tmp = mTextures[sideDirection.ordinal()][colorIndex + 1];
+ }
+ if (sideDirection != facingDirection && tmp.length == 2) {
+ tmp = new ITexture[] { tmp[0] };
+ }
+ return tmp;
+ }
+
+ @Override
+ public boolean isElectric() {
+ return false;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facingDirection) {
+ return (facingDirection.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return true;
+ }
+
+ @Override
+ public int getProgresstime() {
+ return this.mTemperature;
+ }
+
+ @Override
+ public int maxProgresstime() {
+ return 500;
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ if (aBaseMetaTileEntity.isClientSide()) {
+ return true;
+ }
+ if (aPlayer != null) {
+ if (GT_Utility.areStacksEqual(aPlayer.getCurrentEquippedItem(), new ItemStack(Items.water_bucket, 1))) {
+ fill(Materials.Water.getFluid(1000L * (long) aPlayer.getCurrentEquippedItem().stackSize), true);
+
+ if (!aPlayer.capabilities.isCreativeMode) {
+ aPlayer.getCurrentEquippedItem()
+ .func_150996_a(Items.bucket);
+ }
+ } else {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean doesFillContainers() {
+ return true;
+ }
+
+ @Override
+ public boolean doesEmptyContainers() {
+ return true;
+ }
+
+ @Override
+ public boolean canTankBeFilled() {
+ return true;
+ }
+
+ @Override
+ public boolean canTankBeEmptied() {
+ return true;
+ }
+
+ @Override
+ public boolean displaysItemStack() {
+ return false;
+ }
+
+ @Override
+ public boolean displaysStackSize() {
+ return false;
+ }
+
+ @Override
+ public boolean isFluidInputAllowed(FluidStack aFluid) {
+ return GT_ModHandler.isWater(aFluid);
+ }
+
+ @Override
+ public FluidStack getDrainableStack() {
+ return this.mSteam;
+ }
+
+ @Override
+ public FluidStack setDrainableStack(FluidStack aFluid) {
+ this.mSteam = aFluid;
+ return this.mSteam;
+ }
+
+ @Override
+ public boolean isDrainableStackSeparate() {
+ return true;
+ }
+
+ @Override
+ public boolean allowCoverOnSide(ForgeDirection side, GT_ItemStack aCover) {
+ return GregTech_API.getCoverBehaviorNew(aCover.toStack())
+ .isSimpleCover();
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setInteger("mLossTimer", this.mLossTimer);
+ aNBT.setInteger("mTemperature", this.mTemperature);
+ aNBT.setInteger("mProcessingEnergy", this.mProcessingEnergy);
+ aNBT.setInteger("mExcessWater", this.mExcessWater);
+ if (this.mSteam == null) {
+ return;
+ }
+ try {
+ aNBT.setTag("mSteam", this.mSteam.writeToNBT(new NBTTagCompound()));
+ } catch (Throwable ignored) {}
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ this.mLossTimer = aNBT.getInteger("mLossTimer");
+ this.mTemperature = aNBT.getInteger("mTemperature");
+ this.mProcessingEnergy = aNBT.getInteger("mProcessingEnergy");
+ this.mExcessWater = aNBT.getInteger("mExcessWater");
+ this.mSteam = FluidStack.loadFluidStackFromNBT(aNBT.getCompoundTag("mSteam"));
+ }
+
+ /**
+ * Produce some steam. Assume water is present.
+ */
+ protected void produceSteam(int aAmount) {
+ mExcessWater -= aAmount;
+ if (mExcessWater < 0) {
+ int tWaterToConsume = -mExcessWater / GT_Values.STEAM_PER_WATER;
+ mFluid.amount -= tWaterToConsume;
+ mExcessWater += GT_Values.STEAM_PER_WATER * tWaterToConsume;
+ }
+ if (GT_ModHandler.isSteam(this.mSteam)) {
+ this.mSteam.amount += aAmount;
+ } else {
+ this.mSteam = GT_ModHandler.getSteam(aAmount);
+ }
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ pollute(aTick);
+
+ if (isNotAllowedToWork(aBaseMetaTileEntity, aTick)) return;
+
+ calculateCooldown();
+ pushSteamToInventories(aBaseMetaTileEntity);
+
+ if (canNotCreateSteam(aBaseMetaTileEntity, aTick)) {
+ pollute(aTick);
+ return;
+ }
+
+ ventSteamIfTankIsFull();
+ updateFuelTimed(aBaseMetaTileEntity, aTick);
+ calculateHeatUp(aBaseMetaTileEntity, aTick);
+ }
+
+ private boolean isNotAllowedToWork(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ return (!aBaseMetaTileEntity.isServerSide()) || (aTick <= 20L);
+ }
+
+ private void pollute(long aTick) {
+ if (this.mProcessingEnergy > 0 && (aTick % 20L == 0L)) {
+ GT_Pollution.addPollution(getBaseMetaTileEntity(), getPollution());
+ }
+ }
+
+ private void calculateHeatUp(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if ((this.mTemperature < getMaxTemperature()) && (this.mProcessingEnergy > 0)
+ && (aTick % getHeatUpRate() == 0L)) {
+ this.mProcessingEnergy -= getEnergyConsumption();
+ this.mTemperature += getHeatUpAmount();
+ }
+ aBaseMetaTileEntity.setActive(this.mProcessingEnergy > 0);
+ }
+
+ private void updateFuelTimed(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if ((this.mProcessingEnergy <= 0) && (aBaseMetaTileEntity.isAllowedToWork()))
+ updateFuel(aBaseMetaTileEntity, aTick);
+ }
+
+ protected void ventSteamIfTankIsFull() {
+ if ((this.mSteam != null) && (this.mSteam.amount > getSteamCapacity())) {
+ sendSound(SOUND_EVENT_LET_OFF_EXCESS_STEAM);
+ this.mSteam.amount = getSteamCapacity() * 3 / 4;
+ }
+ }
+
+ private boolean canNotCreateSteam(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aTick % 10L != 0L) {
+ return false;
+ }
+
+ if (this.mTemperature > 100) {
+ if ((!GT_ModHandler.isWater(this.mFluid)) || (this.mFluid.amount <= 0)) {
+ this.mHadNoWater = true;
+ } else {
+ if (this.mHadNoWater) {
+ GT_Log.exp.println("Boiler " + this.mName + " had no Water!");
+ onDangerousWaterLack(aBaseMetaTileEntity, aTick);
+ return true;
+ }
+ produceSteam(getProductionPerSecond() / 2);
+ }
+ } else {
+ this.mHadNoWater = false;
+ }
+ return false;
+ }
+
+ protected void onDangerousWaterLack(IGregTechTileEntity tile, long ignoredTicks) {
+ tile.doExplosion(2048L);
+ }
+
+ /**
+ * Pushes Steam to a Side of this Boiler
+ *
+ * @param aBaseMetaTileEntity The tile-entity instance of this Boiler
+ * @param side The direction of the side to push Steam to
+ */
+ protected final void pushSteamToSide(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side) {
+ if (mSteam == null || mSteam.amount == 0) return;
+ final IFluidHandler tTileEntity = aBaseMetaTileEntity.getITankContainerAtSide(side);
+ if (tTileEntity == null) return;
+ GT_Utility.moveFluid(aBaseMetaTileEntity, tTileEntity, side, Math.max(1, this.mSteam.amount / 2), null);
+ }
+
+ /**
+ * Pushes steam to Fluid inventories at all sides except Front and Bottom.
+ *
+ * @param aBaseMetaTileEntity The tile-entity instance of this Boiler
+ */
+ protected void pushSteamToInventories(IGregTechTileEntity aBaseMetaTileEntity) {
+ if (mSteam == null || mSteam.amount == 0) return;
+ for (final ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) {
+ if (direction == aBaseMetaTileEntity.getFrontFacing() || direction == ForgeDirection.DOWN) continue;
+ if (this.mSteam == null) break;
+ pushSteamToSide(aBaseMetaTileEntity, direction);
+ }
+ }
+
+ private void calculateCooldown() {
+ if (this.mTemperature <= 20) {
+ this.mTemperature = 20;
+ this.mLossTimer = 0;
+ } else if (++this.mLossTimer > getCooldownInterval()) {
+ // only loss temperature if hot
+ this.mTemperature -= 1;
+ this.mLossTimer = 0;
+ }
+ }
+
+ protected boolean isAutomatable() {
+ return GT_Mod.gregtechproxy.mAllowSmallBoilerAutomation;
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return isAutomatable() && aIndex == 1 || aIndex == 3;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return isAutomatable() && (aIndex == 0 && isItemValidFluidFilledItem(aStack))
+ || (aIndex == 2 && isItemValidFuel(aStack));
+ }
+
+ @Override
+ public void doSound(byte aIndex, double aX, double aY, double aZ) {
+ if (aIndex == GT_MetaTileEntity_Boiler.SOUND_EVENT_LET_OFF_EXCESS_STEAM) {
+ GT_Utility.doSoundAtClient(SoundResource.RANDOM_FIZZ, 2, 1.0F, aX, aY, aZ);
+
+ new ParticleEventBuilder().setIdentifier(ParticleFX.CLOUD)
+ .setWorld(getBaseMetaTileEntity().getWorld())
+ .setMotion(0D, 0D, 0D)
+ .<ParticleEventBuilder>times(
+ 8,
+ x -> x.setPosition(aX - 0.5D + XSTR_INSTANCE.nextFloat(), aY, aZ - 0.5D + XSTR_INSTANCE.nextFloat())
+ .run());
+ }
+ }
+
+ @Override
+ public int getTankPressure() {
+ return 100;
+ }
+
+ protected abstract int getPollution();
+
+ @Override
+ public int getCapacity() {
+ return 16000;
+ }
+
+ protected int getSteamCapacity() {
+ return getCapacity();
+ }
+
+ protected abstract int getProductionPerSecond();
+
+ protected abstract int getMaxTemperature();
+
+ protected abstract int getEnergyConsumption();
+
+ protected abstract int getCooldownInterval();
+
+ protected int getHeatUpRate() {
+ return 12;
+ }
+
+ protected int getHeatUpAmount() {
+ return 1;
+ }
+
+ protected abstract void updateFuel(IGregTechTileEntity aBaseMetaTileEntity, long aTick);
+
+ @Override
+ public SteamVariant getSteamVariant() {
+ return SteamVariant.BRONZE;
+ }
+
+ protected IDrawable[] getFuelSlotBackground() {
+ return new IDrawable[] { getGUITextureSet().getItemSlot(),
+ GT_UITextures.OVERLAY_SLOT_COAL_STEAM.get(getSteamVariant()) };
+ }
+
+ protected IDrawable[] getAshSlotBackground() {
+ return new IDrawable[] { getGUITextureSet().getItemSlot(),
+ GT_UITextures.OVERLAY_SLOT_DUST_STEAM.get(getSteamVariant()) };
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ builder.widget(
+ new SlotWidget(inventoryHandler, 0).setFilter(this::isItemValidFluidFilledItem)
+ .setPos(43, 25)
+ .setBackground(getGUITextureSet().getItemSlot(), getOverlaySlotIn()))
+ .widget(
+ new SlotWidget(inventoryHandler, 1).setAccess(true, false)
+ .setPos(43, 61)
+ .setBackground(getGUITextureSet().getItemSlot(), getOverlaySlotOut()))
+ .widget(createFuelSlot())
+ .widget(createAshSlot())
+ .widget(
+ new ProgressBar().setProgress(() -> mSteam == null ? 0 : (float) mSteam.amount / getSteamCapacity())
+ .setTexture(getProgressbarEmpty(), GT_UITextures.PROGRESSBAR_BOILER_STEAM, 10)
+ .setDirection(ProgressBar.Direction.UP)
+ .setPos(70, 25)
+ .setSize(10, 54))
+ .widget(
+ new ProgressBar().setProgress(() -> mFluid == null ? 0 : (float) mFluid.amount / getCapacity())
+ .setTexture(getProgressbarEmpty(), GT_UITextures.PROGRESSBAR_BOILER_WATER, 10)
+ .setDirection(ProgressBar.Direction.UP)
+ .setPos(83, 25)
+ .setSize(10, 54))
+ .widget(
+ new ProgressBar().setProgress(() -> (float) mTemperature / maxProgresstime())
+ .setTexture(getProgressbarEmpty(), GT_UITextures.PROGRESSBAR_BOILER_HEAT, 10)
+ .setDirection(ProgressBar.Direction.UP)
+ .setPos(96, 25)
+ .setSize(10, 54))
+ .widget(
+ new ProgressBar()
+ // cap minimum so that one can easily see there's fuel remaining
+ .setProgress(() -> mProcessingEnergy > 0 ? Math.max((float) mProcessingEnergy / 1000, 1f / 5) : 0)
+ .setTexture(getProgressbarFuel(), 14)
+ .setDirection(ProgressBar.Direction.UP)
+ .setPos(116, 45)
+ .setSize(14, 14))
+ .widget(
+ new DrawableWidget().setDrawable(getOverlaySlotCanister())
+ .setPos(43, 43)
+ .setSize(18, 18));
+ }
+
+ private boolean isItemValidFluidFilledItem(@NotNull ItemStack stack) {
+ return isFluidInputAllowed(GT_Utility.getFluidForFilledItem(stack, true));
+ }
+
+ protected Widget createFuelSlot() {
+ return new SlotWidget(inventoryHandler, 2).setFilter(this::isItemValidFuel)
+ .setPos(115, 61)
+ .setBackground(getFuelSlotBackground());
+ }
+
+ protected boolean isItemValidFuel(@NotNull ItemStack stack) {
+ return true;
+ }
+
+ protected SlotWidget createAshSlot() {
+ return (SlotWidget) new SlotWidget(inventoryHandler, 3).setAccess(true, false)
+ .setPos(115, 25)
+ .setBackground(getAshSlotBackground());
+ }
+
+ @Override
+ public GUITextureSet getGUITextureSet() {
+ return GUITextureSet.STEAM.apply(getSteamVariant());
+ }
+
+ @Override
+ public int getTitleColor() {
+ return getSteamVariant() == SteamVariant.BRONZE ? COLOR_TITLE.get() : COLOR_TITLE_WHITE.get();
+ }
+
+ // for GT++
+
+ protected IDrawable getOverlaySlotIn() {
+ return GT_UITextures.OVERLAY_SLOT_IN_STEAM.get(getSteamVariant());
+ }
+
+ protected IDrawable getOverlaySlotOut() {
+ return GT_UITextures.OVERLAY_SLOT_OUT_STEAM.get(getSteamVariant());
+ }
+
+ protected IDrawable getOverlaySlotCanister() {
+ return GT_UITextures.OVERLAY_SLOT_CANISTER_STEAM.get(getSteamVariant());
+ }
+
+ protected UITexture getProgressbarEmpty() {
+ return GT_UITextures.PROGRESSBAR_BOILER_EMPTY_STEAM.get(getSteamVariant());
+ }
+
+ protected UITexture getProgressbarFuel() {
+ return GT_UITextures.PROGRESSBAR_FUEL_STEAM.get(getSteamVariant());
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler_Bronze.java b/src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler_Bronze.java
new file mode 100644
index 0000000000..3450d55290
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler_Bronze.java
@@ -0,0 +1,260 @@
+package gregtech.common.tileentities.boilers;
+
+import static gregtech.api.enums.Textures.BlockIcons.BOILER_FRONT;
+import static gregtech.api.enums.Textures.BlockIcons.BOILER_FRONT_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.BOILER_FRONT_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.BOILER_FRONT_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_BRONZEBRICKS_BOTTOM;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_BRONZEBRICKS_SIDE;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_BRONZEBRICKS_TOP;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE;
+import static gregtech.api.objects.XSTR.XSTR_INSTANCE;
+
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import net.minecraft.block.Block;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.tileentity.TileEntityFurnace;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidContainerRegistry;
+
+import org.jetbrains.annotations.NotNull;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import gregtech.GT_Mod;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.enums.ParticleFX;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.objects.XSTR;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_OreDictUnificator;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.WorldSpawnedEventBuilder.ParticleEventBuilder;
+import gregtech.common.GT_Pollution;
+
+public class GT_MetaTileEntity_Boiler_Bronze extends GT_MetaTileEntity_Boiler {
+
+ public GT_MetaTileEntity_Boiler_Bronze(int aID, String aName, String aNameRegional) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ new String[] { "An early way to get Steam Power", "Produces 120L of Steam per second",
+ "Causes " + GT_Mod.gregtechproxy.mPollutionSmallCoalBoilerPerSecond + " Pollution per second" });
+ }
+
+ public GT_MetaTileEntity_Boiler_Bronze(int aID, String aName, String aNameRegional, String[] aDescription) {
+ super(aID, aName, aNameRegional, aDescription);
+ }
+
+ public GT_MetaTileEntity_Boiler_Bronze(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Boiler_Bronze(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ ITexture[][][] rTextures = new ITexture[5][17][];
+ final ITexture[] texBottom = { TextureFactory.of(MACHINE_BRONZEBRICKS_BOTTOM) },
+ texTop = { TextureFactory.of(MACHINE_BRONZEBRICKS_TOP), TextureFactory.of(OVERLAY_PIPE) },
+ texSide = { TextureFactory.of(MACHINE_BRONZEBRICKS_SIDE), TextureFactory.of(OVERLAY_PIPE) },
+ texFront = { TextureFactory.of(MACHINE_BRONZEBRICKS_SIDE), TextureFactory.of(BOILER_FRONT),
+ TextureFactory.builder()
+ .addIcon(BOILER_FRONT_GLOW)
+ .glow()
+ .build() },
+ texFrontActive = { TextureFactory.of(MACHINE_BRONZEBRICKS_SIDE), TextureFactory.of(BOILER_FRONT_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(BOILER_FRONT_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ for (int i = 0; i < 17; i++) {
+ rTextures[0][i] = texBottom;
+ rTextures[1][i] = texTop;
+ rTextures[2][i] = texSide;
+ rTextures[3][i] = texFront;
+ rTextures[4][i] = texFrontActive;
+ }
+ return rTextures;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Boiler_Bronze(this.mName, this.mTier, this.mDescriptionArray, this.mTextures);
+ }
+
+ /**
+ * Draws random flames and smoke particles in front of active boiler
+ *
+ * @param aBaseMetaTileEntity The entity that will handle the {@link Block#randomDisplayTick}
+ */
+ @SideOnly(Side.CLIENT)
+ @Override
+ public void onRandomDisplayTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ if (aBaseMetaTileEntity.isActive()) {
+
+ final ForgeDirection frontFacing = aBaseMetaTileEntity.getFrontFacing();
+
+ if ((frontFacing.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0
+ && aBaseMetaTileEntity.getCoverIDAtSide(frontFacing) == 0
+ && !aBaseMetaTileEntity.getOpacityAtSide(frontFacing)) {
+
+ final double oX = aBaseMetaTileEntity.getOffsetX(frontFacing, 1) + 8D / 16D;
+ final double oY = aBaseMetaTileEntity.getOffsetY(frontFacing, 1);
+ final double oZ = aBaseMetaTileEntity.getOffsetZ(frontFacing, 1) + 8D / 16D;
+ final double offset = -0.48D;
+ final double horizontal = XSTR_INSTANCE.nextFloat() * 10D / 16D - 5D / 16D;
+
+ final double x, y, z;
+
+ y = oY + XSTR_INSTANCE.nextFloat() * 6D / 16D;
+
+ if (frontFacing == ForgeDirection.WEST) {
+ x = oX - offset;
+ z = oZ + horizontal;
+ } else if (frontFacing == ForgeDirection.EAST) {
+ x = oX + offset;
+ z = oZ + horizontal;
+ } else if (frontFacing == ForgeDirection.NORTH) {
+ x = oX + horizontal;
+ z = oZ - offset;
+ } else // if (frontFacing == ForgeDirection.SOUTH)
+ {
+ x = oX + horizontal;
+ z = oZ + offset;
+ }
+
+ ParticleEventBuilder particleEventBuilder = (new ParticleEventBuilder()).setMotion(0D, 0D, 0D)
+ .setPosition(x, y, z)
+ .setWorld(getBaseMetaTileEntity().getWorld());
+ particleEventBuilder.setIdentifier(ParticleFX.SMOKE)
+ .run();
+ particleEventBuilder.setIdentifier(ParticleFX.FLAME)
+ .run();
+ }
+ }
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ if ((aBaseMetaTileEntity.isServerSide()) && (aTick > 20L)
+ && this.mProcessingEnergy > 0
+ && (aTick % 20L == 0L)) {
+ GT_Pollution.addPollution(getBaseMetaTileEntity(), getPollution());
+ }
+ }
+
+ @Override
+ protected int getPollution() {
+ return GT_Mod.gregtechproxy.mPollutionSmallCoalBoilerPerSecond;
+ }
+
+ @Override
+ protected int getProductionPerSecond() {
+ return 120;
+ }
+
+ @Override
+ protected int getMaxTemperature() {
+ return 500;
+ }
+
+ @Override
+ protected int getEnergyConsumption() {
+ return 1;
+ }
+
+ @Override
+ protected int getCooldownInterval() {
+ return 45;
+ }
+
+ @Override
+ protected void updateFuel(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ ItemStack fuel = mInventory[2];
+ int burnTime = TileEntityFurnace.getItemBurnTime(fuel);
+ getCombustionPotential(fuel, burnTime).ifPresent(ashMaterial -> {
+ aBaseMetaTileEntity.decrStackSize(2, 1);
+ this.mProcessingEnergy += burnTime / 10;
+ boolean isABlock = !Block.getBlockFromItem(fuel.getItem())
+ .equals(Blocks.air);
+ combustFuel(burnTime, isABlock).map(dustSize -> GT_OreDictUnificator.get(dustSize, ashMaterial, 1L))
+ .ifPresent(ashes -> aBaseMetaTileEntity.addStackToSlot(3, ashes));
+ });
+ }
+
+ private static Optional<Materials> getCombustionPotential(ItemStack fuel, int burnTime) {
+ if (burnTime / 10 <= 0 || FluidContainerRegistry.isFilledContainer(fuel)) {
+ return Optional.empty();
+ }
+ String lowerCaseBlockName = Block.getBlockFromItem(fuel.getItem())
+ .getUnlocalizedName()
+ .toLowerCase();
+ if (couldProduceDarkAshes(fuel, lowerCaseBlockName)) {
+ return Optional.of(Materials.DarkAsh);
+ }
+ if (couldProduceRegularAshes(fuel, lowerCaseBlockName, burnTime)) {
+ return Optional.of(Materials.Ash);
+ }
+ return Optional.empty();
+ }
+
+ private static boolean couldProduceDarkAshes(ItemStack fuel, String lowerCaseBlockName) {
+ return GT_Utility.isPartOfMaterials(fuel, Materials.Coal)
+ || GT_Utility.isPartOfMaterials(fuel, Materials.Lignite)
+ || lowerCaseBlockName.matches("tile\\..+compressedcoal");
+ }
+
+ private static boolean couldProduceRegularAshes(ItemStack fuel, String lowerCaseBlockName, int burnTime) {
+ return GT_Utility.isPartOfMaterials(fuel, Materials.Charcoal)
+ || GT_Utility.isPartOfMaterials(fuel, Materials.Diamond)
+ || (Stream.of("^tile\\..+charcoal", "^tile\\..+coke", "^tile\\..+railcraft.cube")
+ .anyMatch(lowerCaseBlockName::matches))
+ || Stream.of("fuelCoke", "fuelCactusCharcoal", "fuelCactusCoke", "fuelSugarCharcoal", "fuelSugarCoke")
+ .anyMatch(name -> GT_OreDictUnificator.isItemStackInstanceOf(fuel, name))
+ || burnTime >= 2000;
+ }
+
+ private static Optional<OrePrefixes> combustFuel(int burnTime, boolean isABlock) {
+ if (isABlock) {
+ return Optional.of(OrePrefixes.dust);
+ } else if (XSTR.XSTR_INSTANCE.nextInt(getAshChanceBound(burnTime)) == 0) {
+ if (burnTime > 100000) {
+ return Optional.of(OrePrefixes.dust);
+ } else if (burnTime > 10000) {
+ return Optional.of(OrePrefixes.dustSmall);
+ } else {
+ return Optional.of(OrePrefixes.dustTiny);
+ }
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * The upper bound for the chance to get ash from combustion
+ * <br>
+ * Ash chance scales based on burn time from 14% at 0 up to 50% at 2000
+ *
+ * @param burnTime number assumed to be positive
+ * @return an upper bound between 7 and 2.
+ */
+ private static int getAshChanceBound(int burnTime) {
+ return (5 - (Math.min(burnTime, 2000) / 400)) + 2;
+ }
+
+ @Override
+ protected boolean isItemValidFuel(@NotNull ItemStack stack) {
+ return getCombustionPotential(stack, TileEntityFurnace.getItemBurnTime(stack)).isPresent();
+ }
+
+}
diff --git a/src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler_Lava.java b/src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler_Lava.java
new file mode 100644
index 0000000000..d0dfe74b64
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler_Lava.java
@@ -0,0 +1,474 @@
+package gregtech.common.tileentities.boilers;
+
+import static gregtech.api.enums.Textures.BlockIcons.BOILER_LAVA_FRONT;
+import static gregtech.api.enums.Textures.BlockIcons.BOILER_LAVA_FRONT_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.BOILER_LAVA_FRONT_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.BOILER_LAVA_FRONT_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.FLUID_IN_SIGN;
+import static gregtech.api.enums.Textures.BlockIcons.FLUID_OUT_SIGN;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_STEELBRICKS_BOTTOM;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_STEELBRICKS_SIDE;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_STEELBRICKS_TOP;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_DRAIN;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_OUT;
+import static gregtech.api.objects.XSTR.XSTR_INSTANCE;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.item.EntityItem;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidRegistry;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.FluidTank;
+import net.minecraftforge.fluids.FluidTankInfo;
+import net.minecraftforge.fluids.IFluidContainerItem;
+import net.minecraftforge.fluids.IFluidHandler;
+import net.minecraftforge.fluids.IFluidTank;
+
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.widget.Widget;
+import com.gtnewhorizons.modularui.common.widget.FluidSlotWidget;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import gregtech.GT_Mod;
+import gregtech.api.enums.Dyes;
+import gregtech.api.enums.ParticleFX;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.SteamVariant;
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.WorldSpawnedEventBuilder.ParticleEventBuilder;
+
+public class GT_MetaTileEntity_Boiler_Lava extends GT_MetaTileEntity_Boiler {
+
+ public static final int COOLDOWN_INTERVAL = 20;
+ public static final int ENERGY_PER_LAVA = 1;
+ public static final int CONSUMPTION_PER_HEATUP = 3;
+ public static final int PRODUCTION_PER_SECOND = 600;
+ private final FluidTank lavaTank = new LavaTank(null, getCapacity());
+ private int mCooledLava = 0;
+
+ public GT_MetaTileEntity_Boiler_Lava(int aID, String aName, String aNameRegional) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ new String[] { "A Boiler running off Lava", "Produces " + PRODUCTION_PER_SECOND + "L of Steam per second",
+ "Causes " + GT_Mod.gregtechproxy.mPollutionHighPressureLavaBoilerPerSecond + " Pollution per second",
+ "Consumes " + ((double) CONSUMPTION_PER_HEATUP / ENERGY_PER_LAVA)
+ + "L of Lava every "
+ + COOLDOWN_INTERVAL
+ + " ticks when fully heat up" });
+ }
+
+ public GT_MetaTileEntity_Boiler_Lava(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Boiler_Lava(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ final ForgeDirection rearDirection = facingDirection.getOpposite();
+ final ITexture[] tmp;
+ if ((sideDirection.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0) {
+ if (sideDirection == facingDirection) {
+ if (active) tmp = mTextures[4][colorIndex + 1];
+ else tmp = mTextures[3][colorIndex + 1];
+ } else if (sideDirection == rearDirection) {
+ tmp = mTextures[5][colorIndex + 1];
+ } else {
+ tmp = mTextures[2][colorIndex + 1];
+ }
+ } else tmp = mTextures[sideDirection.ordinal()][colorIndex + 1];
+ if (sideDirection != facingDirection && tmp.length == 2) {
+ return new ITexture[] { tmp[0] };
+ }
+ return tmp;
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ ITexture[][][] rTextures = new ITexture[6][17][];
+ for (byte color = -1; color < 16; color++) {
+ int i = color + 1;
+ short[] colorModulation = Dyes.getModulation(color, Dyes._NULL.mRGBa);
+ rTextures[0][i] = new ITexture[] { TextureFactory.of(MACHINE_STEELBRICKS_BOTTOM, colorModulation) };
+ rTextures[1][i] = new ITexture[] { TextureFactory.of(MACHINE_STEELBRICKS_TOP, colorModulation),
+ TextureFactory.of(OVERLAY_DRAIN), TextureFactory.of(FLUID_IN_SIGN) };
+ rTextures[2][i] = new ITexture[] { TextureFactory.of(MACHINE_STEELBRICKS_SIDE, colorModulation),
+ TextureFactory.of(OVERLAY_PIPE_OUT), TextureFactory.of(FLUID_IN_SIGN) };
+ rTextures[3][i] = new ITexture[] { TextureFactory.of(MACHINE_STEELBRICKS_SIDE, colorModulation),
+ TextureFactory.of(BOILER_LAVA_FRONT, colorModulation), TextureFactory.of(BOILER_LAVA_FRONT_GLOW) };
+ rTextures[4][i] = new ITexture[] { TextureFactory.of(MACHINE_STEELBRICKS_SIDE, colorModulation),
+ TextureFactory.of(BOILER_LAVA_FRONT_ACTIVE), TextureFactory.builder()
+ .addIcon(BOILER_LAVA_FRONT_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ rTextures[5][i] = new ITexture[] { TextureFactory.of(MACHINE_STEELBRICKS_SIDE, colorModulation),
+ TextureFactory.of(OVERLAY_PIPE_OUT), TextureFactory.of(FLUID_OUT_SIGN) };
+ }
+ return rTextures;
+ }
+
+ @Override
+ public int maxProgresstime() {
+ return 1000;
+ }
+
+ @Override
+ public int getCapacity() {
+ return 32000;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Boiler_Lava(this.mName, this.mTier, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ protected int getPollution() {
+ return GT_Mod.gregtechproxy.mPollutionHighPressureLavaBoilerPerSecond;
+ }
+
+ @Override
+ protected int getProductionPerSecond() {
+ return PRODUCTION_PER_SECOND;
+ }
+
+ @Override
+ protected int getMaxTemperature() {
+ return 1000;
+ }
+
+ @Override
+ protected int getEnergyConsumption() {
+ this.mCooledLava += CONSUMPTION_PER_HEATUP;
+ return CONSUMPTION_PER_HEATUP;
+ }
+
+ @Override
+ protected int getCooldownInterval() {
+ return COOLDOWN_INTERVAL;
+ }
+
+ /**
+ * Attempts to fill an {@link IFluidTank} from the {@link FluidStack} content of an {@link ItemStack}
+ *
+ * @param destinationIFluidTank The destination {@link IFluidTank} to fill
+ * @param SourceItemStack The source {@link ItemStack} containing the Fluid
+ * @return The {@link ItemStack} of the Empty version of the source {@link ItemStack} or {@code null} if none
+ */
+ public static ItemStack fillIFluidTankFromItemStack(IFluidTank destinationIFluidTank, ItemStack SourceItemStack) {
+ if (destinationIFluidTank == null || SourceItemStack == null) return null;
+
+ final FluidStack containedFluidStack = GT_Utility.getFluidForFilledItem(SourceItemStack, true);
+ if (containedFluidStack == null || containedFluidStack.amount == 0) return null;
+
+ final int fillableAmount = destinationIFluidTank.fill(containedFluidStack, false);
+ if (fillableAmount <= 0) return null;
+
+ final Item containerItem = SourceItemStack.getItem();
+ if (containerItem instanceof IFluidContainerItem equippedIFluidContainerItem) {
+ destinationIFluidTank.fill(equippedIFluidContainerItem.drain(SourceItemStack, fillableAmount, true), true);
+ return null;
+ } else {
+ final ItemStack emptyContainerItemStack = GT_Utility.getContainerForFilledItem(SourceItemStack, false);
+ destinationIFluidTank.fill(containedFluidStack, true);
+ return emptyContainerItemStack;
+ }
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ if (aBaseMetaTileEntity.isClientSide() || aPlayer == null) return true;
+
+ final ItemStack equippedItemStack = aPlayer.getCurrentEquippedItem();
+ final FluidStack equippedContainerFluidStack = GT_Utility.getFluidForFilledItem(equippedItemStack, true);
+ final ItemStack returnedItemStack;
+ final IFluidTank tank;
+
+ if (GT_ModHandler.isWater(equippedContainerFluidStack)) {
+ tank = this;
+ } else if (GT_ModHandler.isLava(equippedContainerFluidStack)) {
+ tank = lavaTank;
+ } else {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+ returnedItemStack = fillIFluidTankFromItemStack(tank, equippedItemStack);
+ if (returnedItemStack != null && !aPlayer.capabilities.isCreativeMode) {
+ if (equippedItemStack.stackSize > 1) {
+ if (!aPlayer.inventory.addItemStackToInventory(returnedItemStack)) {
+ aBaseMetaTileEntity.getWorld()
+ .spawnEntityInWorld(
+ new EntityItem(
+ aBaseMetaTileEntity.getWorld(),
+ (double) aBaseMetaTileEntity.getXCoord() + 0.5D,
+ (double) aBaseMetaTileEntity.getYCoord() + 1.5D,
+ (double) aBaseMetaTileEntity.getZCoord() + 0.5D,
+ equippedItemStack));
+ } else if (aPlayer instanceof EntityPlayerMP) {
+ ((EntityPlayerMP) aPlayer).sendContainerToPlayer(aPlayer.inventoryContainer);
+ }
+ aPlayer.inventory.decrStackSize(aPlayer.inventory.currentItem, 1);
+ } else {
+ aPlayer.inventory.setInventorySlotContents(aPlayer.inventory.currentItem, returnedItemStack);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ if (lavaTank.getFluid() != null) aNBT.setTag(
+ "mLava",
+ lavaTank.getFluid()
+ .writeToNBT(new NBTTagCompound()));
+ aNBT.setInteger("mCooledLava", this.mCooledLava);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ lavaTank.setFluid(FluidStack.loadFluidStackFromNBT(aNBT.getCompoundTag("mLava")));
+ this.mCooledLava = aNBT.getInteger("mCooledLava");
+ }
+
+ /**
+ * Pushes steam to Fluid inventory at the rear.
+ *
+ * @param aBaseMetaTileEntity The tile-entity instance of this Lava Boiler
+ */
+ @Override
+ protected void pushSteamToInventories(IGregTechTileEntity aBaseMetaTileEntity) {
+ if (mSteam == null || mSteam.amount == 0) return;
+ pushSteamToSide(
+ aBaseMetaTileEntity,
+ aBaseMetaTileEntity.getFrontFacing()
+ .getOpposite());
+ }
+
+ /**
+ * Drains Lava from Fluid inventory on top
+ *
+ * @param aBaseMetaTileEntity The tile-entity instance of this Lava Boiler
+ */
+ protected void drainLava(IGregTechTileEntity aBaseMetaTileEntity) {
+ final IFluidHandler upTank = aBaseMetaTileEntity.getITankContainerAtSide(ForgeDirection.UP);
+ if (upTank == null) return;
+ // Simulates drain of maximum lava amount up to 1000L that can fit the internal tank
+ final FluidStack drainableLavaStack = upTank.drain(
+ ForgeDirection.DOWN,
+ FluidRegistry.getFluidStack(
+ "lava",
+ Math.min(
+ this.lavaTank.getCapacity()
+ - (this.lavaTank.getFluid() != null ? this.lavaTank.getFluid().amount : 0),
+ 1000)),
+ false);
+ if (!GT_ModHandler.isLava(drainableLavaStack) || drainableLavaStack.amount <= 0) return;
+ // Performs actual drain up and fill internal tank
+ this.lavaTank.fill(upTank.drain(ForgeDirection.DOWN, drainableLavaStack, true), true);
+ }
+
+ /**
+ * Processes cooled Lava into Obsidian
+ *
+ * @return success | failure when cannot output
+ */
+ private boolean lavaToObsidian() {
+ if (this.mCooledLava >= 1000) {
+ if (getBaseMetaTileEntity().addStackToSlot(3, new ItemStack(Blocks.obsidian, 1))) {
+ this.mCooledLava -= 1000;
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Draws random flames and smoke particles in front of this Lava Boiler when it is active
+ *
+ * @param aBaseMetaTileEntity The entity that will handle the {@link Block#randomDisplayTick}
+ */
+ @SideOnly(Side.CLIENT)
+ @Override
+ public void onRandomDisplayTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ if (aBaseMetaTileEntity.isActive()) {
+
+ final ForgeDirection frontFacing = aBaseMetaTileEntity.getFrontFacing();
+
+ if ((frontFacing.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0
+ && aBaseMetaTileEntity.getCoverIDAtSide(frontFacing) == 0
+ && !aBaseMetaTileEntity.getOpacityAtSide(frontFacing)) {
+
+ final double oX = aBaseMetaTileEntity.getOffsetX(frontFacing, 1) + 8D / 16D;
+ final double oY = aBaseMetaTileEntity.getOffsetY(frontFacing, 1);
+ final double oZ = aBaseMetaTileEntity.getOffsetZ(frontFacing, 1) + 8D / 16D;
+ final double offset = -0.48D;
+ final double horizontal = XSTR_INSTANCE.nextFloat() * 10D / 16D - 5D / 16D;
+
+ final double x, y, z;
+
+ y = oY + XSTR_INSTANCE.nextFloat() * 6D / 16D;
+
+ switch (frontFacing) {
+ case WEST -> {
+ x = oX - offset;
+ z = oZ + horizontal;
+ }
+ case EAST -> {
+ x = oX + offset;
+ z = oZ + horizontal;
+ }
+ case NORTH -> {
+ x = oX + horizontal;
+ z = oZ - offset;
+ }
+ default -> { // case SOUTH:
+ x = oX + horizontal;
+ z = oZ + offset;
+ }
+ }
+
+ ParticleEventBuilder particleEventBuilder = (new ParticleEventBuilder()).setMotion(0D, 0D, 0D)
+ .setPosition(x, y, z)
+ .setWorld(getBaseMetaTileEntity().getWorld());
+ particleEventBuilder.setIdentifier(ParticleFX.SMOKE)
+ .run();
+ particleEventBuilder.setIdentifier(ParticleFX.FLAME)
+ .run();
+ }
+ }
+ }
+
+ @Override
+ public boolean isFluidInputAllowed(FluidStack aFluid) {
+ return GT_ModHandler.isWater(aFluid) || GT_ModHandler.isLava(aFluid);
+ }
+
+ @Override
+ public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (!aBaseMetaTileEntity.isServerSide()) return;
+ final FluidStack containedFluidStack = GT_Utility.getFluidForFilledItem(mInventory[getInputSlot()], true);
+ if (GT_ModHandler.isWater(containedFluidStack)) super.onPreTick(aBaseMetaTileEntity, aTick);
+ if (GT_ModHandler.isLava(containedFluidStack)
+ && lavaTank.fill(containedFluidStack, false) == containedFluidStack.amount
+ && aBaseMetaTileEntity.addStackToSlot(
+ getOutputSlot(),
+ GT_Utility.getContainerForFilledItem(mInventory[getInputSlot()], true),
+ 1)) {
+ lavaTank.fill(containedFluidStack, true);
+ aBaseMetaTileEntity.decrStackSize(getInputSlot(), 1);
+ }
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aTick % 20 == 0) drainLava(aBaseMetaTileEntity);
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ }
+
+ @Override
+ protected boolean isAutomatable() {
+ return true;
+ }
+
+ @Override
+ public void doSound(byte aIndex, double aX, double aY, double aZ) {
+ if (aIndex != GT_MetaTileEntity_Boiler.SOUND_EVENT_LET_OFF_EXCESS_STEAM) return;
+
+ final ForgeDirection rearDirection = getBaseMetaTileEntity().getFrontFacing()
+ .getOpposite();
+ GT_Utility.doSoundAtClient(
+ SoundResource.RANDOM_FIZZ,
+ 2,
+ 1.0F,
+ // Sound emitted from center of rear face (Steam Output)
+ aX + 0.5 * rearDirection.offsetX,
+ aY,
+ aZ + 0.5 * rearDirection.offsetZ);
+
+ new ParticleEventBuilder().setIdentifier(ParticleFX.CLOUD)
+ .setWorld(getBaseMetaTileEntity().getWorld())
+ // Particles emitted with a 1 block/s velocity toward rear
+ .setMotion(rearDirection.offsetX / 20D, 0D, rearDirection.offsetZ / 20D)
+ .<ParticleEventBuilder>times(
+ 8,
+ // Particles emitted from center of rear face (Steam Output)
+ x -> x.setPosition(aX + rearDirection.offsetX / 2D, aY, aZ + rearDirection.offsetZ / 2D)
+ .run());
+ }
+
+ @Override
+ protected void updateFuel(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (!lavaToObsidian()) return;
+ if (lavaTank.getFluid() == null || lavaTank.getFluid().amount <= 0) return;
+ final int amountToDrain = Math.min(lavaTank.getFluid().amount, 1000);
+ final FluidStack drainedLava = lavaTank.drain(amountToDrain, false);
+ if (drainedLava == null || drainedLava.amount == 0) return;
+ lavaTank.drain(amountToDrain, true);
+ this.mProcessingEnergy += drainedLava.amount * ENERGY_PER_LAVA;
+ }
+
+ @Override
+ public SteamVariant getSteamVariant() {
+ return SteamVariant.STEEL;
+ }
+
+ @Override
+ public int fill(FluidStack aFluid, boolean doFill) {
+ if (GT_ModHandler.isWater(aFluid)) return super.fill(aFluid, doFill);
+ if (GT_ModHandler.isLava(aFluid)) return lavaTank.fill(aFluid, doFill);
+ return 0;
+ }
+
+ @Override
+ public FluidTankInfo[] getTankInfo(ForgeDirection side) {
+ return new FluidTankInfo[] { super.getTankInfo(side)[0],
+ new FluidTankInfo(this.lavaTank.getFluid(), this.lavaTank.getCapacity()),
+ new FluidTankInfo(getDrainableStack(), getSteamCapacity()) };
+ }
+
+ @Override
+ protected IDrawable[] getAshSlotBackground() {
+ return new IDrawable[] { getGUITextureSet().getItemSlot(),
+ GT_UITextures.OVERLAY_SLOT_BLOCK_STEAM.get(getSteamVariant()) };
+ }
+
+ @Override
+ protected Widget createFuelSlot() {
+ return new FluidSlotWidget(lavaTank).setBackground(getGUITextureSet().getFluidSlot(), getOverlaySlotIn())
+ .setPos(115, 61);
+ }
+
+ static class LavaTank extends FluidTank {
+
+ public LavaTank(FluidStack stack, int capacity) {
+ super(stack, capacity);
+ }
+
+ @Override
+ public int fill(FluidStack resource, boolean doFill) {
+ return GT_ModHandler.isLava(resource) ? super.fill(resource, doFill) : 0;
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler_Solar.java b/src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler_Solar.java
new file mode 100644
index 0000000000..b10b44228d
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler_Solar.java
@@ -0,0 +1,311 @@
+package gregtech.common.tileentities.boilers;
+
+import static mcp.mobius.waila.api.SpecialChars.GOLD;
+import static mcp.mobius.waila.api.SpecialChars.RESET;
+
+import java.util.List;
+
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizons.modularui.api.widget.Widget;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+
+import gregtech.api.enums.Dyes;
+import gregtech.api.enums.SteamVariant;
+import gregtech.api.enums.Textures.BlockIcons;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_LanguageManager;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.config.machinestats.ConfigBronzeSolarBoiler;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public class GT_MetaTileEntity_Boiler_Solar extends GT_MetaTileEntity_Boiler {
+
+ public static final String LPS_FMT = "%s L/s";
+ private static final String localizedDescFormat = GT_LanguageManager.addStringLocalization(
+ "gt.blockmachines.boiler.solar.desc.format",
+ "Steam Power by the Sun%n" + "Produces %sL of Steam per second%n"
+ + "Calcifies over time, reducing Steam output to %sL/s%n"
+ + "Break and replace to descale");
+ protected int calcificationTicks = ConfigBronzeSolarBoiler.calcificationTicks;
+ protected int cooldownTicks = ConfigBronzeSolarBoiler.cooldownTicks;
+ protected int maxOutputPerSecond = ConfigBronzeSolarBoiler.maxOutputPerSecond;
+ protected int minOutputPerSecond = ConfigBronzeSolarBoiler.minOutputPerSecond;
+
+ protected final int basicTemperatureMod = 5; // Base Celsius gain or loss
+ private int mRunTimeTicks = 0;
+
+ public GT_MetaTileEntity_Boiler_Solar(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional, new String[0]);
+ }
+
+ public GT_MetaTileEntity_Boiler_Solar(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Boiler_Solar(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ public int getMaxOutputPerSecond() {
+ return maxOutputPerSecond;
+ }
+
+ @Override
+ public String[] getDescription() {
+ return String
+ .format(
+ localizedDescFormat,
+ GT_Utility.formatNumbers(getMaxOutputPerSecond()),
+ GT_Utility.formatNumbers(getMinOutputPerSecond()))
+ .split("\\R");
+ }
+
+ public int getMinOutputPerSecond() {
+ return minOutputPerSecond;
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ ITexture[][][] rTextures = new ITexture[4][17][];
+ for (int color = -1; color < 16; color++) {
+ int i = color + 1;
+ short[] colorModulation = Dyes.getModulation(color, Dyes._NULL.mRGBa);
+ rTextures[0][i] = new ITexture[] {
+ TextureFactory.of(BlockIcons.MACHINE_BRONZEBRICKS_BOTTOM, colorModulation) };
+ rTextures[1][i] = new ITexture[] { TextureFactory.of(BlockIcons.MACHINE_BRONZEBRICKS_TOP, colorModulation),
+ TextureFactory.of(BlockIcons.BOILER_SOLAR) };
+ rTextures[2][i] = new ITexture[] {
+ TextureFactory.of(BlockIcons.MACHINE_BRONZEBRICKS_SIDE, colorModulation) };
+ rTextures[3][i] = new ITexture[] { TextureFactory.of(BlockIcons.MACHINE_BRONZEBRICKS_SIDE, colorModulation),
+ TextureFactory.of(BlockIcons.OVERLAY_PIPE) };
+ }
+ return rTextures;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ final int i = colorIndex + 1;
+ if ((sideDirection.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0) { // Horizontal
+ if (sideDirection != facingDirection) return mTextures[2][i];
+ return mTextures[3][i];
+ }
+ return mTextures[sideDirection.ordinal()][i];
+ }
+
+ @Override
+ public int maxProgresstime() {
+ return 500;
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setInteger("mRunTime", mRunTimeTicks);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ mRunTimeTicks = aNBT.getInteger("mRunTime");
+ }
+
+ @Override
+ protected void produceSteam(int aAmount) {
+ super.produceSteam(aAmount);
+ // Disable calcification when using distilled water
+ if (mFluid.isFluidEqual(GT_ModHandler.getWater(1))) {
+ // produceSteam is getting called every 10 ticks
+ if (mRunTimeTicks >= 0 && mRunTimeTicks < (Integer.MAX_VALUE - 10)) mRunTimeTicks += 10;
+ else mRunTimeTicks = Integer.MAX_VALUE; // Prevent Integer overflow wrap
+ }
+ }
+
+ @Override
+ protected void pushSteamToInventories(IGregTechTileEntity aBaseMetaTileEntity) {
+ if (mSteam == null || mSteam.amount == 0) return;
+ pushSteamToSide(aBaseMetaTileEntity, aBaseMetaTileEntity.getFrontFacing());
+ }
+
+ @Override
+ protected int getPollution() {
+ return 0;
+ }
+
+ @Override
+ public int getProductionPerSecond() {
+ if (mTemperature < 100) {
+ return 0;
+ }
+ if (mRunTimeTicks > getMaxRuntimeTicks()) {
+ return getMinOutputPerSecond();
+ } else if (mRunTimeTicks > getCalcificationTicks()) {
+ /*
+ * When reaching calcification ticks; discount the proportion of run-time spent on calcification from the
+ * maximum output per second, and return this or the minimum output per second
+ */
+ return getMaxOutputPerSecond()
+ - getMaxOutputPerSecond() * (mRunTimeTicks - getCalcificationTicks()) / getCalcificationTicks();
+ } else {
+ return getMaxOutputPerSecond();
+ }
+ }
+
+ protected int getCalcificationTicks() {
+ return calcificationTicks;
+ }
+
+ protected int getCooldownTicks() {
+ return cooldownTicks;
+ }
+
+ protected int getMaxRuntimeTicks() {
+ // After which min output is reached.
+ return (getMaxOutputPerSecond() - getMinOutputPerSecond()) * getCalcificationTicks() / getMaxOutputPerSecond()
+ + getCalcificationTicks();
+ }
+
+ @Override
+ protected int getMaxTemperature() {
+ return 500;
+ }
+
+ @Override
+ protected int getEnergyConsumption() {
+ return basicTemperatureMod;
+ }
+
+ @Override
+ protected int getCooldownInterval() {
+ return getCooldownTicks() / basicTemperatureMod;
+ }
+
+ @Override
+ protected int getHeatUpAmount() {
+ return basicTemperatureMod;
+ }
+
+ @Override
+ protected void updateFuel(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ World world = aBaseMetaTileEntity.getWorld();
+ // Heat-up every 12s (240 ticks), has to be multiple of 20 ticks
+ if ((aTick % 240L != 0L) || (world.isThundering())) {
+ return;
+ }
+ if (!aBaseMetaTileEntity.getSkyAtSide(ForgeDirection.UP)) {
+ return;
+ }
+ boolean weatherClear = !world.isRaining() || aBaseMetaTileEntity.getBiome().rainfall == 0.0F;
+ if (!weatherClear && world.skylightSubtracted >= 4) {
+ return;
+ }
+ if (weatherClear) {
+ if (world.isDaytime()) {
+ mProcessingEnergy += 8 * basicTemperatureMod;
+ } else {
+ mProcessingEnergy += basicTemperatureMod;
+ }
+ } else {
+ mProcessingEnergy += basicTemperatureMod;
+ }
+ }
+
+ @Override
+ public SteamVariant getSteamVariant() {
+ return SteamVariant.BRONZE;
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return true;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ return String
+ .format(
+ "Heat Capacity: " + EnumChatFormatting.GREEN
+ + "%s %%"
+ + EnumChatFormatting.RESET
+ + " Hot time: "
+ + EnumChatFormatting.RED
+ + "%s s"
+ + EnumChatFormatting.RESET
+ + "%n"
+ + "Min output: "
+ + EnumChatFormatting.RED
+ + LPS_FMT
+ + EnumChatFormatting.RESET
+ + " Max output: "
+ + EnumChatFormatting.RED
+ + LPS_FMT
+ + EnumChatFormatting.RESET
+ + "%n"
+ + "Current Output: "
+ + EnumChatFormatting.YELLOW
+ + LPS_FMT
+ + EnumChatFormatting.RESET,
+ GT_Utility.formatNumbers(getHeatCapacityPercent()),
+ GT_Utility.formatNumbers(getHotTimeSeconds()),
+ GT_Utility.formatNumbers(getMinOutputPerSecond()),
+ GT_Utility.formatNumbers(getMaxOutputPerSecond()),
+ GT_Utility.formatNumbers(getProductionPerSecond()))
+ .split("\\R");
+ }
+
+ public int getHeatCapacityPercent() {
+ return mTemperature * 100 / maxProgresstime();
+ }
+
+ public int getHotTimeSeconds() {
+ return mRunTimeTicks / 20;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Boiler_Solar(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ protected Widget createFuelSlot() {
+ return null;
+ }
+
+ @Override
+ protected SlotWidget createAshSlot() {
+ return null;
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ final NBTTagCompound tag = accessor.getNBTData();
+ currentTip.add(
+ String.format(
+ (GOLD + "Solar Boiler Output: " + RESET + "%d/%d L/s"),
+ tag.getInteger("calcificationOutput"),
+ tag.getInteger("maxCalcificationOutput")));
+
+ super.getWailaBody(itemStack, currentTip, accessor, config);
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ tag.setInteger("calcificationOutput", (getProductionPerSecond()));
+ tag.setInteger("maxCalcificationOutput", (getMaxOutputPerSecond()));
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler_Solar_Steel.java b/src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler_Solar_Steel.java
new file mode 100644
index 0000000000..c537e388d1
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler_Solar_Steel.java
@@ -0,0 +1,71 @@
+package gregtech.common.tileentities.boilers;
+
+import gregtech.api.enums.Dyes;
+import gregtech.api.enums.SteamVariant;
+import gregtech.api.enums.Textures.BlockIcons;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.render.TextureFactory;
+import gregtech.common.config.machinestats.ConfigSteelSolarBoiler;
+
+public class GT_MetaTileEntity_Boiler_Solar_Steel extends GT_MetaTileEntity_Boiler_Solar {
+
+ public GT_MetaTileEntity_Boiler_Solar_Steel(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ initBoilerStats();
+ }
+
+ public GT_MetaTileEntity_Boiler_Solar_Steel(String aName, int aTier, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ initBoilerStats();
+ }
+
+ public GT_MetaTileEntity_Boiler_Solar_Steel(String aName, int aTier, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ initBoilerStats();
+ }
+
+ protected void initBoilerStats() {
+ calcificationTicks = ConfigSteelSolarBoiler.calcificationTicks;
+ cooldownTicks = ConfigSteelSolarBoiler.cooldownTicks;
+ maxOutputPerSecond = ConfigSteelSolarBoiler.maxOutputPerSecond;
+ minOutputPerSecond = ConfigSteelSolarBoiler.minOutputPerSecond;
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+
+ ITexture[][][] rTextures = new ITexture[4][17][];
+ for (int color = -1; color < 16; color++) {
+ int i = color + 1;
+ short[] colorModulation = Dyes.getModulation(color, Dyes._NULL.mRGBa);
+ rTextures[0][i] = new ITexture[] {
+ TextureFactory.of(BlockIcons.MACHINE_STEELBRICKS_BOTTOM, colorModulation) };
+ rTextures[1][i] = new ITexture[] { TextureFactory.of(BlockIcons.MACHINE_STEELBRICKS_TOP, colorModulation),
+ TextureFactory.of(BlockIcons.BOILER_SOLAR) };
+ rTextures[2][i] = new ITexture[] {
+ TextureFactory.of(BlockIcons.MACHINE_STEELBRICKS_SIDE, colorModulation) };
+ rTextures[3][i] = new ITexture[] { TextureFactory.of(BlockIcons.MACHINE_STEELBRICKS_SIDE, colorModulation),
+ TextureFactory.of(BlockIcons.OVERLAY_PIPE) };
+ }
+ return rTextures;
+ }
+
+ @Override
+ public int getCapacity() {
+ return 32000;
+ }
+
+ @Override
+ public SteamVariant getSteamVariant() {
+ return SteamVariant.STEEL;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Boiler_Solar_Steel(this.mName, this.mTier, this.mDescriptionArray, this.mTextures);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler_Steel.java b/src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler_Steel.java
new file mode 100644
index 0000000000..12374c3313
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/boilers/GT_MetaTileEntity_Boiler_Steel.java
@@ -0,0 +1,108 @@
+package gregtech.common.tileentities.boilers;
+
+import static gregtech.api.enums.Textures.BlockIcons.BOILER_FRONT;
+import static gregtech.api.enums.Textures.BlockIcons.BOILER_FRONT_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.BOILER_FRONT_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.BOILER_FRONT_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_STEELBRICKS_BOTTOM;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_STEELBRICKS_SIDE;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_STEELBRICKS_TOP;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE;
+
+import gregtech.GT_Mod;
+import gregtech.api.enums.SteamVariant;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.render.TextureFactory;
+
+public class GT_MetaTileEntity_Boiler_Steel extends GT_MetaTileEntity_Boiler_Bronze {
+
+ public GT_MetaTileEntity_Boiler_Steel(int aID, String aName, String aNameRegional) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ new String[] { "Faster than the Bronze Boiler", "Produces 300L of Steam per second",
+ "Causes " + GT_Mod.gregtechproxy.mPollutionHighPressureCoalBoilerPerSecond + " Pollution per second" });
+ }
+
+ public GT_MetaTileEntity_Boiler_Steel(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Boiler_Steel(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ ITexture[][][] rTextures = new ITexture[5][17][];
+ final ITexture[] texBottom = { TextureFactory.of(MACHINE_STEELBRICKS_BOTTOM) },
+ texTop = { TextureFactory.of(MACHINE_STEELBRICKS_TOP), TextureFactory.of(OVERLAY_PIPE) },
+ texSide = { TextureFactory.of(MACHINE_STEELBRICKS_SIDE), TextureFactory.of(OVERLAY_PIPE) },
+ texFront = { TextureFactory.of(MACHINE_STEELBRICKS_SIDE), TextureFactory.of(BOILER_FRONT),
+ TextureFactory.builder()
+ .addIcon(BOILER_FRONT_GLOW)
+ .glow()
+ .build() },
+ texFrontActive = { TextureFactory.of(MACHINE_STEELBRICKS_SIDE), TextureFactory.of(BOILER_FRONT_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(BOILER_FRONT_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ for (int i = 0; i < 17; i++) {
+ rTextures[0][i] = texBottom;
+ rTextures[1][i] = texTop;
+ rTextures[2][i] = texSide;
+ rTextures[3][i] = texFront;
+ rTextures[4][i] = texFrontActive;
+ }
+ return rTextures;
+ }
+
+ @Override
+ public int maxProgresstime() {
+ return 1000;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Boiler_Steel(this.mName, this.mTier, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ protected int getPollution() {
+ return GT_Mod.gregtechproxy.mPollutionHighPressureCoalBoilerPerSecond;
+ }
+
+ @Override
+ public int getCapacity() {
+ return 32000;
+ }
+
+ @Override
+ protected int getProductionPerSecond() {
+ return 300;
+ }
+
+ @Override
+ protected int getMaxTemperature() {
+ return 1000;
+ }
+
+ @Override
+ protected int getEnergyConsumption() {
+ return 2;
+ }
+
+ @Override
+ protected int getCooldownInterval() {
+ return 40;
+ }
+
+ @Override
+ public SteamVariant getSteamVariant() {
+ return SteamVariant.STEEL;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/casings/functional/Conveyor.java b/src/main/java/gregtech/common/tileentities/casings/functional/Conveyor.java
new file mode 100644
index 0000000000..31dea4cf38
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/casings/functional/Conveyor.java
@@ -0,0 +1,16 @@
+package gregtech.common.tileentities.casings.functional;
+
+import gregtech.api.multitileentity.multiblock.casing.FunctionalCasing;
+
+public class Conveyor extends FunctionalCasing {
+
+ @Override
+ public String getTileEntityName() {
+ return "gt.multitileentity.multiblock.functional.conveyor";
+ }
+
+ @Override
+ public float getPartModifier() {
+ return 1.1f;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/casings/functional/Emitter.java b/src/main/java/gregtech/common/tileentities/casings/functional/Emitter.java
new file mode 100644
index 0000000000..cce660ace4
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/casings/functional/Emitter.java
@@ -0,0 +1,16 @@
+package gregtech.common.tileentities.casings.functional;
+
+import gregtech.api.multitileentity.multiblock.casing.FunctionalCasing;
+
+public class Emitter extends FunctionalCasing {
+
+ @Override
+ public String getTileEntityName() {
+ return "gt.multitileentity.multiblock.functional.emitter";
+ }
+
+ @Override
+ public float getPartModifier() {
+ return 1.3f;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/casings/functional/FieldGenerator.java b/src/main/java/gregtech/common/tileentities/casings/functional/FieldGenerator.java
new file mode 100644
index 0000000000..3787d45af8
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/casings/functional/FieldGenerator.java
@@ -0,0 +1,16 @@
+package gregtech.common.tileentities.casings.functional;
+
+import gregtech.api.multitileentity.multiblock.casing.FunctionalCasing;
+
+public class FieldGenerator extends FunctionalCasing {
+
+ @Override
+ public String getTileEntityName() {
+ return "gt.multitileentity.multiblock.functional.field.generator";
+ }
+
+ @Override
+ public float getPartModifier() {
+ return 2f;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/casings/functional/Motor.java b/src/main/java/gregtech/common/tileentities/casings/functional/Motor.java
new file mode 100644
index 0000000000..cddb293cc2
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/casings/functional/Motor.java
@@ -0,0 +1,16 @@
+package gregtech.common.tileentities.casings.functional;
+
+import gregtech.api.multitileentity.multiblock.casing.FunctionalCasing;
+
+public class Motor extends FunctionalCasing {
+
+ @Override
+ public String getTileEntityName() {
+ return "gt.multitileentity.multiblock.functional.motor";
+ }
+
+ @Override
+ public float getPartModifier() {
+ return 1;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/casings/functional/Piston.java b/src/main/java/gregtech/common/tileentities/casings/functional/Piston.java
new file mode 100644
index 0000000000..2255fbf8a2
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/casings/functional/Piston.java
@@ -0,0 +1,16 @@
+package gregtech.common.tileentities.casings.functional;
+
+import gregtech.api.multitileentity.multiblock.casing.FunctionalCasing;
+
+public class Piston extends FunctionalCasing {
+
+ @Override
+ public String getTileEntityName() {
+ return "gt.multitileentity.multiblock.functional.piston";
+ }
+
+ @Override
+ public float getPartModifier() {
+ return 1.1f;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/casings/functional/Pump.java b/src/main/java/gregtech/common/tileentities/casings/functional/Pump.java
new file mode 100644
index 0000000000..3acddbe9dc
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/casings/functional/Pump.java
@@ -0,0 +1,16 @@
+package gregtech.common.tileentities.casings.functional;
+
+import gregtech.api.multitileentity.multiblock.casing.FunctionalCasing;
+
+public class Pump extends FunctionalCasing {
+
+ @Override
+ public String getTileEntityName() {
+ return "gt.multitileentity.multiblock.functional.pump";
+ }
+
+ @Override
+ public float getPartModifier() {
+ return 1.1f;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/casings/functional/RobotArm.java b/src/main/java/gregtech/common/tileentities/casings/functional/RobotArm.java
new file mode 100644
index 0000000000..2273705397
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/casings/functional/RobotArm.java
@@ -0,0 +1,16 @@
+package gregtech.common.tileentities.casings.functional;
+
+import gregtech.api.multitileentity.multiblock.casing.FunctionalCasing;
+
+public class RobotArm extends FunctionalCasing {
+
+ @Override
+ public String getTileEntityName() {
+ return "gt.multitileentity.multiblock.functional.robot.arm";
+ }
+
+ @Override
+ public float getPartModifier() {
+ return 1.5f;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/casings/functional/Sensor.java b/src/main/java/gregtech/common/tileentities/casings/functional/Sensor.java
new file mode 100644
index 0000000000..e88060e902
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/casings/functional/Sensor.java
@@ -0,0 +1,16 @@
+package gregtech.common.tileentities.casings.functional;
+
+import gregtech.api.multitileentity.multiblock.casing.FunctionalCasing;
+
+public class Sensor extends FunctionalCasing {
+
+ @Override
+ public String getTileEntityName() {
+ return "gt.multitileentity.multiblock.functional.sensor";
+ }
+
+ @Override
+ public float getPartModifier() {
+ return 1.3f;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/casings/upgrade/Ampere.java b/src/main/java/gregtech/common/tileentities/casings/upgrade/Ampere.java
new file mode 100644
index 0000000000..b0f6afdc3a
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/casings/upgrade/Ampere.java
@@ -0,0 +1,47 @@
+package gregtech.common.tileentities.casings.upgrade;
+
+import java.util.List;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+
+import gregtech.api.enums.GT_Values;
+import gregtech.api.multitileentity.interfaces.IMultiBlockController;
+import gregtech.api.multitileentity.multiblock.casing.UpgradeCasing;
+import gregtech.api.util.GT_Utility;
+
+public class Ampere extends UpgradeCasing {
+
+ private long amperage;
+
+ @Override
+ public String getTileEntityName() {
+ return "gt.multitileentity.multiblock.functional.amperage";
+ }
+
+ @Override
+ public void readMultiTileNBT(NBTTagCompound aNBT) {
+ super.readMultiTileNBT(aNBT);
+ amperage = aNBT.getInteger(GT_Values.NBT.UPGRADE_AMPERAGE);
+ }
+
+ @Override
+ protected void customWork(IMultiBlockController target) {
+ target.setMaxAmperage(amperage);
+ }
+
+ @Override
+ public boolean onBlockBroken() {
+ final IMultiBlockController controller = getTarget(false);
+ if (controller != null) {
+ controller.setMaxAmperage(2);
+ }
+ return super.onBlockBroken();
+ }
+
+ @Override
+ public void addToolTips(List<String> list, ItemStack stack, boolean f3_h) {
+ super.addToolTips(list, stack, f3_h);
+ list.add("Increases allowed amperage to " + GT_Utility.formatNumbers(amperage));
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/casings/upgrade/Cleanroom.java b/src/main/java/gregtech/common/tileentities/casings/upgrade/Cleanroom.java
new file mode 100644
index 0000000000..224b74a993
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/casings/upgrade/Cleanroom.java
@@ -0,0 +1,26 @@
+package gregtech.common.tileentities.casings.upgrade;
+
+import gregtech.api.multitileentity.interfaces.IMultiBlockController;
+import gregtech.api.multitileentity.multiblock.casing.UpgradeCasing;
+
+public class Cleanroom extends UpgradeCasing {
+
+ @Override
+ public String getTileEntityName() {
+ return "gt.multitileentity.multiblock.functional.cleanroom";
+ }
+
+ @Override
+ protected void customWork(IMultiBlockController target) {
+ target.setCleanroom(true);
+ }
+
+ @Override
+ public boolean onBlockBroken() {
+ final IMultiBlockController controller = getTarget(false);
+ if (controller != null) {
+ controller.setCleanroom(false);
+ }
+ return super.onBlockBroken();
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/casings/upgrade/Heater.java b/src/main/java/gregtech/common/tileentities/casings/upgrade/Heater.java
new file mode 100644
index 0000000000..6d8bd81d0f
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/casings/upgrade/Heater.java
@@ -0,0 +1,21 @@
+package gregtech.common.tileentities.casings.upgrade;
+
+import gregtech.api.multitileentity.interfaces.IMultiBlockController;
+import gregtech.api.multitileentity.interfaces.UpgradableModularMuTE;
+import gregtech.api.multitileentity.multiblock.casing.UpgradeCasing;
+import gregtech.api.util.GT_StructureUtilityMuTE.UpgradeCasings;
+
+public class Heater extends UpgradeCasing {
+
+ @Override
+ public String getTileEntityName() {
+ return "gt.multitileentity.multiblock.modular.heater";
+ }
+
+ @Override
+ protected void customWork(IMultiBlockController target) {
+ if (target instanceof UpgradableModularMuTE upgradable) {
+ upgradable.increaseMucCount(UpgradeCasings.Heater, this.tier);
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/casings/upgrade/Insulator.java b/src/main/java/gregtech/common/tileentities/casings/upgrade/Insulator.java
new file mode 100644
index 0000000000..6cb0b630d6
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/casings/upgrade/Insulator.java
@@ -0,0 +1,22 @@
+package gregtech.common.tileentities.casings.upgrade;
+
+import gregtech.api.multitileentity.interfaces.IMultiBlockController;
+import gregtech.api.multitileentity.interfaces.UpgradableModularMuTE;
+import gregtech.api.multitileentity.multiblock.casing.UpgradeCasing;
+import gregtech.api.util.GT_StructureUtilityMuTE.UpgradeCasings;
+
+public class Insulator extends UpgradeCasing {
+
+ @Override
+ public String getTileEntityName() {
+ return "gt.multitileentity.multiblock.modular.insulator";
+ }
+
+ @Override
+ protected void customWork(IMultiBlockController target) {
+ if (target instanceof UpgradableModularMuTE upgradable) {
+ upgradable.increaseMucCount(UpgradeCasings.Insulator, this.tier);
+ }
+ }
+
+}
diff --git a/src/main/java/gregtech/common/tileentities/casings/upgrade/Inventory.java b/src/main/java/gregtech/common/tileentities/casings/upgrade/Inventory.java
new file mode 100644
index 0000000000..7e51b3441d
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/casings/upgrade/Inventory.java
@@ -0,0 +1,123 @@
+package gregtech.common.tileentities.casings.upgrade;
+
+import java.util.List;
+import java.util.UUID;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizons.modularui.api.screen.ModularWindow.Builder;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.textfield.TextFieldWidget;
+
+import gregtech.api.enums.GT_Values.NBT;
+import gregtech.api.enums.InventoryType;
+import gregtech.api.multitileentity.interfaces.IMultiBlockController;
+import gregtech.api.multitileentity.multiblock.casing.UpgradeCasing;
+
+public class Inventory extends UpgradeCasing {
+
+ public UUID inventoryID;
+
+ private String inventoryName = "inventory";
+ private int inventorySize;
+ private InventoryType type = InventoryType.Both;
+
+ public String getCustomInventoryName() {
+ return inventoryName;
+ }
+
+ public String getInventoryID() {
+ return inventoryID != null ? inventoryID.toString() : "";
+ }
+
+ public void setInventoryName(String aInventoryName) {
+ inventoryName = aInventoryName;
+ }
+
+ public InventoryType getType() {
+ return type;
+ }
+
+ @Override
+ protected void customWork(IMultiBlockController target) {
+ int invSize = inventorySize;
+ if (type == InventoryType.Both) {
+ invSize /= 2;
+ }
+ target.registerItemInventory(invSize, tier, type, true);
+ if (isServerSide()) {
+ issueClientUpdate();
+ }
+ }
+
+ @Override
+ public String getTileEntityName() {
+ return "gt.multitileentity.multiblock.inventory";
+ }
+
+ @Override
+ public void readMultiTileNBT(NBTTagCompound nbt) {
+ super.readMultiTileNBT(nbt);
+ if (nbt.hasKey(NBT.UPGRADE_INVENTORY_NAME)) {
+ inventoryName = nbt.getString(NBT.UPGRADE_INVENTORY_NAME);
+ } else {
+ inventoryName = "inventory";
+ }
+ inventorySize = nbt.getInteger(NBT.UPGRADE_INVENTORY_SIZE);
+ }
+
+ @Override
+ public void writeMultiTileNBT(NBTTagCompound nbt) {
+ super.writeMultiTileNBT(nbt);
+ if (inventoryID != null) nbt.setString(NBT.UPGRADE_INVENTORY_UUID, inventoryID.toString());
+ if (inventoryName != null) nbt.setString(NBT.UPGRADE_INVENTORY_NAME, inventoryName);
+ }
+
+ @Override
+ public boolean onBlockBroken() {
+ final IMultiBlockController controller = getTarget(false);
+ if (controller != null && inventoryID != null) {
+ controller.unregisterItemInventory(inventoryID, type);
+ }
+ return super.onBlockBroken();
+ }
+
+ @Override
+ public boolean hasGui(ForgeDirection side) {
+ return true;
+ }
+
+ @Override
+ public void addUIWidgets(Builder builder, UIBuildContext buildContext) {
+ builder.widget(
+ new TextFieldWidget().setGetter(() -> inventoryName)
+ .setSetter((val) -> {
+ inventoryName = val;
+ final IMultiBlockController controller = getTarget(false);
+ if (controller != null && inventoryID != null) {
+ controller.changeItemInventoryDisplayName(inventoryID, inventoryName, type);
+ }
+ })
+ .setSize(100, 25)
+ .setPos(50, 30));
+ }
+
+ @Override
+ protected boolean canOpenControllerGui() {
+ return false;
+ }
+
+ @Override
+ public void addToolTips(List<String> list, ItemStack stack, boolean f3_h) {
+ super.addToolTips(list, stack, f3_h);
+ list.add("Adds another item inventory");
+ list.add("Inventory size: " + inventorySize);
+ list.add("Inventory Type: " + type);
+ }
+
+ public void setInventoryId(String inventoryID) {
+ this.inventoryID = UUID.fromString(inventoryID);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/casings/upgrade/Laser.java b/src/main/java/gregtech/common/tileentities/casings/upgrade/Laser.java
new file mode 100644
index 0000000000..b3ac810066
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/casings/upgrade/Laser.java
@@ -0,0 +1,26 @@
+package gregtech.common.tileentities.casings.upgrade;
+
+import gregtech.api.multitileentity.interfaces.IMultiBlockController;
+import gregtech.api.multitileentity.multiblock.casing.UpgradeCasing;
+
+public class Laser extends UpgradeCasing {
+
+ @Override
+ public String getTileEntityName() {
+ return "gt.multitileentity.multiblock.functional.laser";
+ }
+
+ @Override
+ protected void customWork(IMultiBlockController target) {
+ target.setLaserSupport(true);
+ }
+
+ @Override
+ public boolean onBlockBroken() {
+ final IMultiBlockController controller = getTarget(false);
+ if (controller != null) {
+ controller.setLaserSupport(false);
+ }
+ return super.onBlockBroken();
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/casings/upgrade/Tank.java b/src/main/java/gregtech/common/tileentities/casings/upgrade/Tank.java
new file mode 100644
index 0000000000..ebc84be38a
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/casings/upgrade/Tank.java
@@ -0,0 +1,61 @@
+package gregtech.common.tileentities.casings.upgrade;
+
+import java.util.List;
+import java.util.UUID;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+
+import gregtech.api.enums.GT_Values;
+import gregtech.api.multitileentity.interfaces.IMultiBlockController;
+import gregtech.api.multitileentity.multiblock.casing.UpgradeCasing;
+import gregtech.api.util.GT_Utility;
+
+public class Tank extends UpgradeCasing {
+
+ private int tankCount;
+ private int tankCapacity;
+ public UUID tankID;
+ public static final int INPUT = 0;
+ public static final int OUTPUT = 1;
+ public static final int BOTH = 2;
+ private String tankName = "tank";
+ private int type = BOTH;
+
+ @Override
+ protected void customWork(IMultiBlockController aTarget) {
+
+ }
+
+ @Override
+ public String getTileEntityName() {
+ return "gt.multitileentity.multiblock.tank";
+ }
+
+ public String getCustomTankName() {
+ return tankName;
+ }
+
+ public String getTankID() {
+ return tankID.toString();
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ @Override
+ public void readMultiTileNBT(NBTTagCompound aNBT) {
+ super.readMultiTileNBT(aNBT);
+ tankCount = aNBT.getInteger(GT_Values.NBT.UPGRADE_TANK_COUNT);
+ tankCapacity = aNBT.getInteger(GT_Values.NBT.UPGRADE_TANK_CAPACITY);
+ }
+
+ @Override
+ public void addToolTips(List<String> list, ItemStack stack, boolean f3_h) {
+ super.addToolTips(list, stack, f3_h);
+ list.add("Adds another tank inventory");
+ list.add("Number of tanks: " + tankCount);
+ list.add("Tank capacity: " + GT_Utility.formatNumbers(tankCapacity) + " L");
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/casings/upgrade/Wireless.java b/src/main/java/gregtech/common/tileentities/casings/upgrade/Wireless.java
new file mode 100644
index 0000000000..5603f0f8e8
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/casings/upgrade/Wireless.java
@@ -0,0 +1,26 @@
+package gregtech.common.tileentities.casings.upgrade;
+
+import gregtech.api.multitileentity.interfaces.IMultiBlockController;
+import gregtech.api.multitileentity.multiblock.casing.UpgradeCasing;
+
+public class Wireless extends UpgradeCasing {
+
+ @Override
+ public String getTileEntityName() {
+ return "gt.multitileentity.multiblock.functional.wireless";
+ }
+
+ @Override
+ protected void customWork(IMultiBlockController target) {
+ target.setWirelessSupport(true);
+ }
+
+ @Override
+ public boolean onBlockBroken() {
+ final IMultiBlockController controller = getTarget(false);
+ if (controller != null) {
+ controller.setWirelessSupport(false);
+ }
+ return super.onBlockBroken();
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/debug/GT_MetaTileEntity_AdvDebugStructureWriter.java b/src/main/java/gregtech/common/tileentities/debug/GT_MetaTileEntity_AdvDebugStructureWriter.java
new file mode 100644
index 0000000000..e61ae456f0
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/debug/GT_MetaTileEntity_AdvDebugStructureWriter.java
@@ -0,0 +1,437 @@
+package gregtech.common.tileentities.debug;
+
+import static gregtech.GT_Mod.GT_FML_LOGGER;
+import static net.minecraft.util.StatCollector.translateToLocal;
+
+import java.util.HashMap;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.entity.EntityPlayerSP;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.ChatComponentTranslation;
+import net.minecraftforge.client.event.RenderWorldLastEvent;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import org.lwjgl.opengl.GL11;
+
+import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
+import com.gtnewhorizon.structurelib.structure.StructureUtility;
+import com.gtnewhorizon.structurelib.util.Vec3Impl;
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.CycleButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.MultiChildWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+
+import cpw.mods.fml.common.eventhandler.SubscribeEvent;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import gregtech.api.enums.Textures;
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.gui.modularui.GUITextureSet;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.modularui.IAddGregtechLogo;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_TieredMachineBlock;
+import gregtech.api.render.TextureFactory;
+
+public class GT_MetaTileEntity_AdvDebugStructureWriter extends GT_MetaTileEntity_TieredMachineBlock
+ implements IAddGregtechLogo, IAddUIWidgets {
+
+ private static final HashMap<GT_MetaTileEntity_AdvDebugStructureWriter, BoundHighlighter> bondingBoxes = new HashMap<>(
+ 1);
+ private final BoundHighlighter boundingBox = new BoundHighlighter();
+ private final short[] numbers = new short[6];
+ private boolean transpose = false;
+ private boolean showHighlightBox = true;
+ private String[] result = new String[] { "Undefined" };
+
+ public GT_MetaTileEntity_AdvDebugStructureWriter(int aID, String aName, String aNameRegional, int aTier) {
+ super(aID, aName, aNameRegional, aTier, 0, "");
+ }
+
+ public GT_MetaTileEntity_AdvDebugStructureWriter(String aName, int aTier, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, 0, aDescription, aTextures);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_AdvDebugStructureWriter(mName, mTier, "", mTextures);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ return new ITexture[] {
+ Textures.BlockIcons.MACHINE_CASINGS[mTier][colorIndex + 1], sideDirection != facingDirection
+ ? TextureFactory.of(
+ TextureFactory.builder()
+ .addIcon(Textures.BlockIcons.OVERLAY_TELEPORTER_ACTIVE)
+ .glow()
+ .build())
+ : TextureFactory.of(
+ TextureFactory.builder()
+ .addIcon(Textures.BlockIcons.STRUCTURE_MARK)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ return null;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity iGregTechTileEntity, int i, ForgeDirection b,
+ ItemStack itemStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity iGregTechTileEntity, int i, ForgeDirection b,
+ ItemStack itemStack) {
+ return false;
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ for (int i = 0; i < numbers.length; i++) {
+ aNBT.setShort("eData" + i, numbers[i]);
+ }
+ aNBT.setBoolean("Transpose", transpose);
+ aNBT.setBoolean("HighlightBox", showHighlightBox);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ for (int i = 0; i < numbers.length; i++) {
+ numbers[i] = aNBT.getShort("eData" + i);
+ }
+ transpose = aNBT.getBoolean("Transpose");
+ showHighlightBox = aNBT.getBoolean("HighlightBox");
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return false;
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ super.onFirstTick(aBaseMetaTileEntity);
+ bondingBoxes.put(this, boundingBox);
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aBaseMetaTileEntity.isServerSide()) {
+ ExtendedFacing writerFacing = ExtendedFacing.of(aBaseMetaTileEntity.getFrontFacing());
+ double[] abc = new double[3];
+ double[] xyz = new double[3];
+ boundingBox.dim = aBaseMetaTileEntity.getWorld().provider.dimensionId;
+ boundingBox.showHighlightBox = showHighlightBox;
+ abc[0] = -numbers[0] - 0.5;
+ abc[1] = -numbers[1] - 0.5;
+ abc[2] = -numbers[2] - 0.5;
+ writerFacing.getWorldOffset(abc, xyz);
+ boundingBox.pos1 = new Vec3Impl(
+ aBaseMetaTileEntity.getXCoord() + (int) (xyz[0] + 0.5),
+ aBaseMetaTileEntity.getYCoord() + (int) (xyz[1] + 0.5),
+ aBaseMetaTileEntity.getZCoord() + (int) (xyz[2] + 0.5));
+ abc[0] = -numbers[0] + numbers[3] - 0.5;
+ abc[1] = -numbers[1] + numbers[4] - 0.5;
+ abc[2] = -numbers[2] + numbers[5] - 0.5;
+ writerFacing.getWorldOffset(abc, xyz);
+ boundingBox.pos2 = new Vec3Impl(
+ aBaseMetaTileEntity.getXCoord() + (int) (xyz[0] + 0.5),
+ aBaseMetaTileEntity.getYCoord() + (int) (xyz[1] + 0.5),
+ aBaseMetaTileEntity.getZCoord() + (int) (xyz[2] + 0.5));
+ }
+ }
+
+ @Override
+ public void onRemoval() {
+ bondingBoxes.remove(this);
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ IGregTechTileEntity aBaseMetaTileEntity = getBaseMetaTileEntity();
+ printStructure(aPlayer);
+ aBaseMetaTileEntity.disableWorking();
+ }
+
+ public void printStructure(EntityPlayer aPlayer) {
+ IGregTechTileEntity aBaseMetaTileEntity = getBaseMetaTileEntity();
+ String pseudoJavaCode = StructureUtility.getPseudoJavaCode(
+ aBaseMetaTileEntity.getWorld(),
+ ExtendedFacing.of(aBaseMetaTileEntity.getFrontFacing()),
+ aBaseMetaTileEntity.getXCoord(),
+ aBaseMetaTileEntity.getYCoord(),
+ aBaseMetaTileEntity.getZCoord(),
+ numbers[0],
+ numbers[1],
+ numbers[2],
+ te -> te.getClass()
+ .getCanonicalName(),
+ numbers[3],
+ numbers[4],
+ numbers[5],
+ transpose);
+ GT_FML_LOGGER.info(pseudoJavaCode);
+ result = pseudoJavaCode.split("\\n");
+ aPlayer.addChatMessage(
+ new ChatComponentTranslation(translateToLocal("GT5U.machines.advdebugstructurewriter.printed")));
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isElectric() {
+ return false;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public String[] getDescription() {
+ return new String[] { translateToLocal("GT5U.machines.advdebugstructurewriter.tooltip"), // Scans Blocks Around
+ translateToLocal("GT5U.machines.advdebugstructurewriter.tooltip.1"), // Prints Multiblock NonTE
+ // structure check code
+ translateToLocal("GT5U.machines.advdebugstructurewriter.tooltip.2") // ABC axes aligned to machine front
+ };
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return true;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ return result;
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ builder.widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_SCREEN_BLACK)
+ .setSize(90, 112)
+ .setPos(43, 4))
+ .widget(new ButtonWidget().setOnClick((clickData, widget) -> {
+ if (getBaseMetaTileEntity().isServerSide()) {
+ printStructure(
+ widget.getContext()
+ .getPlayer());
+ }
+ })
+ .setBackground(GT_UITextures.BUTTON_STANDARD, GT_UITextures.OVERLAY_BUTTON_PRINT)
+ .setSize(18, 18)
+ .setPos(11, 128)
+ .addTooltip(translateToLocal("GT5U.machines.advdebugstructurewriter.gui.print.tooltip")))
+ .widget(
+ new CycleButtonWidget().setToggle(() -> transpose, aBoolean -> transpose = aBoolean)
+ .setVariableBackground(GT_UITextures.BUTTON_STANDARD_TOGGLE)
+ .setStaticTexture(GT_UITextures.OVERLAY_BUTTON_TRANSPOSE)
+ .setSize(18, 18)
+ .setPos(32, 128)
+ .addTooltip(translateToLocal("GT5U.machines.advdebugstructurewriter.gui.transpose.tooltip")))
+ .widget(
+ new CycleButtonWidget().setToggle(() -> showHighlightBox, aBoolean -> showHighlightBox = aBoolean)
+ .setVariableBackground(GT_UITextures.BUTTON_STANDARD_TOGGLE)
+ .setStaticTexture(GT_UITextures.OVERLAY_BUTTON_BOUNDING_BOX)
+ .setSize(18, 18)
+ .setPos(53, 128)
+ .addTooltip(translateToLocal("GT5U.machines.advdebugstructurewriter.gui.highlight.tooltip")))
+ .widget(
+ new MultiChildWidget()
+ .addChild(
+ new TextWidget(translateToLocal("GT5U.machines.advdebugstructurewriter.gui.origin"))
+ .setDefaultColor(0xf0f0ff)
+ .setPos(0, 0))
+ .addChild(
+ TextWidget.dynamicString(() -> "A: " + numbers[0])
+ .setDefaultColor(0xf0f0ff)
+ .setPos(0, 10))
+ .addChild(
+ TextWidget.dynamicString(() -> "B: " + numbers[1])
+ .setDefaultColor(0xf0f0ff)
+ .setPos(0, 18))
+ .addChild(
+ TextWidget.dynamicString(() -> "C: " + numbers[2])
+ .setDefaultColor(0xf0f0ff)
+ .setPos(0, 26))
+ .addChild(
+ new TextWidget(translateToLocal("GT5U.machines.advdebugstructurewriter.gui.size"))
+ .setDefaultColor(0xf0f0ff)
+ .setPos(0, 52))
+ .addChild(
+ TextWidget.dynamicString(() -> "A: " + numbers[3])
+ .setDefaultColor(0xf0f0ff)
+ .setPos(0, 62))
+ .addChild(
+ TextWidget.dynamicString(() -> "B: " + numbers[4])
+ .setDefaultColor(0xf0f0ff)
+ .setPos(0, 70))
+ .addChild(
+ TextWidget.dynamicString(() -> "C: " + numbers[5])
+ .setDefaultColor(0xf0f0ff)
+ .setPos(0, 78))
+ .setPos(46, 8));
+ addChangeNumberButtons(builder, GT_UITextures.OVERLAY_BUTTON_MINUS_LARGE, -512, -64, 7);
+ addChangeNumberButtons(builder, GT_UITextures.OVERLAY_BUTTON_MINUS_SMALL, -16, -1, 25);
+ addChangeNumberButtons(builder, GT_UITextures.OVERLAY_BUTTON_PLUS_SMALL, 16, 1, 133);
+ addChangeNumberButtons(builder, GT_UITextures.OVERLAY_BUTTON_PLUS_LARGE, 512, 64, 151);
+ }
+
+ private void addChangeNumberButtons(ModularWindow.Builder builder, IDrawable overlay, int addNumberShift,
+ int addNumber, int xPos) {
+ int[] yPos = new int[] { 4, 22, 40, 62, 80, 98 };
+ for (int i = 0; i < yPos.length; i++) {
+ final int index = i; // needed for lambda
+ builder.widget(new ButtonWidget().setOnClick((clickData, widget) -> {
+ numbers[index] += clickData.shift ? addNumberShift : addNumber;
+ if (index >= 3) {
+ numbers[index] = (short) Math.max(numbers[index], 0);
+ }
+ })
+ .setBackground(GT_UITextures.BUTTON_STANDARD, overlay)
+ .setSize(18, 18)
+ .setPos(xPos, yPos[index]));
+ }
+ }
+
+ @Override
+ public GUITextureSet getGUITextureSet() {
+ return new GUITextureSet().setGregTechLogo(GT_UITextures.PICTURE_GT_LOGO_17x17_TRANSPARENT_GRAY);
+ }
+
+ @Override
+ public void addGregTechLogo(ModularWindow.Builder builder) {
+ builder.widget(
+ new DrawableWidget().setDrawable(getGUITextureSet().getGregTechLogo())
+ .setSize(17, 17)
+ .setPos(113, 96));
+ }
+
+ @Override
+ public boolean doesBindPlayerInventory() {
+ return false;
+ }
+
+ public static class ForgeEventHandler {
+
+ public ForgeEventHandler() {
+ MinecraftForge.EVENT_BUS.register(this);
+ }
+
+ @SuppressWarnings("unused")
+ @SideOnly(Side.CLIENT)
+ @SubscribeEvent
+ public void onRenderWorldLast(RenderWorldLastEvent e) {
+ for (BoundHighlighter boundingBox : bondingBoxes.values()) {
+ boundingBox.renderHighlightedBlock(e);
+ }
+ }
+ }
+
+ private static class BoundHighlighter {
+
+ public Vec3Impl pos1;
+ public Vec3Impl pos2;
+ public boolean showHighlightBox;
+ public int dim;
+
+ @SideOnly(Side.CLIENT)
+ private void renderHighlightedBlock(RenderWorldLastEvent event) {
+ if (pos1 == null || pos2 == null || !showHighlightBox) {
+ return;
+ }
+ Minecraft mc = Minecraft.getMinecraft();
+ int dimension = mc.theWorld.provider.dimensionId;
+
+ if (dimension != dim) {
+ pos1 = null;
+ pos2 = null;
+ return;
+ }
+
+ EntityPlayerSP p = mc.thePlayer;
+ double doubleX = p.lastTickPosX + (p.posX - p.lastTickPosX) * event.partialTicks;
+ double doubleY = p.lastTickPosY + (p.posY - p.lastTickPosY) * event.partialTicks;
+ double doubleZ = p.lastTickPosZ + (p.posZ - p.lastTickPosZ) * event.partialTicks;
+
+ GL11.glPushMatrix();
+ GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
+ GL11.glLineWidth(3);
+ GL11.glTranslated(-doubleX, -doubleY, -doubleZ);
+
+ GL11.glDisable(GL11.GL_DEPTH_TEST);
+ GL11.glDisable(GL11.GL_TEXTURE_2D);
+
+ GL11.glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
+ renderHighLightedArenaOutline(pos1.get0(), pos1.get1(), pos1.get2(), pos2.get0(), pos2.get1(), pos2.get2());
+
+ GL11.glPopAttrib();
+ GL11.glPopMatrix();
+ }
+
+ @SideOnly(Side.CLIENT)
+ static void renderHighLightedArenaOutline(double x1, double y1, double z1, double x2, double y2, double z2) {
+ GL11.glBegin(GL11.GL_LINE_STRIP);
+
+ GL11.glVertex3d(x1, y1, z1);
+ GL11.glVertex3d(x1, y2, z1);
+ GL11.glVertex3d(x1, y2, z2);
+ GL11.glVertex3d(x1, y1, z2);
+ GL11.glVertex3d(x1, y1, z1);
+
+ GL11.glVertex3d(x2, y1, z1);
+ GL11.glVertex3d(x2, y2, z1);
+ GL11.glVertex3d(x2, y2, z2);
+ GL11.glVertex3d(x2, y1, z2);
+ GL11.glVertex3d(x2, y1, z1);
+
+ GL11.glVertex3d(x1, y1, z1);
+ GL11.glVertex3d(x2, y1, z1);
+ GL11.glVertex3d(x2, y1, z2);
+ GL11.glVertex3d(x1, y1, z2);
+ GL11.glVertex3d(x1, y2, z2);
+ GL11.glVertex3d(x2, y2, z2);
+ GL11.glVertex3d(x2, y2, z1);
+ GL11.glVertex3d(x2, y1, z1);
+ GL11.glVertex3d(x1, y1, z1);
+ GL11.glVertex3d(x2, y1, z1);
+ GL11.glVertex3d(x2, y2, z1);
+ GL11.glVertex3d(x1, y2, z1);
+ GL11.glVertex3d(x1, y2, z2);
+ GL11.glVertex3d(x2, y2, z2);
+ GL11.glVertex3d(x2, y1, z2);
+ GL11.glVertex3d(x1, y1, z2);
+
+ GL11.glEnd();
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_DieselGenerator.java b/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_DieselGenerator.java
new file mode 100644
index 0000000000..10138bdfc2
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_DieselGenerator.java
@@ -0,0 +1,278 @@
+package gregtech.common.tileentities.generators;
+
+import static gregtech.api.enums.Textures.BlockIcons.DIESEL_GENERATOR_BACK;
+import static gregtech.api.enums.Textures.BlockIcons.DIESEL_GENERATOR_BACK_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.DIESEL_GENERATOR_BACK_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.DIESEL_GENERATOR_BACK_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.DIESEL_GENERATOR_BOTTOM;
+import static gregtech.api.enums.Textures.BlockIcons.DIESEL_GENERATOR_BOTTOM_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.DIESEL_GENERATOR_BOTTOM_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.DIESEL_GENERATOR_BOTTOM_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.DIESEL_GENERATOR_FRONT;
+import static gregtech.api.enums.Textures.BlockIcons.DIESEL_GENERATOR_FRONT_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.DIESEL_GENERATOR_FRONT_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.DIESEL_GENERATOR_FRONT_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.DIESEL_GENERATOR_SIDE;
+import static gregtech.api.enums.Textures.BlockIcons.DIESEL_GENERATOR_SIDE_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.DIESEL_GENERATOR_SIDE_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.DIESEL_GENERATOR_SIDE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.DIESEL_GENERATOR_TOP;
+import static gregtech.api.enums.Textures.BlockIcons.DIESEL_GENERATOR_TOP_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.DIESEL_GENERATOR_TOP_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.DIESEL_GENERATOR_TOP_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAYS_ENERGY_OUT;
+import static gregtech.api.objects.XSTR.XSTR_INSTANCE;
+
+import net.minecraft.block.Block;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import cpw.mods.fml.common.registry.GameRegistry;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import gregtech.GT_Mod;
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.ParticleFX;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicGenerator;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Log;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.WorldSpawnedEventBuilder.ParticleEventBuilder;
+
+public class GT_MetaTileEntity_DieselGenerator extends GT_MetaTileEntity_BasicGenerator {
+
+ public int mEfficiency;
+
+ public GT_MetaTileEntity_DieselGenerator(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ new String[] { "Requires liquid Fuel",
+ "Causes "
+ + (int) (GT_Mod.gregtechproxy.mPollutionBaseDieselGeneratorPerSecond
+ * GT_Mod.gregtechproxy.mPollutionDieselGeneratorReleasedByTier[aTier])
+ + " Pollution per second" });
+ onConfigLoad();
+ }
+
+ public GT_MetaTileEntity_DieselGenerator(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ onConfigLoad();
+ }
+
+ public GT_MetaTileEntity_DieselGenerator(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ onConfigLoad();
+ }
+
+ @Override
+ public boolean isOutputFacing(ForgeDirection side) {
+ return side == getBaseMetaTileEntity().getFrontFacing();
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_DieselGenerator(this.mName, this.mTier, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.dieselFuels;
+ }
+
+ @Override
+ public int getCapacity() {
+ return 16000;
+ }
+
+ public void onConfigLoad() {
+ this.mEfficiency = (100 - this.mTier * 5);
+ }
+
+ @Override
+ public int getEfficiency() {
+ return this.mEfficiency;
+ }
+
+ @Override
+ public int getFuelValue(ItemStack aStack) {
+ if (GT_Utility.isStackInvalid(aStack) || getRecipeMap() == null) return 0;
+ long rValue = super.getFuelValue(aStack);
+ if (ItemList.Fuel_Can_Plastic_Filled.isStackEqual(aStack, false, true)) {
+ rValue = Math.max(rValue, GameRegistry.getFuelValue(aStack) * 3L);
+ }
+ if (rValue > Integer.MAX_VALUE) {
+ throw new ArithmeticException("Integer LOOPBACK!");
+ }
+ return (int) rValue;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aTick % 100 == 0 && mFluid != null && mFluid.amount > this.getCapacity()) {
+ GT_Log.err.println(
+ "Dupe Abuse: " + aBaseMetaTileEntity.getOwnerName()
+ + " Coords: "
+ + aBaseMetaTileEntity.getXCoord()
+ + " "
+ + aBaseMetaTileEntity.getYCoord()
+ + " "
+ + aBaseMetaTileEntity.getZCoord());
+ aBaseMetaTileEntity.setToFire();
+ }
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ }
+
+ /**
+ * Draws random smoke particles on top when active
+ *
+ * @param aBaseMetaTileEntity The entity that will handle the {@link Block#randomDisplayTick}
+ */
+ @SideOnly(Side.CLIENT)
+ @Override
+ public void onRandomDisplayTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ if (aBaseMetaTileEntity.isActive()) {
+
+ if (aBaseMetaTileEntity.getCoverIDAtSide(ForgeDirection.UP) == 0
+ && !aBaseMetaTileEntity.getOpacityAtSide(ForgeDirection.UP)) {
+
+ final double x = aBaseMetaTileEntity.getOffsetX(ForgeDirection.UP, 1) + 2D / 16D
+ + XSTR_INSTANCE.nextFloat() * 14D / 16D;
+ final double y = aBaseMetaTileEntity.getOffsetY(ForgeDirection.UP, 1) + 1D / 32D;
+ final double z = aBaseMetaTileEntity.getOffsetZ(ForgeDirection.UP, 1) + 2D / 16D
+ + XSTR_INSTANCE.nextFloat() * 14D / 16D;
+
+ new ParticleEventBuilder().setMotion(0D, 0D, 0D)
+ .setPosition(x, y, z)
+ .setWorld(getBaseMetaTileEntity().getWorld())
+ .setIdentifier(ParticleFX.SMOKE)
+ .run();
+ }
+ }
+ }
+
+ @Override
+ public ITexture[] getFront(byte aColor) {
+ return new ITexture[] { super.getFront(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(DIESEL_GENERATOR_FRONT),
+ TextureFactory.builder()
+ .addIcon(DIESEL_GENERATOR_FRONT_GLOW)
+ .glow()
+ .build()),
+ OVERLAYS_ENERGY_OUT[this.mTier] };
+ }
+
+ @Override
+ public ITexture[] getBack(byte aColor) {
+ return new ITexture[] { super.getBack(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(DIESEL_GENERATOR_BACK),
+ TextureFactory.builder()
+ .addIcon(DIESEL_GENERATOR_BACK_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getBottom(byte aColor) {
+ return new ITexture[] { super.getBottom(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(DIESEL_GENERATOR_BOTTOM),
+ TextureFactory.builder()
+ .addIcon(DIESEL_GENERATOR_BOTTOM_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getTop(byte aColor) {
+ return new ITexture[] { super.getTop(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(DIESEL_GENERATOR_TOP),
+ TextureFactory.builder()
+ .addIcon(DIESEL_GENERATOR_TOP_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getSides(byte aColor) {
+ return new ITexture[] { super.getSides(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(DIESEL_GENERATOR_SIDE),
+ TextureFactory.builder()
+ .addIcon(DIESEL_GENERATOR_SIDE_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getFrontActive(byte aColor) {
+ return new ITexture[] { super.getFrontActive(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(DIESEL_GENERATOR_FRONT_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(DIESEL_GENERATOR_FRONT_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ OVERLAYS_ENERGY_OUT[this.mTier] };
+ }
+
+ @Override
+ public ITexture[] getBackActive(byte aColor) {
+ return new ITexture[] { super.getBackActive(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(DIESEL_GENERATOR_BACK_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(DIESEL_GENERATOR_BACK_ACTIVE_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getBottomActive(byte aColor) {
+ return new ITexture[] { super.getBottomActive(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(DIESEL_GENERATOR_BOTTOM_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(DIESEL_GENERATOR_BOTTOM_ACTIVE_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getTopActive(byte aColor) {
+ return new ITexture[] { super.getTopActive(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(DIESEL_GENERATOR_TOP_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(DIESEL_GENERATOR_TOP_ACTIVE_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getSidesActive(byte aColor) {
+ return new ITexture[] { super.getSidesActive(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(DIESEL_GENERATOR_SIDE_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(DIESEL_GENERATOR_SIDE_ACTIVE_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public int getPollution() {
+ return (int) (GT_Mod.gregtechproxy.mPollutionBaseDieselGeneratorPerSecond
+ * GT_Mod.gregtechproxy.mPollutionDieselGeneratorReleasedByTier[mTier]);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_GasTurbine.java b/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_GasTurbine.java
new file mode 100644
index 0000000000..ca0a5d6958
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_GasTurbine.java
@@ -0,0 +1,207 @@
+package gregtech.common.tileentities.generators;
+
+import static gregtech.api.enums.Textures.BlockIcons.GAS_TURBINE_BACK;
+import static gregtech.api.enums.Textures.BlockIcons.GAS_TURBINE_BACK_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.GAS_TURBINE_BACK_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.GAS_TURBINE_BACK_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.GAS_TURBINE_BOTTOM;
+import static gregtech.api.enums.Textures.BlockIcons.GAS_TURBINE_BOTTOM_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.GAS_TURBINE_BOTTOM_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.GAS_TURBINE_BOTTOM_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.GAS_TURBINE_FRONT;
+import static gregtech.api.enums.Textures.BlockIcons.GAS_TURBINE_FRONT_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.GAS_TURBINE_FRONT_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.GAS_TURBINE_FRONT_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.GAS_TURBINE_SIDE;
+import static gregtech.api.enums.Textures.BlockIcons.GAS_TURBINE_SIDE_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.GAS_TURBINE_SIDE_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.GAS_TURBINE_SIDE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.GAS_TURBINE_TOP;
+import static gregtech.api.enums.Textures.BlockIcons.GAS_TURBINE_TOP_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.GAS_TURBINE_TOP_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.GAS_TURBINE_TOP_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAYS_ENERGY_OUT;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.GT_Mod;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicGenerator;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+
+public class GT_MetaTileEntity_GasTurbine extends GT_MetaTileEntity_BasicGenerator {
+
+ public int mEfficiency;
+
+ public GT_MetaTileEntity_GasTurbine(int aID, String aName, String aNameRegional, int aTier, int mEfficiency) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ new String[] { "Requires flammable Gasses",
+ "Causes "
+ + (int) (GT_Mod.gregtechproxy.mPollutionBaseGasTurbinePerSecond
+ * GT_Mod.gregtechproxy.mPollutionGasTurbineReleasedByTier[aTier])
+ + " Pollution per second" });
+ this.mEfficiency = mEfficiency;
+ }
+
+ public GT_MetaTileEntity_GasTurbine(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures,
+ int mEfficiency) {
+ super(aName, aTier, aDescription, aTextures);
+ this.mEfficiency = mEfficiency;
+ }
+
+ @Override
+ public boolean isOutputFacing(ForgeDirection side) {
+ return side == getBaseMetaTileEntity().getFrontFacing();
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_GasTurbine(
+ this.mName,
+ this.mTier,
+ this.mDescriptionArray,
+ this.mTextures,
+ this.mEfficiency);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.gasTurbineFuels;
+ }
+
+ @Override
+ public int getCapacity() {
+ return 16000;
+ }
+
+ @Override
+ public int getEfficiency() {
+ return this.mEfficiency;
+ }
+
+ @Override
+ public ITexture[] getFront(byte aColor) {
+ return new ITexture[] { super.getFront(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(GAS_TURBINE_FRONT),
+ TextureFactory.builder()
+ .addIcon(GAS_TURBINE_FRONT_GLOW)
+ .glow()
+ .build()),
+ OVERLAYS_ENERGY_OUT[this.mTier] };
+ }
+
+ @Override
+ public ITexture[] getBack(byte aColor) {
+ return new ITexture[] { super.getBack(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(GAS_TURBINE_BACK),
+ TextureFactory.builder()
+ .addIcon(GAS_TURBINE_BACK_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getBottom(byte aColor) {
+ return new ITexture[] { super.getBottom(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(GAS_TURBINE_BOTTOM),
+ TextureFactory.builder()
+ .addIcon(GAS_TURBINE_BOTTOM_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getTop(byte aColor) {
+ return new ITexture[] { super.getTop(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(GAS_TURBINE_TOP),
+ TextureFactory.builder()
+ .addIcon(GAS_TURBINE_TOP_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getSides(byte aColor) {
+ return new ITexture[] { super.getSides(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(GAS_TURBINE_SIDE),
+ TextureFactory.builder()
+ .addIcon(GAS_TURBINE_SIDE_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getFrontActive(byte aColor) {
+ return new ITexture[] { super.getFrontActive(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(GAS_TURBINE_FRONT_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(GAS_TURBINE_FRONT_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ OVERLAYS_ENERGY_OUT[this.mTier] };
+ }
+
+ @Override
+ public ITexture[] getBackActive(byte aColor) {
+ return new ITexture[] { super.getBackActive(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(GAS_TURBINE_BACK_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(GAS_TURBINE_BACK_ACTIVE_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getBottomActive(byte aColor) {
+ return new ITexture[] { super.getBottomActive(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(GAS_TURBINE_BOTTOM_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(GAS_TURBINE_BOTTOM_ACTIVE_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getTopActive(byte aColor) {
+ return new ITexture[] { super.getTopActive(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(GAS_TURBINE_TOP_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(GAS_TURBINE_TOP_ACTIVE_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getSidesActive(byte aColor) {
+ return new ITexture[] { super.getSidesActive(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(GAS_TURBINE_SIDE_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(GAS_TURBINE_SIDE_ACTIVE_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public int getPollution() {
+ return (int) (GT_Mod.gregtechproxy.mPollutionBaseGasTurbinePerSecond
+ * GT_Mod.gregtechproxy.mPollutionGasTurbineReleasedByTier[mTier]);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_LightningRod.java b/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_LightningRod.java
new file mode 100644
index 0000000000..9809dba278
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_LightningRod.java
@@ -0,0 +1,169 @@
+package gregtech.common.tileentities.generators;
+
+import static gregtech.api.objects.XSTR.XSTR_INSTANCE;
+
+import net.minecraft.entity.effect.EntityLightningBolt;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.Textures.BlockIcons;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_TieredMachineBlock;
+import gregtech.api.render.TextureFactory;
+
+public class GT_MetaTileEntity_LightningRod extends GT_MetaTileEntity_TieredMachineBlock {
+
+ public GT_MetaTileEntity_LightningRod(int aID, String aName, String aNameRegional, int aTier) {
+ super(aID, aName, aNameRegional, aTier, 0, "Generates EU From Lightning Bolts");
+ }
+
+ public GT_MetaTileEntity_LightningRod(String aName, int aTier, int aInvSlotCount, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_LightningRod(String aName, int aTier, int aInvSlotCount, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ if (sideDirection != ForgeDirection.UP) {
+ return new ITexture[] { BlockIcons.MACHINE_CASINGS[mTier][colorIndex + 1],
+ BlockIcons.OVERLAYS_ENERGY_OUT_POWER[mTier] };
+ }
+ if (!active) return new ITexture[] { BlockIcons.MACHINE_CASINGS[mTier][colorIndex + 1],
+ TextureFactory.of(BlockIcons.MACHINE_CASING_FUSION_GLASS) };
+ return new ITexture[] { BlockIcons.MACHINE_CASINGS[mTier][colorIndex + 1],
+ TextureFactory.of(BlockIcons.MACHINE_CASING_FUSION_GLASS_YELLOW), TextureFactory.builder()
+ .addIcon(BlockIcons.MACHINE_CASING_FUSION_GLASS_YELLOW_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ return null;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_LightningRod(
+ this.mName,
+ this.mTier,
+ this.mInventory.length,
+ this.mDescriptionArray,
+ this.mTextures);
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ World aWorld = aBaseMetaTileEntity.getWorld();
+ if (!aWorld.isRemote) {
+ if (aBaseMetaTileEntity.getStoredEU() > 0) {
+ aBaseMetaTileEntity.setActive(true);
+ aBaseMetaTileEntity.decreaseStoredEnergyUnits(aBaseMetaTileEntity.getStoredEU() / 100 + 1, false);
+ } else {
+ aBaseMetaTileEntity.setActive(false);
+ }
+
+ if (aTick % 256 == 0 && (aWorld.isThundering() || (aWorld.isRaining() && XSTR_INSTANCE.nextInt(10) == 0))) {
+ int aRodValue = 0;
+ boolean isRodValid = true;
+ int aX = aBaseMetaTileEntity.getXCoord();
+ int aY = aBaseMetaTileEntity.getYCoord();
+ int aZ = aBaseMetaTileEntity.getZCoord();
+
+ for (int i = aBaseMetaTileEntity.getYCoord() + 1; i < aWorld.getHeight() - 1; i++) {
+ if (isRodValid && aBaseMetaTileEntity.getBlock(aX, i, aZ)
+ .getUnlocalizedName()
+ .equals("blockFenceIron")) {
+ aRodValue++;
+ } else {
+ isRodValid = false;
+ if (aBaseMetaTileEntity.getBlock(aX, i, aZ) != Blocks.air) {
+ aRodValue = 0;
+ break;
+ }
+ }
+ }
+ if (!aWorld.isThundering() && ((aY + aRodValue) < 128)) aRodValue = 0;
+ if (XSTR_INSTANCE.nextInt(4 * aWorld.getHeight()) < (aRodValue * (aY + aRodValue))) {
+ aBaseMetaTileEntity
+ .increaseStoredEnergyUnits(maxEUStore() - aBaseMetaTileEntity.getStoredEU(), false);
+ aWorld.addWeatherEffect(new EntityLightningBolt(aWorld, aX, aY + aRodValue, aZ));
+ // randomly break a rod
+ if (aWorld.isThundering()) {
+ aWorld.setBlockToAir(aX, aY + XSTR_INSTANCE.nextInt(aRodValue) + 1, aZ);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return false;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return facing == ForgeDirection.UP;
+ }
+
+ @Override
+ public boolean isOutputFacing(ForgeDirection side) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnetOutput() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnetInput() {
+ return false;
+ }
+
+ @Override
+ public long maxEUStore() {
+ return 50000000;
+ }
+
+ @Override
+ public long maxEUOutput() {
+ return GT_Values.V[mTier];
+ }
+
+ @Override
+ public long maxAmperesOut() {
+ return 512;
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {}
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {}
+}
diff --git a/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_MagicEnergyConverter.java b/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_MagicEnergyConverter.java
new file mode 100644
index 0000000000..647aa504e5
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_MagicEnergyConverter.java
@@ -0,0 +1,173 @@
+package gregtech.common.tileentities.generators;
+
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_MAGIC;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_MAGIC_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_MAGIC_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_MAGIC_FRONT;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_MAGIC_FRONT_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_MAGIC_FRONT_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_MAGIC_FRONT_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_MAGIC_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAYS_ENERGY_OUT;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicGenerator;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+
+public class GT_MetaTileEntity_MagicEnergyConverter extends GT_MetaTileEntity_BasicGenerator {
+
+ public int mEfficiency;
+
+ public GT_MetaTileEntity_MagicEnergyConverter(int aID, String aName, String aNameRegional, int aTier) {
+ super(aID, aName, aNameRegional, aTier, "Put your strange stuff in here");
+ onConfigLoad();
+ }
+
+ public GT_MetaTileEntity_MagicEnergyConverter(String aName, int aTier, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ onConfigLoad();
+ }
+
+ public GT_MetaTileEntity_MagicEnergyConverter(String aName, int aTier, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ onConfigLoad();
+ }
+
+ @Override
+ public boolean isOutputFacing(ForgeDirection side) {
+ return side == getBaseMetaTileEntity().getFrontFacing();
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_MagicEnergyConverter(
+ this.mName,
+ this.mTier,
+ this.mDescriptionArray,
+ this.mTextures);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.magicFuels;
+ }
+
+ @Override
+ public int getCapacity() {
+ return 16000;
+ }
+
+ public void onConfigLoad() {
+ this.mEfficiency = 100 - this.mTier * 5;
+ }
+
+ @Override
+ public int getEfficiency() {
+ return this.mEfficiency;
+ }
+
+ @Override
+ public ITexture[] getFront(byte aColor) {
+ return new ITexture[] { super.getFront(aColor)[0], TextureFactory.of(MACHINE_CASING_MAGIC),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_MAGIC_GLOW)
+ .glow()
+ .build(),
+ OVERLAYS_ENERGY_OUT[mTier] };
+ }
+
+ @Override
+ public ITexture[] getBack(byte aColor) {
+ return new ITexture[] { super.getBack(aColor)[0], TextureFactory.of(MACHINE_CASING_MAGIC_FRONT),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_MAGIC_FRONT_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottom(byte aColor) {
+ return new ITexture[] { super.getBottom(aColor)[0], TextureFactory.of(MACHINE_CASING_MAGIC),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_MAGIC_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTop(byte aColor) {
+ return new ITexture[] { super.getTop(aColor)[0], TextureFactory.of(MACHINE_CASING_MAGIC),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_MAGIC_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getSides(byte aColor) {
+ return new ITexture[] { super.getSides(aColor)[0], TextureFactory.of(MACHINE_CASING_MAGIC),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_MAGIC_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontActive(byte aColor) {
+ return new ITexture[] { super.getFrontActive(aColor)[0], TextureFactory.of(MACHINE_CASING_MAGIC_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_MAGIC_ACTIVE_GLOW)
+ .glow()
+ .build(),
+ OVERLAYS_ENERGY_OUT[mTier] };
+ }
+
+ @Override
+ public ITexture[] getBackActive(byte aColor) {
+ return new ITexture[] { super.getBackActive(aColor)[0], TextureFactory.of(MACHINE_CASING_MAGIC_FRONT_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_MAGIC_FRONT_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomActive(byte aColor) {
+ return new ITexture[] { super.getBottomActive(aColor)[0], TextureFactory.of(MACHINE_CASING_MAGIC_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_MAGIC_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopActive(byte aColor) {
+ return new ITexture[] { super.getTopActive(aColor)[0], TextureFactory.of(MACHINE_CASING_MAGIC_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_MAGIC_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getSidesActive(byte aColor) {
+ return new ITexture[] { super.getSidesActive(aColor)[0], TextureFactory.of(MACHINE_CASING_MAGIC_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_MAGIC_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public int getPollution() {
+ return 0;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_MagicalEnergyAbsorber.java b/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_MagicalEnergyAbsorber.java
new file mode 100644
index 0000000000..3245607ac2
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_MagicalEnergyAbsorber.java
@@ -0,0 +1,805 @@
+package gregtech.common.tileentities.generators;
+
+import static gregtech.api.enums.GT_Values.V;
+import static gregtech.api.enums.Mods.Thaumcraft;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_DRAGONEGG;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_DRAGONEGG_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_MAGIC;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_MAGIC_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_MAGIC_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_MAGIC_FRONT;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_MAGIC_FRONT_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_MAGIC_FRONT_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_MAGIC_FRONT_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_MAGIC_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAYS_ENERGY_OUT;
+import static gregtech.api.objects.XSTR.XSTR_INSTANCE;
+import static net.minecraft.util.EnumChatFormatting.GRAY;
+import static net.minecraft.util.EnumChatFormatting.GREEN;
+import static net.minecraft.util.EnumChatFormatting.LIGHT_PURPLE;
+import static net.minecraft.util.EnumChatFormatting.RESET;
+import static net.minecraft.util.EnumChatFormatting.UNDERLINE;
+import static net.minecraft.util.EnumChatFormatting.YELLOW;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockDragonEgg;
+import net.minecraft.enchantment.Enchantment;
+import net.minecraft.enchantment.EnchantmentHelper;
+import net.minecraft.entity.item.EntityEnderCrystal;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.init.Blocks;
+import net.minecraft.init.Items;
+import net.minecraft.item.ItemEnchantedBook;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.AxisAlignedBB;
+import net.minecraft.world.World;
+import net.minecraft.world.chunk.Chunk;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.google.common.base.Enums;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import gregtech.api.enums.ParticleFX;
+import gregtech.api.enums.TC_Aspects;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicGenerator;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_LanguageManager;
+import gregtech.api.util.GT_Log;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.WorldSpawnedEventBuilder.ParticleEventBuilder;
+import gregtech.common.config.machinestats.ConfigMachines;
+import thaumcraft.api.aspects.Aspect;
+import thaumcraft.api.aspects.AspectList;
+import thaumcraft.api.aspects.AspectSourceHelper;
+import thaumcraft.api.aspects.IAspectContainer;
+import thaumcraft.api.visnet.VisNetHandler;
+
+interface MagicalEnergyBBListener {
+
+ void onMagicalEnergyBBUpdate();
+}
+
+public class GT_MetaTileEntity_MagicalEnergyAbsorber extends GT_MetaTileEntity_BasicGenerator
+ implements MagicalEnergyBBListener {
+
+ private static final ConcurrentHashMap<UUID, GT_MetaTileEntity_MagicalEnergyAbsorber> sSubscribedCrystals = new ConcurrentHashMap<>(
+ 4);
+ private static final List<Aspect> sPrimalAspects = (Thaumcraft.isModLoaded()) ? Aspect.getPrimalAspects()
+ : new ArrayList<>();
+ private static final Map<Aspect, Integer> sAspectsEnergy = new HashMap<>();
+ private static boolean sAllowMultipleEggs = false;
+ private static GT_MetaTileEntity_MagicalEnergyAbsorber sActiveSiphon = null;
+ private static int sEnergyPerEndercrystal = 512;
+ private static int sEnergyFromVis = 20;
+ private static int sEnergyPerEssentia = 320;
+ private static int sDragonEggEnergyPerTick = 2048;
+ private static int sCreeperEggEnergyPerTick = 512;
+ private final MagicalEnergyBB mMagicalEnergyBB = new MagicalEnergyBB(this, mTier, mTier + 2);
+ private int mEfficiency;
+ private int mMaxVisPerDrain;
+ private long mNextGenerateTickRate = 1;
+ private int mNoGenerationTicks = 0;
+ private boolean mUsingEssentia = true;
+
+ public GT_MetaTileEntity_MagicalEnergyAbsorber(int aID, String aName, String aNameRegional, int aTier) {
+ super(aID, aName, aNameRegional, aTier, "Feasts on magic close to it:");
+ onConfigLoad();
+ }
+
+ private GT_MetaTileEntity_MagicalEnergyAbsorber(String aName, int aTier, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ onConfigLoad();
+ }
+
+ /**
+ * Populates static variables dependant on config settings
+ */
+ private static void sharedConfigLoad() {
+ sAllowMultipleEggs = ConfigMachines.allowMultipleEggs;
+ if (Thaumcraft.isModLoaded()) {
+ for (Aspect tAspect : Aspect.aspects.values()) {
+ // noinspection UnstableApiUsage
+ sAspectsEnergy.put(
+ tAspect,
+ Enums.getIfPresent(
+ TC_Aspects.class,
+ tAspect.getTag()
+ .toUpperCase(Locale.ENGLISH))
+ .or(TC_Aspects.AER).mValue * sEnergyPerEssentia);
+ }
+ }
+ }
+
+ private static void setActiveSiphon(GT_MetaTileEntity_MagicalEnergyAbsorber aSiphon) {
+ sActiveSiphon = aSiphon;
+ }
+
+ public void onConfigLoad() {
+ sharedConfigLoad();
+ mEfficiency = 100 - mTier * 10;
+ mMaxVisPerDrain = (int) Math.round(Math.sqrt((double) (V[mTier] * 10000) / (sEnergyFromVis * getEfficiency())));
+ if (Math.pow(mMaxVisPerDrain, 2) * sEnergyFromVis * getEfficiency() < V[mTier]) {
+ mMaxVisPerDrain += 1;
+ }
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ if (aPlayer.isSneaking()) mMagicalEnergyBB.decreaseTier();
+ else mMagicalEnergyBB.increaseTier();
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ String.format(
+ GT_LanguageManager.addStringLocalization(
+ "Interaction_DESCRIPTION_MagicalEnergyAbsorber_Screwdriver",
+ "Absorption range: %s blocks"),
+ mMagicalEnergyBB.getRange(),
+ true));
+ mMagicalEnergyBB.update();
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ if (!aBaseMetaTileEntity.isServerSide()) return;
+ mMagicalEnergyBB.update();
+ }
+
+ @Override
+ public void onRemoval() {
+ super.onRemoval();
+ releaseEgg();
+ unsubscribeCrystals();
+ }
+
+ private void releaseEgg() {
+ if (sActiveSiphon == this) {
+ setActiveSiphon(null);
+ }
+ }
+
+ private void unsubscribeCrystals() {
+ for (UUID tCrystalID : sSubscribedCrystals.keySet()) {
+ sSubscribedCrystals.remove(tCrystalID, this);
+ }
+ }
+
+ /**
+ * Call-back from the Bounding Box when its content is updated
+ */
+ @Override
+ public void onMagicalEnergyBBUpdate() {
+ List<UUID> tCrystalIDsInRange = mMagicalEnergyBB.getLivingCrystalIDs();
+ // Release unreachable Crystals subscriptions
+ for (UUID tSubscribedCrystalID : sSubscribedCrystals.keySet()) {
+ if (!tCrystalIDsInRange.contains(tSubscribedCrystalID)) {
+ sSubscribedCrystals.remove(tSubscribedCrystalID, this);
+ }
+ }
+ // Subscribe to available and not already subscribed Crystals
+ for (UUID tCrystalID : tCrystalIDsInRange) {
+ sSubscribedCrystals.putIfAbsent(tCrystalID, this);
+ }
+ }
+
+ @Override
+ public String[] getDescription() {
+ final String LI = "- %%%";
+ final String EU_PER = "%%%EU per ";
+ List<String> description = new ArrayList<>();
+ description
+ .add(UNDERLINE + "Feasts on " + LIGHT_PURPLE + UNDERLINE + "magic" + GRAY + UNDERLINE + " close to it:");
+ description.add(
+ LI + (sAllowMultipleEggs ? "A " : "An " + YELLOW + UNDERLINE + "EXCLUSIVE" + RESET)
+ + GRAY
+ + " "
+ + LIGHT_PURPLE
+ + "Dragon Egg"
+ + GRAY
+ + " atop");
+ if (sEnergyPerEndercrystal > 0) {
+ description.add(LI + sEnergyPerEndercrystal + EU_PER + LIGHT_PURPLE + "Ender Crystal" + GRAY + " in range");
+ }
+ if (Thaumcraft.isModLoaded()) {
+ description.add(LI + mMaxVisPerDrain + "%%%CV/t from an " + LIGHT_PURPLE + "Energised Node" + GRAY);
+ description.add(
+ LI + (sEnergyPerEssentia * getEfficiency()) / 100
+ + EU_PER
+ + LIGHT_PURPLE
+ + "Essentia"
+ + GRAY
+ + " Aspect-Value from containers in range");
+ }
+ description.add(" ");
+ description.add(UNDERLINE + "Lookup range (Use Screwdriver to change):");
+ description.add("Default: %%%" + GREEN + mMagicalEnergyBB.getDefaultRange());
+ description.add("Max: %%%" + GREEN + mMagicalEnergyBB.getMaxRange());
+ description.add(" ");
+ description
+ .add(UNDERLINE + "Fuels on " + LIGHT_PURPLE + UNDERLINE + "enchantments" + GRAY + UNDERLINE + " input:");
+ description.add(
+ "- Item: %%%" + (10000 * getEfficiency()) / 100
+ + EU_PER
+ + LIGHT_PURPLE
+ + "enchant"
+ + GRAY
+ + " weight × level / max");
+ description.add("- Book: %%%" + 10000 + EU_PER + LIGHT_PURPLE + "enchant" + GRAY + " weight × level / max");
+ description.add(" ");
+ description.add("Efficiency: %%%" + GREEN + getEfficiency() + "%");
+ return description.toArray(new String[0]);
+ }
+
+ @Override
+ public ITexture[] getFront(byte aColor) {
+ return new ITexture[] { super.getFront(aColor)[0], TextureFactory.of(MACHINE_CASING_MAGIC),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_MAGIC_GLOW)
+ .glow()
+ .build(),
+ OVERLAYS_ENERGY_OUT[mTier] };
+ }
+
+ @Override
+ public ITexture[] getBack(byte aColor) {
+ return new ITexture[] { super.getBack(aColor)[0], TextureFactory.of(MACHINE_CASING_MAGIC_FRONT),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_MAGIC_FRONT_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottom(byte aColor) {
+ return new ITexture[] { super.getBottom(aColor)[0], TextureFactory.of(MACHINE_CASING_MAGIC),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_MAGIC_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTop(byte aColor) {
+ return new ITexture[] { super.getTop(aColor)[0], TextureFactory.of(MACHINE_CASING_DRAGONEGG) };
+ }
+
+ @Override
+ public ITexture[] getSides(byte aColor) {
+ return new ITexture[] { super.getSides(aColor)[0], TextureFactory.of(MACHINE_CASING_MAGIC),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_MAGIC_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontActive(byte aColor) {
+ return new ITexture[] { super.getFrontActive(aColor)[0], TextureFactory.of(MACHINE_CASING_MAGIC_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_MAGIC_ACTIVE_GLOW)
+ .glow()
+ .build(),
+ OVERLAYS_ENERGY_OUT[mTier] };
+ }
+
+ @Override
+ public ITexture[] getBackActive(byte aColor) {
+ return new ITexture[] { super.getBackActive(aColor)[0], TextureFactory.of(MACHINE_CASING_MAGIC_FRONT_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_MAGIC_FRONT_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomActive(byte aColor) {
+ return new ITexture[] { super.getBottomActive(aColor)[0], TextureFactory.of(MACHINE_CASING_MAGIC_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_MAGIC_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopActive(byte aColor) {
+ return new ITexture[] { super.getTopActive(aColor)[0], TextureFactory.of(MACHINE_CASING_DRAGONEGG),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_DRAGONEGG_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getSidesActive(byte aColor) {
+ return new ITexture[] { super.getSidesActive(aColor)[0], TextureFactory.of(MACHINE_CASING_MAGIC_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_MAGIC_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public boolean isOutputFacing(ForgeDirection side) {
+ return side == getBaseMetaTileEntity().getFrontFacing();
+ }
+
+ @Override
+ public long maxEUStore() {
+ return Math.max(getEUVar(), V[mTier] * 16000 + getMinimumStoredEU());
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (!aBaseMetaTileEntity.isServerSide()) return;
+ if (!aBaseMetaTileEntity.isAllowedToWork()) return;
+ if ((aBaseMetaTileEntity.getUniversalEnergyStored() >= aBaseMetaTileEntity.getEUCapacity())) return;
+
+ long tGeneratedEU;
+
+ if (aTick % 100 == 0 && mUsingEssentia) mMagicalEnergyBB.update();
+
+ // Adaptive EU Generation Ticking
+ if (aTick % mNextGenerateTickRate == 0) {
+ tGeneratedEU = generateEU();
+ if (tGeneratedEU > 0) {
+ mNoGenerationTicks = 0;
+ if (tGeneratedEU >= 2 * V[mTier])
+ mNextGenerateTickRate = (long) (1.0D / ((2.0D * (double) (V[mTier])) / (double) tGeneratedEU));
+ else mNextGenerateTickRate = 1;
+ mInventory[getStackDisplaySlot()] = new ItemStack(Blocks.fire, 1);
+ mInventory[getStackDisplaySlot()].setStackDisplayName("Generating: " + tGeneratedEU + " EU");
+ } else {
+ mInventory[getStackDisplaySlot()] = null;
+ mNoGenerationTicks += 1;
+ }
+ if (mNoGenerationTicks > 20) {
+ mNoGenerationTicks = 0;
+ mNextGenerateTickRate = 20;
+ }
+ aBaseMetaTileEntity.increaseStoredEnergyUnits(tGeneratedEU, true);
+ aBaseMetaTileEntity.setActive(
+ aBaseMetaTileEntity.isAllowedToWork()
+ && aBaseMetaTileEntity.getUniversalEnergyStored() >= maxEUOutput() + getMinimumStoredEU());
+ }
+ }
+
+ /**
+ * Draws random portal particles on top when active with an egg on top
+ *
+ * @param aBaseMetaTileEntity The entity that will handle the {@link Block#randomDisplayTick}
+ */
+ @SideOnly(Side.CLIENT)
+ @Override
+ public void onRandomDisplayTick(IGregTechTileEntity aBaseMetaTileEntity) {
+
+ if (aBaseMetaTileEntity.isActive()) {
+
+ if (isEgg(aBaseMetaTileEntity.getBlockAtSide(ForgeDirection.UP))) {
+
+ final double oX = aBaseMetaTileEntity.getXCoord() + 8D / 16D;
+ final double oY = aBaseMetaTileEntity.getYCoord() + 17D / 32D;
+ final double oZ = aBaseMetaTileEntity.getZCoord() + 8D / 16D;
+
+ final ParticleEventBuilder particleEventBuilder = new ParticleEventBuilder()
+ .setWorld(getBaseMetaTileEntity().getWorld())
+ .setIdentifier(ParticleFX.PORTAL);
+
+ for (int i = 0; i < 9; i++) {
+ final double dX = (XSTR_INSTANCE.nextFloat() - 0.5D) / 2D;
+ final double dY = XSTR_INSTANCE.nextFloat() * 1.5;
+ final double dZ = (XSTR_INSTANCE.nextFloat() - 0.5D) / 2D;
+
+ final double x = oX + dX;
+ final double y = oY + dY;
+ final double z = oZ + dZ;
+
+ final double mX = dX * 4D;
+ final double dXZ = Math.sqrt(dX * dX + dZ * dZ);
+ final double mY = -(dXZ * dY) / 4D;
+ final double mZ = dZ * 4D;
+
+ particleEventBuilder.setMotion(mX, mY, mZ)
+ .setPosition(x, y, z)
+ .run();
+ }
+ }
+ }
+ }
+
+ @Override
+ public int getPollution() {
+ return 0;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.magicFuels;
+ }
+
+ @Override
+ public int getEfficiency() {
+ return mEfficiency;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ // Restrict input to disenchantable items or enchanted books
+ return (isDisenchantableItem(aStack) || isEnchantedBook(aStack));
+ }
+
+ @Override
+ public int getCapacity() {
+ return 16000;
+ }
+
+ private boolean isDisenchantableItem(ItemStack aStack) {
+ return ((aStack.isItemEnchanted()) && (aStack.getItem()
+ .getItemEnchantability() > 0));
+ }
+
+ private boolean isEnchantedBook(ItemStack aStack) {
+ return (aStack.getItem() instanceof ItemEnchantedBook);
+ }
+
+ private long generateEU() {
+ long tEU;
+
+ mUsingEssentia = false;
+ if ((tEU = absorbFromEgg()) > 0) return tEU;
+ if ((tEU = absorbFromEnderCrystals()) > 0) return tEU;
+ if ((tEU = absorbFromEnchantedItems()) > 0) return tEU;
+ if ((tEU = absorbFromVisNet()) > 0) return tEU;
+ mUsingEssentia = true;
+ if ((tEU = absorbFromEssentiaContainers()) > 0) return tEU;
+ return 0;
+ }
+
+ private long absorbFromEnchantedItems() {
+ ItemStack tStack = getBaseMetaTileEntity().getStackInSlot(getInputSlot());
+ if (tStack == null) return 0;
+ if (tStack.stackSize == 0) return 0;
+ if (!(isDisenchantableItem(tStack) || isEnchantedBook(tStack))) return 0;
+ long tEU = 0;
+ // Convert enchantments to their EU Value
+ Map<?, ?> tMap = EnchantmentHelper.getEnchantments(tStack);
+ for (Map.Entry<?, ?> e : tMap.entrySet()) {
+ if ((Integer) e.getKey() < Enchantment.enchantmentsList.length) {
+ Enchantment tEnchantment = Enchantment.enchantmentsList[(Integer) e.getKey()];
+ Integer tLevel = (Integer) e.getValue();
+ tEU += 1000000L * tLevel / tEnchantment.getMaxLevel() / tEnchantment.getWeight();
+ }
+ }
+
+ ItemStack tOutputStack = GT_Utility.copyAmount(1, tStack);
+ if (tOutputStack != null) {
+ if (isDisenchantableItem(tOutputStack)) {
+ tEU = tEU * getEfficiency() / 100;
+ EnchantmentHelper.setEnchantments(new HashMap<>(), tOutputStack);
+ } else if (isEnchantedBook(tOutputStack)) {
+ tOutputStack = new ItemStack(Items.book, 1);
+ }
+ }
+
+ // Only consume input when it can store EU and push output
+ if ((getBaseMetaTileEntity().getStoredEU() + tEU) < getBaseMetaTileEntity().getEUCapacity()
+ && getBaseMetaTileEntity().addStackToSlot(getOutputSlot(), tOutputStack)) {
+ decrStackSize(getInputSlot(), 1);
+ } else {
+ tEU = 0;
+ }
+ return tEU;
+ }
+
+ private boolean hasEgg() {
+ Block above = getBaseMetaTileEntity().getBlockOffset(0, 1, 0);
+ return isEgg(above);
+ }
+
+ private long absorbFromEgg() {
+ if (!hasEgg()) return 0;
+ if (!sAllowMultipleEggs) {
+ if (sActiveSiphon != null && sActiveSiphon != this
+ && sActiveSiphon.getBaseMetaTileEntity() != null
+ && !sActiveSiphon.getBaseMetaTileEntity()
+ .isInvalidTileEntity()
+ && sActiveSiphon.isChunkLoaded()
+ && sActiveSiphon.hasEgg()) {
+ getBaseMetaTileEntity().doExplosion(Integer.MAX_VALUE);
+ } else {
+ setActiveSiphon(this);
+ }
+ }
+ Block egg = getBaseMetaTileEntity().getBlockOffset(0, 1, 0);
+ if (egg == Blocks.dragon_egg) {
+ return sDragonEggEnergyPerTick;
+ } else if (egg.getUnlocalizedName()
+ .contains("creeperEgg")) {
+ return sCreeperEggEnergyPerTick;
+ }
+ return 0;
+ }
+
+ private long absorbFromEnderCrystals() {
+ if (sEnergyPerEndercrystal <= 0) return 0;
+ long tEU = 0;
+ for (GT_MetaTileEntity_MagicalEnergyAbsorber tSubscriber : sSubscribedCrystals.values()) {
+ if (tSubscriber == this) { // This Crystal is for me
+ tEU += sEnergyPerEndercrystal;
+ }
+ }
+ return tEU;
+ }
+
+ private long absorbFromVisNet() {
+ if (!Thaumcraft.isModLoaded()) return 0;
+
+ long tEU;
+ IGregTechTileEntity tBaseMetaTileEntity = getBaseMetaTileEntity();
+ World tWorld = tBaseMetaTileEntity.getWorld();
+ int tX = tBaseMetaTileEntity.getXCoord();
+ int tY = tBaseMetaTileEntity.getYCoord();
+ int tZ = tBaseMetaTileEntity.getZCoord();
+
+ // Attempt to drain as much Vis as needed for max EU/t, from all primal aspects.
+ int toDrain = mMaxVisPerDrain;
+
+ for (int i = sPrimalAspects.size() - 1; i >= 0 && toDrain > 0; i--) {
+ toDrain -= VisNetHandler.drainVis(tWorld, tX, tY, tZ, sPrimalAspects.get(i), toDrain);
+ }
+
+ int drained = mMaxVisPerDrain - toDrain;
+ tEU = (long) Math.min(maxEUOutput(), (Math.pow(drained, 2) * sEnergyFromVis * getEfficiency() / 10000));
+
+ return tEU;
+ }
+
+ private long absorbFromEssentiaContainers() {
+ if (!Thaumcraft.isModLoaded()) return 0;
+
+ long tEU = 0;
+
+ long tEUtoGen = getBaseMetaTileEntity().getEUCapacity() - getBaseMetaTileEntity().getUniversalEnergyStored();
+ List<Aspect> mAvailableEssentiaAspects = mMagicalEnergyBB.getAvailableAspects();
+
+ // try to drain 1 of whatever aspect available in containers within RANGE
+ for (int i = mAvailableEssentiaAspects.size() - 1; i >= 0 && tEUtoGen > 0; i--) {
+ Aspect aspect = mAvailableEssentiaAspects.get(i);
+ long tAspectEU = ((long) sAspectsEnergy.get(aspect) * getEfficiency()) / 100;
+ if (tAspectEU <= tEUtoGen && AspectSourceHelper.drainEssentia(
+ (TileEntity) getBaseMetaTileEntity(),
+ aspect,
+ ForgeDirection.UNKNOWN,
+ mMagicalEnergyBB.getRange())) {
+ tEUtoGen -= tAspectEU;
+ tEU += tAspectEU;
+ }
+ }
+ return tEU;
+ }
+
+ private boolean isEgg(Block aBlock) {
+ if (aBlock == null) return false;
+ if (aBlock == Blocks.air) return false;
+ if (aBlock == Blocks.dragon_egg) return true;
+ if (aBlock instanceof BlockDragonEgg) return true;
+ return (aBlock.getUnlocalizedName()
+ .equals("tile.dragonEgg"));
+ }
+
+ private boolean isChunkLoaded() {
+ IGregTechTileEntity tBaseMetaTileEntity = getBaseMetaTileEntity();
+ int tX = tBaseMetaTileEntity.getXCoord();
+ int tY = tBaseMetaTileEntity.getYCoord();
+ World tWorld = tBaseMetaTileEntity.getWorld();
+ Chunk tChunk = tWorld.getChunkFromBlockCoords(tX, tY);
+ return tChunk.isChunkLoaded;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_MagicalEnergyAbsorber(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setInteger("mMagicalEnergyBBTier", mMagicalEnergyBB.getTier());
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ mMagicalEnergyBB.setTier(aNBT.getInteger("mMagicalEnergyBBTier"));
+ }
+
+ /**
+ * Handles Bounding Box ranged operations for Magic sources
+ */
+ static class MagicalEnergyBB {
+
+ private final GT_MetaTileEntity_MagicalEnergyAbsorber mAbsorber;
+ private final MagicalEnergyBBListener mListener;
+ private final int mDefaultTier;
+ private int mTier;
+ private final int mMaxTier;
+ private final List<UUID> mLivingCrystalIDs = new ArrayList<>();
+ private List<Aspect> mAvailableAspects;
+
+ /**
+ * @param aAbsorber user and subscriber for updated BB content
+ * @param aDefaultTier Initial tier value
+ * @param aMaxTier Maximum allowed tier
+ */
+ MagicalEnergyBB(GT_MetaTileEntity_MagicalEnergyAbsorber aAbsorber, int aDefaultTier, int aMaxTier) {
+ mAbsorber = aAbsorber;
+ mListener = aAbsorber;
+ mMaxTier = Math.max(Math.max(aMaxTier, 0), Math.max(aDefaultTier, 0));
+ mDefaultTier = Math.min(aDefaultTier, mMaxTier);
+ mTier = mDefaultTier;
+ if (Thaumcraft.isModLoaded()) mAvailableAspects = new ArrayList<>(Aspect.aspects.size());
+ }
+
+ int getTier() {
+ return mTier;
+ }
+
+ /**
+ * Set Bounding Box Tier within allowed bounds
+ *
+ * @param aTier new tier value
+ * @return effective new tier
+ */
+ int setTier(int aTier) {
+ if (aTier >= 0) {
+ mTier = Math.min(aTier, mMaxTier);
+ } else {
+ mTier = 0;
+ }
+ return mTier;
+ }
+
+ int getRange() {
+ return getRange(mTier);
+ }
+
+ int getRange(int aTier) {
+ return 1 << aTier;
+ }
+
+ int getDefaultTier() {
+ return mDefaultTier;
+ }
+
+ int getDefaultRange() {
+ return getRange(getDefaultTier());
+ }
+
+ int getMaxTier() {
+ return mMaxTier;
+ }
+
+ int getMaxRange() {
+ return getRange(getMaxTier());
+ }
+
+ private AxisAlignedBB getAxisAlignedBB() {
+ double tRange = getRange();
+ IGregTechTileEntity tBaseMetaTileEntity = mAbsorber.getBaseMetaTileEntity();
+ double tX = tBaseMetaTileEntity.getXCoord();
+ double tY = tBaseMetaTileEntity.getYCoord();
+ double tZ = tBaseMetaTileEntity.getZCoord();
+ return AxisAlignedBB
+ .getBoundingBox(tX - tRange, tY - tRange, tZ - tRange, tX + tRange, tY + tRange, tZ + tRange);
+ }
+
+ private void scanLivingCrystals() {
+ World tWorld = mAbsorber.getBaseMetaTileEntity()
+ .getWorld();
+ mLivingCrystalIDs.clear();
+ for (EntityEnderCrystal o : tWorld.getEntitiesWithinAABB(EntityEnderCrystal.class, getAxisAlignedBB())) {
+ if (o.isEntityAlive()) {
+ mLivingCrystalIDs.add(o.getPersistentID());
+ }
+ }
+ }
+
+ private void scanAvailableAspects() {
+ if (!Thaumcraft.isModLoaded()) return;
+ IGregTechTileEntity tBaseMetaTileEntity = mAbsorber.getBaseMetaTileEntity();
+ if (tBaseMetaTileEntity.isInvalidTileEntity()) return;
+ int tRange = getRange();
+ int tY = tBaseMetaTileEntity.getYCoord();
+ int tMaxY = tBaseMetaTileEntity.getWorld()
+ .getHeight() - 1;
+ // Make sure relative Y range stays between 0 and world max Y
+ int rYMin = (tY - tRange >= 0) ? -tRange : -(tY);
+ int rYMax = (((tY + tRange) <= tMaxY) ? tRange : tMaxY - tY);
+ mAvailableAspects.clear();
+ for (int rX = -tRange; rX <= tRange; rX++) {
+ for (int rZ = -tRange; rZ <= tRange; rZ++) {
+ // rY < rYMax is not a bug. See: thaumcraft.common.lib.events.EssentiaHandler.getSources()
+ for (int rY = rYMin; rY < rYMax; rY++) {
+ TileEntity tTile = tBaseMetaTileEntity.getTileEntityOffset(rX, rY, rZ);
+ if (tTile instanceof IAspectContainer) {
+ AspectList tAspectList = ((IAspectContainer) tTile).getAspects();
+ if (tAspectList == null || tAspectList.aspects.isEmpty()) continue;
+ Set<Aspect> tAspects = tAspectList.aspects.keySet();
+ mAvailableAspects.addAll(tAspects);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * @return List of Living Ender Crystal Entity IDs in range
+ */
+ List<UUID> getLivingCrystalIDs() {
+ return mLivingCrystalIDs;
+ }
+
+ /**
+ * @return List of drainable Essentia Aspects from containers in range
+ */
+ List<Aspect> getAvailableAspects() {
+ return mAvailableAspects;
+ }
+
+ /**
+ * Scan range for magic sources
+ */
+ void update() {
+ if (mAbsorber == null) return;
+ if (mAbsorber.getBaseMetaTileEntity() == null) return;
+ if (mAbsorber.getBaseMetaTileEntity()
+ .isInvalidTileEntity()) return;
+ if (mAbsorber.getBaseMetaTileEntity()
+ .getWorld() == null) return;
+ scanLivingCrystals();
+ scanAvailableAspects();
+ if (mListener != null) {
+ mListener.onMagicalEnergyBBUpdate();
+ }
+ }
+
+ void increaseTier() {
+ offsetTier(1);
+ }
+
+ void decreaseTier() {
+ offsetTier(-1);
+ }
+
+ /**
+ * Change the Bounding Box tier relatively to offset with wrapping at tier limits
+ *
+ * @param aOffset relative tier change
+ */
+ void offsetTier(int aOffset) {
+ int tNumTiers = mMaxTier + 1;
+ int tTier = (mTier + aOffset + tNumTiers) % tNumTiers;
+ int tTrueTier = setTier(tTier);
+ if (tTier != tTrueTier) {
+ GT_Log.out.format("Absorber's BB Tier set to %d was capped to %d", tTier, tTrueTier);
+ }
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_NaquadahReactor.java b/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_NaquadahReactor.java
new file mode 100644
index 0000000000..1dd378e746
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_NaquadahReactor.java
@@ -0,0 +1,201 @@
+package gregtech.common.tileentities.generators;
+
+import static gregtech.api.enums.Textures.BlockIcons.NAQUADAH_REACTOR_SOLID_BACK;
+import static gregtech.api.enums.Textures.BlockIcons.NAQUADAH_REACTOR_SOLID_BACK_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.NAQUADAH_REACTOR_SOLID_BACK_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.NAQUADAH_REACTOR_SOLID_BACK_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.NAQUADAH_REACTOR_SOLID_BOTTOM;
+import static gregtech.api.enums.Textures.BlockIcons.NAQUADAH_REACTOR_SOLID_BOTTOM_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.NAQUADAH_REACTOR_SOLID_BOTTOM_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.NAQUADAH_REACTOR_SOLID_BOTTOM_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.NAQUADAH_REACTOR_SOLID_FRONT;
+import static gregtech.api.enums.Textures.BlockIcons.NAQUADAH_REACTOR_SOLID_FRONT_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.NAQUADAH_REACTOR_SOLID_FRONT_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.NAQUADAH_REACTOR_SOLID_FRONT_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.NAQUADAH_REACTOR_SOLID_SIDE;
+import static gregtech.api.enums.Textures.BlockIcons.NAQUADAH_REACTOR_SOLID_SIDE_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.NAQUADAH_REACTOR_SOLID_SIDE_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.NAQUADAH_REACTOR_SOLID_SIDE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.NAQUADAH_REACTOR_SOLID_TOP;
+import static gregtech.api.enums.Textures.BlockIcons.NAQUADAH_REACTOR_SOLID_TOP_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.NAQUADAH_REACTOR_SOLID_TOP_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.NAQUADAH_REACTOR_SOLID_TOP_GLOW;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicGenerator;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+
+public class GT_MetaTileEntity_NaquadahReactor extends GT_MetaTileEntity_BasicGenerator {
+
+ private int mEfficiency;
+
+ public GT_MetaTileEntity_NaquadahReactor(int aID, String aName, String[] aDescription, String aNameRegional,
+ int aTier) {
+ super(aID, aName, aNameRegional, aTier, aDescription);
+ if (aTier > 8 || aTier < 4) {
+ new Exception("Tier without Recipe Map!").printStackTrace();
+ }
+ mEfficiency = getBaseEff();
+ }
+
+ public GT_MetaTileEntity_NaquadahReactor(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ if (aTier > 8 || aTier < 4) {
+ new Exception("Tier without Recipe Map!").printStackTrace();
+ }
+ mEfficiency = getBaseEff();
+ }
+
+ @Override
+ public boolean isOutputFacing(ForgeDirection side) {
+ if (side == ForgeDirection.UNKNOWN) return false;
+ return ((side.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0)
+ && (side != getBaseMetaTileEntity().getFrontFacing())
+ && (side != getBaseMetaTileEntity().getBackFacing());
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_NaquadahReactor(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ RecipeMap<?> ret;
+ switch (mTier) {
+ case 4 -> ret = RecipeMaps.smallNaquadahReactorFuels;
+ case 5 -> ret = RecipeMaps.largeNaquadahReactorFuels;
+ case 6 -> ret = RecipeMaps.hugeNaquadahReactorFuels;
+ case 7 -> ret = RecipeMaps.extremeNaquadahReactorFuels;
+ case 8 -> ret = RecipeMaps.ultraHugeNaquadahReactorFuels;
+ default -> ret = null;
+ }
+ return ret;
+ }
+
+ @Override
+ public int getCapacity() {
+ return getRecipeMap() != null ? getRecipeMap().getBackend()
+ .getProperties().minFluidInputs > 0 ? 8000 * (mTier + 1) : 0 : 0;
+ }
+
+ @Override
+ public int getEfficiency() {
+ return mEfficiency;
+ }
+
+ private int getBaseEff() {
+ return mTier == 4 ? 80 : 100 + (50 * (mTier - 5));
+ }
+
+ @Override
+ public ITexture[] getFront(byte aColor) {
+ return new ITexture[] { super.getFront(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(NAQUADAH_REACTOR_SOLID_FRONT),
+ TextureFactory.builder()
+ .addIcon(NAQUADAH_REACTOR_SOLID_FRONT_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getBack(byte aColor) {
+ return new ITexture[] { super.getBack(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(NAQUADAH_REACTOR_SOLID_BACK),
+ TextureFactory.builder()
+ .addIcon(NAQUADAH_REACTOR_SOLID_BACK_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getBottom(byte aColor) {
+ return new ITexture[] { super.getBottom(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(NAQUADAH_REACTOR_SOLID_BOTTOM),
+ TextureFactory.builder()
+ .addIcon(NAQUADAH_REACTOR_SOLID_BOTTOM_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getTop(byte aColor) {
+ return new ITexture[] { super.getTop(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(NAQUADAH_REACTOR_SOLID_TOP),
+ TextureFactory.builder()
+ .addIcon(NAQUADAH_REACTOR_SOLID_TOP_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getSides(byte aColor) {
+ return new ITexture[] { super.getSides(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(NAQUADAH_REACTOR_SOLID_SIDE),
+ TextureFactory.builder()
+ .addIcon(NAQUADAH_REACTOR_SOLID_SIDE_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getFrontActive(byte aColor) {
+ return new ITexture[] { super.getFrontActive(aColor)[0], TextureFactory.of(NAQUADAH_REACTOR_SOLID_FRONT_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(NAQUADAH_REACTOR_SOLID_FRONT_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBackActive(byte aColor) {
+ return new ITexture[] { super.getBackActive(aColor)[0], TextureFactory.of(NAQUADAH_REACTOR_SOLID_BACK_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(NAQUADAH_REACTOR_SOLID_BACK_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomActive(byte aColor) {
+ return new ITexture[] { super.getBottomActive(aColor)[0],
+ TextureFactory.of(NAQUADAH_REACTOR_SOLID_BOTTOM_ACTIVE), TextureFactory.builder()
+ .addIcon(NAQUADAH_REACTOR_SOLID_BOTTOM_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopActive(byte aColor) {
+ return new ITexture[] { super.getTopActive(aColor)[0], TextureFactory.of(NAQUADAH_REACTOR_SOLID_TOP_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(NAQUADAH_REACTOR_SOLID_TOP_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getSidesActive(byte aColor) {
+ return new ITexture[] { super.getSidesActive(aColor)[0], TextureFactory.of(NAQUADAH_REACTOR_SOLID_SIDE_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(NAQUADAH_REACTOR_SOLID_SIDE_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public int getPollution() {
+ return 0;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_PlasmaGenerator.java b/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_PlasmaGenerator.java
new file mode 100644
index 0000000000..fa980ce480
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_PlasmaGenerator.java
@@ -0,0 +1,142 @@
+package gregtech.common.tileentities.generators;
+
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_FUSION_GLASS;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_FUSION_GLASS_YELLOW;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_FUSION_GLASS_YELLOW_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAYS_ENERGY_OUT;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicGenerator;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+
+public class GT_MetaTileEntity_PlasmaGenerator extends GT_MetaTileEntity_BasicGenerator {
+
+ public int mEfficiency;
+
+ public GT_MetaTileEntity_PlasmaGenerator(int aID, String aName, String aNameRegional, int aTier) {
+ super(aID, aName, aNameRegional, aTier, "Plasma into energy");
+ setEfficiency();
+ }
+
+ public GT_MetaTileEntity_PlasmaGenerator(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ setEfficiency();
+ }
+
+ public GT_MetaTileEntity_PlasmaGenerator(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ setEfficiency();
+ }
+
+ @Override
+ public ITexture[] getFront(byte aColor) {
+ return new ITexture[] { super.getFront(aColor)[0], TextureFactory.of(MACHINE_CASING_FUSION_GLASS),
+ OVERLAYS_ENERGY_OUT[mTier] };
+ }
+
+ @Override
+ public ITexture[] getBack(byte aColor) {
+ return new ITexture[] { super.getBack(aColor)[0], TextureFactory.of(MACHINE_CASING_FUSION_GLASS) };
+ }
+
+ @Override
+ public ITexture[] getBottom(byte aColor) {
+ return new ITexture[] { super.getBottom(aColor)[0], TextureFactory.of(MACHINE_CASING_FUSION_GLASS) };
+ }
+
+ @Override
+ public ITexture[] getTop(byte aColor) {
+ return new ITexture[] { super.getTop(aColor)[0], TextureFactory.of(MACHINE_CASING_FUSION_GLASS) };
+ }
+
+ @Override
+ public ITexture[] getSides(byte aColor) {
+ return new ITexture[] { super.getSides(aColor)[0], TextureFactory.of(MACHINE_CASING_FUSION_GLASS) };
+ }
+
+ @Override
+ public ITexture[] getFrontActive(byte aColor) {
+ return new ITexture[] { super.getFrontActive(aColor)[0], TextureFactory.of(MACHINE_CASING_FUSION_GLASS_YELLOW),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_FUSION_GLASS_YELLOW_GLOW)
+ .glow()
+ .build(),
+ OVERLAYS_ENERGY_OUT[mTier] };
+ }
+
+ @Override
+ public ITexture[] getBackActive(byte aColor) {
+ return new ITexture[] { super.getBackActive(aColor)[0], TextureFactory.of(MACHINE_CASING_FUSION_GLASS_YELLOW),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_FUSION_GLASS_YELLOW_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomActive(byte aColor) {
+ return new ITexture[] { super.getBottomActive(aColor)[0], TextureFactory.of(MACHINE_CASING_FUSION_GLASS_YELLOW),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_FUSION_GLASS_YELLOW_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopActive(byte aColor) {
+ return new ITexture[] { super.getTopActive(aColor)[0], TextureFactory.of(MACHINE_CASING_FUSION_GLASS_YELLOW),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_FUSION_GLASS_YELLOW_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getSidesActive(byte aColor) {
+ return new ITexture[] { super.getSidesActive(aColor)[0], TextureFactory.of(MACHINE_CASING_FUSION_GLASS_YELLOW),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_FUSION_GLASS_YELLOW_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public boolean isOutputFacing(ForgeDirection side) {
+ return side == getBaseMetaTileEntity().getFrontFacing();
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.plasmaFuels;
+ }
+
+ @Override
+ public int getEfficiency() {
+ return this.mEfficiency;
+ }
+
+ @Override
+ public int getCapacity() {
+ return 16000;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_PlasmaGenerator(this.mName, this.mTier, this.mDescriptionArray, this.mTextures);
+ }
+
+ public void setEfficiency() {
+ this.mEfficiency = Math.max(10, 10 + Math.min(90, this.mTier * 10));
+ }
+
+ @Override
+ public int getPollution() {
+ return 0;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_SteamTurbine.java b/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_SteamTurbine.java
new file mode 100644
index 0000000000..9c1b8db562
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/generators/GT_MetaTileEntity_SteamTurbine.java
@@ -0,0 +1,238 @@
+package gregtech.common.tileentities.generators;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAYS_ENERGY_OUT;
+import static gregtech.api.enums.Textures.BlockIcons.STEAM_TURBINE_BACK;
+import static gregtech.api.enums.Textures.BlockIcons.STEAM_TURBINE_BACK_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.STEAM_TURBINE_BACK_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.STEAM_TURBINE_BACK_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.STEAM_TURBINE_BOTTOM;
+import static gregtech.api.enums.Textures.BlockIcons.STEAM_TURBINE_BOTTOM_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.STEAM_TURBINE_BOTTOM_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.STEAM_TURBINE_BOTTOM_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.STEAM_TURBINE_FRONT;
+import static gregtech.api.enums.Textures.BlockIcons.STEAM_TURBINE_FRONT_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.STEAM_TURBINE_FRONT_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.STEAM_TURBINE_FRONT_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.STEAM_TURBINE_SIDE;
+import static gregtech.api.enums.Textures.BlockIcons.STEAM_TURBINE_SIDE_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.STEAM_TURBINE_SIDE_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.STEAM_TURBINE_SIDE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.STEAM_TURBINE_TOP;
+import static gregtech.api.enums.Textures.BlockIcons.STEAM_TURBINE_TOP_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.STEAM_TURBINE_TOP_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.STEAM_TURBINE_TOP_GLOW;
+
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicGenerator;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_ModHandler;
+
+public class GT_MetaTileEntity_SteamTurbine extends GT_MetaTileEntity_BasicGenerator {
+
+ public int mEfficiency;
+
+ public GT_MetaTileEntity_SteamTurbine(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ new String[] { "Converts Steam into EU", "Base rate: 2L of Steam -> 1 EU" });
+ this.mEfficiency = 6 + this.mTier;
+ }
+
+ public GT_MetaTileEntity_SteamTurbine(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ this.mEfficiency = 6 + this.mTier;
+ }
+
+ public GT_MetaTileEntity_SteamTurbine(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ this.mEfficiency = 6 + this.mTier;
+ }
+
+ @Override
+ public boolean isOutputFacing(ForgeDirection side) {
+ return side == getBaseMetaTileEntity().getFrontFacing();
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_SteamTurbine(this.mName, this.mTier, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return null;
+ }
+
+ @Override
+ public String[] getDescription() {
+ String[] desc = new String[mDescriptionArray.length + 2];
+ System.arraycopy(mDescriptionArray, 0, desc, 0, mDescriptionArray.length);
+ desc[mDescriptionArray.length] = "Fuel Efficiency: " + (600 / getEfficiency()) + "%";
+ desc[mDescriptionArray.length + 1] = String.format(
+ "Consumes up to %sL of Steam per second",
+ (int) (4000 * (8 * Math.pow(4, mTier) + Math.pow(2, Math.max(mTier - 1, 0))) / (600 / getEfficiency())));
+ return desc;
+ }
+
+ @Override
+ public int getCapacity() {
+ return 24000 * this.mTier;
+ }
+
+ @Override
+ public int getEfficiency() {
+ return this.mEfficiency;
+ }
+
+ @Override
+ public long getFuelValue(FluidStack aLiquid, boolean aLong) {
+ return getFuelValue(aLiquid);
+ }
+
+ @Override
+ public int getFuelValue(FluidStack aLiquid) {
+ if (aLiquid == null) return 0;
+ return GT_ModHandler.isAnySteam(aLiquid) ? 3 : 0;
+ }
+
+ @Override
+ public int consumedFluidPerOperation(FluidStack aLiquid) {
+ return this.mEfficiency;
+ }
+
+ @Override
+ public ITexture[] getFront(byte aColor) {
+ return new ITexture[] { super.getFront(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(STEAM_TURBINE_FRONT),
+ TextureFactory.builder()
+ .addIcon(STEAM_TURBINE_FRONT_GLOW)
+ .glow()
+ .build()),
+ OVERLAYS_ENERGY_OUT[this.mTier] };
+ }
+
+ @Override
+ public ITexture[] getBack(byte aColor) {
+ return new ITexture[] { super.getBack(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(STEAM_TURBINE_BACK),
+ TextureFactory.builder()
+ .addIcon(STEAM_TURBINE_BACK_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getBottom(byte aColor) {
+ return new ITexture[] { super.getBottom(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(STEAM_TURBINE_BOTTOM),
+ TextureFactory.builder()
+ .addIcon(STEAM_TURBINE_BOTTOM_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getTop(byte aColor) {
+ return new ITexture[] { super.getTop(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(STEAM_TURBINE_TOP),
+ TextureFactory.builder()
+ .addIcon(STEAM_TURBINE_TOP_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getSides(byte aColor) {
+ return new ITexture[] { super.getSides(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(STEAM_TURBINE_SIDE),
+ TextureFactory.builder()
+ .addIcon(STEAM_TURBINE_SIDE_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getFrontActive(byte aColor) {
+ return new ITexture[] { super.getFrontActive(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(STEAM_TURBINE_FRONT_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(STEAM_TURBINE_FRONT_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ OVERLAYS_ENERGY_OUT[this.mTier] };
+ }
+
+ @Override
+ public ITexture[] getBackActive(byte aColor) {
+ return new ITexture[] { super.getBackActive(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(STEAM_TURBINE_BACK_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(STEAM_TURBINE_BACK_ACTIVE_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getBottomActive(byte aColor) {
+ return new ITexture[] { super.getBottomActive(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(STEAM_TURBINE_BOTTOM_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(STEAM_TURBINE_BOTTOM_ACTIVE_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getTopActive(byte aColor) {
+ return new ITexture[] { super.getTopActive(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(STEAM_TURBINE_TOP_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(STEAM_TURBINE_TOP_ACTIVE_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public ITexture[] getSidesActive(byte aColor) {
+ return new ITexture[] { super.getSidesActive(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(STEAM_TURBINE_SIDE_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(STEAM_TURBINE_SIDE_ACTIVE_GLOW)
+ .glow()
+ .build()) };
+ }
+
+ @Override
+ public int getPollution() {
+ return 0;
+ }
+
+ @Override
+ public boolean isFluidInputAllowed(FluidStack aFluid) {
+ if (GT_ModHandler.isSuperHeatedSteam(aFluid)) {
+ aFluid.amount = 0;
+ aFluid = null;
+ return false;
+ }
+ return super.isFluidInputAllowed(aFluid);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_BasicHull_Bronze.java b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_BasicHull_Bronze.java
new file mode 100644
index 0000000000..252cc6a5b8
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_BasicHull_Bronze.java
@@ -0,0 +1,45 @@
+package gregtech.common.tileentities.machines;
+
+import gregtech.api.enums.Dyes;
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicHull_NonElectric;
+import gregtech.api.render.TextureFactory;
+
+public class GT_MetaTileEntity_BasicHull_Bronze extends GT_MetaTileEntity_BasicHull_NonElectric {
+
+ public GT_MetaTileEntity_BasicHull_Bronze(int aID, String aName, String aNameRegional, int aTier,
+ String aDescription) {
+ super(aID, aName, aNameRegional, aTier, aDescription);
+ }
+
+ public GT_MetaTileEntity_BasicHull_Bronze(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_BasicHull_Bronze(String aName, int aTier, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_BasicHull_Bronze(this.mName, this.mTier, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ ITexture[][][] rTextures = new ITexture[3][17][];
+ for (byte i = -1; i < 16; i = (byte) (i + 1)) {
+ rTextures[0][(i + 1)] = new ITexture[] {
+ TextureFactory.of(Textures.BlockIcons.MACHINE_BRONZE_BOTTOM, Dyes.getModulation(i, Dyes._NULL.mRGBa)) };
+ rTextures[1][(i + 1)] = new ITexture[] {
+ TextureFactory.of(Textures.BlockIcons.MACHINE_BRONZE_TOP, Dyes.getModulation(i, Dyes._NULL.mRGBa)) };
+ rTextures[2][(i + 1)] = new ITexture[] {
+ TextureFactory.of(Textures.BlockIcons.MACHINE_BRONZE_SIDE, Dyes.getModulation(i, Dyes._NULL.mRGBa)) };
+ }
+ return rTextures;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_BasicHull_BronzeBricks.java b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_BasicHull_BronzeBricks.java
new file mode 100644
index 0000000000..ee788ba8c0
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_BasicHull_BronzeBricks.java
@@ -0,0 +1,53 @@
+package gregtech.common.tileentities.machines;
+
+import gregtech.api.enums.Dyes;
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicHull_NonElectric;
+import gregtech.api.render.TextureFactory;
+
+public class GT_MetaTileEntity_BasicHull_BronzeBricks extends GT_MetaTileEntity_BasicHull_NonElectric {
+
+ public GT_MetaTileEntity_BasicHull_BronzeBricks(int aID, String aName, String aNameRegional, int aTier,
+ String aDescription) {
+ super(aID, aName, aNameRegional, aTier, aDescription);
+ }
+
+ public GT_MetaTileEntity_BasicHull_BronzeBricks(String aName, int aTier, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_BasicHull_BronzeBricks(String aName, int aTier, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_BasicHull_BronzeBricks(
+ this.mName,
+ this.mTier,
+ this.mDescriptionArray,
+ this.mTextures);
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ ITexture[][][] rTextures = new ITexture[3][17][];
+ for (byte i = -1; i < 16; i = (byte) (i + 1)) {
+ ITexture[] tmp0 = { TextureFactory
+ .of(Textures.BlockIcons.MACHINE_BRONZEBRICKS_BOTTOM, Dyes.getModulation(i, Dyes._NULL.mRGBa)) };
+ rTextures[0][(i + 1)] = tmp0;
+ ITexture[] tmp1 = { TextureFactory
+ .of(Textures.BlockIcons.MACHINE_BRONZEBRICKS_TOP, Dyes.getModulation(i, Dyes._NULL.mRGBa)) };
+ rTextures[1][(i + 1)] = tmp1;
+ ITexture[] tmp2 = { TextureFactory
+ .of(Textures.BlockIcons.MACHINE_BRONZEBRICKS_SIDE, Dyes.getModulation(i, Dyes._NULL.mRGBa)) };
+ rTextures[2][(i + 1)] = tmp2;
+ }
+ return rTextures;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_BasicHull_Steel.java b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_BasicHull_Steel.java
new file mode 100644
index 0000000000..85e461bf47
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_BasicHull_Steel.java
@@ -0,0 +1,47 @@
+package gregtech.common.tileentities.machines;
+
+import gregtech.api.enums.Dyes;
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicHull_NonElectric;
+import gregtech.api.render.TextureFactory;
+
+public class GT_MetaTileEntity_BasicHull_Steel extends GT_MetaTileEntity_BasicHull_NonElectric {
+
+ public GT_MetaTileEntity_BasicHull_Steel(int aID, String aName, String aNameRegional, int aTier,
+ String aDescription) {
+ super(aID, aName, aNameRegional, aTier, aDescription);
+ }
+
+ public GT_MetaTileEntity_BasicHull_Steel(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_BasicHull_Steel(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_BasicHull_Steel(this.mName, this.mTier, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ ITexture[][][] rTextures = new ITexture[3][17][];
+ for (byte i = -1; i < 16; i = (byte) (i + 1)) {
+ ITexture[] tmp0 = {
+ TextureFactory.of(Textures.BlockIcons.MACHINE_STEEL_BOTTOM, Dyes.getModulation(i, Dyes._NULL.mRGBa)) };
+ rTextures[0][(i + 1)] = tmp0;
+ ITexture[] tmp1 = {
+ TextureFactory.of(Textures.BlockIcons.MACHINE_STEEL_TOP, Dyes.getModulation(i, Dyes._NULL.mRGBa)) };
+ rTextures[1][(i + 1)] = tmp1;
+ ITexture[] tmp2 = {
+ TextureFactory.of(Textures.BlockIcons.MACHINE_STEEL_SIDE, Dyes.getModulation(i, Dyes._NULL.mRGBa)) };
+ rTextures[2][(i + 1)] = tmp2;
+ }
+ return rTextures;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_BasicHull_SteelBricks.java b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_BasicHull_SteelBricks.java
new file mode 100644
index 0000000000..cd5f547d22
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_BasicHull_SteelBricks.java
@@ -0,0 +1,53 @@
+package gregtech.common.tileentities.machines;
+
+import gregtech.api.enums.Dyes;
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicHull_NonElectric;
+import gregtech.api.render.TextureFactory;
+
+public class GT_MetaTileEntity_BasicHull_SteelBricks extends GT_MetaTileEntity_BasicHull_NonElectric {
+
+ public GT_MetaTileEntity_BasicHull_SteelBricks(int aID, String aName, String aNameRegional, int aTier,
+ String aDescription) {
+ super(aID, aName, aNameRegional, aTier, aDescription);
+ }
+
+ public GT_MetaTileEntity_BasicHull_SteelBricks(String aName, int aTier, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_BasicHull_SteelBricks(String aName, int aTier, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_BasicHull_SteelBricks(
+ this.mName,
+ this.mTier,
+ this.mDescriptionArray,
+ this.mTextures);
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ ITexture[][][] rTextures = new ITexture[3][17][];
+ for (byte i = -1; i < 16; i = (byte) (i + 1)) {
+ ITexture[] tmp0 = { TextureFactory
+ .of(Textures.BlockIcons.MACHINE_STEELBRICKS_BOTTOM, Dyes.getModulation(i, Dyes._NULL.mRGBa)) };
+ rTextures[0][(i + 1)] = tmp0;
+ ITexture[] tmp1 = { TextureFactory
+ .of(Textures.BlockIcons.MACHINE_STEELBRICKS_TOP, Dyes.getModulation(i, Dyes._NULL.mRGBa)) };
+ rTextures[1][(i + 1)] = tmp1;
+ ITexture[] tmp2 = { TextureFactory
+ .of(Textures.BlockIcons.MACHINE_STEELBRICKS_SIDE, Dyes.getModulation(i, Dyes._NULL.mRGBa)) };
+ rTextures[2][(i + 1)] = tmp2;
+ }
+ return rTextures;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_CraftingInput_ME.java b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_CraftingInput_ME.java
new file mode 100644
index 0000000000..09f12904d7
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_CraftingInput_ME.java
@@ -0,0 +1,1040 @@
+package gregtech.common.tileentities.machines;
+
+import static gregtech.api.enums.GT_Values.TIER_COLORS;
+import static gregtech.api.enums.GT_Values.VN;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_ME_CRAFTING_INPUT_BUFFER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_ME_CRAFTING_INPUT_BUS;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nullable;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.InventoryCrafting;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.ChatComponentText;
+import net.minecraft.util.ChatComponentTranslation;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.Constants;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.event.ForgeEventFactory;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.jetbrains.annotations.NotNull;
+
+import com.glodblock.github.common.item.ItemFluidPacket;
+import com.google.common.collect.ImmutableList;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.math.Size;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotGroup;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+
+import appeng.api.AEApi;
+import appeng.api.implementations.ICraftingPatternItem;
+import appeng.api.implementations.IPowerChannelState;
+import appeng.api.networking.GridFlags;
+import appeng.api.networking.IGridNode;
+import appeng.api.networking.crafting.ICraftingPatternDetails;
+import appeng.api.networking.crafting.ICraftingProvider;
+import appeng.api.networking.crafting.ICraftingProviderHelper;
+import appeng.api.networking.events.MENetworkCraftingPatternChange;
+import appeng.api.networking.security.BaseActionSource;
+import appeng.api.networking.security.IActionHost;
+import appeng.api.networking.security.MachineSource;
+import appeng.api.storage.IMEMonitor;
+import appeng.api.storage.data.IAEFluidStack;
+import appeng.api.storage.data.IAEItemStack;
+import appeng.api.util.AECableType;
+import appeng.api.util.DimensionalCoord;
+import appeng.api.util.IInterfaceViewable;
+import appeng.core.AppEng;
+import appeng.core.sync.GuiBridge;
+import appeng.helpers.ICustomNameObject;
+import appeng.items.misc.ItemEncodedPattern;
+import appeng.items.tools.quartz.ToolQuartzCuttingKnife;
+import appeng.me.GridAccessException;
+import appeng.me.helpers.AENetworkProxy;
+import appeng.me.helpers.IGridProxyable;
+import appeng.util.IWideReadableNumberConverter;
+import appeng.util.Platform;
+import appeng.util.ReadableNumberConverter;
+import gregtech.GT_Mod;
+import gregtech.api.enums.ItemList;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.IConfigurationCircuitSupport;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.modularui.IAddGregtechLogo;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_InputBus;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.extensions.ArrayExt;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public class GT_MetaTileEntity_Hatch_CraftingInput_ME extends GT_MetaTileEntity_Hatch_InputBus
+ implements IConfigurationCircuitSupport, IAddGregtechLogo, IAddUIWidgets, IPowerChannelState, ICraftingProvider,
+ IGridProxyable, IDualInputHatch, ICustomNameObject, IInterfaceViewable {
+
+ // Each pattern slot in the crafting input hatch has its own internal inventory
+ public static class PatternSlot implements IDualInputInventory {
+
+ public interface SharedItemGetter {
+
+ ItemStack[] getSharedItem();
+ }
+
+ private final ItemStack pattern;
+ private final ICraftingPatternDetails patternDetails;
+ private final List<ItemStack> itemInventory;
+ private final List<FluidStack> fluidInventory;
+ private final SharedItemGetter sharedItemGetter;
+
+ public PatternSlot(ItemStack pattern, World world, SharedItemGetter getter) {
+ this.pattern = pattern;
+ this.patternDetails = ((ICraftingPatternItem) Objects.requireNonNull(pattern.getItem()))
+ .getPatternForItem(pattern, world);
+ this.itemInventory = new ArrayList<>();
+ this.fluidInventory = new ArrayList<>();
+ this.sharedItemGetter = getter;
+ }
+
+ public PatternSlot(ItemStack pattern, NBTTagCompound nbt, World world, SharedItemGetter getter) {
+ this.pattern = pattern;
+ this.patternDetails = ((ICraftingPatternItem) Objects.requireNonNull(pattern.getItem()))
+ .getPatternForItem(pattern, world);
+ this.itemInventory = new ArrayList<>();
+ this.fluidInventory = new ArrayList<>();
+ this.sharedItemGetter = getter;
+ NBTTagList inv = nbt.getTagList("inventory", Constants.NBT.TAG_COMPOUND);
+ for (int i = 0; i < inv.tagCount(); i++) {
+ NBTTagCompound tagItemStack = inv.getCompoundTagAt(i);
+ ItemStack item = GT_Utility.loadItem(tagItemStack);
+ if (item != null) {
+ if (item.stackSize > 0) {
+ itemInventory.add(item);
+ }
+ } else {
+ GT_Mod.GT_FML_LOGGER.warn(
+ "An error occurred while loading contents of ME Crafting Input Bus. This item has been voided: "
+ + tagItemStack);
+ }
+ }
+ NBTTagList fluidInv = nbt.getTagList("fluidInventory", Constants.NBT.TAG_COMPOUND);
+ for (int i = 0; i < fluidInv.tagCount(); i++) {
+ NBTTagCompound tagFluidStack = fluidInv.getCompoundTagAt(i);
+ FluidStack fluid = FluidStack.loadFluidStackFromNBT(tagFluidStack);
+ if (fluid != null) {
+ if (fluid.amount > 0) {
+ fluidInventory.add(fluid);
+ }
+ } else {
+ GT_Mod.GT_FML_LOGGER.warn(
+ "An error occurred while loading contents of ME Crafting Input Bus. This fluid has been voided: "
+ + tagFluidStack);
+ }
+ }
+ }
+
+ public boolean hasChanged(ItemStack newPattern, World world) {
+ return newPattern == null
+ || (!ItemStack.areItemStacksEqual(pattern, newPattern) && !this.patternDetails.equals(
+ ((ICraftingPatternItem) Objects.requireNonNull(pattern.getItem()))
+ .getPatternForItem(newPattern, world)));
+ }
+
+ public void updateSlotItems() {
+ for (int i = itemInventory.size() - 1; i >= 0; i--) {
+ ItemStack itemStack = itemInventory.get(i);
+ if (itemStack == null || itemStack.stackSize <= 0) {
+ itemInventory.remove(i);
+ }
+ }
+ }
+
+ public void updateSlotFluids() {
+ for (int i = fluidInventory.size() - 1; i >= 0; i--) {
+ FluidStack fluidStack = fluidInventory.get(i);
+ if (fluidStack == null || fluidStack.amount <= 0) {
+ fluidInventory.remove(i);
+ }
+ }
+ }
+
+ public boolean isItemEmpty() {
+ updateSlotItems();
+ return itemInventory.isEmpty() && sharedItemGetter.getSharedItem().length == 0;
+ }
+
+ public boolean isFluidEmpty() {
+ updateSlotFluids();
+ return fluidInventory.isEmpty();
+ }
+
+ public boolean isEmpty() {
+ return isItemEmpty() && isFluidEmpty();
+ }
+
+ @Override
+ public ItemStack[] getItemInputs() {
+ if (isItemEmpty()) return new ItemStack[0];
+ return ArrayUtils.addAll(itemInventory.toArray(new ItemStack[0]), sharedItemGetter.getSharedItem());
+ }
+
+ @Override
+ public FluidStack[] getFluidInputs() {
+ if (isFluidEmpty()) return new FluidStack[0];
+ return fluidInventory.toArray(new FluidStack[0]);
+ }
+
+ public ICraftingPatternDetails getPatternDetails() {
+ return patternDetails;
+ }
+
+ public void refund(AENetworkProxy proxy, BaseActionSource src) throws GridAccessException {
+ IMEMonitor<IAEItemStack> sg = proxy.getStorage()
+ .getItemInventory();
+ for (ItemStack itemStack : itemInventory) {
+ if (itemStack == null || itemStack.stackSize == 0) continue;
+ IAEItemStack rest = Platform.poweredInsert(
+ proxy.getEnergy(),
+ sg,
+ AEApi.instance()
+ .storage()
+ .createItemStack(itemStack),
+ src);
+ itemStack.stackSize = rest != null && rest.getStackSize() > 0 ? (int) rest.getStackSize() : 0;
+ }
+ IMEMonitor<IAEFluidStack> fsg = proxy.getStorage()
+ .getFluidInventory();
+ for (FluidStack fluidStack : fluidInventory) {
+ if (fluidStack == null || fluidStack.amount == 0) continue;
+ IAEFluidStack rest = Platform.poweredInsert(
+ proxy.getEnergy(),
+ fsg,
+ AEApi.instance()
+ .storage()
+ .createFluidStack(fluidStack),
+ src);
+ fluidStack.amount = rest != null && rest.getStackSize() > 0 ? (int) rest.getStackSize() : 0;
+ }
+ }
+
+ private void insertItem(ItemStack inserted) {
+ for (ItemStack itemStack : itemInventory) {
+ if (GT_Utility.areStacksEqual(inserted, itemStack)) {
+ if (itemStack.stackSize > Integer.MAX_VALUE - inserted.stackSize) {
+ inserted.stackSize -= Integer.MAX_VALUE - itemStack.stackSize;
+ itemStack.stackSize = Integer.MAX_VALUE;
+ } else {
+ itemStack.stackSize += inserted.stackSize;
+ return;
+ }
+ }
+ }
+ if (inserted.stackSize > 0) {
+ itemInventory.add(inserted);
+ }
+ }
+
+ private void insertFluid(FluidStack inserted) {
+ for (FluidStack fluidStack : fluidInventory) {
+ if (GT_Utility.areFluidsEqual(inserted, fluidStack)) {
+ if (fluidStack.amount > Integer.MAX_VALUE - inserted.amount) {
+ inserted.amount -= Integer.MAX_VALUE - fluidStack.amount;
+ fluidStack.amount = Integer.MAX_VALUE;
+ } else {
+ fluidStack.amount += inserted.amount;
+ return;
+ }
+ }
+ }
+ if (inserted.amount > 0) {
+ fluidInventory.add(inserted);
+ }
+ }
+
+ public boolean insertItemsAndFluids(InventoryCrafting inventoryCrafting) {
+ for (int i = 0; i < inventoryCrafting.getSizeInventory(); ++i) {
+ ItemStack itemStack = inventoryCrafting.getStackInSlot(i);
+ if (itemStack == null) continue;
+
+ if (itemStack.getItem() instanceof ItemFluidPacket) { // insert fluid
+ FluidStack fluidStack = ItemFluidPacket.getFluidStack(itemStack);
+ if (fluidStack != null) insertFluid(fluidStack);
+ } else { // insert item
+ insertItem(itemStack);
+ }
+ }
+ return true;
+ }
+
+ public NBTTagCompound writeToNBT(NBTTagCompound nbt) {
+ nbt.setTag("pattern", pattern.writeToNBT(new NBTTagCompound()));
+
+ NBTTagList itemInventoryNbt = new NBTTagList();
+ for (ItemStack itemStack : this.itemInventory) {
+ itemInventoryNbt.appendTag(GT_Utility.saveItem(itemStack));
+ }
+ nbt.setTag("inventory", itemInventoryNbt);
+
+ NBTTagList fluidInventoryNbt = new NBTTagList();
+ for (FluidStack fluidStack : fluidInventory) {
+ fluidInventoryNbt.appendTag(fluidStack.writeToNBT(new NBTTagCompound()));
+ }
+ nbt.setTag("fluidInventory", fluidInventoryNbt);
+
+ return nbt;
+ }
+ }
+
+ // mInventory is used for storing patterns, circuit and manual slot (typically NC items)
+ private static final int MAX_PATTERN_COUNT = 4 * 9;
+ private static final int SLOT_MANUAL_SIZE = 9;
+ private static final int MAX_INV_COUNT = MAX_PATTERN_COUNT + SLOT_MANUAL_SIZE + 1;
+ private static final int SLOT_CIRCUIT = MAX_PATTERN_COUNT;
+ private static final int SLOT_MANUAL_START = SLOT_CIRCUIT + 1;
+ private static final int MANUAL_SLOT_WINDOW = 10;
+ private BaseActionSource requestSource = null;
+ private @Nullable AENetworkProxy gridProxy = null;
+
+ // holds all internal inventories
+ private PatternSlot[] internalInventory = new PatternSlot[MAX_PATTERN_COUNT];
+
+ // a hash map for faster lookup of pattern slots, not necessarily all valid.
+ private Map<ICraftingPatternDetails, PatternSlot> patternDetailsPatternSlotMap = new HashMap<>(MAX_PATTERN_COUNT);
+
+ private boolean needPatternSync = true;
+ private boolean justHadNewItems = false;
+
+ private String customName = null;
+ private boolean supportFluids;
+ private boolean additionalConnection = false;
+
+ public GT_MetaTileEntity_Hatch_CraftingInput_ME(int aID, String aName, String aNameRegional,
+ boolean supportFluids) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ supportFluids ? 11 : 6,
+ MAX_INV_COUNT,
+ new String[] { "Advanced item input for Multiblocks",
+ "Hatch Tier: " + TIER_COLORS[supportFluids ? 11 : 6] + VN[supportFluids ? 11 : 6],
+ "Processes patterns directly from ME",
+ supportFluids ? "It supports patterns including fluids"
+ : "It does not support patterns including fluids",
+ "Change ME connection behavior by right-clicking with wire cutter" });
+ disableSort = true;
+ this.supportFluids = supportFluids;
+ }
+
+ public GT_MetaTileEntity_Hatch_CraftingInput_ME(String aName, int aTier, String[] aDescription,
+ ITexture[][][] aTextures, boolean supportFluids) {
+ super(aName, aTier, MAX_INV_COUNT, aDescription, aTextures);
+ this.supportFluids = supportFluids;
+ disableSort = true;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Hatch_CraftingInput_ME(mName, mTier, mDescriptionArray, mTextures, supportFluids);
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return getTexturesInactive(aBaseTexture);
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture,
+ TextureFactory.of(supportFluids ? OVERLAY_ME_CRAFTING_INPUT_BUFFER : OVERLAY_ME_CRAFTING_INPUT_BUS) };
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) {
+ super.onPostTick(aBaseMetaTileEntity, aTimer);
+
+ if (getBaseMetaTileEntity().isServerSide()) {
+ if (needPatternSync && aTimer % 10 == 0) {
+ needPatternSync = !postMEPatternChange();
+ }
+ if (aTimer % 20 == 0) {
+ getBaseMetaTileEntity().setActive(isActive());
+ }
+ }
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ super.onFirstTick(aBaseMetaTileEntity);
+ getProxy().onReady();
+ }
+
+ @Override
+ public IGridNode getGridNode(ForgeDirection dir) {
+ return getProxy().getNode();
+ }
+
+ @Override
+ public AECableType getCableConnectionType(ForgeDirection forgeDirection) {
+ return isOutputFacing(forgeDirection) ? AECableType.SMART : AECableType.NONE;
+ }
+
+ private void updateValidGridProxySides() {
+ if (additionalConnection) {
+ getProxy().setValidSides(EnumSet.complementOf(EnumSet.of(ForgeDirection.UNKNOWN)));
+ } else {
+ getProxy().setValidSides(EnumSet.of(getBaseMetaTileEntity().getFrontFacing()));
+ }
+ }
+
+ @Override
+ public void onFacingChange() {
+ updateValidGridProxySides();
+ }
+
+ @Override
+ public void securityBreak() {}
+
+ @Override
+ public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer,
+ float aX, float aY, float aZ) {
+ additionalConnection = !additionalConnection;
+ updateValidGridProxySides();
+ aPlayer.addChatComponentMessage(
+ new ChatComponentTranslation("GT5U.hatch.additionalConnection." + additionalConnection));
+ return true;
+ }
+
+ @Override
+ public AENetworkProxy getProxy() {
+ if (gridProxy == null) {
+ gridProxy = new AENetworkProxy(this, "proxy", ItemList.Hatch_CraftingInput_Bus_ME.get(1), true);
+ gridProxy.setFlags(GridFlags.REQUIRE_CHANNEL);
+ updateValidGridProxySides();
+ if (getBaseMetaTileEntity().getWorld() != null) gridProxy.setOwner(
+ getBaseMetaTileEntity().getWorld()
+ .getPlayerEntityByName(getBaseMetaTileEntity().getOwnerName()));
+ }
+
+ return this.gridProxy;
+ }
+
+ @Override
+ public DimensionalCoord getLocation() {
+ return new DimensionalCoord(
+ getBaseMetaTileEntity().getWorld(),
+ getBaseMetaTileEntity().getXCoord(),
+ getBaseMetaTileEntity().getYCoord(),
+ getBaseMetaTileEntity().getZCoord());
+ }
+
+ @Override
+ public int rows() {
+ return 4;
+ }
+
+ @Override
+ public int rowSize() {
+ return 9;
+ }
+
+ @Override
+ public IInventory getPatterns() {
+ return this;
+ }
+
+ @Override
+ public String getName() {
+ if (hasCustomName()) {
+ return getCustomName();
+ }
+ StringBuilder name = new StringBuilder();
+ if (getCrafterIcon() != null) {
+ name.append(getCrafterIcon().getDisplayName());
+ } else {
+ name.append(getLocalName());
+ }
+
+ if (mInventory[SLOT_CIRCUIT] != null) {
+ name.append(" - ");
+ name.append(mInventory[SLOT_CIRCUIT].getItemDamage());
+ }
+ if (mInventory[SLOT_MANUAL_START] != null) {
+ name.append(" - ");
+ name.append(mInventory[SLOT_MANUAL_START].getDisplayName());
+ }
+ return name.toString();
+ }
+
+ @Override
+ public TileEntity getTileEntity() {
+ return (TileEntity) getBaseMetaTileEntity();
+ }
+
+ @Override
+ public boolean shouldDisplay() {
+ return true;
+ }
+
+ @Override
+ public void gridChanged() {
+ needPatternSync = true;
+ }
+
+ @Override
+ public boolean isPowered() {
+ return getProxy() != null && getProxy().isPowered();
+ }
+
+ @Override
+ public boolean isActive() {
+ return getProxy() != null && getProxy().isActive();
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+
+ // save internalInventory
+ NBTTagList internalInventoryNBT = new NBTTagList();
+ for (int i = 0; i < internalInventory.length; i++) {
+ if (internalInventory[i] != null) {
+ NBTTagCompound internalInventorySlotNBT = new NBTTagCompound();
+ internalInventorySlotNBT.setInteger("patternSlot", i);
+ internalInventorySlotNBT
+ .setTag("patternSlotNBT", internalInventory[i].writeToNBT(new NBTTagCompound()));
+ internalInventoryNBT.appendTag(internalInventorySlotNBT);
+ }
+ }
+ aNBT.setTag("internalInventory", internalInventoryNBT);
+ if (customName != null) aNBT.setString("customName", customName);
+ aNBT.setBoolean("additionalConnection", additionalConnection);
+ getProxy().writeToNBT(aNBT);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ // load internalInventory
+ NBTTagList internalInventoryNBT = aNBT.getTagList("internalInventory", Constants.NBT.TAG_COMPOUND);
+ for (int i = 0; i < internalInventoryNBT.tagCount(); i++) {
+ NBTTagCompound internalInventorySlotNBT = internalInventoryNBT.getCompoundTagAt(i);
+ int patternSlot = internalInventorySlotNBT.getInteger("patternSlot");
+ NBTTagCompound patternSlotNBT = internalInventorySlotNBT.getCompoundTag("patternSlotNBT");
+ ItemStack pattern = ItemStack.loadItemStackFromNBT(patternSlotNBT.getCompoundTag("pattern"));
+ if (pattern != null) {
+ internalInventory[patternSlot] = new PatternSlot(
+ pattern,
+ patternSlotNBT,
+ getBaseMetaTileEntity().getWorld(),
+ this::getSharedItems);
+ } else {
+ GT_Mod.GT_FML_LOGGER.warn(
+ "An error occurred while loading contents of ME Crafting Input Bus. This pattern has been voided: "
+ + patternSlotNBT);
+ }
+ }
+
+ // Migrate from 4x8 to 4x9 pattern inventory
+ int oldPatternCount = 4 * 8;
+ int oldSlotManual = oldPatternCount + 1;
+ int oldSlotCircuit = oldPatternCount;
+
+ if (internalInventory[oldSlotManual] == null && mInventory[oldSlotManual] != null) {
+ mInventory[SLOT_MANUAL_START] = mInventory[oldSlotManual];
+ mInventory[oldSlotManual] = null;
+ }
+ if (internalInventory[oldSlotCircuit] == null && mInventory[oldSlotCircuit] != null) {
+ mInventory[SLOT_CIRCUIT] = mInventory[oldSlotCircuit];
+ mInventory[oldSlotCircuit] = null;
+ }
+
+ // reconstruct patternDetailsPatternSlotMap
+ patternDetailsPatternSlotMap.clear();
+ for (PatternSlot patternSlot : internalInventory) {
+ if (patternSlot != null) {
+ patternDetailsPatternSlotMap.put(patternSlot.getPatternDetails(), patternSlot);
+ }
+ }
+
+ if (aNBT.hasKey("customName")) customName = aNBT.getString("customName");
+ additionalConnection = aNBT.getBoolean("additionalConnection");
+
+ getProxy().readFromNBT(aNBT);
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return true;
+ }
+
+ private String describePattern(ICraftingPatternDetails patternDetails) {
+ return Arrays.stream(patternDetails.getCondensedOutputs())
+ .map(
+ aeItemStack -> aeItemStack.getItem()
+ .getItemStackDisplayName(aeItemStack.getItemStack()))
+ .collect(Collectors.joining(", "));
+ }
+
+ @Override
+ public String[] getInfoData() {
+ List<String> ret = new ArrayList<>();
+ ret.add(
+ "The bus is " + ((getProxy() != null && getProxy().isActive()) ? EnumChatFormatting.GREEN + "online"
+ : EnumChatFormatting.RED + "offline" + getAEDiagnostics()) + EnumChatFormatting.RESET);
+ ret.add("Internal Inventory: ");
+ int i = 0;
+ for (PatternSlot slot : internalInventory) {
+ if (slot == null) continue;
+ IWideReadableNumberConverter nc = ReadableNumberConverter.INSTANCE;
+
+ i += 1;
+ ret.add(
+ "Slot " + i
+ + " "
+ + EnumChatFormatting.BLUE
+ + describePattern(slot.patternDetails)
+ + EnumChatFormatting.RESET);
+ Map<GT_Utility.ItemId, Long> itemMap = GT_Utility.convertItemListToMap(slot.itemInventory);
+ for (Map.Entry<GT_Utility.ItemId, Long> entry : itemMap.entrySet()) {
+ ItemStack item = entry.getKey()
+ .getItemStack();
+ long amount = entry.getValue();
+ ret.add(
+ item.getItem()
+ .getItemStackDisplayName(item) + ": "
+ + EnumChatFormatting.GOLD
+ + nc.toWideReadableForm(amount)
+ + EnumChatFormatting.RESET);
+ }
+ Map<Fluid, Long> fluidMap = GT_Utility.convertFluidListToMap(slot.fluidInventory);
+ for (Map.Entry<Fluid, Long> entry : fluidMap.entrySet()) {
+ FluidStack fluid = new FluidStack(entry.getKey(), 1);
+ long amount = entry.getValue();
+ ret.add(
+ fluid.getLocalizedName() + ": "
+ + EnumChatFormatting.AQUA
+ + nc.toWideReadableForm(amount)
+ + EnumChatFormatting.RESET);
+ }
+ }
+ return ret.toArray(new String[0]);
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public int getCircuitSlot() {
+ return SLOT_CIRCUIT;
+ }
+
+ @Override
+ public int getCircuitSlotX() {
+ return 170;
+ }
+
+ @Override
+ public int getCircuitSlotY() {
+ return 64;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return true;
+ }
+
+ @Override
+ public int getGUIWidth() {
+ return super.getGUIWidth() + 16;
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.@NotNull Builder builder, UIBuildContext buildContext) {
+ buildContext.addSyncedWindow(MANUAL_SLOT_WINDOW, this::createSlotManualWindow);
+ builder.widget(
+ SlotGroup.ofItemHandler(inventoryHandler, 9)
+ .startFromSlot(0)
+ .endAtSlot(MAX_PATTERN_COUNT - 1)
+ .phantom(false)
+ .background(getGUITextureSet().getItemSlot(), GT_UITextures.OVERLAY_SLOT_PATTERN_ME)
+ .widgetCreator(slot -> new SlotWidget(slot) {
+
+ @Override
+ protected ItemStack getItemStackForRendering(Slot slotIn) {
+ ItemStack stack = slot.getStack();
+ if (stack == null || !(stack.getItem() instanceof ItemEncodedPattern patternItem)) {
+ return stack;
+ }
+ ItemStack output = patternItem.getOutput(stack);
+ return output != null ? output : stack;
+ }
+ }.setFilter(itemStack -> itemStack.getItem() instanceof ICraftingPatternItem)
+ .setChangeListener(() -> onPatternChange(slot.getSlotIndex(), slot.getStack())))
+ .build()
+ .setPos(7, 9))
+ .widget(new ButtonWidget().setOnClick((clickData, widget) -> {
+ if (clickData.mouseButton == 0) {
+ widget.getContext()
+ .openSyncedWindow(MANUAL_SLOT_WINDOW);
+ }
+ })
+ .setPlayClickSound(true)
+ .setBackground(GT_UITextures.BUTTON_STANDARD, GT_UITextures.OVERLAY_BUTTON_PLUS_LARGE)
+ .addTooltips(ImmutableList.of("Place manual items"))
+ .setSize(16, 16)
+ .setPos(170, 45))
+ .widget(new ButtonWidget().setOnClick((clickData, widget) -> {
+ if (clickData.mouseButton == 0) {
+ refundAll();
+ }
+ })
+ .setPlayClickSound(true)
+ .setBackground(GT_UITextures.BUTTON_STANDARD, GT_UITextures.OVERLAY_BUTTON_EXPORT)
+ .addTooltips(ImmutableList.of("Return all internally stored items back to AE"))
+ .setSize(16, 16)
+ .setPos(170, 28));
+ }
+
+ @Override
+ public void updateSlots() {
+ for (int slotId = SLOT_MANUAL_START; slotId < SLOT_MANUAL_START + SLOT_MANUAL_SIZE; ++slotId) {
+ if (mInventory[slotId] != null && mInventory[slotId].stackSize <= 0) mInventory[slotId] = null;
+ }
+ }
+
+ private BaseActionSource getRequest() {
+ if (requestSource == null) requestSource = new MachineSource((IActionHost) getBaseMetaTileEntity());
+ return requestSource;
+ }
+
+ private void onPatternChange(int index, ItemStack newItem) {
+ if (!getBaseMetaTileEntity().isServerSide()) return;
+
+ World world = getBaseMetaTileEntity().getWorld();
+
+ // remove old if applicable
+ PatternSlot originalPattern = internalInventory[index];
+ if (originalPattern != null) {
+ if (originalPattern.hasChanged(newItem, world)) {
+ try {
+ originalPattern.refund(getProxy(), getRequest());
+ } catch (GridAccessException ignored) {}
+ internalInventory[index] = null;
+ needPatternSync = true;
+ } else {
+ return; // nothing has changed
+ }
+ }
+
+ // original does not exist or has changed
+ if (newItem == null || !(newItem.getItem() instanceof ICraftingPatternItem)) return;
+
+ PatternSlot patternSlot = new PatternSlot(newItem, world, this::getSharedItems);
+ internalInventory[index] = patternSlot;
+ patternDetailsPatternSlotMap.put(patternSlot.getPatternDetails(), patternSlot);
+
+ needPatternSync = true;
+ }
+
+ public ItemStack[] getSharedItems() {
+ ItemStack[] sharedItems = new ItemStack[SLOT_MANUAL_SIZE + 1];
+ sharedItems[0] = mInventory[SLOT_CIRCUIT];
+ System.arraycopy(mInventory, SLOT_MANUAL_START, sharedItems, 1, SLOT_MANUAL_SIZE);
+ return ArrayExt.withoutNulls(sharedItems, ItemStack[]::new);
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ NBTTagCompound tag = accessor.getNBTData();
+ if (tag.hasKey("name"))
+ currenttip.add(EnumChatFormatting.AQUA + tag.getString("name") + EnumChatFormatting.RESET);
+ if (tag.hasKey("inventory")) {
+ NBTTagList inventory = tag.getTagList("inventory", Constants.NBT.TAG_COMPOUND);
+ for (int i = 0; i < inventory.tagCount(); ++i) {
+ NBTTagCompound item = inventory.getCompoundTagAt(i);
+ String name = item.getString("name");
+ long amount = item.getLong("amount");
+ currenttip.add(
+ name + ": "
+ + EnumChatFormatting.GOLD
+ + ReadableNumberConverter.INSTANCE.toWideReadableForm(amount)
+ + EnumChatFormatting.RESET);
+ }
+ }
+ super.getWailaBody(itemStack, currenttip, accessor, config);
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+
+ NBTTagList inventory = new NBTTagList();
+ HashMap<String, Long> nameToAmount = new HashMap<>();
+ for (Iterator<PatternSlot> it = inventories(); it.hasNext();) {
+ PatternSlot i = it.next();
+ for (ItemStack item : i.itemInventory) {
+ if (item != null && item.stackSize > 0) {
+ String name = item.getDisplayName();
+ nameToAmount.merge(name, (long) item.stackSize, Long::sum);
+ }
+ }
+ for (FluidStack fluid : i.fluidInventory) {
+ if (fluid != null && fluid.amount > 0) {
+ String name = fluid.getLocalizedName();
+ nameToAmount.merge(name, (long) fluid.amount, Long::sum);
+ }
+ }
+ }
+ for (Map.Entry<String, Long> entry : nameToAmount.entrySet()) {
+ NBTTagCompound item = new NBTTagCompound();
+ item.setString("name", entry.getKey());
+ item.setLong("amount", entry.getValue());
+ inventory.appendTag(item);
+ }
+
+ tag.setTag("inventory", inventory);
+ if (!Objects.equals(getName(), getLocalName())) {
+ tag.setString("name", getName());
+ }
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ }
+
+ @Override
+ public void provideCrafting(ICraftingProviderHelper craftingTracker) {
+ if (!isActive()) return;
+
+ for (PatternSlot slot : internalInventory) {
+ if (slot == null) continue;
+ ICraftingPatternDetails details = slot.getPatternDetails();
+ if (details == null) {
+ GT_Mod.GT_FML_LOGGER.warn(
+ "Found an invalid pattern at " + getBaseMetaTileEntity().getCoords()
+ + " in dim "
+ + getBaseMetaTileEntity().getWorld().provider.dimensionId);
+ continue;
+ }
+ craftingTracker.addCraftingOption(this, details);
+ }
+ }
+
+ @Override
+ public boolean pushPattern(ICraftingPatternDetails patternDetails, InventoryCrafting table) {
+ if (!isActive()) return false;
+
+ if (!supportFluids) {
+ for (int i = 0; i < table.getSizeInventory(); ++i) {
+ ItemStack itemStack = table.getStackInSlot(i);
+ if (itemStack == null) continue;
+ if (itemStack.getItem() instanceof ItemFluidPacket) return false;
+ }
+ }
+ if (!patternDetailsPatternSlotMap.get(patternDetails)
+ .insertItemsAndFluids(table)) {
+ return false;
+ }
+ justHadNewItems = true;
+ return true;
+ }
+
+ @Override
+ public boolean isBusy() {
+ return false;
+ }
+
+ @Override
+ public Iterator<PatternSlot> inventories() {
+ return Arrays.stream(internalInventory)
+ .filter(Objects::nonNull)
+ .iterator();
+ }
+
+ @Override
+ public void onBlockDestroyed() {
+ refundAll();
+ super.onBlockDestroyed();
+ }
+
+ private void refundAll() {
+ for (PatternSlot slot : internalInventory) {
+ if (slot == null) continue;
+ try {
+ slot.refund(getProxy(), getRequest());
+ } catch (GridAccessException ignored) {}
+ }
+ }
+
+ @Override
+ public boolean justUpdated() {
+ boolean ret = justHadNewItems;
+ justHadNewItems = false;
+ return ret;
+ }
+
+ @Override
+ public void onLeftclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ if (!(aPlayer instanceof EntityPlayerMP)) return;
+
+ ItemStack dataStick = aPlayer.inventory.getCurrentItem();
+ if (!ItemList.Tool_DataStick.isStackEqual(dataStick, false, true)) return;
+
+ NBTTagCompound tag = new NBTTagCompound();
+ tag.setString("type", "CraftingInputBuffer");
+ tag.setInteger("x", aBaseMetaTileEntity.getXCoord());
+ tag.setInteger("y", aBaseMetaTileEntity.getYCoord());
+ tag.setInteger("z", aBaseMetaTileEntity.getZCoord());
+
+ dataStick.stackTagCompound = tag;
+ dataStick.setStackDisplayName(
+ "Crafting Input Buffer Link Data Stick (" + aBaseMetaTileEntity
+ .getXCoord() + ", " + aBaseMetaTileEntity.getYCoord() + ", " + aBaseMetaTileEntity.getZCoord() + ")");
+ aPlayer.addChatMessage(new ChatComponentText("Saved Link Data to Data Stick"));
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer, ForgeDirection side,
+ float aX, float aY, float aZ) {
+ final ItemStack is = aPlayer.inventory.getCurrentItem();
+ if (is != null && is.getItem() instanceof ToolQuartzCuttingKnife) {
+ if (ForgeEventFactory.onItemUseStart(aPlayer, is, 1) <= 0) return false;
+ IGregTechTileEntity te = getBaseMetaTileEntity();
+ aPlayer.openGui(
+ AppEng.instance(),
+ GuiBridge.GUI_RENAMER.ordinal() << 5 | (side.ordinal()),
+ te.getWorld(),
+ te.getXCoord(),
+ te.getYCoord(),
+ te.getZCoord());
+ return true;
+ }
+ return super.onRightclick(aBaseMetaTileEntity, aPlayer, side, aX, aY, aZ);
+ }
+
+ @Override
+ public ItemStack getCrafterIcon() {
+ return getMachineCraftingIcon();
+ }
+
+ private boolean postMEPatternChange() {
+ // don't post until it's active
+ if (!getProxy().isActive()) return false;
+ try {
+ getProxy().getGrid()
+ .postEvent(new MENetworkCraftingPatternChange(this, getProxy().getNode()));
+ } catch (GridAccessException ignored) {
+ return false;
+ }
+ return true;
+ }
+
+ protected ModularWindow createSlotManualWindow(final EntityPlayer player) {
+ final int WIDTH = 68;
+ final int HEIGHT = 68;
+ final int PARENT_WIDTH = getGUIWidth();
+ final int PARENT_HEIGHT = getGUIHeight();
+ ModularWindow.Builder builder = ModularWindow.builder(WIDTH, HEIGHT);
+ builder.setBackground(GT_UITextures.BACKGROUND_SINGLEBLOCK_DEFAULT);
+ builder.setGuiTint(getGUIColorization());
+ builder.setDraggable(true);
+ // make sure the manual window is within the parent window
+ // otherwise picking up manual items would toss them
+ // See GuiContainer.java flag1
+ builder.setPos(
+ (size, window) -> Alignment.Center.getAlignedPos(size, new Size(PARENT_WIDTH, PARENT_HEIGHT))
+ .add(Alignment.TopRight.getAlignedPos(new Size(PARENT_WIDTH, PARENT_HEIGHT), new Size(WIDTH, HEIGHT))));
+ builder.widget(
+ SlotGroup.ofItemHandler(inventoryHandler, 3)
+ .startFromSlot(SLOT_MANUAL_START)
+ .endAtSlot(SLOT_MANUAL_START + SLOT_MANUAL_SIZE - 1)
+ .phantom(false)
+ .background(getGUITextureSet().getItemSlot())
+ .build()
+ .setPos(7, 7));
+ return builder.build();
+ }
+
+ @Override
+ public void setInventorySlotContents(int aIndex, ItemStack aStack) {
+ super.setInventorySlotContents(aIndex, aStack);
+ if (aIndex >= MAX_PATTERN_COUNT) return;
+ onPatternChange(aIndex, aStack);
+ needPatternSync = true;
+ }
+
+ @Override
+ public String getCustomName() {
+ return customName;
+ }
+
+ @Override
+ public boolean hasCustomName() {
+ return customName != null;
+ }
+
+ @Override
+ public void setCustomName(String name) {
+ customName = name;
+ }
+
+ @Override
+ public Optional<IDualInputInventory> getFirstNonEmptyInventory() {
+ for (PatternSlot slot : internalInventory) {
+ if (slot != null && !slot.isEmpty()) return Optional.of(slot);
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public boolean supportsFluids() {
+ return this.supportFluids;
+ }
+
+ @Override
+ public List<ItemStack> getItemsForHoloGlasses() {
+ List<ItemStack> list = new ArrayList<>();
+ for (PatternSlot slot : internalInventory) {
+ if (slot == null) continue;
+
+ IAEItemStack[] outputs = slot.getPatternDetails()
+ .getCondensedOutputs();
+ list.add(outputs[0].getItemStack());
+ }
+ return list;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_CraftingInput_Slave.java b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_CraftingInput_Slave.java
new file mode 100644
index 0000000000..6dede1bea5
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_CraftingInput_Slave.java
@@ -0,0 +1,262 @@
+package gregtech.common.tileentities.machines;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_ME_CRAFTING_INPUT_SLAVE;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.ChatComponentText;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.enums.ItemList;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_InputBus;
+import gregtech.api.render.TextureFactory;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public class GT_MetaTileEntity_Hatch_CraftingInput_Slave extends GT_MetaTileEntity_Hatch_InputBus
+ implements IDualInputHatch {
+
+ private GT_MetaTileEntity_Hatch_CraftingInput_ME master; // use getMaster() to access
+ private int masterX, masterY, masterZ;
+ private boolean masterSet = false; // indicate if values of masterX, masterY, masterZ are valid
+
+ public GT_MetaTileEntity_Hatch_CraftingInput_Slave(int aID, String aName, String aNameRegional) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ 6,
+ 0,
+ new String[] { "Proxy for Crafting Input Buffer/Bus",
+ "Link with Crafting Input Buffer/Bus using Data Stick to share inventory",
+ "Left click on the Crafting Input Buffer/Bus, then right click on this block to link them", });
+ disableSort = true;
+ }
+
+ public GT_MetaTileEntity_Hatch_CraftingInput_Slave(String aName, int aTier, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, 0, aDescription, aTextures);
+ disableSort = true;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Hatch_CraftingInput_Slave(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return getTexturesInactive(aBaseTexture);
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_ME_CRAFTING_INPUT_SLAVE) };
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) {
+ super.onPostTick(aBaseMetaTileEntity, aTimer);
+ if (aTimer % 100 == 0 && masterSet && getMaster() == null) {
+ trySetMasterFromCoord(masterX, masterY, masterZ);
+ }
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+
+ if (aNBT.hasKey("master")) {
+ NBTTagCompound masterNBT = aNBT.getCompoundTag("master");
+ masterX = masterNBT.getInteger("x");
+ masterY = masterNBT.getInteger("y");
+ masterZ = masterNBT.getInteger("z");
+ masterSet = true;
+ }
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ if (masterSet) {
+ NBTTagCompound masterNBT = new NBTTagCompound();
+ masterNBT.setInteger("x", masterX);
+ masterNBT.setInteger("y", masterY);
+ masterNBT.setInteger("z", masterZ);
+ aNBT.setTag("master", masterNBT);
+ }
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return true;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ var ret = new ArrayList<String>();
+ if (getMaster() != null) {
+ ret.add(
+ "This bus is linked to the Crafting Input Buffer at " + masterX
+ + ", "
+ + masterY
+ + ", "
+ + masterZ
+ + ".");
+ ret.addAll(Arrays.asList(getMaster().getInfoData()));
+ } else ret.add("This bus is not linked to any Crafting Input Buffer.");
+ return ret.toArray(new String[0]);
+ }
+
+ public GT_MetaTileEntity_Hatch_CraftingInput_ME getMaster() {
+ if (master == null) return null;
+ if (master.getBaseMetaTileEntity() == null) { // master disappeared
+ master = null;
+ }
+ return master;
+ }
+
+ @Override
+ public byte getTierForStructure() {
+ return getMaster() == null ? super.getTierForStructure() : getMaster().getTierForStructure();
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public Iterator<GT_MetaTileEntity_Hatch_CraftingInput_ME.PatternSlot> inventories() {
+ return getMaster() != null ? getMaster().inventories() : Collections.emptyIterator();
+ }
+
+ @Override
+ public Optional<IDualInputInventory> getFirstNonEmptyInventory() {
+ return getMaster() != null ? getMaster().getFirstNonEmptyInventory() : Optional.empty();
+ }
+
+ @Override
+ public boolean supportsFluids() {
+ return getMaster() != null && getMaster().supportsFluids();
+ }
+
+ @Override
+ public boolean justUpdated() {
+ return getMaster() != null && getMaster().justUpdated();
+ }
+
+ public GT_MetaTileEntity_Hatch_CraftingInput_ME trySetMasterFromCoord(int x, int y, int z) {
+ var tileEntity = getBaseMetaTileEntity().getWorld()
+ .getTileEntity(x, y, z);
+ if (tileEntity == null) return null;
+ if (!(tileEntity instanceof IGregTechTileEntity gtTileEntity)) return null;
+ var metaTileEntity = gtTileEntity.getMetaTileEntity();
+ if (!(metaTileEntity instanceof GT_MetaTileEntity_Hatch_CraftingInput_ME)) return null;
+ masterX = x;
+ masterY = y;
+ masterZ = z;
+ masterSet = true;
+ master = (GT_MetaTileEntity_Hatch_CraftingInput_ME) metaTileEntity;
+ return master;
+ }
+
+ private boolean tryLinkDataStick(EntityPlayer aPlayer) {
+ ItemStack dataStick = aPlayer.inventory.getCurrentItem();
+
+ if (!ItemList.Tool_DataStick.isStackEqual(dataStick, false, true)) {
+ return false;
+ }
+ if (!dataStick.hasTagCompound() || !dataStick.stackTagCompound.getString("type")
+ .equals("CraftingInputBuffer")) {
+ return false;
+ }
+
+ NBTTagCompound nbt = dataStick.stackTagCompound;
+ int x = nbt.getInteger("x");
+ int y = nbt.getInteger("y");
+ int z = nbt.getInteger("z");
+ if (trySetMasterFromCoord(x, y, z) != null) {
+ aPlayer.addChatMessage(new ChatComponentText("Link successful"));
+ return true;
+ }
+ aPlayer.addChatMessage(new ChatComponentText("Link failed"));
+ return true;
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ if (!(aPlayer instanceof EntityPlayerMP)) {
+ return false;
+ }
+ if (tryLinkDataStick(aPlayer)) {
+ return true;
+ }
+ var master = getMaster();
+ if (master != null) {
+ return master.onRightclick(master.getBaseMetaTileEntity(), aPlayer);
+ }
+ return false;
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ NBTTagCompound tag = accessor.getNBTData();
+ currenttip.add((tag.getBoolean("linked") ? "Linked" : "Not linked"));
+
+ if (tag.hasKey("masterX")) {
+ currenttip.add(
+ "Bound to " + tag
+ .getInteger("masterX") + ", " + tag.getInteger("masterY") + ", " + tag.getInteger("masterZ"));
+ }
+
+ if (tag.hasKey("masterName")) {
+ currenttip.add(EnumChatFormatting.GOLD + tag.getString("masterName") + EnumChatFormatting.RESET);
+ }
+
+ super.getWailaBody(itemStack, currenttip, accessor, config);
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+
+ tag.setBoolean("linked", getMaster() != null);
+ if (masterSet) {
+ tag.setInteger("masterX", masterX);
+ tag.setInteger("masterY", masterY);
+ tag.setInteger("masterZ", masterZ);
+ }
+ if (getMaster() != null) tag.setString("masterName", getMaster().getName());
+
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ }
+
+ @Override
+ public List<ItemStack> getItemsForHoloGlasses() {
+ return getMaster() != null ? getMaster().getItemsForHoloGlasses() : null;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_InputBus_ME.java b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_InputBus_ME.java
new file mode 100644
index 0000000000..36b20acc55
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_InputBus_ME.java
@@ -0,0 +1,887 @@
+package gregtech.common.tileentities.machines;
+
+import static gregtech.api.enums.GT_Values.TIER_COLORS;
+import static gregtech.api.enums.GT_Values.VN;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_ME_INPUT_HATCH;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_ME_INPUT_HATCH_ACTIVE;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.ChatComponentTranslation;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizons.modularui.api.ModularUITextures;
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.math.Color;
+import com.gtnewhorizons.modularui.api.math.Size;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.internal.wrapper.BaseSlot;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.CycleButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotGroup;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+import com.gtnewhorizons.modularui.common.widget.textfield.NumericWidget;
+
+import appeng.api.config.Actionable;
+import appeng.api.config.PowerMultiplier;
+import appeng.api.implementations.IPowerChannelState;
+import appeng.api.networking.GridFlags;
+import appeng.api.networking.security.BaseActionSource;
+import appeng.api.networking.security.IActionHost;
+import appeng.api.networking.security.MachineSource;
+import appeng.api.storage.IMEMonitor;
+import appeng.api.storage.data.IAEItemStack;
+import appeng.api.util.AECableType;
+import appeng.core.localization.WailaText;
+import appeng.me.GridAccessException;
+import appeng.me.helpers.AENetworkProxy;
+import appeng.me.helpers.IGridProxyable;
+import appeng.util.item.AEItemStack;
+import gregtech.api.enums.ItemList;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.IConfigurationCircuitSupport;
+import gregtech.api.interfaces.IDataCopyable;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.modularui.IAddGregtechLogo;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_InputBus;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_MultiBlockBase;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.recipe.check.SimpleCheckRecipeResult;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.shutdown.ShutDownReasonRegistry;
+import gregtech.common.gui.modularui.widget.AESlotWidget;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public class GT_MetaTileEntity_Hatch_InputBus_ME extends GT_MetaTileEntity_Hatch_InputBus
+ implements IConfigurationCircuitSupport, IRecipeProcessingAwareHatch, IAddGregtechLogo, IAddUIWidgets,
+ IPowerChannelState, ISmartInputHatch, IDataCopyable {
+
+ private static final int SLOT_COUNT = 16;
+ public static final String COPIED_DATA_IDENTIFIER = "stockingBus";
+ private BaseActionSource requestSource = null;
+ private @Nullable AENetworkProxy gridProxy = null;
+ private final ItemStack[] shadowInventory = new ItemStack[SLOT_COUNT];
+ private final int[] savedStackSizes = new int[SLOT_COUNT];
+ private boolean processingRecipe = false;
+ private final boolean autoPullAvailable;
+ private boolean autoPullItemList = false;
+ private int minAutoPullStackSize = 1;
+ private int autoPullRefreshTime = 100;
+ private static final int CONFIG_WINDOW_ID = 10;
+ private boolean additionalConnection = false;
+ private boolean justHadNewItems = false;
+ private boolean expediteRecipeCheck = false;
+
+ public GT_MetaTileEntity_Hatch_InputBus_ME(int aID, boolean autoPullAvailable, String aName, String aNameRegional) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ autoPullAvailable ? 6 : 3,
+ SLOT_COUNT * 2 + 2,
+ getDescriptionArray(autoPullAvailable));
+ this.autoPullAvailable = autoPullAvailable;
+ disableSort = true;
+ }
+
+ public GT_MetaTileEntity_Hatch_InputBus_ME(String aName, boolean autoPullAvailable, int aTier,
+ String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, SLOT_COUNT * 2 + 2, aDescription, aTextures);
+ this.autoPullAvailable = autoPullAvailable;
+ disableSort = true;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Hatch_InputBus_ME(mName, autoPullAvailable, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_ME_INPUT_HATCH_ACTIVE) };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_ME_INPUT_HATCH) };
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) {
+ if (getBaseMetaTileEntity().isServerSide()) {
+ if (aTimer % autoPullRefreshTime == 0 && autoPullItemList) {
+ refreshItemList();
+ }
+ if (aTimer % 20 == 0) {
+ getBaseMetaTileEntity().setActive(isActive());
+ }
+ }
+ super.onPostTick(aBaseMetaTileEntity, aTimer);
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ super.onFirstTick(aBaseMetaTileEntity);
+ getProxy().onReady();
+ }
+
+ @Override
+ public AECableType getCableConnectionType(ForgeDirection forgeDirection) {
+ return isOutputFacing(forgeDirection) ? AECableType.SMART : AECableType.NONE;
+ }
+
+ private void updateValidGridProxySides() {
+ if (additionalConnection) {
+ getProxy().setValidSides(EnumSet.complementOf(EnumSet.of(ForgeDirection.UNKNOWN)));
+ } else {
+ getProxy().setValidSides(EnumSet.of(getBaseMetaTileEntity().getFrontFacing()));
+ }
+ }
+
+ @Override
+ public void onFacingChange() {
+ updateValidGridProxySides();
+ }
+
+ @Override
+ public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer,
+ float aX, float aY, float aZ) {
+ additionalConnection = !additionalConnection;
+ updateValidGridProxySides();
+ aPlayer.addChatComponentMessage(
+ new ChatComponentTranslation("GT5U.hatch.additionalConnection." + additionalConnection));
+ return true;
+ }
+
+ @Override
+ public AENetworkProxy getProxy() {
+ if (gridProxy == null) {
+ if (getBaseMetaTileEntity() instanceof IGridProxyable) {
+ gridProxy = new AENetworkProxy(
+ (IGridProxyable) getBaseMetaTileEntity(),
+ "proxy",
+ autoPullAvailable ? ItemList.Hatch_Input_Bus_ME_Advanced.get(1)
+ : ItemList.Hatch_Input_Bus_ME.get(1),
+ true);
+ gridProxy.setFlags(GridFlags.REQUIRE_CHANNEL);
+ updateValidGridProxySides();
+ if (getBaseMetaTileEntity().getWorld() != null) gridProxy.setOwner(
+ getBaseMetaTileEntity().getWorld()
+ .getPlayerEntityByName(getBaseMetaTileEntity().getOwnerName()));
+ }
+ }
+ return this.gridProxy;
+ }
+
+ @Override
+ public boolean isPowered() {
+ return getProxy() != null && getProxy().isPowered();
+ }
+
+ @Override
+ public boolean isActive() {
+ return getProxy() != null && getProxy().isActive();
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ int[] sizes = new int[16];
+ for (int i = 0; i < 16; ++i) sizes[i] = mInventory[i + 16] == null ? 0 : mInventory[i + 16].stackSize;
+ aNBT.setIntArray("sizes", sizes);
+ aNBT.setBoolean("autoStock", autoPullItemList);
+ aNBT.setInteger("minAutoPullStackSize", minAutoPullStackSize);
+ aNBT.setBoolean("additionalConnection", additionalConnection);
+ aNBT.setBoolean("expediteRecipeCheck", expediteRecipeCheck);
+ aNBT.setInteger("refreshTime", autoPullRefreshTime);
+ getProxy().writeToNBT(aNBT);
+ }
+
+ private void setAutoPullItemList(boolean pullItemList) {
+ if (!autoPullAvailable) {
+ return;
+ }
+
+ autoPullItemList = pullItemList;
+ if (!autoPullItemList) {
+ for (int i = 0; i < SLOT_COUNT; i++) {
+ mInventory[i] = null;
+ }
+ } else {
+ refreshItemList();
+ }
+ updateAllInformationSlots();
+ }
+
+ public boolean doFastRecipeCheck() {
+ return expediteRecipeCheck;
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ if (aNBT.hasKey("sizes")) {
+ int[] sizes = aNBT.getIntArray("sizes");
+ if (sizes.length == 16) {
+ for (int i = 0; i < 16; ++i) {
+ if (sizes[i] != 0 && mInventory[i] != null) {
+ ItemStack s = mInventory[i].copy();
+ s.stackSize = sizes[i];
+ mInventory[i + 16] = s;
+ }
+ }
+ }
+ }
+ autoPullItemList = aNBT.getBoolean("autoStock");
+ minAutoPullStackSize = aNBT.getInteger("minAutoPullStackSize");
+ additionalConnection = aNBT.getBoolean("additionalConnection");
+ expediteRecipeCheck = aNBT.getBoolean("expediteRecipeCheck");
+ if (aNBT.hasKey("refreshTime")) {
+ autoPullRefreshTime = aNBT.getInteger("refreshTime");
+ }
+ getProxy().readFromNBT(aNBT);
+
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return true;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ return new String[] {
+ "The bus is " + ((getProxy() != null && getProxy().isActive()) ? EnumChatFormatting.GREEN + "online"
+ : EnumChatFormatting.RED + "offline" + getAEDiagnostics()) + EnumChatFormatting.RESET };
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ if (!autoPullAvailable) {
+ return;
+ }
+
+ setAutoPullItemList(!autoPullItemList);
+ aPlayer.addChatMessage(
+ new ChatComponentTranslation(
+ "GT5U.machines.stocking_bus.auto_pull_toggle." + (autoPullItemList ? "enabled" : "disabled")));
+ }
+
+ @Override
+ public void updateSlots() {
+ if (mInventory[getManualSlot()] != null && mInventory[getManualSlot()].stackSize <= 0)
+ mInventory[getManualSlot()] = null;
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer, ForgeDirection side,
+ float aX, float aY, float aZ) {
+ if (!(aPlayer instanceof EntityPlayerMP))
+ return super.onRightclick(aBaseMetaTileEntity, aPlayer, side, aX, aY, aZ);
+ ItemStack dataStick = aPlayer.inventory.getCurrentItem();
+ if (!ItemList.Tool_DataStick.isStackEqual(dataStick, false, true))
+ return super.onRightclick(aBaseMetaTileEntity, aPlayer, side, aX, aY, aZ);
+
+ if (!pasteCopiedData(aPlayer, dataStick.stackTagCompound)) return false;
+
+ updateValidGridProxySides();
+ aPlayer.addChatMessage(new ChatComponentTranslation("GT5U.machines.stocking_bus.loaded"));
+ return true;
+ }
+
+ @Override
+ public void onLeftclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ if (!(aPlayer instanceof EntityPlayerMP)) return;
+
+ ItemStack dataStick = aPlayer.inventory.getCurrentItem();
+ if (!ItemList.Tool_DataStick.isStackEqual(dataStick, false, true)) return;
+
+ dataStick.stackTagCompound = getCopiedData(aPlayer);
+ dataStick.setStackDisplayName("Stocking Input Bus Configuration");
+ aPlayer.addChatMessage(new ChatComponentTranslation("GT5U.machines.stocking_bus.saved"));
+ }
+
+ @Override
+ public String getCopiedDataIdentifier(EntityPlayer player) {
+ return COPIED_DATA_IDENTIFIER;
+ }
+
+ @Override
+ public boolean pasteCopiedData(EntityPlayer player, NBTTagCompound nbt) {
+ if (nbt == null || !COPIED_DATA_IDENTIFIER.equals(nbt.getString("type"))) return false;
+ ItemStack circuit = GT_Utility.loadItem(nbt, "circuit");
+ if (GT_Utility.isStackInvalid(circuit)) circuit = null;
+
+ if (autoPullAvailable) {
+ setAutoPullItemList(nbt.getBoolean("autoPull"));
+ minAutoPullStackSize = nbt.getInteger("minStackSize");
+ // Data sticks created before refreshTime was implemented should not cause stocking buses to
+ // spam divide by zero errors
+ if (nbt.hasKey("refreshTime")) {
+ autoPullRefreshTime = nbt.getInteger("refreshTime");
+ }
+ }
+
+ additionalConnection = nbt.getBoolean("additionalConnection");
+ if (!autoPullItemList) {
+ NBTTagList stockingItems = nbt.getTagList("itemsToStock", 10);
+ for (int i = 0; i < stockingItems.tagCount(); i++) {
+ this.mInventory[i] = GT_Utility.loadItem(stockingItems.getCompoundTagAt(i));
+ }
+ }
+ setInventorySlotContents(getCircuitSlot(), circuit);
+ return true;
+ }
+
+ @Override
+ public NBTTagCompound getCopiedData(EntityPlayer player) {
+ NBTTagCompound tag = new NBTTagCompound();
+ tag.setString("type", COPIED_DATA_IDENTIFIER);
+ tag.setBoolean("autoPull", autoPullItemList);
+ tag.setInteger("minStackSize", minAutoPullStackSize);
+ tag.setInteger("refreshTime", autoPullRefreshTime);
+ tag.setBoolean("additionalConnection", additionalConnection);
+ tag.setTag("circuit", GT_Utility.saveItem(getStackInSlot(getCircuitSlot())));
+
+ NBTTagList stockingItems = new NBTTagList();
+
+ if (!autoPullItemList) {
+ for (int index = 0; index < SLOT_COUNT; index++) {
+ stockingItems.appendTag(GT_Utility.saveItem(mInventory[index]));
+ }
+ tag.setTag("itemsToStock", stockingItems);
+ }
+ return tag;
+ }
+
+ private int getManualSlot() {
+ return SLOT_COUNT * 2 + 1;
+ }
+
+ @Override
+ public int getCircuitSlot() {
+ return SLOT_COUNT * 2;
+ }
+
+ @Override
+ public int getCircuitSlotX() {
+ return 80;
+ }
+
+ @Override
+ public int getCircuitSlotY() {
+ return 64;
+ }
+
+ @Override
+ public boolean setStackToZeroInsteadOfNull(int aIndex) {
+ return aIndex != getManualSlot();
+ }
+
+ @Override
+ public boolean justUpdated() {
+ if (expediteRecipeCheck) {
+ boolean ret = justHadNewItems;
+ justHadNewItems = false;
+ return ret;
+ }
+ return false;
+ }
+
+ public void setRecipeCheck(boolean value) {
+ expediteRecipeCheck = value;
+ }
+
+ @Override
+ public void setInventorySlotContents(int aIndex, ItemStack aStack) {
+ if (expediteRecipeCheck && aStack != null) {
+ justHadNewItems = true;
+ }
+ super.setInventorySlotContents(aIndex, aStack);
+ }
+
+ @Override
+ public ItemStack getStackInSlot(int aIndex) {
+ if (!processingRecipe) return super.getStackInSlot(aIndex);
+ if (aIndex < 0 || aIndex > mInventory.length) return null;
+ if (aIndex >= SLOT_COUNT && aIndex < SLOT_COUNT * 2)
+ // Display slots
+ return null;
+ if (aIndex == getCircuitSlot() || aIndex == getManualSlot()) return mInventory[aIndex];
+ if (mInventory[aIndex] != null) {
+ AENetworkProxy proxy = getProxy();
+ if (proxy == null || !proxy.isActive()) {
+ return null;
+ }
+ try {
+ IMEMonitor<IAEItemStack> sg = proxy.getStorage()
+ .getItemInventory();
+ IAEItemStack request = AEItemStack.create(mInventory[aIndex]);
+ request.setStackSize(Integer.MAX_VALUE);
+ IAEItemStack result = sg.extractItems(request, Actionable.SIMULATE, getRequestSource());
+ if (result != null) {
+ this.shadowInventory[aIndex] = result.getItemStack();
+ this.savedStackSizes[aIndex] = this.shadowInventory[aIndex].stackSize;
+ this.setInventorySlotContents(aIndex + SLOT_COUNT, this.shadowInventory[aIndex]);
+ return this.shadowInventory[aIndex];
+ } else {
+ // Request failed
+ this.setInventorySlotContents(aIndex + SLOT_COUNT, null);
+ return null;
+ }
+ } catch (final GridAccessException ignored) {}
+ return null;
+ } else {
+ // AE available but no items requested
+ this.setInventorySlotContents(aIndex + SLOT_COUNT, null);
+ }
+ return mInventory[aIndex];
+ }
+
+ private BaseActionSource getRequestSource() {
+ if (requestSource == null) requestSource = new MachineSource((IActionHost) getBaseMetaTileEntity());
+ return requestSource;
+ }
+
+ @Override
+ public void onExplosion() {
+ for (int i = 0; i < SLOT_COUNT; i++) {
+ mInventory[i] = null;
+ }
+ }
+
+ @Override
+ public void startRecipeProcessing() {
+ processingRecipe = true;
+ updateAllInformationSlots();
+ }
+
+ private void refreshItemList() {
+ AENetworkProxy proxy = getProxy();
+ try {
+ IMEMonitor<IAEItemStack> sg = proxy.getStorage()
+ .getItemInventory();
+ Iterator<IAEItemStack> iterator = sg.getStorageList()
+ .iterator();
+ int index = 0;
+ while (iterator.hasNext() && index < SLOT_COUNT) {
+ IAEItemStack currItem = iterator.next();
+ if (currItem.getStackSize() >= minAutoPullStackSize) {
+ ItemStack itemstack = GT_Utility.copyAmount(1, currItem.getItemStack());
+ if (expediteRecipeCheck) {
+ ItemStack previous = this.mInventory[index];
+ if (itemstack != null) {
+ justHadNewItems = !ItemStack.areItemStacksEqual(itemstack, previous);
+ }
+ }
+ this.mInventory[index] = itemstack;
+ index++;
+ }
+ }
+ for (int i = index; i < SLOT_COUNT; i++) {
+ mInventory[i] = null;
+ }
+
+ } catch (final GridAccessException ignored) {}
+ }
+
+ private void updateAllInformationSlots() {
+ for (int index = 0; index < SLOT_COUNT; index++) {
+ updateInformationSlot(index, mInventory[index]);
+ }
+ }
+
+ @Override
+ public CheckRecipeResult endRecipeProcessing(GT_MetaTileEntity_MultiBlockBase controller) {
+ CheckRecipeResult checkRecipeResult = CheckRecipeResultRegistry.SUCCESSFUL;
+ for (int i = 0; i < SLOT_COUNT; ++i) {
+ if (savedStackSizes[i] != 0) {
+ ItemStack oldStack = shadowInventory[i];
+ if (oldStack == null || oldStack.stackSize < savedStackSizes[i]) {
+ AENetworkProxy proxy = getProxy();
+ try {
+ IMEMonitor<IAEItemStack> sg = proxy.getStorage()
+ .getItemInventory();
+ IAEItemStack request = AEItemStack.create(mInventory[i]);
+ int toExtract = savedStackSizes[i] - (oldStack == null ? 0 : oldStack.stackSize);
+ request.setStackSize(toExtract);
+ IAEItemStack result = sg.extractItems(request, Actionable.MODULATE, getRequestSource());
+ proxy.getEnergy()
+ .extractAEPower(request.getStackSize(), Actionable.MODULATE, PowerMultiplier.CONFIG);
+ setInventorySlotContents(i + SLOT_COUNT, oldStack);
+ if (result == null || result.getStackSize() != toExtract) {
+ controller.stopMachine(ShutDownReasonRegistry.CRITICAL_NONE);
+ checkRecipeResult = SimpleCheckRecipeResult
+ .ofFailurePersistOnShutdown("stocking_bus_fail_extraction");
+ }
+ } catch (final GridAccessException ignored) {
+ controller.stopMachine(ShutDownReasonRegistry.CRITICAL_NONE);
+ checkRecipeResult = SimpleCheckRecipeResult
+ .ofFailurePersistOnShutdown("stocking_hatch_fail_extraction");
+ }
+ }
+ savedStackSizes[i] = 0;
+ shadowInventory[i] = null;
+ if (mInventory[i + SLOT_COUNT] != null && mInventory[i + SLOT_COUNT].stackSize <= 0) {
+ mInventory[i + SLOT_COUNT] = null;
+ }
+ }
+ }
+ processingRecipe = false;
+ return checkRecipeResult;
+ }
+
+ /**
+ * Update the right side of the GUI, which shows the amounts of items set on the left side
+ */
+ public ItemStack updateInformationSlot(int aIndex, ItemStack aStack) {
+ if (aIndex >= 0 && aIndex < SLOT_COUNT) {
+ if (aStack == null) {
+ super.setInventorySlotContents(aIndex + SLOT_COUNT, null);
+ } else {
+ AENetworkProxy proxy = getProxy();
+ if (!proxy.isActive()) {
+ super.setInventorySlotContents(aIndex + SLOT_COUNT, null);
+ return null;
+ }
+ try {
+ IMEMonitor<IAEItemStack> sg = proxy.getStorage()
+ .getItemInventory();
+ IAEItemStack request = AEItemStack.create(mInventory[aIndex]);
+ request.setStackSize(Integer.MAX_VALUE);
+ IAEItemStack result = sg.extractItems(request, Actionable.SIMULATE, getRequestSource());
+ ItemStack s = (result != null) ? result.getItemStack() : null;
+ // We want to track changes in any ItemStack to notify any connected controllers to make a recipe
+ // check early
+ if (expediteRecipeCheck) {
+ ItemStack previous = getStackInSlot(aIndex + SLOT_COUNT);
+ if (s != null) {
+ justHadNewItems = !ItemStack.areItemStacksEqual(s, previous);
+ }
+ }
+ setInventorySlotContents(aIndex + SLOT_COUNT, s);
+ return s;
+ } catch (final GridAccessException ignored) {}
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Used to avoid slot update.
+ */
+ public ItemStack getShadowItemStack(int index) {
+ if (index < 0 || index >= shadowInventory.length) {
+ return null;
+ }
+ return shadowInventory[index];
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return aIndex == getManualSlot();
+ }
+
+ @Override
+ public int getGUIHeight() {
+ return 179;
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ final SlotWidget[] aeSlotWidgets = new SlotWidget[16];
+
+ if (autoPullAvailable) {
+ buildContext.addSyncedWindow(CONFIG_WINDOW_ID, this::createStackSizeConfigurationWindow);
+ }
+
+ builder.widget(
+ SlotGroup.ofItemHandler(inventoryHandler, 4)
+ .startFromSlot(0)
+ .endAtSlot(15)
+ .phantom(true)
+ .slotCreator(index -> new BaseSlot(inventoryHandler, index, true) {
+
+ @Override
+ public boolean isEnabled() {
+ return !autoPullItemList && super.isEnabled();
+ }
+ })
+ .widgetCreator(slot -> (SlotWidget) new SlotWidget(slot) {
+
+ @Override
+ protected void phantomClick(ClickData clickData, ItemStack cursorStack) {
+ if (clickData.mouseButton != 0 || !getMcSlot().isEnabled()) return;
+ final int aSlotIndex = getMcSlot().getSlotIndex();
+ if (cursorStack == null) {
+ getMcSlot().putStack(null);
+ } else {
+ if (containsSuchStack(cursorStack)) return;
+ getMcSlot().putStack(GT_Utility.copyAmount(1, cursorStack));
+ }
+ if (getBaseMetaTileEntity().isServerSide()) {
+ final ItemStack newInfo = updateInformationSlot(aSlotIndex, cursorStack);
+ aeSlotWidgets[getMcSlot().getSlotIndex()].getMcSlot()
+ .putStack(newInfo);
+ }
+ }
+
+ @Override
+ public IDrawable[] getBackground() {
+ IDrawable slot;
+ if (autoPullItemList) {
+ slot = GT_UITextures.SLOT_DARK_GRAY;
+ } else {
+ slot = ModularUITextures.ITEM_SLOT;
+ }
+ return new IDrawable[] { slot, GT_UITextures.OVERLAY_SLOT_ARROW_ME };
+ }
+
+ @Override
+ public List<String> getExtraTooltip() {
+ if (autoPullItemList) {
+ return Collections.singletonList(
+ StatCollector.translateToLocal("GT5U.machines.stocking_bus.cannot_set_slot"));
+ } else {
+ return Collections
+ .singletonList(StatCollector.translateToLocal("modularui.phantom.single.clear"));
+ }
+ }
+
+ private boolean containsSuchStack(ItemStack tStack) {
+ for (int i = 0; i < 16; ++i) {
+ if (GT_Utility.areStacksEqual(mInventory[i], tStack, false)) return true;
+ }
+ return false;
+ }
+ }.dynamicTooltip(() -> {
+ if (autoPullItemList) {
+ return Collections.singletonList(
+ StatCollector.translateToLocal("GT5U.machines.stocking_bus.cannot_set_slot"));
+ } else {
+ return Collections.emptyList();
+ }
+ })
+ .setUpdateTooltipEveryTick(true))
+ .build()
+ .setPos(7, 9))
+ .widget(
+ SlotGroup.ofItemHandler(inventoryHandler, 4)
+ .startFromSlot(16)
+ .endAtSlot(31)
+ .phantom(true)
+ .background(GT_UITextures.SLOT_DARK_GRAY)
+ .widgetCreator(
+ slot -> aeSlotWidgets[slot.getSlotIndex() - 16] = new AESlotWidget(slot).disableInteraction())
+ .build()
+ .setPos(97, 9))
+ .widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_ARROW_DOUBLE)
+ .setPos(82, 30)
+ .setSize(12, 12));
+
+ if (autoPullAvailable) {
+ builder.widget(new ButtonWidget().setOnClick((clickData, widget) -> {
+ if (clickData.mouseButton == 0) {
+ setAutoPullItemList(!autoPullItemList);
+ } else if (clickData.mouseButton == 1 && !widget.isClient()) {
+ widget.getContext()
+ .openSyncedWindow(CONFIG_WINDOW_ID);
+ }
+ })
+ .setBackground(() -> {
+ if (autoPullItemList) {
+ return new IDrawable[] { GT_UITextures.BUTTON_STANDARD_PRESSED,
+ GT_UITextures.OVERLAY_BUTTON_AUTOPULL_ME };
+ } else {
+ return new IDrawable[] { GT_UITextures.BUTTON_STANDARD,
+ GT_UITextures.OVERLAY_BUTTON_AUTOPULL_ME_DISABLED };
+ }
+ })
+ .addTooltips(
+ Arrays.asList(
+ StatCollector.translateToLocal("GT5U.machines.stocking_bus.auto_pull.tooltip.1"),
+ StatCollector.translateToLocal("GT5U.machines.stocking_bus.auto_pull.tooltip.2")))
+ .setSize(16, 16)
+ .setPos(80, 10))
+ .widget(new FakeSyncWidget.BooleanSyncer(() -> autoPullItemList, this::setAutoPullItemList));
+ }
+
+ builder.widget(TextWidget.dynamicString(() -> {
+ boolean isActive = isActive();
+ boolean isPowered = isPowered();
+ boolean isBooting = isBooting();
+ EnumChatFormatting color = (isActive && isPowered) ? EnumChatFormatting.GREEN : EnumChatFormatting.DARK_RED;
+ return color + WailaText.getPowerState(isActive, isPowered, isBooting);
+ })
+ .setTextAlignment(Alignment.Center)
+ .setSize(90, 9)
+ .setPos(43, 84))
+ .widget(
+ new SlotWidget(inventoryHandler, getManualSlot())
+ // ghost slots are prioritized over manual slot
+ .setShiftClickPriority(11)
+ .setPos(79, 45));
+ }
+
+ protected ModularWindow createStackSizeConfigurationWindow(final EntityPlayer player) {
+ final int WIDTH = 78;
+ final int HEIGHT = 115;
+ final int PARENT_WIDTH = getGUIWidth();
+ final int PARENT_HEIGHT = getGUIHeight();
+ ModularWindow.Builder builder = ModularWindow.builder(WIDTH, HEIGHT);
+ builder.setBackground(GT_UITextures.BACKGROUND_SINGLEBLOCK_DEFAULT);
+ builder.setGuiTint(getGUIColorization());
+ builder.setDraggable(true);
+ builder.setPos(
+ (size, window) -> Alignment.Center.getAlignedPos(size, new Size(PARENT_WIDTH, PARENT_HEIGHT))
+ .add(
+ Alignment.TopRight.getAlignedPos(new Size(PARENT_WIDTH, PARENT_HEIGHT), new Size(WIDTH, HEIGHT))
+ .add(WIDTH - 3, 0)));
+ builder.widget(
+ TextWidget.localised("GT5U.machines.stocking_bus.min_stack_size")
+ .setPos(3, 2)
+ .setSize(74, 14))
+ .widget(
+ new NumericWidget().setSetter(val -> minAutoPullStackSize = (int) val)
+ .setGetter(() -> minAutoPullStackSize)
+ .setBounds(1, Integer.MAX_VALUE)
+ .setScrollValues(1, 4, 64)
+ .setTextAlignment(Alignment.Center)
+ .setTextColor(Color.WHITE.normal)
+ .setSize(70, 18)
+ .setPos(3, 18)
+ .setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD));
+ builder.widget(
+ TextWidget.localised("GT5U.machines.stocking_bus.refresh_time")
+ .setPos(3, 42)
+ .setSize(74, 14))
+ .widget(
+ new NumericWidget().setSetter(val -> autoPullRefreshTime = (int) val)
+ .setGetter(() -> autoPullRefreshTime)
+ .setBounds(1, Integer.MAX_VALUE)
+ .setScrollValues(1, 4, 64)
+ .setTextAlignment(Alignment.Center)
+ .setTextColor(Color.WHITE.normal)
+ .setSize(70, 18)
+ .setPos(3, 58)
+ .setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD));
+ builder.widget(
+ TextWidget.localised("GT5U.machines.stocking_bus.force_check")
+ .setPos(3, 88)
+ .setSize(50, 14))
+ .widget(
+ new CycleButtonWidget().setToggle(() -> expediteRecipeCheck, val -> setRecipeCheck(val))
+ .setTextureGetter(
+ state -> expediteRecipeCheck ? GT_UITextures.OVERLAY_BUTTON_CHECKMARK
+ : GT_UITextures.OVERLAY_BUTTON_CROSS)
+ .setBackground(GT_UITextures.BUTTON_STANDARD)
+ .setPos(53, 87)
+ .setSize(16, 16)
+ .addTooltip(StatCollector.translateToLocal("GT5U.machines.stocking_bus.hatch_warning")));
+ return builder.build();
+ }
+
+ @Override
+ public void addGregTechLogo(ModularWindow.Builder builder) {
+ builder.widget(
+ new DrawableWidget().setDrawable(getGUITextureSet().getGregTechLogo())
+ .setSize(17, 17)
+ .setPos(80, 63));
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ if (!autoPullAvailable) {
+ super.getWailaBody(itemStack, currenttip, accessor, config);
+ return;
+ }
+
+ NBTTagCompound tag = accessor.getNBTData();
+ boolean autopull = tag.getBoolean("autoPull");
+ int minSize = tag.getInteger("minStackSize");
+ currenttip.add(
+ StatCollector.translateToLocal("GT5U.waila.stocking_bus.auto_pull." + (autopull ? "enabled" : "disabled")));
+ if (autopull) {
+ currenttip.add(
+ StatCollector.translateToLocalFormatted(
+ "GT5U.waila.stocking_bus.min_stack_size",
+ GT_Utility.formatNumbers(minSize)));
+ }
+ super.getWailaBody(itemStack, currenttip, accessor, config);
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ if (!autoPullAvailable) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ return;
+ }
+
+ tag.setBoolean("autoPull", autoPullItemList);
+ tag.setInteger("minStackSize", minAutoPullStackSize);
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ }
+
+ private static String[] getDescriptionArray(boolean autoPullAvailable) {
+ List<String> strings = new ArrayList<>(8);
+ strings.add("Advanced item input for Multiblocks");
+ strings.add("Hatch Tier: " + TIER_COLORS[autoPullAvailable ? 6 : 3] + VN[autoPullAvailable ? 6 : 3]);
+ strings.add("Retrieves directly from ME");
+ strings.add("Keeps 16 item types in stock");
+
+ if (autoPullAvailable) {
+ strings.add(
+ "Auto-Pull from ME mode will automatically stock the first 16 items in the ME system, updated every 5 seconds.");
+ strings.add("Toggle by right-clicking with screwdriver, or use the GUI.");
+ strings.add(
+ "Use the GUI to limit the minimum stack size for Auto-Pulling, adjust the slot refresh timer and enable fast recipe checks.");
+ strings.add("WARNING: Fast recipe checks can be laggy. Use with caution.");
+ }
+
+ strings.add("Change ME connection behavior by right-clicking with wire cutter.");
+ strings.add("Configuration data can be copy+pasted using a data stick.");
+ return strings.toArray(new String[0]);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_Input_ME.java b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_Input_ME.java
new file mode 100644
index 0000000000..d59dbb9ce8
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_Input_ME.java
@@ -0,0 +1,949 @@
+package gregtech.common.tileentities.machines;
+
+import static gregtech.api.enums.GT_Values.TIER_COLORS;
+import static gregtech.api.enums.GT_Values.VN;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_ME_INPUT_FLUID_HATCH;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_ME_INPUT_FLUID_HATCH_ACTIVE;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import javax.annotation.Nullable;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.ChatComponentTranslation;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import com.gtnewhorizons.modularui.api.ModularUITextures;
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.drawable.Text;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.math.Color;
+import com.gtnewhorizons.modularui.api.math.Pos2d;
+import com.gtnewhorizons.modularui.api.math.Size;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.api.widget.Interactable;
+import com.gtnewhorizons.modularui.common.fluid.FluidStackTank;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.CycleButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.FluidSlotWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotGroup;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+import com.gtnewhorizons.modularui.common.widget.textfield.NumericWidget;
+
+import appeng.api.config.Actionable;
+import appeng.api.config.PowerMultiplier;
+import appeng.api.implementations.IPowerChannelState;
+import appeng.api.networking.GridFlags;
+import appeng.api.networking.security.BaseActionSource;
+import appeng.api.networking.security.IActionHost;
+import appeng.api.networking.security.MachineSource;
+import appeng.api.storage.IMEMonitor;
+import appeng.api.storage.data.IAEFluidStack;
+import appeng.api.util.AECableType;
+import appeng.core.localization.WailaText;
+import appeng.me.GridAccessException;
+import appeng.me.helpers.AENetworkProxy;
+import appeng.me.helpers.IGridProxyable;
+import appeng.util.item.AEFluidStack;
+import gregtech.api.enums.ItemList;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.IDataCopyable;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.modularui.IAddGregtechLogo;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Input;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_MultiBlockBase;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.recipe.check.SimpleCheckRecipeResult;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.shutdown.ShutDownReasonRegistry;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public class GT_MetaTileEntity_Hatch_Input_ME extends GT_MetaTileEntity_Hatch_Input implements IPowerChannelState,
+ IAddGregtechLogo, IAddUIWidgets, IRecipeProcessingAwareHatch, ISmartInputHatch, IDataCopyable {
+
+ private static final int SLOT_COUNT = 16;
+ public static final String COPIED_DATA_IDENTIFIER = "stockingHatch";
+
+ protected final FluidStack[] storedFluids = new FluidStack[SLOT_COUNT];
+ protected final FluidStack[] storedInformationFluids = new FluidStack[SLOT_COUNT];
+
+ // these two fields should ALWAYS be mutated simultaneously
+ // in most cases, you should call setSavedFluid() instead of trying to write to the array directly
+ // a desync of these two fields can lead to catastrophe
+ protected final FluidStack[] shadowStoredFluids = new FluidStack[SLOT_COUNT];
+ private final int[] savedStackSizes = new int[SLOT_COUNT];
+
+ private boolean additionalConnection = false;
+
+ protected BaseActionSource requestSource = null;
+
+ @Nullable
+ protected AENetworkProxy gridProxy = null;
+
+ private final boolean autoPullAvailable;
+ protected boolean autoPullFluidList = false;
+ protected int minAutoPullAmount = 1;
+ private int autoPullRefreshTime = 100;
+ protected boolean processingRecipe = false;
+ private boolean justHadNewFluids = false;
+ private boolean expediteRecipeCheck = false;
+
+ protected static final int CONFIG_WINDOW_ID = 10;
+
+ protected static final FluidStack[] EMPTY_FLUID_STACK = new FluidStack[0];
+
+ public GT_MetaTileEntity_Hatch_Input_ME(int aID, boolean autoPullAvailable, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional, autoPullAvailable ? 10 : 8, 1, getDescriptionArray(autoPullAvailable));
+ this.autoPullAvailable = autoPullAvailable;
+ }
+
+ public GT_MetaTileEntity_Hatch_Input_ME(String aName, boolean autoPullAvailable, int aTier, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, 1, aTier, aDescription, aTextures);
+ this.autoPullAvailable = autoPullAvailable;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Hatch_Input_ME(mName, autoPullAvailable, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_ME_INPUT_FLUID_HATCH_ACTIVE) };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_ME_INPUT_FLUID_HATCH) };
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) {
+ if (getBaseMetaTileEntity().isServerSide()) {
+ if (aTimer % autoPullRefreshTime == 0 && autoPullFluidList) {
+ refreshFluidList();
+ }
+ if (aTimer % 20 == 0) {
+ getBaseMetaTileEntity().setActive(isActive());
+ }
+ }
+ super.onPostTick(aBaseMetaTileEntity, aTimer);
+ }
+
+ private void refreshFluidList() {
+ AENetworkProxy proxy = getProxy();
+ if (proxy == null || !proxy.isActive()) {
+ return;
+ }
+
+ try {
+ IMEMonitor<IAEFluidStack> sg = proxy.getStorage()
+ .getFluidInventory();
+ Iterator<IAEFluidStack> iterator = sg.getStorageList()
+ .iterator();
+
+ int index = 0;
+ while (iterator.hasNext() && index < SLOT_COUNT) {
+ IAEFluidStack currItem = iterator.next();
+ if (currItem.getStackSize() >= minAutoPullAmount) {
+ FluidStack fluidStack = GT_Utility.copyAmount(1, currItem.getFluidStack());
+ if (expediteRecipeCheck) {
+ FluidStack previous = storedFluids[index];
+ if (fluidStack != null && previous != null) {
+ justHadNewFluids = !fluidStack.isFluidEqual(previous);
+ }
+ }
+ storedFluids[index] = fluidStack;
+ index++;
+ }
+ }
+
+ for (int i = index; i < SLOT_COUNT; i++) {
+ storedFluids[i] = null;
+ }
+ } catch (final GridAccessException ignored) {}
+ }
+
+ @Override
+ public boolean displaysStackSize() {
+ return true;
+ }
+
+ protected void setSavedFluid(int i, FluidStack stack) {
+ shadowStoredFluids[i] = stack;
+ savedStackSizes[i] = stack == null ? 0 : stack.amount;
+ }
+
+ public FluidStack[] getStoredFluids() {
+ if (!processingRecipe) {
+ return EMPTY_FLUID_STACK;
+ }
+
+ AENetworkProxy proxy = getProxy();
+ if (proxy == null || !proxy.isActive()) {
+ return EMPTY_FLUID_STACK;
+ }
+
+ updateAllInformationSlots();
+
+ for (int i = 0; i < SLOT_COUNT; i++) {
+ if (storedFluids[i] == null) {
+ setSavedFluid(i, null);
+ continue;
+ }
+
+ FluidStack fluidStackWithAmount = storedInformationFluids[i];
+
+ setSavedFluid(i, fluidStackWithAmount);
+ }
+
+ return shadowStoredFluids;
+ }
+
+ @Override
+ public boolean justUpdated() {
+ if (expediteRecipeCheck) {
+ boolean ret = justHadNewFluids;
+ justHadNewFluids = false;
+ return ret;
+ }
+ return false;
+ }
+
+ public void setRecipeCheck(boolean value) {
+ expediteRecipeCheck = value;
+ }
+
+ @Override
+ public FluidStack drain(ForgeDirection side, FluidStack aFluid, boolean doDrain) {
+ // this is an ME input hatch. allowing draining via logistics would be very wrong (and against
+ // canTankBeEmptied()) but we do need to support draining from controller, which uses the UNKNOWN direction.
+ if (side != ForgeDirection.UNKNOWN) return null;
+ FluidStack stored = getMatchingFluidStack(aFluid);
+ if (stored == null) return null;
+ FluidStack drained = GT_Utility.copyAmount(Math.min(stored.amount, aFluid.amount), stored);
+ if (doDrain) {
+ stored.amount -= drained.amount;
+ }
+ return drained;
+ }
+
+ @Override
+ public void startRecipeProcessing() {
+ processingRecipe = true;
+ updateAllInformationSlots();
+ }
+
+ @Override
+ public CheckRecipeResult endRecipeProcessing(GT_MetaTileEntity_MultiBlockBase controller) {
+ CheckRecipeResult checkRecipeResult = CheckRecipeResultRegistry.SUCCESSFUL;
+ AENetworkProxy proxy = getProxy();
+
+ for (int i = 0; i < SLOT_COUNT; ++i) {
+ FluidStack oldStack = shadowStoredFluids[i];
+ int toExtract = savedStackSizes[i] - (oldStack != null ? oldStack.amount : 0);
+ if (toExtract <= 0) continue;
+
+ try {
+ IMEMonitor<IAEFluidStack> sg = proxy.getStorage()
+ .getFluidInventory();
+
+ IAEFluidStack request = AEFluidStack.create(storedFluids[i]);
+ request.setStackSize(toExtract);
+ IAEFluidStack extractionResult = sg.extractItems(request, Actionable.MODULATE, getRequestSource());
+ proxy.getEnergy()
+ .extractAEPower(toExtract, Actionable.MODULATE, PowerMultiplier.CONFIG);
+
+ if (extractionResult == null || extractionResult.getStackSize() != toExtract) {
+ controller.stopMachine(ShutDownReasonRegistry.CRITICAL_NONE);
+ checkRecipeResult = SimpleCheckRecipeResult
+ .ofFailurePersistOnShutdown("stocking_hatch_fail_extraction");
+ }
+ } catch (GridAccessException ignored) {
+ controller.stopMachine(ShutDownReasonRegistry.CRITICAL_NONE);
+ checkRecipeResult = SimpleCheckRecipeResult
+ .ofFailurePersistOnShutdown("stocking_hatch_fail_extraction");
+ }
+ setSavedFluid(i, null);
+ if (storedInformationFluids[i] != null && storedInformationFluids[i].amount <= 0) {
+ storedInformationFluids[i] = null;
+ }
+ }
+
+ processingRecipe = false;
+ return checkRecipeResult;
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ super.onFirstTick(aBaseMetaTileEntity);
+ getProxy().onReady();
+ }
+
+ @Override
+ public AECableType getCableConnectionType(ForgeDirection forgeDirection) {
+ return isOutputFacing(forgeDirection) ? AECableType.SMART : AECableType.NONE;
+ }
+
+ private void updateValidGridProxySides() {
+ if (additionalConnection) {
+ getProxy().setValidSides(EnumSet.complementOf(EnumSet.of(ForgeDirection.UNKNOWN)));
+ } else {
+ getProxy().setValidSides(EnumSet.of(getBaseMetaTileEntity().getFrontFacing()));
+ }
+ }
+
+ @Override
+ public void onFacingChange() {
+ updateValidGridProxySides();
+ }
+
+ @Override
+ public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer,
+ float aX, float aY, float aZ) {
+ additionalConnection = !additionalConnection;
+ updateValidGridProxySides();
+ aPlayer.addChatComponentMessage(
+ new ChatComponentTranslation("GT5U.hatch.additionalConnection." + additionalConnection));
+ return true;
+ }
+
+ @Override
+ public AENetworkProxy getProxy() {
+ if (gridProxy == null) {
+ if (getBaseMetaTileEntity() instanceof IGridProxyable) {
+ gridProxy = new AENetworkProxy(
+ (IGridProxyable) getBaseMetaTileEntity(),
+ "proxy",
+ autoPullAvailable ? ItemList.Hatch_Input_ME_Advanced.get(1) : ItemList.Hatch_Input_ME.get(1),
+ true);
+ gridProxy.setFlags(GridFlags.REQUIRE_CHANNEL);
+ updateValidGridProxySides();
+ if (getBaseMetaTileEntity().getWorld() != null) gridProxy.setOwner(
+ getBaseMetaTileEntity().getWorld()
+ .getPlayerEntityByName(getBaseMetaTileEntity().getOwnerName()));
+ }
+ }
+ return this.gridProxy;
+ }
+
+ @Override
+ public boolean isPowered() {
+ return getProxy() != null && getProxy().isPowered();
+ }
+
+ @Override
+ public boolean isActive() {
+ return getProxy() != null && getProxy().isActive();
+ }
+
+ private void setAutoPullFluidList(boolean pullFluidList) {
+ if (!autoPullAvailable) {
+ return;
+ }
+
+ autoPullFluidList = pullFluidList;
+ if (!autoPullFluidList) {
+ Arrays.fill(storedFluids, null);
+ } else {
+ refreshFluidList();
+ }
+ updateAllInformationSlots();
+ }
+
+ public boolean doFastRecipeCheck() {
+ return expediteRecipeCheck;
+ }
+
+ private void updateAllInformationSlots() {
+ for (int index = 0; index < SLOT_COUNT; index++) {
+ updateInformationSlot(index);
+ }
+ }
+
+ public void updateInformationSlot(int index) {
+ if (index < 0 || index >= SLOT_COUNT) {
+ return;
+ }
+
+ FluidStack fluidStack = storedFluids[index];
+ if (fluidStack == null) {
+ storedInformationFluids[index] = null;
+ return;
+ }
+
+ AENetworkProxy proxy = getProxy();
+ if (proxy == null || !proxy.isActive()) {
+ storedInformationFluids[index] = null;
+ return;
+ }
+
+ try {
+ IMEMonitor<IAEFluidStack> sg = proxy.getStorage()
+ .getFluidInventory();
+ IAEFluidStack request = AEFluidStack.create(fluidStack);
+ request.setStackSize(Integer.MAX_VALUE);
+ IAEFluidStack result = sg.extractItems(request, Actionable.SIMULATE, getRequestSource());
+ FluidStack resultFluid = (result != null) ? result.getFluidStack() : null;
+ // We want to track if any FluidStack is modified to notify any connected controllers to make a recipe check
+ // early
+ if (expediteRecipeCheck) {
+ FluidStack previous = storedInformationFluids[index];
+ if (resultFluid != null) {
+ justHadNewFluids = !resultFluid.isFluidEqual(previous);
+ }
+ }
+ storedInformationFluids[index] = resultFluid;
+ } catch (final GridAccessException ignored) {}
+ }
+
+ private BaseActionSource getRequestSource() {
+ if (requestSource == null) requestSource = new MachineSource((IActionHost) getBaseMetaTileEntity());
+ return requestSource;
+ }
+
+ public FluidStack getMatchingFluidStack(FluidStack fluidStack) {
+ if (fluidStack == null) return null;
+
+ AENetworkProxy proxy = getProxy();
+ if (proxy == null || !proxy.isActive()) {
+ return null;
+ }
+
+ for (int i = 0; i < storedFluids.length; i++) {
+ if (storedFluids[i] == null) {
+ continue;
+ }
+
+ if (GT_Utility.areFluidsEqual(fluidStack, storedFluids[i], false)) {
+ updateInformationSlot(i);
+ if (storedInformationFluids[i] != null) {
+ setSavedFluid(i, storedInformationFluids[i]);
+ return shadowStoredFluids[i];
+ }
+
+ setSavedFluid(i, null);
+ return null;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Used to avoid slot update.
+ */
+ public FluidStack getShadowFluidStack(int index) {
+ if (index < 0 || index >= storedFluids.length) {
+ return null;
+ }
+
+ return shadowStoredFluids[index];
+ }
+
+ public int getFluidSlot(FluidStack fluidStack) {
+ if (fluidStack == null) return -1;
+
+ for (int i = 0; i < storedFluids.length; i++) {
+ if (storedFluids[i] == null) {
+ continue;
+ }
+
+ if (GT_Utility.areFluidsEqual(fluidStack, storedFluids[i], false)) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ @Override
+ public boolean canTankBeEmptied() {
+ return false;
+ }
+
+ @Override
+ public boolean canTankBeFilled() {
+ return false;
+ }
+
+ @Override
+ public boolean doesEmptyContainers() {
+ return false;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return false;
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+
+ NBTTagList nbtTagList = new NBTTagList();
+ for (int i = 0; i < SLOT_COUNT; i++) {
+ FluidStack fluidStack = storedFluids[i];
+ if (fluidStack == null) {
+ continue;
+ }
+ NBTTagCompound fluidTag = fluidStack.writeToNBT(new NBTTagCompound());
+ if (storedInformationFluids[i] != null)
+ fluidTag.setInteger("informationAmount", storedInformationFluids[i].amount);
+ nbtTagList.appendTag(fluidTag);
+ }
+
+ aNBT.setTag("storedFluids", nbtTagList);
+ aNBT.setBoolean("autoPull", autoPullFluidList);
+ aNBT.setInteger("minAmount", minAutoPullAmount);
+ aNBT.setBoolean("additionalConnection", additionalConnection);
+ aNBT.setBoolean("expediteRecipeCheck", expediteRecipeCheck);
+ aNBT.setInteger("refreshTime", autoPullRefreshTime);
+ getProxy().writeToNBT(aNBT);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ if (aNBT.hasKey("storedFluids")) {
+ NBTTagList nbtTagList = aNBT.getTagList("storedFluids", 10);
+ int c = Math.min(nbtTagList.tagCount(), SLOT_COUNT);
+ for (int i = 0; i < c; i++) {
+ NBTTagCompound nbtTagCompound = nbtTagList.getCompoundTagAt(i);
+ FluidStack fluidStack = GT_Utility.loadFluid(nbtTagCompound);
+ storedFluids[i] = fluidStack;
+
+ if (nbtTagCompound.hasKey("informationAmount")) {
+ int informationAmount = nbtTagCompound.getInteger("informationAmount");
+ storedInformationFluids[i] = GT_Utility.copyAmount(informationAmount, fluidStack);
+ }
+ }
+ }
+
+ minAutoPullAmount = aNBT.getInteger("minAmount");
+ autoPullFluidList = aNBT.getBoolean("autoPull");
+ additionalConnection = aNBT.getBoolean("additionalConnection");
+ expediteRecipeCheck = aNBT.getBoolean("expediteRecipeCheck");
+ if (aNBT.hasKey("refreshTime")) {
+ autoPullRefreshTime = aNBT.getInteger("refreshTime");
+ }
+ getProxy().readFromNBT(aNBT);
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ if (!autoPullAvailable) {
+ return;
+ }
+
+ setAutoPullFluidList(!autoPullFluidList);
+ aPlayer.addChatMessage(
+ new ChatComponentTranslation(
+ "GT5U.machines.stocking_hatch.auto_pull_toggle." + (autoPullFluidList ? "enabled" : "disabled")));
+ }
+
+ @Override
+ public String getCopiedDataIdentifier(EntityPlayer player) {
+ return COPIED_DATA_IDENTIFIER;
+ }
+
+ @Override
+ public boolean pasteCopiedData(EntityPlayer player, NBTTagCompound nbt) {
+ if (nbt == null || !COPIED_DATA_IDENTIFIER.equals(nbt.getString("type"))) return false;
+
+ if (autoPullAvailable) {
+ setAutoPullFluidList(nbt.getBoolean("autoPull"));
+ minAutoPullAmount = nbt.getInteger("minAmount");
+ autoPullRefreshTime = nbt.getInteger("refreshTime");
+ }
+ additionalConnection = nbt.getBoolean("additionalConnection");
+
+ if (!autoPullFluidList) {
+ NBTTagList stockingFluids = nbt.getTagList("fluidsToStock", 10);
+ for (int i = 0; i < stockingFluids.tagCount(); i++) {
+ storedFluids[i] = GT_Utility.loadFluid(stockingFluids.getCompoundTagAt(i));
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public NBTTagCompound getCopiedData(EntityPlayer player) {
+ NBTTagCompound tag = new NBTTagCompound();
+ tag.setString("type", COPIED_DATA_IDENTIFIER);
+ tag.setBoolean("autoPull", autoPullFluidList);
+ tag.setInteger("minAmount", minAutoPullAmount);
+ tag.setBoolean("additionalConnection", additionalConnection);
+ tag.setInteger("refreshTime", autoPullRefreshTime);
+
+ NBTTagList stockingFluids = new NBTTagList();
+ if (!autoPullFluidList) {
+ for (int index = 0; index < SLOT_COUNT; index++) {
+ FluidStack fluidStack = storedFluids[index];
+ if (fluidStack == null) {
+ continue;
+ }
+ stockingFluids.appendTag(fluidStack.writeToNBT(new NBTTagCompound()));
+ }
+ tag.setTag("fluidsToStock", stockingFluids);
+ }
+ return tag;
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer, ForgeDirection side,
+ float aX, float aY, float aZ) {
+ if (!(aPlayer instanceof EntityPlayerMP))
+ return super.onRightclick(aBaseMetaTileEntity, aPlayer, side, aX, aY, aZ);
+ ItemStack dataStick = aPlayer.inventory.getCurrentItem();
+ if (!ItemList.Tool_DataStick.isStackEqual(dataStick, false, true))
+ return super.onRightclick(aBaseMetaTileEntity, aPlayer, side, aX, aY, aZ);
+
+ if (!pasteCopiedData(aPlayer, dataStick.stackTagCompound)) return false;
+
+ updateValidGridProxySides();
+ aPlayer.addChatMessage(new ChatComponentTranslation("GT5U.machines.stocking_bus.loaded"));
+ return true;
+ }
+
+ @Override
+ public void onLeftclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ if (!(aPlayer instanceof EntityPlayerMP)) return;
+
+ ItemStack dataStick = aPlayer.inventory.getCurrentItem();
+ if (!ItemList.Tool_DataStick.isStackEqual(dataStick, false, true)) return;
+
+ dataStick.stackTagCompound = getCopiedData(aPlayer);
+ dataStick.setStackDisplayName("Stocking Input Hatch Configuration");
+ aPlayer.addChatMessage(new ChatComponentTranslation("GT5U.machines.stocking_bus.saved"));
+ }
+
+ @Override
+ public void onExplosion() {
+ for (int i = 0; i < SLOT_COUNT; i++) {
+ mInventory[i] = null;
+ }
+ }
+
+ public boolean containsSuchStack(FluidStack tStack) {
+ for (int i = 0; i < 16; ++i) {
+ if (GT_Utility.areFluidsEqual(storedFluids[i], tStack, false)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int getGUIHeight() {
+ return 179;
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ if (autoPullAvailable) {
+ buildContext.addSyncedWindow(CONFIG_WINDOW_ID, this::createStackSizeConfigurationWindow);
+ }
+
+ builder.widget(
+ SlotGroup.ofFluidTanks(
+ IntStream.range(0, SLOT_COUNT)
+ .mapToObj(index -> createTankForFluidStack(storedFluids, index, 1))
+ .collect(Collectors.toList()),
+ 4)
+ .phantom(true)
+ .widgetCreator((slotIndex, h) -> (FluidSlotWidget) new FluidSlotWidget(h) {
+
+ @Override
+ protected void tryClickPhantom(ClickData clickData, ItemStack cursorStack) {
+ if (clickData.mouseButton != 0 || autoPullFluidList) return;
+
+ FluidStack heldFluid = getFluidForPhantomItem(cursorStack);
+ if (cursorStack == null) {
+ storedFluids[slotIndex] = null;
+ } else {
+ if (containsSuchStack(heldFluid)) return;
+ storedFluids[slotIndex] = heldFluid;
+ }
+ if (getBaseMetaTileEntity().isServerSide()) {
+ updateInformationSlot(slotIndex);
+ detectAndSendChanges(false);
+ }
+ }
+
+ @Override
+ protected void tryScrollPhantom(int direction) {}
+
+ @Override
+ public IDrawable[] getBackground() {
+ IDrawable slot;
+ if (autoPullFluidList) {
+ slot = GT_UITextures.SLOT_DARK_GRAY;
+ } else {
+ slot = ModularUITextures.FLUID_SLOT;
+ }
+ return new IDrawable[] { slot, GT_UITextures.OVERLAY_SLOT_ARROW_ME };
+ }
+
+ @Override
+ public void buildTooltip(List<Text> tooltip) {
+ FluidStack fluid = getContent();
+ if (fluid != null) {
+ addFluidNameInfo(tooltip, fluid);
+
+ if (!autoPullFluidList) {
+ tooltip.add(Text.localised("modularui.phantom.single.clear"));
+ }
+ } else {
+ tooltip.add(
+ Text.localised("modularui.fluid.empty")
+ .format(EnumChatFormatting.WHITE));
+ }
+
+ if (autoPullFluidList) {
+ tooltip.add(Text.localised("GT5U.machines.stocking_bus.cannot_set_slot"));
+ }
+ }
+ }.setUpdateTooltipEveryTick(true))
+ .build()
+ .setPos(new Pos2d(7, 9)));
+
+ builder.widget(
+ SlotGroup.ofFluidTanks(
+ IntStream.range(0, SLOT_COUNT)
+ .mapToObj(index -> createTankForFluidStack(storedInformationFluids, index, Integer.MAX_VALUE))
+ .collect(Collectors.toList()),
+ 4)
+ .phantom(true)
+ .widgetCreator((slotIndex, h) -> (FluidSlotWidget) new FluidSlotWidget(h) {
+
+ @Override
+ protected void tryClickPhantom(ClickData clickData, ItemStack cursorStack) {}
+
+ @Override
+ protected void tryScrollPhantom(int direction) {}
+
+ @Override
+ public void buildTooltip(List<Text> tooltip) {
+ FluidStack fluid = getContent();
+ if (fluid != null) {
+ addFluidNameInfo(tooltip, fluid);
+ tooltip.add(Text.localised("modularui.fluid.phantom.amount", fluid.amount));
+ addAdditionalFluidInfo(tooltip, fluid);
+ if (!Interactable.hasShiftDown()) {
+ tooltip.add(Text.EMPTY);
+ tooltip.add(Text.localised("modularui.tooltip.shift"));
+ }
+ } else {
+ tooltip.add(
+ Text.localised("modularui.fluid.empty")
+ .format(EnumChatFormatting.WHITE));
+ }
+ }
+ }.setUpdateTooltipEveryTick(true))
+ .background(GT_UITextures.SLOT_DARK_GRAY)
+ .controlsAmount(true)
+ .build()
+ .setPos(new Pos2d(97, 9)));
+
+ if (autoPullAvailable) {
+ builder.widget(new ButtonWidget().setOnClick((clickData, widget) -> {
+ if (clickData.mouseButton == 0) {
+ setAutoPullFluidList(!autoPullFluidList);
+ } else if (clickData.mouseButton == 1 && !widget.isClient()) {
+ widget.getContext()
+ .openSyncedWindow(CONFIG_WINDOW_ID);
+ }
+ })
+ .setPlayClickSound(true)
+ .setBackground(() -> {
+ if (autoPullFluidList) {
+ return new IDrawable[] { GT_UITextures.BUTTON_STANDARD_PRESSED,
+ GT_UITextures.OVERLAY_BUTTON_AUTOPULL_ME };
+ } else {
+ return new IDrawable[] { GT_UITextures.BUTTON_STANDARD,
+ GT_UITextures.OVERLAY_BUTTON_AUTOPULL_ME_DISABLED };
+ }
+ })
+ .addTooltips(
+ Arrays.asList(
+ StatCollector.translateToLocal("GT5U.machines.stocking_hatch.auto_pull.tooltip.1"),
+ StatCollector.translateToLocal("GT5U.machines.stocking_hatch.auto_pull.tooltip.2")))
+ .setSize(16, 16)
+ .setPos(80, 10))
+ .widget(new FakeSyncWidget.BooleanSyncer(() -> autoPullFluidList, this::setAutoPullFluidList));
+ }
+
+ builder.widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_ARROW_DOUBLE)
+ .setPos(82, 30)
+ .setSize(12, 12))
+ .widget(TextWidget.dynamicString(() -> {
+ boolean isActive = isActive();
+ boolean isPowered = isPowered();
+ boolean isBooting = isBooting();
+ EnumChatFormatting color = (isActive && isPowered) ? EnumChatFormatting.GREEN
+ : EnumChatFormatting.DARK_RED;
+ return color + WailaText.getPowerState(isActive, isPowered, isBooting);
+ })
+ .setTextAlignment(Alignment.Center)
+ .setSize(90, 9)
+ .setPos(43, 84));
+ }
+
+ private FluidStackTank createTankForFluidStack(FluidStack[] fluidStacks, int slotIndex, int capacity) {
+ return new FluidStackTank(() -> fluidStacks[slotIndex], (stack) -> {
+ if (getBaseMetaTileEntity().isServerSide()) {
+ return;
+ }
+
+ fluidStacks[slotIndex] = stack;
+ }, capacity);
+ }
+
+ protected ModularWindow createStackSizeConfigurationWindow(final EntityPlayer player) {
+ final int WIDTH = 78;
+ final int HEIGHT = 115;
+ final int PARENT_WIDTH = getGUIWidth();
+ final int PARENT_HEIGHT = getGUIHeight();
+ ModularWindow.Builder builder = ModularWindow.builder(WIDTH, HEIGHT);
+ builder.setBackground(GT_UITextures.BACKGROUND_SINGLEBLOCK_DEFAULT);
+ builder.setGuiTint(getGUIColorization());
+ builder.setDraggable(true);
+ builder.setPos(
+ (size, window) -> Alignment.Center.getAlignedPos(size, new Size(PARENT_WIDTH, PARENT_HEIGHT))
+ .add(
+ Alignment.TopRight.getAlignedPos(new Size(PARENT_WIDTH, PARENT_HEIGHT), new Size(WIDTH, HEIGHT))
+ .add(WIDTH - 3, 0)));
+ builder.widget(
+ TextWidget.localised("GT5U.machines.stocking_hatch.min_amount")
+ .setPos(3, 2)
+ .setSize(74, 14))
+ .widget(
+ new NumericWidget().setSetter(val -> minAutoPullAmount = (int) val)
+ .setGetter(() -> minAutoPullAmount)
+ .setBounds(1, Integer.MAX_VALUE)
+ .setScrollValues(1, 4, 64)
+ .setTextAlignment(Alignment.Center)
+ .setTextColor(Color.WHITE.normal)
+ .setSize(70, 18)
+ .setPos(3, 18)
+ .setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD));
+ builder.widget(
+ TextWidget.localised("GT5U.machines.stocking_bus.refresh_time")
+ .setPos(3, 42)
+ .setSize(74, 14))
+ .widget(
+ new NumericWidget().setSetter(val -> autoPullRefreshTime = (int) val)
+ .setGetter(() -> autoPullRefreshTime)
+ .setBounds(1, Integer.MAX_VALUE)
+ .setScrollValues(1, 4, 64)
+ .setTextAlignment(Alignment.Center)
+ .setTextColor(Color.WHITE.normal)
+ .setSize(70, 18)
+ .setPos(3, 58)
+ .setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD));
+ builder.widget(
+ TextWidget.localised("GT5U.machines.stocking_bus.force_check")
+ .setPos(3, 88)
+ .setSize(50, 14))
+ .widget(
+ new CycleButtonWidget().setToggle(() -> expediteRecipeCheck, val -> setRecipeCheck(val))
+ .setTextureGetter(
+ state -> expediteRecipeCheck ? GT_UITextures.OVERLAY_BUTTON_CHECKMARK
+ : GT_UITextures.OVERLAY_BUTTON_CROSS)
+ .setBackground(GT_UITextures.BUTTON_STANDARD)
+ .setPos(53, 87)
+ .setSize(16, 16)
+ .addTooltip(StatCollector.translateToLocal("GT5U.machines.stocking_bus.hatch_warning")));
+ return builder.build();
+ }
+
+ @Override
+ public void addGregTechLogo(ModularWindow.Builder builder) {
+ builder.widget(
+ new DrawableWidget().setDrawable(getGUITextureSet().getGregTechLogo())
+ .setSize(17, 17)
+ .setPos(80, 63));
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ if (!autoPullAvailable) {
+ super.getWailaBody(itemStack, currenttip, accessor, config);
+ return;
+ }
+
+ NBTTagCompound tag = accessor.getNBTData();
+ boolean autopull = tag.getBoolean("autoPull");
+ int minSize = tag.getInteger("minAmount");
+ currenttip.add(
+ StatCollector.translateToLocal("GT5U.waila.stocking_bus.auto_pull." + (autopull ? "enabled" : "disabled")));
+ if (autopull) {
+ currenttip.add(
+ StatCollector.translateToLocalFormatted(
+ "GT5U.waila.stocking_hatch.min_amount",
+ GT_Utility.formatNumbers(minSize)));
+ }
+ super.getWailaBody(itemStack, currenttip, accessor, config);
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ if (!autoPullAvailable) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ return;
+ }
+
+ tag.setBoolean("autoPull", autoPullFluidList);
+ tag.setInteger("minAmount", minAutoPullAmount);
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ }
+
+ private static String[] getDescriptionArray(boolean autoPullAvailable) {
+ List<String> strings = new ArrayList<>(8);
+ strings.add("Advanced fluid input for Multiblocks");
+ strings.add("Hatch Tier: " + TIER_COLORS[autoPullAvailable ? 10 : 8] + VN[autoPullAvailable ? 10 : 8]);
+ strings.add("Retrieves directly from ME");
+ strings.add("Keeps 16 fluid types in stock");
+
+ if (autoPullAvailable) {
+ strings.add(
+ "Auto-Pull from ME mode will automatically stock the first 16 fluid in the ME system, updated every 5 seconds.");
+ strings.add("Toggle by right-clicking with screwdriver, or use the GUI.");
+ strings.add(
+ "Use the GUI to limit the minimum stack size for Auto-Pulling, adjust the slot refresh timer and enable fast recipe checks.");
+ strings.add("WARNING: Fast recipe checks can be laggy. Use with caution.");
+ }
+
+ strings.add("Change ME connection behavior by right-clicking with wire cutter.");
+ strings.add("Configuration data can be copy+pasted using a data stick.");
+ return strings.toArray(new String[0]);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_OutputBus_ME.java b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_OutputBus_ME.java
new file mode 100644
index 0000000000..f4d78ec9b3
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_OutputBus_ME.java
@@ -0,0 +1,380 @@
+package gregtech.common.tileentities.machines;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_ME_HATCH;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_ME_HATCH_ACTIVE;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTBase;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraft.util.ChatComponentTranslation;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+
+import appeng.api.AEApi;
+import appeng.api.implementations.IPowerChannelState;
+import appeng.api.networking.GridFlags;
+import appeng.api.networking.security.BaseActionSource;
+import appeng.api.networking.security.IActionHost;
+import appeng.api.networking.security.MachineSource;
+import appeng.api.storage.IMEMonitor;
+import appeng.api.storage.data.IAEItemStack;
+import appeng.api.storage.data.IItemList;
+import appeng.api.util.AECableType;
+import appeng.items.storage.ItemBasicStorageCell;
+import appeng.me.GridAccessException;
+import appeng.me.helpers.AENetworkProxy;
+import appeng.me.helpers.IGridProxyable;
+import appeng.util.IWideReadableNumberConverter;
+import appeng.util.Platform;
+import appeng.util.ReadableNumberConverter;
+import gregtech.GT_Mod;
+import gregtech.api.enums.ItemList;
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_OutputBus;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_Hatch_OutputBus_ME extends GT_MetaTileEntity_Hatch_OutputBus
+ implements IPowerChannelState {
+
+ private long baseCapacity = 1_600;
+
+ private BaseActionSource requestSource = null;
+ private @Nullable AENetworkProxy gridProxy = null;
+ final IItemList<IAEItemStack> itemCache = AEApi.instance()
+ .storage()
+ .createItemList();
+ long lastOutputTick = 0;
+ long lastInputTick = 0;
+ long tickCounter = 0;
+ boolean additionalConnection = false;
+
+ public GT_MetaTileEntity_Hatch_OutputBus_ME(int aID, String aName, String aNameRegional) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ 3,
+ new String[] { "Item Output for Multiblocks", "Stores directly into ME", "Can cache 1600 items by default",
+ "Change cache size by inserting a storage cell",
+ "Change ME connection behavior by right-clicking with wire cutter" },
+ 1);
+ }
+
+ public GT_MetaTileEntity_Hatch_OutputBus_ME(String aName, int aTier, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, 1, aDescription, aTextures);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Hatch_OutputBus_ME(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_ME_HATCH_ACTIVE) };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_ME_HATCH) };
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ super.onFirstTick(aBaseMetaTileEntity);
+ getProxy().onReady();
+ }
+
+ @Override
+ public boolean storeAll(ItemStack aStack) {
+ aStack.stackSize = store(aStack);
+ return aStack.stackSize == 0;
+ }
+
+ private long getCachedAmount() {
+ long itemAmount = 0;
+ for (IAEItemStack item : itemCache) {
+ itemAmount += item.getStackSize();
+ }
+ return itemAmount;
+ }
+
+ private long getCacheCapacity() {
+ ItemStack upgradeItemStack = mInventory[0];
+ if (upgradeItemStack != null && upgradeItemStack.getItem() instanceof ItemBasicStorageCell) {
+ return ((ItemBasicStorageCell) upgradeItemStack.getItem()).getBytesLong(upgradeItemStack) * 8;
+ }
+ return baseCapacity;
+ }
+
+ /**
+ * Check if the internal cache can still fit more items in it
+ */
+ public boolean canAcceptItem() {
+ if (getCachedAmount() < getCacheCapacity()) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Attempt to store items in connected ME network. Returns how many items did not fit (if the network was down e.g.)
+ *
+ * @param stack input stack
+ * @return amount of items left over
+ */
+ public int store(final ItemStack stack) {
+ // Always allow insertion on the same tick so we can output the entire recipe
+ if (canAcceptItem() || (lastInputTick == tickCounter)) {
+ itemCache.add(
+ AEApi.instance()
+ .storage()
+ .createItemStack(stack));
+ lastInputTick = tickCounter;
+ return 0;
+ }
+ return stack.stackSize;
+ }
+
+ private BaseActionSource getRequest() {
+ if (requestSource == null) requestSource = new MachineSource((IActionHost) getBaseMetaTileEntity());
+ return requestSource;
+ }
+
+ @Override
+ public AECableType getCableConnectionType(ForgeDirection forgeDirection) {
+ return isOutputFacing(forgeDirection) ? AECableType.SMART : AECableType.NONE;
+ }
+
+ private void updateValidGridProxySides() {
+ if (additionalConnection) {
+ getProxy().setValidSides(EnumSet.complementOf(EnumSet.of(ForgeDirection.UNKNOWN)));
+ } else {
+ getProxy().setValidSides(EnumSet.of(getBaseMetaTileEntity().getFrontFacing()));
+ }
+ }
+
+ @Override
+ public void onFacingChange() {
+ updateValidGridProxySides();
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ if (!getBaseMetaTileEntity().getCoverInfoAtSide(side)
+ .isGUIClickable()) return;
+ }
+
+ @Override
+ public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer,
+ float aX, float aY, float aZ) {
+ additionalConnection = !additionalConnection;
+ updateValidGridProxySides();
+ aPlayer.addChatComponentMessage(
+ new ChatComponentTranslation("GT5U.hatch.additionalConnection." + additionalConnection));
+ return true;
+ }
+
+ @Override
+ public AENetworkProxy getProxy() {
+ if (gridProxy == null) {
+ if (getBaseMetaTileEntity() instanceof IGridProxyable) {
+ gridProxy = new AENetworkProxy(
+ (IGridProxyable) getBaseMetaTileEntity(),
+ "proxy",
+ ItemList.Hatch_Output_Bus_ME.get(1),
+ true);
+ gridProxy.setFlags(GridFlags.REQUIRE_CHANNEL);
+ updateValidGridProxySides();
+ if (getBaseMetaTileEntity().getWorld() != null) gridProxy.setOwner(
+ getBaseMetaTileEntity().getWorld()
+ .getPlayerEntityByName(getBaseMetaTileEntity().getOwnerName()));
+ }
+ }
+ return this.gridProxy;
+ }
+
+ private void flushCachedStack() {
+ AENetworkProxy proxy = getProxy();
+ if (proxy == null) {
+ return;
+ }
+ try {
+ IMEMonitor<IAEItemStack> sg = proxy.getStorage()
+ .getItemInventory();
+ for (IAEItemStack s : itemCache) {
+ if (s.getStackSize() == 0) continue;
+ IAEItemStack rest = Platform.poweredInsert(proxy.getEnergy(), sg, s, getRequest());
+ if (rest != null && rest.getStackSize() > 0) {
+ s.setStackSize(rest.getStackSize());
+ break;
+ }
+ s.setStackSize(0);
+ }
+ } catch (final GridAccessException ignored) {
+
+ }
+ lastOutputTick = tickCounter;
+ }
+
+ @Override
+ public boolean isPowered() {
+ return getProxy() != null && getProxy().isPowered();
+ }
+
+ @Override
+ public boolean isActive() {
+ return getProxy() != null && getProxy().isActive();
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (getBaseMetaTileEntity().isServerSide()) {
+ tickCounter = aTick;
+ if (tickCounter > (lastOutputTick + 40)) flushCachedStack();
+ if (tickCounter % 20 == 0) getBaseMetaTileEntity().setActive(isActive());
+ }
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ }
+
+ @Override
+ public void addAdditionalTooltipInformation(ItemStack stack, List<String> tooltip) {
+
+ if (stack.hasTagCompound() && stack.stackTagCompound.hasKey("baseCapacity")) {
+ tooltip.add(
+ "Current cache capacity: " + EnumChatFormatting.YELLOW
+ + ReadableNumberConverter.INSTANCE
+ .toWideReadableForm(stack.stackTagCompound.getLong("baseCapacity")));
+ }
+ }
+
+ @Override
+ public void setItemNBT(NBTTagCompound aNBT) {
+ super.setItemNBT(aNBT);
+ aNBT.setLong("baseCapacity", baseCapacity);
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+
+ NBTTagList items = new NBTTagList();
+ for (IAEItemStack s : itemCache) {
+ if (s.getStackSize() == 0) continue;
+ NBTTagCompound tag = new NBTTagCompound();
+ tag.setTag("itemStack", GT_Utility.saveItem(s.getItemStack()));
+ tag.setLong("size", s.getStackSize());
+ items.appendTag(tag);
+ }
+ aNBT.setBoolean("additionalConnection", additionalConnection);
+ aNBT.setTag("cachedItems", items);
+ aNBT.setLong("baseCapacity", baseCapacity);
+ getProxy().writeToNBT(aNBT);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+
+ NBTBase t = aNBT.getTag("cachedStack"); // legacy
+ if (t instanceof NBTTagCompound) itemCache.add(
+ AEApi.instance()
+ .storage()
+ .createItemStack(GT_Utility.loadItem((NBTTagCompound) t)));
+ t = aNBT.getTag("cachedItems");
+ if (t instanceof NBTTagList l) {
+ for (int i = 0; i < l.tagCount(); ++i) {
+ NBTTagCompound tag = l.getCompoundTagAt(i);
+ if (!tag.hasKey("itemStack")) { // legacy #868
+ itemCache.add(
+ AEApi.instance()
+ .storage()
+ .createItemStack(GT_Utility.loadItem(l.getCompoundTagAt(i))));
+ continue;
+ }
+ NBTTagCompound tagItemStack = tag.getCompoundTag("itemStack");
+ final IAEItemStack s = AEApi.instance()
+ .storage()
+ .createItemStack(GT_Utility.loadItem(tagItemStack));
+ if (s != null) {
+ s.setStackSize(tag.getLong("size"));
+ itemCache.add(s);
+ } else {
+ GT_Mod.GT_FML_LOGGER.warn(
+ "An error occurred while loading contents of ME Output Bus. This item has been voided: "
+ + tagItemStack);
+ }
+ }
+ }
+ additionalConnection = aNBT.getBoolean("additionalConnection");
+ baseCapacity = aNBT.getLong("baseCapacity");
+ // Set the base capacity of existing hatches to be infinite
+ if (baseCapacity == 0) {
+ baseCapacity = Long.MAX_VALUE;
+ }
+ getProxy().readFromNBT(aNBT);
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return true;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ List<String> ss = new ArrayList<>();
+ ss.add(
+ "The bus is " + ((getProxy() != null && getProxy().isActive()) ? EnumChatFormatting.GREEN + "online"
+ : EnumChatFormatting.RED + "offline" + getAEDiagnostics()) + EnumChatFormatting.RESET);
+ IWideReadableNumberConverter nc = ReadableNumberConverter.INSTANCE;
+ ss.add("Item cache capacity: " + nc.toWideReadableForm(getCacheCapacity()));
+ if (itemCache.isEmpty()) {
+ ss.add("The bus has no cached items");
+ } else {
+ ss.add(String.format("The bus contains %d cached stacks: ", itemCache.size()));
+ int counter = 0;
+ for (IAEItemStack s : itemCache) {
+ ss.add(
+ s.getItem()
+ .getItemStackDisplayName(s.getItemStack()) + ": "
+ + EnumChatFormatting.GOLD
+ + nc.toWideReadableForm(s.getStackSize())
+ + EnumChatFormatting.RESET);
+ if (++counter > 100) break;
+ }
+ }
+ return ss.toArray(new String[itemCache.size() + 2]);
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ getBaseMetaTileEntity().add1by1Slot(builder);
+ }
+
+ @Override
+ public boolean acceptsItemLock() {
+ return false;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_Output_ME.java b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_Output_ME.java
new file mode 100644
index 0000000000..b173987aa2
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_Output_ME.java
@@ -0,0 +1,443 @@
+package gregtech.common.tileentities.machines;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_ME_FLUID_HATCH;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_ME_FLUID_HATCH_ACTIVE;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTBase;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraft.util.ChatComponentTranslation;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import com.glodblock.github.common.item.FCBaseItemCell;
+import com.glodblock.github.common.storage.IStorageFluidCell;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+
+import appeng.api.AEApi;
+import appeng.api.config.Actionable;
+import appeng.api.config.PowerMultiplier;
+import appeng.api.implementations.IPowerChannelState;
+import appeng.api.networking.GridFlags;
+import appeng.api.networking.energy.IEnergySource;
+import appeng.api.networking.security.BaseActionSource;
+import appeng.api.networking.security.IActionHost;
+import appeng.api.networking.security.MachineSource;
+import appeng.api.networking.security.PlayerSource;
+import appeng.api.storage.IMEInventory;
+import appeng.api.storage.IMEMonitor;
+import appeng.api.storage.data.IAEFluidStack;
+import appeng.api.storage.data.IItemList;
+import appeng.api.util.AECableType;
+import appeng.core.stats.Stats;
+import appeng.me.GridAccessException;
+import appeng.me.helpers.AENetworkProxy;
+import appeng.me.helpers.IGridProxyable;
+import appeng.util.IWideReadableNumberConverter;
+import appeng.util.ReadableNumberConverter;
+import gregtech.GT_Mod;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.ItemList;
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Output;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_Hatch_Output_ME extends GT_MetaTileEntity_Hatch_Output implements IPowerChannelState {
+
+ private long baseCapacity = 128_000;
+
+ private BaseActionSource requestSource = null;
+ private @Nullable AENetworkProxy gridProxy = null;
+ final IItemList<IAEFluidStack> fluidCache = AEApi.instance()
+ .storage()
+ .createFluidList();
+ long lastOutputTick = 0;
+ long lastInputTick = 0;
+ long tickCounter = 0;
+ boolean additionalConnection = false;
+
+ public GT_MetaTileEntity_Hatch_Output_ME(int aID, String aName, String aNameRegional) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ 3,
+ new String[] { "Fluid Output for Multiblocks", "Stores directly into ME",
+ "Can cache up to 128kL of fluids by default", "Change cache size by inserting a fluid storage cell",
+ "Change ME connection behavior by right-clicking with wire cutter" },
+ 1);
+ }
+
+ public GT_MetaTileEntity_Hatch_Output_ME(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 1, aDescription, aTextures);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Hatch_Output_ME(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_ME_FLUID_HATCH_ACTIVE) };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_ME_FLUID_HATCH) };
+ }
+
+ @Override
+ public byte getTierForStructure() {
+ return (byte) (GT_Values.V.length - 2);
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ super.onFirstTick(aBaseMetaTileEntity);
+ getProxy().onReady();
+ }
+
+ @Override
+ public int fill(FluidStack aFluid, boolean doFill) {
+ if (doFill) {
+ return tryFillAE(aFluid);
+ } else {
+ if (aFluid == null) return 0;
+ return aFluid.amount;
+ }
+ }
+
+ @Override
+ public int getCapacity() {
+ return 0;
+ }
+
+ private long getCachedAmount() {
+ long fluidAmount = 0;
+ for (IAEFluidStack fluid : fluidCache) {
+ fluidAmount += fluid.getStackSize();
+ }
+ return fluidAmount;
+ }
+
+ private long getCacheCapacity() {
+ ItemStack upgradeItemStack = mInventory[0];
+ if (upgradeItemStack != null && upgradeItemStack.getItem() instanceof IStorageFluidCell) {
+ return ((FCBaseItemCell) upgradeItemStack.getItem()).getBytes(upgradeItemStack) * 2048;
+ }
+ return baseCapacity;
+ }
+
+ /**
+ * Check if the internal cache can still fit more fluids in it
+ */
+ public boolean canAcceptFluid() {
+ if (getCachedAmount() < getCacheCapacity()) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Attempt to store fluid in connected ME network. Returns how much fluid is accepted (if the network was down e.g.)
+ *
+ * @param aFluid input fluid
+ * @return amount of fluid filled
+ */
+ public int tryFillAE(final FluidStack aFluid) {
+ if (aFluid == null) return 0;
+ // Always allow insertion on the same tick so we can output the entire recipe
+ if (canAcceptFluid() || (lastInputTick == tickCounter)) {
+ fluidCache.add(
+ AEApi.instance()
+ .storage()
+ .createFluidStack(aFluid));
+ lastInputTick = tickCounter;
+ return aFluid.amount;
+ }
+ return 0;
+ }
+
+ private BaseActionSource getRequest() {
+ if (requestSource == null) requestSource = new MachineSource((IActionHost) getBaseMetaTileEntity());
+ return requestSource;
+ }
+
+ @Override
+ public AECableType getCableConnectionType(ForgeDirection side) {
+ return isOutputFacing(side) ? AECableType.SMART : AECableType.NONE;
+ }
+
+ private void updateValidGridProxySides() {
+ if (additionalConnection) {
+ getProxy().setValidSides(EnumSet.complementOf(EnumSet.of(ForgeDirection.UNKNOWN)));
+ } else {
+ getProxy().setValidSides(EnumSet.of(getBaseMetaTileEntity().getFrontFacing()));
+ }
+ }
+
+ @Override
+ public void onFacingChange() {
+ updateValidGridProxySides();
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return true;
+ }
+
+ @Override
+ public boolean doesFillContainers() {
+ return false;
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ // Don't allow to lock fluid in me fluid hatch
+ if (!getBaseMetaTileEntity().getCoverInfoAtSide(side)
+ .isGUIClickable()) return;
+ }
+
+ @Override
+ public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer,
+ float aX, float aY, float aZ) {
+ additionalConnection = !additionalConnection;
+ updateValidGridProxySides();
+ aPlayer.addChatComponentMessage(
+ new ChatComponentTranslation("GT5U.hatch.additionalConnection." + additionalConnection));
+ return true;
+ }
+
+ @Override
+ public AENetworkProxy getProxy() {
+ if (gridProxy == null) {
+ if (getBaseMetaTileEntity() instanceof IGridProxyable) {
+ gridProxy = new AENetworkProxy(
+ (IGridProxyable) getBaseMetaTileEntity(),
+ "proxy",
+ ItemList.Hatch_Output_ME.get(1),
+ true);
+ gridProxy.setFlags(GridFlags.REQUIRE_CHANNEL);
+ updateValidGridProxySides();
+ if (getBaseMetaTileEntity().getWorld() != null) gridProxy.setOwner(
+ getBaseMetaTileEntity().getWorld()
+ .getPlayerEntityByName(getBaseMetaTileEntity().getOwnerName()));
+ }
+ }
+ return this.gridProxy;
+ }
+
+ private void flushCachedStack() {
+ if (fluidCache.isEmpty()) return;
+ AENetworkProxy proxy = getProxy();
+ if (proxy == null) {
+ return;
+ }
+ try {
+ IMEMonitor<IAEFluidStack> sg = proxy.getStorage()
+ .getFluidInventory();
+ for (IAEFluidStack s : fluidCache) {
+ if (s.getStackSize() == 0) continue;
+ IAEFluidStack rest = fluidAEInsert(proxy.getEnergy(), sg, s, getRequest());
+ if (rest != null && rest.getStackSize() > 0) {
+ s.setStackSize(rest.getStackSize());
+ continue;
+ }
+ s.setStackSize(0);
+ }
+ } catch (final GridAccessException ignored) {}
+ lastOutputTick = tickCounter;
+ }
+
+ @Override
+ public boolean isPowered() {
+ return getProxy() != null && getProxy().isPowered();
+ }
+
+ @Override
+ public boolean isActive() {
+ return getProxy() != null && getProxy().isActive();
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (getBaseMetaTileEntity().isServerSide()) {
+ tickCounter = aTick;
+ if (tickCounter > (lastOutputTick + 40)) flushCachedStack();
+ if (tickCounter % 20 == 0) getBaseMetaTileEntity().setActive(isActive());
+ }
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ }
+
+ @Override
+ public void addAdditionalTooltipInformation(ItemStack stack, List<String> tooltip) {
+
+ if (stack.hasTagCompound() && stack.stackTagCompound.hasKey("baseCapacity")) {
+ tooltip.add(
+ "Current cache capacity: " + EnumChatFormatting.YELLOW
+ + ReadableNumberConverter.INSTANCE
+ .toWideReadableForm(stack.stackTagCompound.getLong("baseCapacity"))
+ + "L");
+ }
+ }
+
+ @Override
+ public void setItemNBT(NBTTagCompound aNBT) {
+ super.setItemNBT(aNBT);
+ aNBT.setLong("baseCapacity", baseCapacity);
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ NBTTagList fluids = new NBTTagList();
+ for (IAEFluidStack s : fluidCache) {
+ if (s.getStackSize() == 0) continue;
+ NBTTagCompound tag = new NBTTagCompound();
+ NBTTagCompound tagFluidStack = new NBTTagCompound();
+ s.getFluidStack()
+ .writeToNBT(tagFluidStack);
+ tag.setTag("fluidStack", tagFluidStack);
+ tag.setLong("size", s.getStackSize());
+ fluids.appendTag(tag);
+ }
+ aNBT.setTag("cachedFluids", fluids);
+ aNBT.setBoolean("additionalConnection", additionalConnection);
+ aNBT.setLong("baseCapacity", baseCapacity);
+ getProxy().writeToNBT(aNBT);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ NBTBase t = aNBT.getTag("cachedFluids");
+ if (t instanceof NBTTagList l) {
+ for (int i = 0; i < l.tagCount(); ++i) {
+ NBTTagCompound tag = l.getCompoundTagAt(i);
+ NBTTagCompound tagFluidStack = tag.getCompoundTag("fluidStack");
+ final IAEFluidStack s = AEApi.instance()
+ .storage()
+ .createFluidStack(GT_Utility.loadFluid(tagFluidStack));
+ if (s != null) {
+ s.setStackSize(tag.getLong("size"));
+ fluidCache.add(s);
+ } else {
+ GT_Mod.GT_FML_LOGGER.warn(
+ "An error occurred while loading contents of ME Output Hatch. This fluid has been voided: "
+ + tagFluidStack);
+ }
+ }
+ }
+ additionalConnection = aNBT.getBoolean("additionalConnection");
+ baseCapacity = aNBT.getLong("baseCapacity");
+ // Set the base capacity of existing hatches to be infinite
+ if (baseCapacity == 0) {
+ baseCapacity = Long.MAX_VALUE;
+ }
+ getProxy().readFromNBT(aNBT);
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return true;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ List<String> ss = new ArrayList<>();
+ ss.add(
+ "The hatch is " + ((getProxy() != null && getProxy().isActive()) ? EnumChatFormatting.GREEN + "online"
+ : EnumChatFormatting.RED + "offline" + getAEDiagnostics()) + EnumChatFormatting.RESET);
+ IWideReadableNumberConverter nc = ReadableNumberConverter.INSTANCE;
+ ss.add("Fluid cache capacity: " + nc.toWideReadableForm(getCacheCapacity()) + " mB");
+ if (fluidCache.isEmpty()) {
+ ss.add("The bus has no cached fluids");
+ } else {
+ ss.add(String.format("The hatch contains %d cached fluids: ", fluidCache.size()));
+ int counter = 0;
+ for (IAEFluidStack s : fluidCache) {
+ ss.add(
+ s.getFluidStack()
+ .getLocalizedName() + ": "
+ + EnumChatFormatting.GOLD
+ + nc.toWideReadableForm(s.getStackSize())
+ + " mB"
+ + EnumChatFormatting.RESET);
+ if (++counter > 100) break;
+ }
+ }
+ return ss.toArray(new String[fluidCache.size() + 2]);
+ }
+
+ public static IAEFluidStack fluidAEInsert(final IEnergySource energy, final IMEInventory<IAEFluidStack> cell,
+ final IAEFluidStack input, final BaseActionSource src) {
+ final IAEFluidStack possible = cell.injectItems(input.copy(), Actionable.SIMULATE, src);
+
+ long stored = input.getStackSize();
+ if (possible != null) {
+ stored -= possible.getStackSize();
+ }
+ // 1000 mb fluid will be considered as 1 item
+ long power = Math.max(1, stored / 1000);
+
+ final double availablePower = energy.extractAEPower(power, Actionable.SIMULATE, PowerMultiplier.CONFIG);
+
+ final long itemToAdd = Math.min((long) (availablePower + 0.9) * 1000, stored);
+
+ if (itemToAdd > 0) {
+ energy.extractAEPower(power, Actionable.MODULATE, PowerMultiplier.CONFIG);
+
+ if (itemToAdd < input.getStackSize()) {
+ final long original = input.getStackSize();
+ final IAEFluidStack split = input.copy();
+ split.decStackSize(itemToAdd);
+ input.setStackSize(itemToAdd);
+ split.add(cell.injectItems(input, Actionable.MODULATE, src));
+
+ if (src.isPlayer()) {
+ final long diff = original - split.getStackSize();
+ Stats.ItemsInserted.addToPlayer(((PlayerSource) src).player, (int) diff);
+ }
+
+ return split;
+ }
+
+ final IAEFluidStack ret = cell.injectItems(input, Actionable.MODULATE, src);
+
+ if (src.isPlayer()) {
+ final long diff = ret == null ? input.getStackSize() : input.getStackSize() - ret.getStackSize();
+ Stats.ItemsInserted.addToPlayer(((PlayerSource) src).player, (int) diff);
+ }
+
+ return ret;
+ }
+
+ return input;
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ getBaseMetaTileEntity().add1by1Slot(builder);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/IDualInputHatch.java b/src/main/java/gregtech/common/tileentities/machines/IDualInputHatch.java
new file mode 100644
index 0000000000..c89aaaff40
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/IDualInputHatch.java
@@ -0,0 +1,21 @@
+package gregtech.common.tileentities.machines;
+
+import java.util.Iterator;
+import java.util.Optional;
+
+import net.minecraft.item.ItemStack;
+
+public interface IDualInputHatch {
+
+ boolean justUpdated();
+
+ Iterator<? extends IDualInputInventory> inventories();
+
+ void updateTexture(int id);
+
+ void updateCraftingIcon(ItemStack icon);
+
+ Optional<IDualInputInventory> getFirstNonEmptyInventory();
+
+ public boolean supportsFluids();
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/IDualInputInventory.java b/src/main/java/gregtech/common/tileentities/machines/IDualInputInventory.java
new file mode 100644
index 0000000000..01649fe181
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/IDualInputInventory.java
@@ -0,0 +1,11 @@
+package gregtech.common.tileentities.machines;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.fluids.FluidStack;
+
+public interface IDualInputInventory {
+
+ ItemStack[] getItemInputs();
+
+ FluidStack[] getFluidInputs();
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/IRecipeProcessingAwareHatch.java b/src/main/java/gregtech/common/tileentities/machines/IRecipeProcessingAwareHatch.java
new file mode 100644
index 0000000000..c7a97ce969
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/IRecipeProcessingAwareHatch.java
@@ -0,0 +1,26 @@
+package gregtech.common.tileentities.machines;
+
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_MultiBlockBase;
+import gregtech.api.recipe.check.CheckRecipeResult;
+
+/**
+ * Implement this interface for {@link gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch}
+ * if it does special stuff while multiblock controller is processing recipe.
+ */
+public interface IRecipeProcessingAwareHatch {
+
+ /**
+ * Called when multiblock controller starts processing.
+ * {@link #endRecipeProcessing} is called on the same tick.
+ */
+ void startRecipeProcessing();
+
+ /**
+ * Called when multiblock controller ends processing. {@link #startRecipeProcessing} is called on the same tick.
+ *
+ * @param controller Caller of this method.
+ * @return Result of the process of this method. {@code !wasSuccessful()} means the returned result should
+ * overwrite the result calculated on multiblock whatever the reason is.
+ */
+ CheckRecipeResult endRecipeProcessing(GT_MetaTileEntity_MultiBlockBase controller);
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/ISmartInputHatch.java b/src/main/java/gregtech/common/tileentities/machines/ISmartInputHatch.java
new file mode 100644
index 0000000000..dc5cac45f8
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/ISmartInputHatch.java
@@ -0,0 +1,15 @@
+package gregtech.common.tileentities.machines;
+
+public interface ISmartInputHatch {
+
+ /*
+ * An interface to allow advanced interaction between hatches and a multiblock controller
+ * Adapted from the Crafting Input Buffer functionality
+ */
+
+ // Have the contents of the hatch changed since the last check?
+ boolean justUpdated();
+
+ public boolean doFastRecipeCheck();
+
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_AdvSeismicProspector.java b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_AdvSeismicProspector.java
new file mode 100644
index 0000000000..78ed9982fc
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_AdvSeismicProspector.java
@@ -0,0 +1,309 @@
+package gregtech.common.tileentities.machines.basic;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_ROCK_BREAKER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_ROCK_BREAKER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_ROCK_BREAKER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_ROCK_BREAKER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ROCK_BREAKER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ROCK_BREAKER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ROCK_BREAKER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ROCK_BREAKER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_ROCK_BREAKER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_ROCK_BREAKER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_ROCK_BREAKER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_ROCK_BREAKER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_ROCK_BREAKER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_ROCK_BREAKER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_ROCK_BREAKER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_ROCK_BREAKER_GLOW;
+import static gregtech.common.GT_UndergroundOil.undergroundOilReadInformation;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.world.ChunkCoordIntPair;
+import net.minecraft.world.chunk.Chunk;
+import net.minecraftforge.fluids.FluidStack;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine;
+import gregtech.api.objects.ItemData;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_OreDictUnificator;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.blocks.GT_Block_Ores_Abstract;
+import gregtech.common.blocks.GT_TileEntity_Ores;
+import ic2.core.Ic2Items;
+
+public class GT_MetaTileEntity_AdvSeismicProspector extends GT_MetaTileEntity_BasicMachine {
+
+ boolean ready = false;
+ final int radius;
+ final int step;
+ int cX;
+ int cZ;
+
+ public GT_MetaTileEntity_AdvSeismicProspector(int aID, String aName, String aNameRegional, int aTier, int aRadius,
+ int aStep) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 1, // amperage
+ "",
+ 1, // input slot count
+ 1, // output slot count
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_SIDE_ROCK_BREAKER_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_ROCK_BREAKER_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_SIDE_ROCK_BREAKER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_ROCK_BREAKER_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_TOP_ROCK_BREAKER_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_ROCK_BREAKER_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_TOP_ROCK_BREAKER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_ROCK_BREAKER_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_FRONT_ROCK_BREAKER_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ROCK_BREAKER_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_FRONT_ROCK_BREAKER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ROCK_BREAKER_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_BOTTOM_ROCK_BREAKER_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_ROCK_BREAKER_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_BOTTOM_ROCK_BREAKER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_ROCK_BREAKER_GLOW)
+ .glow()
+ .build()));
+ radius = aRadius;
+ step = aStep;
+ }
+
+ @Override
+ public String[] getDescription() {
+ return new String[] { "Place, activate with explosives",
+ "2 Powderbarrels, " + "4 Glyceryl Trinitrate, " + "16 TNT, or " + "8 ITNT",
+ "Use Data Stick, Scan Data Stick, Print Data Stick, Bind Pages into Book",
+ "Ore prospecting area = " + radius * 2 + "x" + radius * 2 + " ONLY blocks below prospector",
+ "Oil prospecting area 3x3 oilfields, each is 8x8 chunks" };
+ }
+
+ protected GT_MetaTileEntity_AdvSeismicProspector(String aName, int aTier, String[] aDescription,
+ ITexture[][][] aTextures, int aRadius, int aStep) {
+ super(aName, aTier, 1, aDescription, aTextures, 1, 1);
+ radius = aRadius;
+ step = aStep;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_AdvSeismicProspector(
+ this.mName,
+ this.mTier,
+ this.mDescriptionArray,
+ this.mTextures,
+ this.radius,
+ this.step);
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ if (aBaseMetaTileEntity.isServerSide()) {
+ ItemStack aStack = aPlayer.getCurrentEquippedItem();
+
+ if (!ready && (GT_Utility.consumeItems(aPlayer, aStack, Item.getItemFromBlock(Blocks.tnt), 16)
+ || GT_Utility.consumeItems(aPlayer, aStack, Ic2Items.industrialTnt.getItem(), 8)
+ || GT_Utility.consumeItems(aPlayer, aStack, Materials.Glyceryl, 4)
+ || GT_Utility.consumeItems(aPlayer, aStack, ItemList.Block_Powderbarrel.getItem(), 2))) {
+
+ this.ready = true;
+ this.mMaxProgresstime = (aPlayer.capabilities.isCreativeMode ? 20 : 800);
+
+ } else if (ready && mMaxProgresstime == 0
+ && aStack != null
+ && aStack.stackSize == 1
+ && aStack.getItem() == ItemList.Tool_DataStick.getItem()) {
+ this.ready = false;
+
+ // prospecting ores
+ HashMap<String, Integer> tOres = new HashMap<>(36);
+
+ prospectOres(tOres);
+
+ // prospecting oils
+ ArrayList<String> tOils = new ArrayList<>();
+ prospectOils(tOils);
+
+ GT_Utility.ItemNBT.setAdvancedProspectionData(
+ mTier,
+ aStack,
+ this.getBaseMetaTileEntity()
+ .getXCoord(),
+ this.getBaseMetaTileEntity()
+ .getYCoord(),
+ this.getBaseMetaTileEntity()
+ .getZCoord(),
+ this.getBaseMetaTileEntity()
+ .getWorld().provider.dimensionId,
+ tOils,
+ GT_Utility.sortByValueToList(tOres),
+ radius);
+ }
+ }
+
+ return true;
+ }
+
+ private void prospectOils(ArrayList<String> aOils) {
+
+ int xChunk = (getBaseMetaTileEntity().getXCoord() >> 7) << 3; // oil field aligned chunk coords
+ int zChunk = (getBaseMetaTileEntity().getZCoord() >> 7) << 3;
+
+ LinkedHashMap<ChunkCoordIntPair, FluidStack> tFluids = new LinkedHashMap<>();
+ int oilFieldCount = 0;
+
+ try {
+ final int oilfieldSize = 8;
+ for (int z = -1; z <= 1; ++z) {
+ for (int x = -1; x <= 1; ++x) {
+ ChunkCoordIntPair cInts = new ChunkCoordIntPair(x, z);
+ int min = Integer.MAX_VALUE;
+ int max = Integer.MIN_VALUE;
+
+ for (int i = 0; i < oilfieldSize; i++) {
+ for (int j = 0; j < oilfieldSize; j++) {
+ Chunk tChunk = getBaseMetaTileEntity().getWorld()
+ .getChunkFromChunkCoords(xChunk + i + x * oilfieldSize, zChunk + j + z * oilfieldSize);
+ FluidStack tFluid = undergroundOilReadInformation(tChunk);
+ if (tFluid != null) {
+ if (tFluid.amount > max) max = tFluid.amount;
+ if (tFluid.amount < min) min = tFluid.amount;
+ if (!tFluids.containsKey(cInts)) {
+ tFluids.put(cInts, tFluid);
+ }
+ }
+ }
+ }
+
+ aOils.add(
+ ++oilFieldCount + ","
+ + min
+ + "-"
+ + max
+ + ","
+ + tFluids.get(cInts)
+ .getLocalizedName());
+ }
+ }
+ } catch (Exception ignored) {}
+ }
+
+ private void prospectOres(Map<String, Integer> aOres) {
+ int tLeftXBound = this.getBaseMetaTileEntity()
+ .getXCoord() - radius;
+ int tRightXBound = tLeftXBound + 2 * radius;
+
+ int tLeftZBound = this.getBaseMetaTileEntity()
+ .getZCoord() - radius;
+ int tRightZBound = tLeftZBound + 2 * radius;
+
+ for (int i = tLeftXBound; i <= tRightXBound; i += step) {
+ if (Math.abs(i >> 4) % 3 != 1) continue;
+ for (int k = tLeftZBound; k <= tRightZBound; k += step) {
+ if (Math.abs(k >> 4) % 3 != 1) continue;
+
+ cX = (i >> 4) << 4;
+ cZ = (k >> 4) << 4;
+
+ String separator = (cX + 8) + "," + (cZ + 8) + " --------";
+ aOres.put(separator, 1);
+ prospectHole(i, k, aOres);
+ }
+ }
+ }
+
+ private void prospectHole(int i, int k, Map<String, Integer> aOres) {
+ String tFoundOre;
+ for (int j = this.getBaseMetaTileEntity()
+ .getYCoord(); j > 0; j--) {
+ tFoundOre = checkForOre(i, j, k);
+ if (tFoundOre != null) countOre(aOres, tFoundOre, cX, cZ);
+ }
+ }
+
+ private String checkForOre(int x, int y, int z) {
+ Block tBlock = this.getBaseMetaTileEntity()
+ .getBlock(x, y, z);
+
+ if (tBlock instanceof GT_Block_Ores_Abstract) {
+ TileEntity tTileEntity = getBaseMetaTileEntity().getWorld()
+ .getTileEntity(x, y, z);
+
+ if ((tTileEntity instanceof GT_TileEntity_Ores) && (((GT_TileEntity_Ores) tTileEntity).mMetaData < 16000)) { // Filtering
+ // small
+ // ores
+ Materials tMaterial = GregTech_API.sGeneratedMaterials[((GT_TileEntity_Ores) tTileEntity).mMetaData
+ % 1000];
+
+ if ((tMaterial != null) && (tMaterial != Materials._NULL)) return tMaterial.mDefaultLocalName;
+ }
+ } else {
+ int tMetaID = getBaseMetaTileEntity().getWorld()
+ .getBlockMetadata(x, y, z);
+ ItemStack is = new ItemStack(tBlock, 1, tMetaID);
+ ItemData association = GT_OreDictUnificator.getAssociation(is);
+ if ((association != null) && (association.mPrefix.toString()
+ .startsWith("ore"))) return association.mMaterial.mMaterial.mDefaultLocalName;
+ else if (GT_Utility.isOre(tBlock, tMetaID)) return tBlock.getLocalizedName();
+ }
+ return null;
+ }
+
+ private static void countOre(Map<String, Integer> map, String ore, int cCX, int cCZ) {
+ ore = (cCX + 8) + "," + (cCZ + 8) + " has " + ore;
+ Integer oldCount = map.get(ore);
+ oldCount = (oldCount == null) ? 0 : oldCount;
+
+ map.put(ore, oldCount + 1);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_BetterJukebox.java b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_BetterJukebox.java
new file mode 100644
index 0000000000..f9a9fba8f4
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_BetterJukebox.java
@@ -0,0 +1,702 @@
+package gregtech.common.tileentities.machines.basic;
+
+import static gregtech.api.enums.GT_Values.V;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASINGS;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_OUT;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_JUKEBOX;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_JUKEBOX;
+import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Random;
+import java.util.UUID;
+
+import net.minecraft.item.ItemRecord;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.MathHelper;
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.common.util.Constants;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import org.joml.Vector4i;
+
+import com.google.common.collect.ImmutableList;
+import com.gtnewhorizons.modularui.api.drawable.FallbackableUITexture;
+import com.gtnewhorizons.modularui.api.drawable.UITexture;
+import com.gtnewhorizons.modularui.api.math.Pos2d;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.CycleButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.ProgressBar;
+import com.gtnewhorizons.modularui.common.widget.SliderWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+
+import appeng.api.implementations.tiles.ISoundP2PHandler;
+import appeng.me.GridAccessException;
+import appeng.me.cache.helpers.TunnelCollection;
+import appeng.me.helpers.AENetworkProxy;
+import appeng.parts.p2p.PartP2PSound;
+import gregtech.api.enums.GTVoltageIndex;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine;
+import gregtech.api.recipe.BasicUIProperties;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_MusicSystem;
+import gregtech.common.gui.modularui.UIHelper;
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+
+public class GT_MetaTileEntity_BetterJukebox extends GT_MetaTileEntity_BasicMachine
+ implements IAddUIWidgets, ISoundP2PHandler {
+
+ // Stored state
+ public UUID jukeboxUuid = UNSET_UUID;
+ public boolean loopMode = true;
+ public boolean shuffleMode = false;
+ public int playbackSlot = 0;
+ public float playbackVolume = BalanceMath.VANILLA_JUKEBOX_RANGE;
+ public float p2pVolume = BalanceMath.VANILLA_JUKEBOX_RANGE;
+ public long discProgressMs = 0;
+ /** Makes all music discs play for 4 seconds */
+ public boolean superFastDebugMode = false;
+ // Computed state
+ private final Vector4i interdimPositionCache = new Vector4i(); // XYZ, Dimension ID
+ private GT_MusicSystem.MusicSource musicSource = null;
+ private boolean powered = false;
+ private long discStartMs = 0;
+ public long discDurationMs = 1;
+ private ItemRecord currentlyPlaying = null;
+
+ // Constants
+ public static final UUID UNSET_UUID = UUID.nameUUIDFromBytes(new byte[] { 0 });
+ public static final int INPUT_SLOTS = 21;
+ private static final Random SHUFFLER = new Random();
+
+ public enum HeadphoneLimit {
+
+ BLOCK_RANGE,
+ INSIDE_DIMENSION,
+ BETWEEN_DIMENSIONS;
+
+ public static final ImmutableList<HeadphoneLimit> ENTRIES = ImmutableList.copyOf(values());
+ }
+
+ public static final class BalanceMath {
+
+ public static int MAX_TIER = GTVoltageIndex.IV;
+ public static float VANILLA_JUKEBOX_RANGE = 4.0f; // 64 blocks
+
+ private static final float[] LISTENING_VOLUME = new float[] { //
+ VANILLA_JUKEBOX_RANGE, // ULV (unpowered fallback)
+ VANILLA_JUKEBOX_RANGE + 1.0f, // LV, 80 blocks
+ VANILLA_JUKEBOX_RANGE + 2.0f, // MV, 96 blocks
+ VANILLA_JUKEBOX_RANGE + 4.0f, // HV, 118 blocks
+ VANILLA_JUKEBOX_RANGE + 5.0f, // EV, 144 blocks
+ VANILLA_JUKEBOX_RANGE + 6.0f, // IV, 160 blocks, equivalent to default load distance of 10 chunks
+ };
+
+ private static final int[] HEADPHONE_BLOCK_RANGE = new int[] { //
+ 64, // ULV (unpowered fallback)
+ 128, // LV
+ 160, // MV
+ 320, // HV
+ 9001, // EV, alreadu unlimited here - this value is ignored
+ 9002, // IV, already unlimited here - this value is ignored
+ };
+
+ public static float listeningVolume(int tier) {
+ tier = MathHelper.clamp_int(tier, 0, MAX_TIER);
+ return LISTENING_VOLUME[tier];
+ }
+
+ public static int headphoneBlockRange(int tier) {
+ tier = MathHelper.clamp_int(tier, 0, MAX_TIER);
+ return HEADPHONE_BLOCK_RANGE[tier];
+ }
+
+ public static HeadphoneLimit headphoneLimit(int tier) {
+ if (tier <= GTVoltageIndex.HV) {
+ return HeadphoneLimit.BLOCK_RANGE;
+ } else if (tier == GTVoltageIndex.EV) {
+ return HeadphoneLimit.INSIDE_DIMENSION;
+ } else {
+ return HeadphoneLimit.BETWEEN_DIMENSIONS;
+ }
+ }
+
+ public static float volumeToAttenuationDistance(float range) {
+ // SoundManager.playSound logic
+ return 16.0f * range;
+ }
+
+ public static float attenuationDistanceToVolume(float blockRange) {
+ return blockRange / 16.0f;
+ }
+
+ public static long eutUsage(int tier) {
+ tier = MathHelper.clamp_int(tier, 0, MAX_TIER);
+ return V[tier] / 16;
+ }
+ }
+
+ private static String[] buildDescription(int aTier) {
+ ArrayList<String> strings = new ArrayList<>(4);
+ strings.add("Plays music better than your average vanilla jukebox.");
+ if (BalanceMath.headphoneLimit(aTier) != HeadphoneLimit.BLOCK_RANGE) {
+ strings.add(EnumChatFormatting.BLUE + "The raw power of Hatsune Miku in your ears");
+ }
+ strings.add(
+ String.format(
+ "Range: %s%.1f blocks",
+ EnumChatFormatting.WHITE,
+ BalanceMath.volumeToAttenuationDistance(BalanceMath.listeningVolume(aTier))));
+ strings.add(switch (BalanceMath.headphoneLimit(aTier)) {
+ case BLOCK_RANGE -> String.format(
+ "Headphone signal range: %s%d blocks",
+ EnumChatFormatting.WHITE,
+ BalanceMath.headphoneBlockRange(aTier));
+ case INSIDE_DIMENSION -> String
+ .format("Headphones work anywhere in %sthe same dimension", EnumChatFormatting.WHITE);
+ case BETWEEN_DIMENSIONS -> String
+ .format("Headphones work anywhere, in %sany dimension", EnumChatFormatting.WHITE);
+ });
+ strings.add(String.format("Cost: %s%d EU/t", EnumChatFormatting.WHITE, BalanceMath.eutUsage(aTier)));
+ strings.add(GT_Values.AuthorEigenRaven);
+ return strings.toArray(new String[0]);
+ }
+
+ public GT_MetaTileEntity_BetterJukebox(int aID, String aName, String aNameRegional, int aTier) {
+ super(aID, aName, aNameRegional, aTier, 1, buildDescription(aTier), INPUT_SLOTS, 1);
+ playbackVolume = BalanceMath.listeningVolume(aTier);
+ }
+
+ public GT_MetaTileEntity_BetterJukebox(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 1, aDescription, aTextures, INPUT_SLOTS, 1);
+ playbackVolume = BalanceMath.listeningVolume(aTier);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_BetterJukebox(this.mName, this.mTier, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ if (sideDirection == baseMetaTileEntity.getFrontFacing()) {
+ return new ITexture[] { MACHINE_CASINGS[mTier][colorIndex + 1], TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+ if (sideDirection != ForgeDirection.UP) {
+ return new ITexture[] { MACHINE_CASINGS[mTier][colorIndex + 1], TextureFactory.of(OVERLAY_SIDE_JUKEBOX) };
+ }
+ return new ITexture[] { MACHINE_CASINGS[mTier][colorIndex + 1], TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_JUKEBOX)
+ .extFacing()
+ .build() };
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ super.onFirstTick(aBaseMetaTileEntity);
+ if (aBaseMetaTileEntity.isClientSide()) {
+ return;
+ }
+ final Vector4i interdimPosition = interdimPositionCache;
+ interdimPosition.x = aBaseMetaTileEntity.getXCoord();
+ interdimPosition.y = aBaseMetaTileEntity.getYCoord();
+ interdimPosition.z = aBaseMetaTileEntity.getZCoord();
+ interdimPosition.w = aBaseMetaTileEntity.getWorld().provider.dimensionId;
+ if (jukeboxUuid == UNSET_UUID) {
+ jukeboxUuid = UUID.randomUUID();
+ markDirty();
+ }
+ if (musicSource == null) {
+ musicSource = GT_MusicSystem.ServerSystem.registerOrGetMusicSource(jukeboxUuid);
+ musicSource.originPosition.set(interdimPosition);
+ musicSource.headphoneLimit = BalanceMath.headphoneLimit(mTier);
+ musicSource.headphoneBlockRange = BalanceMath.headphoneBlockRange(mTier);
+ musicSource.startedPlayingAtMs = System.currentTimeMillis();
+ updateEmitterList();
+ }
+ if (doesSlotContainValidRecord(playbackSlot)
+ && mInventory[playbackSlot].getItem() instanceof ItemRecord record) {
+ final ResourceLocation resource = record.getRecordResource(record.recordName);
+ currentlyPlaying = record;
+ // Assume a safe disc duration of 500 seconds if not known in the registry
+ discDurationMs = GT_MusicSystem.getMusicRecordDurations()
+ .getOrDefault(resource, 500_000);
+ discStartMs = System.currentTimeMillis() - discProgressMs;
+ musicSource.setRecord(
+ new ResourceLocation(resource.getResourceDomain(), "records." + resource.getResourcePath()),
+ discProgressMs);
+ }
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) {
+ try {
+ if (aBaseMetaTileEntity.isClientSide() || !aBaseMetaTileEntity.isAllowedToWork() || musicSource == null) {
+ if (currentlyPlaying != null) {
+ stopCurrentSong(System.currentTimeMillis());
+ }
+ return;
+ }
+ final Vector4i interdimPosition = interdimPositionCache;
+ interdimPosition.x = aBaseMetaTileEntity.getXCoord();
+ interdimPosition.y = aBaseMetaTileEntity.getYCoord();
+ interdimPosition.z = aBaseMetaTileEntity.getZCoord();
+ interdimPosition.w = aBaseMetaTileEntity.getWorld().provider.dimensionId;
+ final long now = System.currentTimeMillis();
+
+ if (superFastDebugMode && discDurationMs > 4000) {
+ discDurationMs = 4000;
+ }
+
+ // power check
+ final boolean hasMinimumEU = aBaseMetaTileEntity.isUniversalEnergyStored(getMinimumStoredEU());
+ if (currentlyPlaying != null && hasMinimumEU
+ && aBaseMetaTileEntity.decreaseStoredEnergyUnits(BalanceMath.eutUsage(mTier), false)) {
+ if (!powered) { // just got power again
+ powered = true;
+ musicSource.modified = true;
+ musicSource.headphoneLimit = BalanceMath.headphoneLimit(mTier);
+ musicSource.headphoneBlockRange = BalanceMath.headphoneBlockRange(mTier);
+ updateEmitterList();
+ }
+ } else if ((!hasMinimumEU || currentlyPlaying != null) && powered) { // was powered, but no longer is
+ powered = false;
+ musicSource.modified = true;
+ musicSource.headphoneLimit = HeadphoneLimit.BLOCK_RANGE;
+ musicSource.headphoneBlockRange = BalanceMath.headphoneBlockRange(0);
+ updateEmitterList();
+ }
+
+ // check if current disc finished
+ if (currentlyPlaying != null) {
+ discProgressMs = now - discStartMs;
+ final boolean hasValidRecord = doesSlotContainValidRecord(playbackSlot);
+ final boolean wasDiscSwapped = hasValidRecord
+ && mInventory[getInputSlot() + playbackSlot].getItem() != currentlyPlaying;
+ if (discProgressMs >= discDurationMs || !hasValidRecord || wasDiscSwapped) {
+ stopCurrentSong(now);
+ if (!loopMode) {
+ // should be empty, but swap just in case it's not
+ final ItemStack oldOut = mInventory[getOutputSlot()];
+ mInventory[getOutputSlot()] = mInventory[getInputSlot() + playbackSlot];
+ mInventory[getInputSlot() + playbackSlot] = oldOut;
+ markDirty();
+ }
+ if (!(hasValidRecord && wasDiscSwapped)) {
+ // don't switch slots if someone just put a new disc in the active slot
+ pickNextSlot();
+ }
+ } else {
+ // keep on playing
+ return;
+ }
+ }
+
+ if (playbackSlot < 0 || playbackSlot >= INPUT_SLOTS
+ || ((aTimer % 10) == 0 && !doesSlotContainValidRecord(playbackSlot))) {
+ pickNextSlot();
+ }
+
+ final boolean hasValidRecord = doesSlotContainValidRecord(playbackSlot);
+ final boolean canStartPlaying = loopMode || isOutputEmpty();
+ if (!hasValidRecord) {
+ stopCurrentSong(now);
+ } else if (canStartPlaying
+ && mInventory[getInputSlot() + playbackSlot].getItem() instanceof ItemRecord record) {
+ final ResourceLocation resource = record.getRecordResource(record.recordName);
+ currentlyPlaying = record;
+ musicSource.setRecord(
+ new ResourceLocation(resource.getResourceDomain(), "records." + resource.getResourcePath()));
+ // Assume a safe disc duration of 500 seconds if not known in the registry
+ discDurationMs = GT_MusicSystem.getMusicRecordDurations()
+ .getOrDefault(resource, 500_000);
+ discProgressMs = 0;
+ discStartMs = now;
+ }
+ } finally {
+ super.onPostTick(aBaseMetaTileEntity, aTimer);
+ }
+ }
+
+ private void stopCurrentSong(long nowMs) {
+ if (currentlyPlaying == null) {
+ return;
+ }
+ musicSource.setRecord(null);
+ currentlyPlaying = null;
+ discDurationMs = 1;
+ discProgressMs = 0;
+ discStartMs = nowMs;
+ markDirty();
+ }
+
+ private void pickNextSlot() {
+ playbackSlot = MathHelper.clamp_int(playbackSlot, 0, INPUT_SLOTS);
+ if (shuffleMode) {
+ final int[] validSlots = new int[INPUT_SLOTS];
+ int validSlotCount = 0;
+ for (int i = 0; i < INPUT_SLOTS; i++) {
+ if (i != playbackSlot && doesSlotContainValidRecord(i)) {
+ validSlots[validSlotCount++] = i;
+ }
+ }
+ switch (validSlotCount) {
+ case 0 -> {}
+ case 1 -> {
+ playbackSlot = validSlots[0];
+ }
+ default -> {
+ playbackSlot = validSlots[SHUFFLER.nextInt(validSlotCount)];
+ }
+ }
+ } else {
+ int attempt = 0;
+ int nextSlot = playbackSlot;
+ do {
+ attempt++;
+ nextSlot = (nextSlot + 1) % INPUT_SLOTS;
+ } while (!doesSlotContainValidRecord(nextSlot) && attempt <= INPUT_SLOTS);
+ if (attempt <= INPUT_SLOTS) {
+ playbackSlot = nextSlot;
+ }
+ }
+ }
+
+ public boolean doesSlotContainValidRecord(int slot) {
+ return mInventory[getInputSlot() + slot] != null
+ && mInventory[getInputSlot() + slot].getItem() instanceof ItemRecord;
+ }
+
+ @Override
+ public void onRemoval() {
+ final IGregTechTileEntity baseTE = getBaseMetaTileEntity();
+ if (baseTE == null) {
+ return;
+ }
+ if (!baseTE.isServerSide()) {
+ return;
+ }
+ if (jukeboxUuid == UNSET_UUID) {
+ return;
+ }
+ GT_MusicSystem.ServerSystem.removeMusicSource(jukeboxUuid);
+ }
+
+ @Override
+ public long getMinimumStoredEU() {
+ return BalanceMath.eutUsage(mTier) * 20;
+ }
+
+ @Override
+ public long maxEUStore() {
+ return 512L + BalanceMath.eutUsage(mTier) * 50;
+ }
+
+ @Override
+ public long maxAmperesIn() {
+ return 1;
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ return null;
+ }
+
+ public static final String NBTKEY_UUID_LOW = "jukeboxUUIDLow";
+ public static final String NBTKEY_UUID_HIGH = "jukeboxUUIDHigh";
+ public static final String NBTKEY_LOOP_MODE = "loopMode";
+ public static final String NBTKEY_SHUFFLE_MODE = "shuffleMode";
+ public static final String NBTKEY_PLAYBACK_SLOT = "playbackSlot";
+ public static final String NBTKEY_VOLUME_PLAY = "playbackVolume";
+ public static final String NBTKEY_VOLUME_P2P = "p2pVolume";
+ public static final String NBTKEY_DISC_PROGRESS_MS = "discProgressMs";
+
+ @Override
+ public String[] getInfoData() {
+ return new String[] { "Jukebox UUID: " + ((jukeboxUuid == UNSET_UUID) ? "unset" : jukeboxUuid),
+ "Loop mode: " + loopMode, "Shuffle mode: " + shuffleMode, "Played the disc for [ms]: " + discProgressMs,
+ "Current disc duration [ms]: " + discDurationMs,
+ "Playback range [blocks]: " + BalanceMath.volumeToAttenuationDistance(playbackVolume),
+ "P2P range [blocks]: " + BalanceMath.volumeToAttenuationDistance(playbackVolume),
+ "Raw playback strength: " + playbackVolume, "Raw p2p strength: " + p2pVolume };
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ if (jukeboxUuid != UNSET_UUID) {
+ aNBT.setLong(NBTKEY_UUID_LOW, jukeboxUuid.getLeastSignificantBits());
+ aNBT.setLong(NBTKEY_UUID_HIGH, jukeboxUuid.getMostSignificantBits());
+ aNBT.setBoolean(NBTKEY_LOOP_MODE, loopMode);
+ aNBT.setBoolean(NBTKEY_SHUFFLE_MODE, shuffleMode);
+ aNBT.setInteger(NBTKEY_PLAYBACK_SLOT, playbackSlot);
+ aNBT.setFloat(NBTKEY_VOLUME_PLAY, playbackVolume);
+ aNBT.setFloat(NBTKEY_VOLUME_P2P, p2pVolume);
+ aNBT.setLong(NBTKEY_DISC_PROGRESS_MS, discProgressMs);
+ }
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ if (aNBT.hasKey(NBTKEY_UUID_LOW, Constants.NBT.TAG_ANY_NUMERIC)
+ && aNBT.hasKey(NBTKEY_UUID_HIGH, Constants.NBT.TAG_ANY_NUMERIC)) {
+ jukeboxUuid = new UUID(aNBT.getLong(NBTKEY_UUID_HIGH), aNBT.getLong(NBTKEY_UUID_LOW));
+ }
+ if (aNBT.hasKey(NBTKEY_LOOP_MODE, Constants.NBT.TAG_ANY_NUMERIC)) {
+ loopMode = aNBT.getBoolean(NBTKEY_LOOP_MODE);
+ }
+ if (aNBT.hasKey(NBTKEY_SHUFFLE_MODE, Constants.NBT.TAG_ANY_NUMERIC)) {
+ shuffleMode = aNBT.getBoolean(NBTKEY_SHUFFLE_MODE);
+ }
+ if (aNBT.hasKey(NBTKEY_PLAYBACK_SLOT, Constants.NBT.TAG_ANY_NUMERIC)) {
+ playbackSlot = aNBT.getInteger(NBTKEY_PLAYBACK_SLOT);
+ }
+ if (aNBT.hasKey(NBTKEY_VOLUME_PLAY, Constants.NBT.TAG_ANY_NUMERIC)) {
+ playbackVolume = aNBT.getFloat(NBTKEY_VOLUME_PLAY);
+ }
+ if (aNBT.hasKey(NBTKEY_VOLUME_P2P, Constants.NBT.TAG_ANY_NUMERIC)) {
+ p2pVolume = aNBT.getFloat(NBTKEY_VOLUME_P2P);
+ }
+ if (aNBT.hasKey(NBTKEY_DISC_PROGRESS_MS, Constants.NBT.TAG_ANY_NUMERIC)) {
+ discProgressMs = aNBT.getLong(NBTKEY_DISC_PROGRESS_MS);
+ }
+
+ final float maxVolume = BalanceMath.listeningVolume(mTier);
+ playbackVolume = MathHelper.clamp_float(playbackVolume, 0.0f, maxVolume);
+ p2pVolume = MathHelper.clamp_float(p2pVolume, 0.0f, maxVolume);
+ }
+
+ @Override
+ protected BasicUIProperties getUIProperties() {
+ return super.getUIProperties().toBuilder()
+ .itemInputPositionsGetter(count -> UIHelper.getGridPositions(count, 7, 6, 7, 3))
+ .itemOutputPositionsGetter(count -> UIHelper.getGridPositions(count, 153, 24, 1))
+ .specialItemPositionGetter(() -> new Pos2d(115, 62))
+ .progressBarPos(Pos2d.cartesian(133, 24))
+ .progressBarTexture(new FallbackableUITexture(GT_UITextures.PROGRESSBAR_ARROW))
+ .build();
+ }
+
+ @Override
+ protected void addProgressBar(ModularWindow.Builder builder, BasicUIProperties uiProperties) {
+ builder.widget(
+ setNEITransferRect(
+ new ProgressBar().setProgress(() -> discProgressMs / (float) Math.max(1, discDurationMs))
+ .setTexture(uiProperties.progressBarTexture.get(), uiProperties.progressBarImageSize)
+ .setDirection(uiProperties.progressBarDirection)
+ .setPos(uiProperties.progressBarPos)
+ .setSize(uiProperties.progressBarSize)
+ .setUpdateTooltipEveryTick(true)
+ .attachSyncer(
+ new FakeSyncWidget.LongSyncer(() -> this.discProgressMs, val -> this.discProgressMs = val),
+ builder)
+ .attachSyncer(
+ new FakeSyncWidget.LongSyncer(() -> this.discDurationMs, val -> this.discDurationMs = val),
+ builder)
+ .dynamicTooltip(
+ () -> Collections.singletonList(
+ String.format("%,.2f / %,.2f", discProgressMs / 1000.0f, discDurationMs / 1000.0f))),
+ uiProperties.neiTransferRectId));
+ addProgressBarSpecialTextures(builder, uiProperties);
+ }
+
+ @Override
+ protected SlotWidget createChargerSlot(int x, int y) {
+ return super.createChargerSlot(97, 62);
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ super.addUIWidgets(builder, buildContext);
+ final BasicUIProperties props = getUIProperties();
+ final List<Pos2d> inputSlots = props.itemInputPositionsGetter.apply(mInputSlotCount);
+ // Loop
+ builder.widget(
+ new CycleButtonWidget().setToggle(() -> loopMode, val -> loopMode = val)
+ .setStaticTexture(GT_UITextures.OVERLAY_BUTTON_CYCLIC)
+ .setVariableBackground(GT_UITextures.BUTTON_STANDARD_TOGGLE)
+ .setGTTooltip(() -> mTooltipCache.getData("GT5U.machines.betterjukebox.loop.tooltip"))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setPos(153, 6)
+ .setSize(18, 18));
+ // Shuffle
+ builder.widget(new CycleButtonWidget().setToggle(() -> shuffleMode, val -> {
+ shuffleMode = val;
+ if (shuffleMode) {
+ playbackSlot = -1;
+ } else {
+ playbackSlot = 0;
+ }
+ })
+ .setStaticTexture(GT_UITextures.OVERLAY_BUTTON_SHUFFLE)
+ .setVariableBackground(GT_UITextures.BUTTON_STANDARD_TOGGLE)
+ .setGTTooltip(() -> mTooltipCache.getData("GT5U.machines.betterjukebox.shuffle.tooltip"))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setPos(153, 42)
+ .setSize(18, 18));
+ // Currently playing slot highlight using the hotbar active texture
+ final DrawableWidget slotHighlight = new DrawableWidget();
+ builder.widget(
+ slotHighlight
+ .setDrawable(
+ new UITexture(
+ new ResourceLocation("minecraft", "textures/gui/widgets.png"),
+ 0.0f,
+ 22.0f / 256.0f,
+ 24.0f / 256.0f,
+ 46.0f / 256.0f))
+ .setSize(24, 24)
+ .attachSyncer(new FakeSyncWidget.IntegerSyncer(() -> this.playbackSlot, val -> {
+ this.playbackSlot = val;
+ slotHighlight.checkNeedsRebuild();
+ }), builder)
+ .setPosProvider(
+ (screenSize, window, parent) -> inputSlots.get(MathHelper.clamp_int(playbackSlot, 0, INPUT_SLOTS))
+ .add(-3, -3)));
+ // Attenuation distance (controls internal "volume")
+ // Caching tooltip data caches the formatted p2p range value, so we have to use the uncached variant here.
+ builder.widget(
+ new SliderWidget()
+ .setBounds(0.0f, BalanceMath.volumeToAttenuationDistance(BalanceMath.listeningVolume(mTier)))
+ .setGetter(this::getPlaybackBlockRange)
+ .setSetter(this::setPlaybackBlockRange)
+ .dynamicTooltip(
+ () -> mTooltipCache.getUncachedTooltipData(
+ "GT5U.machines.betterjukebox.attenuationDistance.tooltip",
+ (int) getPlaybackBlockRange()).text)
+ .setUpdateTooltipEveryTick(true)
+ .setPos(44, 63)
+ .setSize(52, 8));
+ builder.widget(
+ new SliderWidget()
+ .setBounds(0.0f, BalanceMath.volumeToAttenuationDistance(BalanceMath.listeningVolume(mTier)))
+ .setGetter(this::getP2PBlockRange)
+ .setSetter(this::setP2PBlockRange)
+ .dynamicTooltip(
+ () -> mTooltipCache.getUncachedTooltipData(
+ "GT5U.machines.betterjukebox.p2pAttenuationDistance.tooltip",
+ (int) getP2PBlockRange()).text)
+ .setUpdateTooltipEveryTick(true)
+ .setPos(44, 71)
+ .setSize(52, 8));
+ }
+
+ private float getPlaybackBlockRange() {
+ return BalanceMath.volumeToAttenuationDistance(playbackVolume);
+ }
+
+ private float getP2PBlockRange() {
+ return BalanceMath.volumeToAttenuationDistance(p2pVolume);
+ }
+
+ private void setPlaybackBlockRange(float blockRange) {
+ float volume = BalanceMath.attenuationDistanceToVolume(blockRange);
+ volume = MathHelper.clamp_float(volume, 0.0f, BalanceMath.listeningVolume(mTier));
+ if (volume != playbackVolume) {
+ playbackVolume = volume;
+ if (getBaseMetaTileEntity().isServerSide()) {
+ updateEmitterList();
+ }
+ }
+ }
+
+ private void setP2PBlockRange(float blockRange) {
+ float volume = BalanceMath.attenuationDistanceToVolume(blockRange);
+ volume = MathHelper.clamp_float(volume, 0.0f, BalanceMath.listeningVolume(mTier));
+ if (volume != p2pVolume) {
+ p2pVolume = volume;
+ if (getBaseMetaTileEntity().isServerSide()) {
+ updateEmitterList();
+ }
+ }
+ }
+
+ private final EnumMap<ForgeDirection, PartP2PSound> attachedSoundP2P = new EnumMap<>(ForgeDirection.class);
+ private final ObjectArrayList<PartP2PSound> combinedOutputsListCache = new ObjectArrayList<>(new PartP2PSound[0]);
+
+ private void updateEmitterList() {
+ final GT_MusicSystem.MusicSource target = musicSource;
+ if (target == null) {
+ return;
+ }
+ final ObjectArrayList<PartP2PSound> emitters = combinedOutputsListCache;
+ emitters.clear();
+
+ attachedSoundP2P.forEach((ignored, p2p) -> {
+ if (p2p != null) {
+ try {
+ p2p.getOutputs()
+ .forEach(emitters::add);
+ } catch (GridAccessException e) {
+ // skip
+ }
+ }
+ });
+
+ IGregTechTileEntity te = getBaseMetaTileEntity();
+ if (te == null) {
+ return;
+ }
+ final Vector4i position = new Vector4i();
+ target.resizeEmitterArray(1 + emitters.size());
+ position.set(te.getXCoord(), te.getYCoord(), te.getZCoord(), te.getWorld().provider.dimensionId);
+ final float actualVolume = MathHelper
+ .clamp_float(playbackVolume, 0.0f, BalanceMath.listeningVolume(powered ? mTier : 0));
+ target.setEmitter(0, position, actualVolume);
+ final float actualP2PVolume = MathHelper
+ .clamp_float(p2pVolume, 0.0f, powered ? BalanceMath.listeningVolume(mTier) : 0.0f);
+ for (int i = 0; i < emitters.size(); i++) {
+ final PartP2PSound p2p = emitters.get(i);
+ final AENetworkProxy proxy = p2p.getProxy();
+ final TileEntity emitterTe = p2p.getTile();
+ final ForgeDirection dir = p2p.getSide();
+ position.set(
+ emitterTe.xCoord + dir.offsetX,
+ emitterTe.yCoord + dir.offsetY,
+ emitterTe.zCoord + dir.offsetZ,
+ emitterTe.getWorldObj().provider.dimensionId);
+ final boolean active = proxy.isActive();
+ target.setEmitter(1 + i, position, active ? actualP2PVolume : 0);
+ }
+ }
+
+ @Override
+ public boolean allowSoundProxying(PartP2PSound p2p) {
+ return false; // the jukebox proxies sounds by itself
+ }
+
+ @Override
+ public void onSoundP2PAttach(PartP2PSound p2p) {
+ attachedSoundP2P.put(p2p.getSide(), p2p);
+ updateEmitterList();
+ }
+
+ @Override
+ public void onSoundP2PDetach(PartP2PSound p2p) {
+ attachedSoundP2P.put(p2p.getSide(), null);
+ updateEmitterList();
+ }
+
+ @Override
+ public void onSoundP2POutputUpdate(PartP2PSound p2p, TunnelCollection<PartP2PSound> outputs) {
+ updateEmitterList();
+ }
+
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Boxinator.java b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Boxinator.java
new file mode 100644
index 0000000000..8d2e0959e1
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Boxinator.java
@@ -0,0 +1,229 @@
+package gregtech.common.tileentities.machines.basic;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.MachineType;
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_Boxinator extends GT_MetaTileEntity_BasicMachine {
+
+ ItemStack aInputCache;
+ ItemStack aOutputCache;
+ int aTypeCache = 0;
+
+ public GT_MetaTileEntity_Boxinator(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 1,
+ MachineType.PACKAGER.tooltipDescription(),
+ 2,
+ 1,
+ TextureFactory.of(
+ TextureFactory
+ .of(new Textures.BlockIcons.CustomIcon("basicmachines/boxinator/OVERLAY_SIDE_BOXINATOR_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(
+ new Textures.BlockIcons.CustomIcon(
+ "basicmachines/boxinator/OVERLAY_SIDE_BOXINATOR_ACTIVE_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(new Textures.BlockIcons.CustomIcon("basicmachines/boxinator/OVERLAY_SIDE_BOXINATOR")),
+ TextureFactory.builder()
+ .addIcon(new Textures.BlockIcons.CustomIcon("basicmachines/boxinator/OVERLAY_SIDE_BOXINATOR_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory
+ .of(new Textures.BlockIcons.CustomIcon("basicmachines/boxinator/OVERLAY_FRONT_BOXINATOR_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(
+ new Textures.BlockIcons.CustomIcon(
+ "basicmachines/boxinator/OVERLAY_FRONT_BOXINATOR_ACTIVE_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory
+ .of(new Textures.BlockIcons.CustomIcon("basicmachines/boxinator/OVERLAY_FRONT_BOXINATOR")),
+ TextureFactory.builder()
+ .addIcon(new Textures.BlockIcons.CustomIcon("basicmachines/boxinator/OVERLAY_FRONT_BOXINATOR_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory
+ .of(new Textures.BlockIcons.CustomIcon("basicmachines/boxinator/OVERLAY_TOP_BOXINATOR_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(
+ new Textures.BlockIcons.CustomIcon("basicmachines/boxinator/OVERLAY_TOP_BOXINATOR_ACTIVE_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(new Textures.BlockIcons.CustomIcon("basicmachines/boxinator/OVERLAY_TOP_BOXINATOR")),
+ TextureFactory.builder()
+ .addIcon(new Textures.BlockIcons.CustomIcon("basicmachines/boxinator/OVERLAY_TOP_BOXINATOR_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory
+ .of(new Textures.BlockIcons.CustomIcon("basicmachines/boxinator/OVERLAY_BOTTOM_BOXINATOR_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(
+ new Textures.BlockIcons.CustomIcon(
+ "basicmachines/boxinator/OVERLAY_BOTTOM_BOXINATOR_ACTIVE_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory
+ .of(new Textures.BlockIcons.CustomIcon("basicmachines/boxinator/OVERLAY_BOTTOM_BOXINATOR")),
+ TextureFactory.builder()
+ .addIcon(
+ new Textures.BlockIcons.CustomIcon("basicmachines/boxinator/OVERLAY_BOTTOM_BOXINATOR_GLOW"))
+ .glow()
+ .build()));
+ }
+
+ public GT_MetaTileEntity_Boxinator(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 1, aDescription, aTextures, 2, 1);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Boxinator(this.mName, this.mTier, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.packagerRecipes;
+ }
+
+ private boolean hasValidCache(ItemStack mItem, int mType, boolean mClearOnFailure) {
+ if (aInputCache != null && aOutputCache != null
+ && aTypeCache == mType
+ && aInputCache.isItemEqual(mItem)
+ && ItemStack.areItemStackTagsEqual(mItem, aInputCache)) return true;
+ // clear cache if it was invalid
+ if (mClearOnFailure) {
+ aInputCache = null;
+ aOutputCache = null;
+ aTypeCache = 0;
+ }
+ return false;
+ }
+
+ private void cacheItem(ItemStack mInputItem, ItemStack mOutputItem, int mType) {
+ aTypeCache = mType;
+ aOutputCache = mOutputItem.copy();
+ aInputCache = mInputItem.copy();
+ }
+
+ @Override
+ public int checkRecipe() {
+ int tCheck = super.checkRecipe();
+ if (tCheck != DID_NOT_FIND_RECIPE) {
+ return tCheck;
+ }
+ ItemStack tSlot0 = getInputAt(0);
+ ItemStack tSlot1 = getInputAt(1);
+ if ((GT_Utility.isStackValid(tSlot0)) && (GT_Utility.isStackValid(tSlot1))
+ && (GT_Utility.getContainerItem(tSlot0, true) == null)) {
+ if ((ItemList.Schematic_1by1.isStackEqual(tSlot1)) && (tSlot0.stackSize >= 1)) {
+ boolean tIsCached = hasValidCache(tSlot0, 1, true);
+ this.mOutputItems[0] = tIsCached ? aOutputCache.copy() : GT_ModHandler.getRecipeOutput(tSlot0);
+ if (this.mOutputItems[0] != null) {
+ if (canOutput(this.mOutputItems[0])) {
+ tSlot0.stackSize -= 1;
+ calculateOverclockedNess(30, 16);
+ // In case recipe is too OP for that machine
+ if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1)
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ if (!tIsCached) cacheItem(tSlot0, this.mOutputItems[0], 1);
+ return FOUND_AND_SUCCESSFULLY_USED_RECIPE;
+ }
+ }
+ return DID_NOT_FIND_RECIPE;
+ }
+ if ((ItemList.Schematic_2by2.isStackEqual(tSlot1)) && (getInputAt(0).stackSize >= 4)) {
+ boolean tIsCached = hasValidCache(tSlot0, 2, true);
+ this.mOutputItems[0] = tIsCached ? aOutputCache.copy()
+ : GT_ModHandler.getRecipeOutput(tSlot0, tSlot0, null, tSlot0, tSlot0);
+ if (this.mOutputItems[0] != null) {
+ if (canOutput(this.mOutputItems[0])) {
+ getInputAt(0).stackSize -= 4;
+ calculateOverclockedNess(30, 32);
+ // In case recipe is too OP for that machine
+ if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1)
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ if (!tIsCached) cacheItem(tSlot0, this.mOutputItems[0], 2);
+ return FOUND_AND_SUCCESSFULLY_USED_RECIPE;
+ }
+ }
+ return DID_NOT_FIND_RECIPE;
+ }
+ if ((ItemList.Schematic_3by3.isStackEqual(tSlot1)) && (getInputAt(0).stackSize >= 9)) {
+ boolean tIsCached = hasValidCache(tSlot0, 3, true);
+ this.mOutputItems[0] = tIsCached ? aOutputCache.copy()
+ : GT_ModHandler
+ .getRecipeOutput(tSlot0, tSlot0, tSlot0, tSlot0, tSlot0, tSlot0, tSlot0, tSlot0, tSlot0);
+ if (this.mOutputItems[0] != null) {
+ if (canOutput(this.mOutputItems[0])) {
+ getInputAt(0).stackSize -= 9;
+ calculateOverclockedNess(30, 64);
+ // In case recipe is too OP for that machine
+ if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1)
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ if (!tIsCached) cacheItem(tSlot0, this.mOutputItems[0], 3);
+ return FOUND_AND_SUCCESSFULLY_USED_RECIPE;
+ }
+ }
+ return DID_NOT_FIND_RECIPE;
+ }
+ }
+ return DID_NOT_FIND_RECIPE;
+ }
+
+ @Override
+ protected boolean allowPutStackValidated(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ if (!super.allowPutStackValidated(aBaseMetaTileEntity, aIndex, side, aStack)) {
+ return false;
+ }
+ ItemStack tInput1 = getInputAt(1);
+ if ((ItemList.Schematic_1by1.isStackEqual(tInput1)) || (ItemList.Schematic_2by2.isStackEqual(tInput1))
+ || (ItemList.Schematic_3by3.isStackEqual(tInput1))) {
+ if (hasValidCache(aStack, aTypeCache, false)) return true;
+ if (RecipeMaps.packagerRecipes.findRecipe(
+ getBaseMetaTileEntity(),
+ true,
+ gregtech.api.enums.GT_Values.V[mTier],
+ null,
+ GT_Utility.copyAmount(64, aStack),
+ tInput1) != null) {
+ return true;
+ }
+ if (ItemList.Schematic_1by1.isStackEqual(getInputAt(1)) && GT_ModHandler.getRecipeOutput(aStack) != null)
+ return true;
+ if (ItemList.Schematic_2by2.isStackEqual(getInputAt(1))
+ && GT_ModHandler.getRecipeOutput(aStack, aStack, null, aStack, aStack) != null) {
+ return true;
+ }
+ return ItemList.Schematic_3by3.isStackEqual(getInputAt(1)) && (GT_ModHandler
+ .getRecipeOutput(aStack, aStack, aStack, aStack, aStack, aStack, aStack, aStack, aStack) != null);
+ } else {
+ return RecipeMaps.packagerRecipes.containsInput(aStack);
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Charger.java b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Charger.java
new file mode 100644
index 0000000000..de43fd715e
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Charger.java
@@ -0,0 +1,110 @@
+package gregtech.common.tileentities.machines.basic;
+
+import static gregtech.api.enums.GT_Values.V;
+
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.BaseMetaTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicBatteryBuffer;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_Charger extends GT_MetaTileEntity_BasicBatteryBuffer {
+
+ public GT_MetaTileEntity_Charger(int aID, String aName, String aNameRegional, int aTier, String aDescription,
+ int aSlotCount) {
+ super(aID, aName, aNameRegional, aTier, aDescription, aSlotCount);
+ }
+
+ public GT_MetaTileEntity_Charger(String aName, int aTier, String aDescription, ITexture[][][] aTextures,
+ int aSlotCount) {
+ super(aName, aTier, aDescription, aTextures, aSlotCount);
+ }
+
+ public GT_MetaTileEntity_Charger(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures,
+ int aSlotCount) {
+ super(aName, aTier, aDescription, aTextures, aSlotCount);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Charger(mName, mTier, mDescriptionArray, mTextures, mInventory.length);
+ }
+
+ @Override
+ public long getMinimumStoredEU() {
+ return V[mTier] * 64L * mInventory.length;
+ }
+
+ @Override
+ public long maxEUStore() {
+ return V[mTier] * 256L * mInventory.length;
+ }
+
+ @Override
+ public long maxAmperesIn() {
+ return Math.max(mChargeableCount * 8L, 4L);
+ }
+
+ @Override
+ public long maxAmperesOut() {
+ return Math.max(mBatteryCount * 4L, 2L);
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aBaseMetaTileEntity.isServerSide()) {
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ if (this.getBaseMetaTileEntity() instanceof BaseMetaTileEntity) {
+ BaseMetaTileEntity mBaseMetaTileEntity = (BaseMetaTileEntity) getBaseMetaTileEntity();
+ if (mBaseMetaTileEntity.getMetaTileEntity() instanceof MetaTileEntity mMetaTileEntity) {
+ // for (int t = 0; t < 6; t++) {
+ if (mMetaTileEntity.dechargerSlotCount() > 0
+ && mBaseMetaTileEntity.getStoredEU() < mBaseMetaTileEntity.getEUCapacity()) {
+ for (int i = mMetaTileEntity.dechargerSlotStartIndex(),
+ k = mMetaTileEntity.dechargerSlotCount() + i; i < k; i++) {
+ if (mMetaTileEntity.mInventory[i] != null
+ && mBaseMetaTileEntity.getStoredEU() < mBaseMetaTileEntity.getEUCapacity()) {
+ mBaseMetaTileEntity.increaseStoredEnergyUnits(
+ GT_ModHandler.dischargeElectricItem(
+ mMetaTileEntity.mInventory[i],
+ GT_Utility.safeInt(
+ Math.min(
+ V[mTier] * 15,
+ mBaseMetaTileEntity.getEUCapacity()
+ - mBaseMetaTileEntity.getStoredEU())),
+ (int) Math.min(Integer.MAX_VALUE, mMetaTileEntity.getInputTier()),
+ true,
+ false,
+ false),
+ true);
+ if (mMetaTileEntity.mInventory[i].stackSize <= 0) mMetaTileEntity.mInventory[i] = null;
+ }
+ }
+ }
+ if (mMetaTileEntity.rechargerSlotCount() > 0 && mBaseMetaTileEntity.getStoredEU() > 0) {
+ for (int i = mMetaTileEntity.rechargerSlotStartIndex(),
+ k = mMetaTileEntity.rechargerSlotCount() + i; i < k; i++) {
+ if (mBaseMetaTileEntity.getStoredEU() > 0 && mMetaTileEntity.mInventory[i] != null) {
+ mBaseMetaTileEntity
+ .decreaseStoredEU(
+ GT_ModHandler.chargeElectricItem(
+ mMetaTileEntity.mInventory[i],
+ GT_Utility
+ .safeInt(Math.min(V[mTier] * 15, mBaseMetaTileEntity.getStoredEU())),
+ (int) Math.min(Integer.MAX_VALUE, mMetaTileEntity.getOutputTier()),
+ true,
+ false),
+ true);
+ if (mMetaTileEntity.mInventory[i].stackSize <= 0) mMetaTileEntity.mInventory[i] = null;
+ }
+ }
+ // }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_IndustrialApiary.java b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_IndustrialApiary.java
new file mode 100644
index 0000000000..9249673c7c
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_IndustrialApiary.java
@@ -0,0 +1,1555 @@
+package gregtech.common.tileentities.machines.basic;
+
+import static gregtech.api.enums.GT_Values.AuthorKuba;
+import static gregtech.api.enums.GT_Values.V;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_INDUSTRIAL_APIARY;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_INDUSTRIAL_APIARY_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_INDUSTRIAL_APIARY_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_INDUSTRIAL_APIARY_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_INDUSTRIAL_APIARY;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_INDUSTRIAL_APIARY_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_INDUSTRIAL_APIARY_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_INDUSTRIAL_APIARY_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_INDUSTRIAL_APIARY;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_INDUSTRIAL_APIARY_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_INDUSTRIAL_APIARY_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_INDUSTRIAL_APIARY_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_INDUSTRIAL_APIARY;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_INDUSTRIAL_APIARY_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_INDUSTRIAL_APIARY_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_INDUSTRIAL_APIARY_GLOW;
+import static gregtech.api.metatileentity.BaseTileEntity.STALLED_STUTTERING_TOOLTIP;
+import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY;
+import static gregtech.api.util.GT_Utility.moveMultipleItemStacks;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.ChunkCoordinates;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.util.Vec3;
+import net.minecraft.world.World;
+import net.minecraft.world.biome.BiomeGenBase;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.google.common.collect.ImmutableSet;
+import com.gtnewhorizons.modularui.api.drawable.FallbackableUITexture;
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.forge.IItemHandlerModifiable;
+import com.gtnewhorizons.modularui.api.math.Pos2d;
+import com.gtnewhorizons.modularui.api.screen.ModularUIContext;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.builder.UIInfo;
+import com.gtnewhorizons.modularui.common.internal.wrapper.BaseSlot;
+import com.gtnewhorizons.modularui.common.internal.wrapper.ModularUIContainer;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.CycleButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotGroup;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+import com.mojang.authlib.GameProfile;
+
+import forestry.api.apiculture.BeeManager;
+import forestry.api.apiculture.EnumBeeChromosome;
+import forestry.api.apiculture.EnumBeeType;
+import forestry.api.apiculture.FlowerManager;
+import forestry.api.apiculture.IAlleleBeeAcceleratableEffect;
+import forestry.api.apiculture.IAlleleBeeEffect;
+import forestry.api.apiculture.IAlleleBeeSpecies;
+import forestry.api.apiculture.IApiaristTracker;
+import forestry.api.apiculture.IBee;
+import forestry.api.apiculture.IBeeGenome;
+import forestry.api.apiculture.IBeeHousing;
+import forestry.api.apiculture.IBeeHousingInventory;
+import forestry.api.apiculture.IBeeListener;
+import forestry.api.apiculture.IBeeModifier;
+import forestry.api.apiculture.IBeeRoot;
+import forestry.api.apiculture.IBeekeepingLogic;
+import forestry.api.apiculture.IBeekeepingMode;
+import forestry.api.arboriculture.EnumGermlingType;
+import forestry.api.core.BiomeHelper;
+import forestry.api.core.EnumHumidity;
+import forestry.api.core.EnumTemperature;
+import forestry.api.core.ForestryAPI;
+import forestry.api.core.IErrorLogic;
+import forestry.api.core.IErrorState;
+import forestry.api.genetics.AlleleManager;
+import forestry.api.genetics.IEffectData;
+import forestry.api.genetics.IIndividual;
+import forestry.apiculture.genetics.Bee;
+import forestry.apiculture.genetics.alleles.AlleleEffectThrottled;
+import forestry.core.errors.EnumErrorCode;
+import forestry.plugins.PluginApiculture;
+import gregtech.GT_Mod;
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine;
+import gregtech.api.recipe.BasicUIProperties;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_ApiaryModifier;
+import gregtech.api.util.GT_ApiaryUpgrade;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.GT_Client;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public class GT_MetaTileEntity_IndustrialApiary extends GT_MetaTileEntity_BasicMachine
+ implements IBeeHousing, IBeeHousingInventory, IErrorLogic, IBeeModifier, IBeeListener, IAddUIWidgets {
+
+ public static final int beeCycleLength = 550;
+ public static final int baseEUtUsage = 37;
+ private static final int queen = 5;
+ private static final int drone = 6;
+ private static final int upgradeSlot = drone + 1;
+ private static final int upgradeSlotCount = 4;
+ private static Field AlleleBeeEffectThrottledField;
+
+ final IBeeRoot beeRoot = (IBeeRoot) AlleleManager.alleleRegistry.getSpeciesRoot("rootBees");
+
+ public int mSpeed = 0;
+ public boolean mLockedSpeed = true;
+ public boolean mAutoQueen = true;
+
+ private ItemStack usedQueen = null;
+ private IBee usedQueenBee = null;
+ private IEffectData[] effectData = new IEffectData[2];
+
+ public GT_MetaTileEntity_IndustrialApiary(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 4,
+ new String[] { "BEES GOES BRRRR", EnumChatFormatting.GRAY + AuthorKuba,
+ "Effective production chance as a percent is", "2.8 * b^0.52 * (p + t)^0.52 * s^0.37",
+ "where b is the base production chance as a percent,",
+ "p is the production modifier (2 w/o upgrades, or 4 * 1.2^n with n production upgrades),",
+ "t is 8 for the industrial apiary, and", "s is the speed value for the bee",
+ "Outputs are generated at the end of every bee tick (...)",
+ "Primary outputs are rolled once with base chance, once with half base" },
+ 6,
+ 9,
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_SIDE_INDUSTRIAL_APIARY_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_INDUSTRIAL_APIARY_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_SIDE_INDUSTRIAL_APIARY),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_INDUSTRIAL_APIARY_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_FRONT_INDUSTRIAL_APIARY_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_INDUSTRIAL_APIARY_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_FRONT_INDUSTRIAL_APIARY),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_INDUSTRIAL_APIARY_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_TOP_INDUSTRIAL_APIARY_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_INDUSTRIAL_APIARY_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_TOP_INDUSTRIAL_APIARY),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_INDUSTRIAL_APIARY_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_BOTTOM_INDUSTRIAL_APIARY_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_INDUSTRIAL_APIARY_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_BOTTOM_INDUSTRIAL_APIARY),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_INDUSTRIAL_APIARY_GLOW)
+ .glow()
+ .build()));
+ }
+
+ public GT_MetaTileEntity_IndustrialApiary(String aName, int aTier, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, 4, aDescription, aTextures, 6, 9);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_IndustrialApiary(this.mName, this.mTier, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ if (aBaseMetaTileEntity.isClientSide()) return true;
+ if (!GT_Mod.gregtechproxy.mForceFreeFace) {
+ openGUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ if (aBaseMetaTileEntity.getAirAtSide(side)) {
+ openGUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+ }
+ GT_Utility.sendChatToPlayer(aPlayer, "No free Side!");
+ return true;
+ }
+
+ private void openGUI(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ IndustrialApiaryUI.open(
+ aPlayer,
+ aBaseMetaTileEntity.getWorld(),
+ aBaseMetaTileEntity.getXCoord(),
+ aBaseMetaTileEntity.getYCoord(),
+ aBaseMetaTileEntity.getZCoord());
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setInteger("mSpeed", mSpeed);
+ aNBT.setBoolean("mLockedSpeed", mLockedSpeed);
+ aNBT.setBoolean("mAutoQueen", mAutoQueen);
+ if (usedQueen != null) aNBT.setTag("usedQueen", usedQueen.writeToNBT(new NBTTagCompound()));
+ aNBT.setBoolean("retrievingPollenInThisOperation", retrievingPollenInThisOperation);
+ aNBT.setInteger("pollinationDelay", pollinationDelay);
+ aNBT.setFloat("usedBeeLife", usedBeeLife);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ mSpeed = aNBT.getInteger("mSpeed");
+ mLockedSpeed = aNBT.getBoolean("mLockedSpeed");
+ if (aNBT.hasKey("mAutoQueen")) mAutoQueen = aNBT.getBoolean("mAutoQueen");
+ if (aNBT.hasKey("usedQueen")) usedQueen = ItemStack.loadItemStackFromNBT(aNBT.getCompoundTag("usedQueen"));
+ retrievingPollenInThisOperation = aNBT.getBoolean("retrievingPollenInThisOperation");
+ pollinationDelay = aNBT.getInteger("pollinationDelay");
+ usedBeeLife = aNBT.getFloat("usedBeeLife");
+ }
+
+ boolean retrievingPollenInThisOperation = false;
+ IIndividual retrievedpollen = null;
+ int pollinationDelay = 100;
+ float usedBeeLife = 0f;
+
+ @Override
+ public int checkRecipe() {
+ updateModifiers();
+ if (canWork()) {
+
+ final ItemStack queen = getQueen();
+ usedQueen = queen.copy();
+ if (beeRoot.getType(queen) == EnumBeeType.QUEEN) {
+ final IBee bee = beeRoot.getMember(queen);
+ usedQueenBee = bee;
+
+ // LIFE CYCLES
+
+ float mod = this.getLifespanModifier(null, null, 1.f);
+ final IBeekeepingMode mode = beeRoot.getBeekeepingMode(this.getWorld());
+ final IBeeModifier beemodifier = mode.getBeeModifier();
+ mod *= beemodifier.getLifespanModifier(null, null, 1.f);
+ final int h = bee.getHealth();
+ mod = 1.f / mod;
+ final float cycles = h / mod;
+
+ // PRODUCTS
+
+ final HashMap<GT_Utility.ItemId, ItemStack> pollen = new HashMap<>();
+
+ if (isRetrievingPollen && floweringMod > 0f) {
+ final int icycles = (int) cycles
+ + (getWorld().rand.nextFloat() < (cycles - (float) ((int) cycles)) ? 1 : 0);
+ for (int z = 0; z < icycles; z++) {
+ final IIndividual p = bee.retrievePollen(this);
+ if (p != null) {
+ final ItemStack s = p.getGenome()
+ .getSpeciesRoot()
+ .getMemberStack(p, EnumGermlingType.POLLEN.ordinal());
+ if (s != null) {
+ final GT_Utility.ItemId id = GT_Utility.ItemId.createNoCopy(s);
+ pollen.computeIfAbsent(id, k -> {
+ final ItemStack ns = s.copy();
+ ns.stackSize = 0;
+ return ns;
+ });
+ pollen.get(id).stackSize += s.stackSize;
+ }
+ }
+ }
+ }
+
+ retrievedpollen = null;
+ retrievingPollenInThisOperation = isRetrievingPollen;
+
+ final IBeeGenome genome = bee.getGenome();
+ final IAlleleBeeSpecies primary = genome.getPrimary();
+ final IAlleleBeeSpecies secondary = genome.getSecondary();
+
+ final float speed = genome.getSpeed();
+ final float prodMod = getProductionModifier(null, 0f) + beemodifier.getProductionModifier(null, 0f);
+
+ final HashMap<GT_Utility.ItemId, Float> drops = new HashMap<>();
+ final HashMap<GT_Utility.ItemId, ItemStack> dropstacks = new HashMap<>();
+
+ for (Map.Entry<ItemStack, Float> entry : primary.getProductChances()
+ .entrySet()) {
+ final GT_Utility.ItemId id = GT_Utility.ItemId.createNoCopy(entry.getKey());
+ drops.merge(
+ id,
+ Bee.getFinalChance(entry.getValue(), speed, prodMod, 8f) * (float) entry.getKey().stackSize
+ * cycles,
+ Float::sum);
+ dropstacks.computeIfAbsent(id, k -> entry.getKey());
+ }
+ for (Map.Entry<ItemStack, Float> entry : secondary.getProductChances()
+ .entrySet()) {
+ final GT_Utility.ItemId id = GT_Utility.ItemId.createNoCopy(entry.getKey());
+ drops.merge(
+ id,
+ Bee.getFinalChance(entry.getValue() / 2f, speed, prodMod, 8f) * (float) entry.getKey().stackSize
+ * cycles,
+ Float::sum);
+ dropstacks.computeIfAbsent(id, k -> entry.getKey());
+ }
+ if (primary.isJubilant(genome, this) && secondary.isJubilant(genome, this))
+ for (Map.Entry<ItemStack, Float> entry : primary.getSpecialtyChances()
+ .entrySet()) {
+ final GT_Utility.ItemId id = GT_Utility.ItemId.createNoCopy(entry.getKey());
+ drops.merge(
+ id,
+ Bee.getFinalChance(entry.getValue(), speed, prodMod, 8f)
+ * (float) entry.getKey().stackSize
+ * cycles,
+ Float::sum);
+ dropstacks.computeIfAbsent(id, k -> entry.getKey());
+ }
+
+ int i = 0;
+ final int imax = mOutputItems.length;
+
+ final IApiaristTracker breedingTracker = beeRoot.getBreedingTracker(getWorld(), getOwner());
+
+ if (!bee.canSpawn()) {
+ final ItemStack convert = new ItemStack(PluginApiculture.items.beePrincessGE);
+ final NBTTagCompound nbttagcompound = new NBTTagCompound();
+ queen.writeToNBT(nbttagcompound);
+ convert.setTagCompound(nbttagcompound);
+ this.mOutputItems[i++] = convert;
+ } else {
+ final IBee b = bee.spawnPrincess(this);
+ if (b != null) {
+ final ItemStack princess = beeRoot.getMemberStack(b, EnumBeeType.PRINCESS.ordinal());
+ breedingTracker.registerPrincess(b);
+ this.mOutputItems[i++] = princess;
+ }
+ final IBee[] d = bee.spawnDrones(this);
+ if (d != null && d.length > 0) {
+ final HashMap<GT_Utility.ItemId, ItemStack> drones = new HashMap<>(d.length);
+ for (IBee dr : d) {
+ final ItemStack drone = beeRoot.getMemberStack(dr, EnumBeeType.DRONE.ordinal());
+ breedingTracker.registerDrone(dr);
+ final GT_Utility.ItemId drid = GT_Utility.ItemId.createNoCopy(drone);
+ if (drones.containsKey(drid)) drones.get(drid).stackSize += drone.stackSize;
+ else {
+ this.mOutputItems[i++] = drone;
+ drones.put(drid, drone);
+ }
+ }
+ }
+ }
+
+ final int imin = i;
+
+ setQueen(null);
+
+ for (Map.Entry<GT_Utility.ItemId, Float> entry : drops.entrySet()) {
+ final ItemStack s = dropstacks.get(entry.getKey())
+ .copy();
+ s.stackSize = entry.getValue()
+ .intValue()
+ + (getWorld().rand.nextFloat() < (entry.getValue() - (float) entry.getValue()
+ .intValue()) ? 1 : 0);
+ if (s.stackSize > 0 && i < imax) while (true) {
+ if (s.stackSize <= s.getMaxStackSize()) {
+ this.mOutputItems[i++] = s;
+ break;
+ } else this.mOutputItems[i++] = s.splitStack(s.getMaxStackSize());
+ if (i >= imax) break;
+ }
+ }
+
+ for (ItemStack s : pollen.values()) if (i < imax) this.mOutputItems[i++] = s;
+ else break;
+
+ // Overclock
+
+ usedBeeLife = cycles * (float) beeCycleLength;
+ this.mMaxProgresstime = (int) usedBeeLife;
+ final int timemaxdivider = this.mMaxProgresstime / 100;
+ final int useddivider = 1 << this.mSpeed;
+ int actualdivider = useddivider;
+ this.mMaxProgresstime /= Math.min(actualdivider, timemaxdivider);
+ actualdivider /= Math.min(actualdivider, timemaxdivider);
+ for (i--; i >= imin; i--) this.mOutputItems[i].stackSize *= actualdivider;
+
+ pollinationDelay = Math.max((int) (this.mMaxProgresstime / cycles), 20); // don't run too often
+
+ this.mProgresstime = 0;
+ this.mEUt = (int) ((float) baseEUtUsage * this.energyMod * useddivider);
+ if (useddivider == 2) this.mEUt += 32;
+ else if (useddivider > 2) this.mEUt += (32 * (useddivider << (this.mSpeed - 2)));
+ } else {
+ // Breeding time
+
+ retrievingPollenInThisOperation = true; // Don't pollinate when breeding
+
+ this.mMaxProgresstime = 100;
+ this.mProgresstime = 0;
+ final int useddivider = Math.min(100, 1 << this.mSpeed);
+ this.mMaxProgresstime /= useddivider;
+ this.mEUt = (int) ((float) baseEUtUsage * this.energyMod * useddivider);
+ if (useddivider == 2) this.mEUt += 32;
+ else if (useddivider > 2) this.mEUt += (32 * (useddivider << (this.mSpeed - 2)));
+
+ final IBee princess = beeRoot.getMember(getQueen());
+ usedQueenBee = princess;
+ final IBee drone = beeRoot.getMember(getDrone());
+ princess.mate(drone);
+ final NBTTagCompound nbttagcompound = new NBTTagCompound();
+ princess.writeToNBT(nbttagcompound);
+ this.mOutputItems[0] = new ItemStack(PluginApiculture.items.beeQueenGE);
+ this.mOutputItems[0].setTagCompound(nbttagcompound);
+ beeRoot.getBreedingTracker(getWorld(), getOwner())
+ .registerQueen(princess);
+
+ setQueen(null);
+ getDrone().stackSize -= 1;
+ if (getDrone().stackSize == 0) setDrone(null);
+ }
+
+ return FOUND_AND_SUCCESSFULLY_USED_RECIPE;
+ }
+
+ return DID_NOT_FIND_RECIPE;
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ updateModifiers();
+ }
+
+ @Override
+ protected boolean hasEnoughEnergyToCheckRecipe() {
+ return getBaseMetaTileEntity().isUniversalEnergyStored(V[mSpeed] * 8L);
+ }
+
+ @Override
+ public long maxAmperesIn() {
+ return 4L;
+ }
+
+ private void doEffect() {
+ final IBeeGenome genome = usedQueenBee.getGenome();
+ final IAlleleBeeEffect effect = genome.getEffect();
+ if (!(effect instanceof IAlleleBeeAcceleratableEffect)) {
+ effectData[0] = effect.validateStorage(effectData[0]);
+ effect.doEffect(genome, effectData[0], this);
+ }
+
+ if (!effect.isCombinable()) return;
+
+ final IAlleleBeeEffect secondary = (IAlleleBeeEffect) genome.getInactiveAllele(EnumBeeChromosome.EFFECT);
+ if (!secondary.isCombinable()) return;
+
+ if (!(secondary instanceof IAlleleBeeAcceleratableEffect)) {
+ effectData[1] = secondary.validateStorage(effectData[1]);
+ secondary.doEffect(genome, effectData[1], this);
+ }
+ }
+
+ private void doAcceleratedEffects() {
+ final IBeeGenome genome = usedQueenBee.getGenome();
+ final IAlleleBeeEffect effect = genome.getEffect();
+ try {
+ if (AlleleBeeEffectThrottledField == null) {
+ AlleleBeeEffectThrottledField = AlleleEffectThrottled.class.getDeclaredField("throttle");
+ AlleleBeeEffectThrottledField.setAccessible(true);
+ }
+ if (effect instanceof IAlleleBeeAcceleratableEffect) {
+ effectData[0] = effect.validateStorage(effectData[0]);
+ effectData[0] = ((IAlleleBeeAcceleratableEffect) effect).doEffectAccelerated(
+ genome,
+ effectData[0],
+ this,
+ usedBeeLife / (effect instanceof AlleleEffectThrottled
+ ? (float) AlleleBeeEffectThrottledField.getInt(effect)
+ : 1f));
+ }
+
+ if (!effect.isCombinable()) return;
+
+ final IAlleleBeeEffect secondary = (IAlleleBeeEffect) genome.getInactiveAllele(EnumBeeChromosome.EFFECT);
+ if (!secondary.isCombinable()) return;
+
+ if (secondary instanceof IAlleleBeeAcceleratableEffect) {
+ effectData[1] = secondary.validateStorage(effectData[1]);
+ effectData[1] = ((IAlleleBeeAcceleratableEffect) secondary).doEffectAccelerated(
+ genome,
+ effectData[0],
+ this,
+ usedBeeLife / (secondary instanceof AlleleEffectThrottled
+ ? (float) AlleleBeeEffectThrottledField.getInt(secondary)
+ : 1f));
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aBaseMetaTileEntity.isClientSide()) {
+ if (GT_Client.changeDetected == 4) {
+ /*
+ * Client tick counter that is set to 5 on hiding pipes and covers. It triggers a texture update next
+ * client tick when reaching 4, with provision for 3 more update tasks, spreading client change
+ * detection related work and network traffic on different ticks, until it reaches 0.
+ */
+ aBaseMetaTileEntity.issueTextureUpdate();
+ }
+ if (aBaseMetaTileEntity.isActive()) {
+ if (usedQueen != null) {
+ if (aTick % 2 == 0) {
+ // FX on client, effect on server
+ final IBee bee = beeRoot.getMember(usedQueen);
+ effectData = bee.doFX(effectData, this);
+ }
+ }
+ }
+ }
+ if (aBaseMetaTileEntity.isServerSide()) {
+
+ mCharge = aBaseMetaTileEntity.getStoredEU() / 2 > aBaseMetaTileEntity.getEUCapacity() / 3;
+ mDecharge = aBaseMetaTileEntity.getStoredEU() < aBaseMetaTileEntity.getEUCapacity() / 3;
+
+ doDisplayThings();
+
+ if (!aBaseMetaTileEntity.isActive()) {
+ if (aBaseMetaTileEntity.isAllowedToWork()
+ && (aBaseMetaTileEntity.hasInventoryBeenModified() || aTick % 600 == 0
+ || aBaseMetaTileEntity.hasWorkJustBeenEnabled())
+ && hasEnoughEnergyToCheckRecipe()) {
+ final int check = checkRecipe();
+ if (check == FOUND_AND_SUCCESSFULLY_USED_RECIPE) {
+ aBaseMetaTileEntity.setActive(true);
+ }
+ }
+ } else {
+
+ if (this.mProgresstime < 0) {
+ this.mProgresstime++;
+ return;
+ }
+ if (this.mStuttering) {
+ if (!aBaseMetaTileEntity.isAllowedToWork()) return;
+ if (aTick % 100 == 0) this.mStuttering = false;
+ return;
+ }
+ if (this.hasErrors()) {
+ if (!aBaseMetaTileEntity.isAllowedToWork()) return;
+ if (aTick % 100 == 0) if (!canWork(usedQueen)) this.stutterProcess();
+ return;
+ }
+
+ if (!drainEnergyForProcess(this.mEUt)) {
+ this.mStuttering = true;
+ this.stutterProcess();
+ return;
+ }
+ this.mProgresstime++;
+ if (usedQueen != null) {
+ if (usedQueenBee == null) usedQueenBee = beeRoot.getMember(usedQueen);
+ doEffect();
+ if (!retrievingPollenInThisOperation && floweringMod > 0f
+ && this.mProgresstime % pollinationDelay == 0) {
+ if (retrievedpollen == null) retrievedpollen = usedQueenBee.retrievePollen(this);
+ if (retrievedpollen != null && (usedQueenBee.pollinateRandom(this, retrievedpollen)
+ || this.mProgresstime % (pollinationDelay * 5) == 0)) retrievedpollen = null;
+ }
+ }
+
+ if (this.mProgresstime % 100 == 0) {
+ if (!canWork(usedQueen)) {
+ this.stutterProcess();
+ return;
+ }
+ }
+
+ if (this.mProgresstime >= this.mMaxProgresstime) {
+ if (usedQueenBee != null) doAcceleratedEffects();
+ updateModifiers();
+ for (int i = 0; i < mOutputItems.length; i++)
+ if (mOutputItems[i] != null) for (int j = 0; j < mOutputItems.length; j++) {
+ if (j == 0 && isAutomated) {
+ if (beeRoot.isMember(mOutputItems[i], EnumBeeType.QUEEN.ordinal())
+ || beeRoot.isMember(mOutputItems[i], EnumBeeType.PRINCESS.ordinal())) {
+ if (aBaseMetaTileEntity.addStackToSlot(queen, mOutputItems[i])) break;
+ } else if (beeRoot.isMember(mOutputItems[i], EnumBeeType.DRONE.ordinal()))
+ if (aBaseMetaTileEntity.addStackToSlot(drone, mOutputItems[i])) break;
+ } else if (mAutoQueen && i == 0
+ && j == 0
+ && beeRoot.isMember(mOutputItems[0], EnumBeeType.QUEEN.ordinal())
+ && aBaseMetaTileEntity.addStackToSlot(queen, mOutputItems[0])) break;
+ if (aBaseMetaTileEntity
+ .addStackToSlot(getOutputSlot() + ((j + i) % mOutputItems.length), mOutputItems[i]))
+ break;
+ }
+ Arrays.fill(mOutputItems, null);
+ mEUt = 0;
+ mProgresstime = 0;
+ mMaxProgresstime = 0;
+ mStuttering = false;
+ aBaseMetaTileEntity.setActive(false);
+
+ if (doesAutoOutput() && !isOutputEmpty() && aBaseMetaTileEntity.getFrontFacing() != mMainFacing) {
+ final TileEntity tTileEntity2 = aBaseMetaTileEntity
+ .getTileEntityAtSide(aBaseMetaTileEntity.getFrontFacing());
+ final long tStoredEnergy = aBaseMetaTileEntity.getUniversalEnergyStored();
+ int tMaxStacks = (int) (tStoredEnergy / 64L);
+ if (tMaxStacks > mOutputItems.length) tMaxStacks = mOutputItems.length;
+
+ moveMultipleItemStacks(
+ aBaseMetaTileEntity,
+ tTileEntity2,
+ aBaseMetaTileEntity.getFrontFacing(),
+ aBaseMetaTileEntity.getBackFacing(),
+ null,
+ false,
+ (byte) 64,
+ (byte) 1,
+ (byte) 64,
+ (byte) 1,
+ tMaxStacks);
+ }
+
+ if (aBaseMetaTileEntity.isAllowedToWork() && checkRecipe() == FOUND_AND_SUCCESSFULLY_USED_RECIPE)
+ aBaseMetaTileEntity.setActive(true);
+ }
+ }
+ }
+ }
+
+ public void cancelProcess() {
+ if (this.getBaseMetaTileEntity()
+ .isActive()
+ && this.getBaseMetaTileEntity()
+ .isServerSide()
+ && usedQueen != null
+ && beeRoot.isMember(usedQueen, EnumBeeType.QUEEN.ordinal())) {
+ Arrays.fill(mOutputItems, null);
+ mEUt = 0;
+ mProgresstime = 0;
+ mMaxProgresstime = 0;
+ mStuttering = false;
+ this.getBaseMetaTileEntity()
+ .setActive(false);
+ setQueen(usedQueen);
+ this.getBaseMetaTileEntity()
+ .disableWorking();
+ }
+ }
+
+ @Override
+ public boolean isItemValidForSlot(int aIndex, ItemStack aStack) {
+ if (aStack == null) return false;
+ if (aIndex < getInputSlot()) return true;
+ if (aIndex == queen) return beeRoot.isMember(aStack, EnumBeeType.QUEEN.ordinal())
+ || beeRoot.isMember(aStack, EnumBeeType.PRINCESS.ordinal());
+ else if (aIndex == drone) return beeRoot.isMember(aStack, EnumBeeType.DRONE.ordinal());
+ else if (aIndex < getOutputSlot()) {
+ if (!GT_ApiaryUpgrade.isUpgrade(aStack)) return false;
+ for (int i = upgradeSlot; i < upgradeSlot + upgradeSlotCount; i++) {
+ if (aIndex == i) continue;
+ final ItemStack s = getStackInSlot(i);
+ if (s == null) continue;
+ if (GT_Utility.areStacksEqual(getStackInSlot(i), aStack)) return false;
+ if (GT_ApiaryUpgrade.isUpgrade(aStack)) {
+ if (!GT_ApiaryUpgrade.getUpgrade(aStack)
+ .isAllowedToWorkWith(getStackInSlot(i))) return false;
+ } else if (GT_ApiaryUpgrade.isUpgrade(s)) {
+ if (!GT_ApiaryUpgrade.getUpgrade(s)
+ .isAllowedToWorkWith(aStack)) return false;
+ }
+ }
+ return true;
+ } else return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ if (!super.allowPutStack(aBaseMetaTileEntity, aIndex, side, aStack)) return false;
+ return isItemValidForSlot(aIndex, aStack);
+ }
+
+ @Override
+ public void setInventorySlotContents(int aIndex, ItemStack aStack) {
+ if (aIndex == queen && aStack != null && getBaseMetaTileEntity().isClientSide()) usedQueen = aStack.copy();
+ super.setInventorySlotContents(aIndex, aStack);
+ }
+
+ // Gets called on slot click //
+ public void onInventoryUpdate(int aIndex) {
+ if (aIndex > drone && aIndex < getOutputSlot()) updateModifiers();
+ }
+
+ public ItemStack getUsedQueen() {
+ return usedQueen;
+ }
+
+ // region IBeeHousing
+
+ @Override
+ public Iterable<IBeeModifier> getBeeModifiers() {
+ return Collections.singletonList(this);
+ }
+
+ @Override
+ public Iterable<IBeeListener> getBeeListeners() {
+ return Collections.singletonList(this);
+ }
+
+ @Override
+ public IBeeHousingInventory getBeeInventory() {
+ return this;
+ }
+
+ @Override
+ public IBeekeepingLogic getBeekeepingLogic() {
+ return dummylogic;
+ }
+
+ @Override
+ public int getBlockLightValue() {
+ return this.getBaseMetaTileEntity()
+ .getLightLevelAtSide(ForgeDirection.UP);
+ }
+
+ @Override
+ public boolean canBlockSeeTheSky() {
+ return this.getBaseMetaTileEntity()
+ .getSkyAtSide(ForgeDirection.UP);
+ }
+
+ @Override
+ public World getWorld() {
+ return this.getBaseMetaTileEntity()
+ .getWorld();
+ }
+
+ GameProfile owner = null;
+
+ @Override
+ public GameProfile getOwner() {
+ if (owner == null) owner = new GameProfile(
+ this.getBaseMetaTileEntity()
+ .getOwnerUuid(),
+ this.getBaseMetaTileEntity()
+ .getOwnerName());
+ return owner;
+ }
+
+ @Override
+ public Vec3 getBeeFXCoordinates() {
+ return Vec3.createVectorHelper(
+ getBaseMetaTileEntity().getXCoord() + 0.5,
+ getBaseMetaTileEntity().getYCoord() + 0.5,
+ getBaseMetaTileEntity().getZCoord() + 0.5);
+ }
+
+ @Override
+ public BiomeGenBase getBiome() {
+ if (biomeOverride == null) return this.getBaseMetaTileEntity()
+ .getBiome();
+ return biomeOverride;
+ }
+
+ @Override
+ public EnumTemperature getTemperature() {
+ if (BiomeHelper.isBiomeHellish(getBiome())) return EnumTemperature.HELLISH;
+ return EnumTemperature.getFromValue(getBiome().temperature + temperatureMod);
+ }
+
+ @Override
+ public EnumHumidity getHumidity() {
+ return EnumHumidity.getFromValue(getBiome().rainfall + humidityMod);
+ }
+
+ @Override
+ public IErrorLogic getErrorLogic() {
+ return this;
+ }
+
+ @Override
+ public ChunkCoordinates getCoordinates() {
+ return this.getBaseMetaTileEntity()
+ .getCoords();
+ }
+
+ // endregion
+
+ // region IBeeHousingInventory
+ @Override
+ public ItemStack getQueen() {
+ return getStackInSlot(queen);
+ }
+
+ @Override
+ public ItemStack getDrone() {
+ return getStackInSlot(drone);
+ }
+
+ @Override
+ public void setQueen(ItemStack itemStack) {
+ setInventorySlotContents(queen, itemStack);
+ }
+
+ @Override
+ public void setDrone(ItemStack itemStack) {
+ setInventorySlotContents(drone, itemStack);
+ }
+
+ @Override
+ public boolean addProduct(ItemStack itemStack, boolean b) {
+ throw new RuntimeException("Should not happen :F");
+ }
+ // endregion
+
+ // region IErrorLogic
+
+ public HashSet<IErrorState> mErrorStates = new HashSet<>();
+
+ @Override
+ public boolean setCondition(boolean b, IErrorState iErrorState) {
+ if (b) mErrorStates.add(iErrorState);
+ else mErrorStates.remove(iErrorState);
+ return b;
+ }
+
+ @Override
+ public boolean contains(IErrorState iErrorState) {
+ return mErrorStates.contains(iErrorState);
+ }
+
+ @Override
+ public boolean hasErrors() {
+ return !mErrorStates.isEmpty();
+ }
+
+ @Override
+ public void clearErrors() {
+ mErrorStates.clear();
+ }
+
+ @Override
+ public void writeData(DataOutputStream dataOutputStream) throws IOException {
+ dataOutputStream.write(mErrorStates.size());
+ for (IErrorState s : mErrorStates) dataOutputStream.writeUTF(s.getUniqueName());
+ }
+
+ @Override
+ public void readData(DataInputStream dataInputStream) throws IOException {
+ for (int i = dataInputStream.readInt(); i > 0; i--)
+ mErrorStates.add(ForestryAPI.errorStateRegistry.getErrorState(dataInputStream.readUTF()));
+ }
+
+ @Override
+ public ImmutableSet<IErrorState> getErrorStates() {
+ return ImmutableSet.copyOf(mErrorStates);
+ }
+
+ private String flowerType = "";
+ private ChunkCoordinates flowercoords = null;
+ private Block flowerBlock;
+ private int flowerBlockMeta;
+
+ private boolean checkFlower(IBee bee) {
+ final String flowerType = bee.getGenome()
+ .getFlowerProvider()
+ .getFlowerType();
+ if (!this.flowerType.equals(flowerType)) flowercoords = null;
+ if (flowercoords != null) {
+ if (getWorld().getBlock(flowercoords.posX, flowercoords.posY, flowercoords.posZ) != flowerBlock
+ || getWorld().getBlockMetadata(flowercoords.posX, flowercoords.posY, flowercoords.posZ)
+ != flowerBlockMeta)
+ if (!FlowerManager.flowerRegistry
+ .isAcceptedFlower(flowerType, getWorld(), flowercoords.posX, flowercoords.posY, flowercoords.posZ))
+ flowercoords = null;
+ else {
+ flowerBlock = getWorld().getBlock(flowercoords.posX, flowercoords.posY, flowercoords.posZ);
+ flowerBlockMeta = getWorld()
+ .getBlockMetadata(flowercoords.posX, flowercoords.posY, flowercoords.posZ);
+ }
+ }
+ if (flowercoords == null) {
+ flowercoords = FlowerManager.flowerRegistry.getAcceptedFlowerCoordinates(this, bee, flowerType);
+ if (flowercoords != null) {
+ flowerBlock = getWorld().getBlock(flowercoords.posX, flowercoords.posY, flowercoords.posZ);
+ flowerBlockMeta = getWorld().getBlockMetadata(flowercoords.posX, flowercoords.posY, flowercoords.posZ);
+ this.flowerType = flowerType;
+ }
+ }
+ return flowercoords != null;
+ }
+
+ private boolean canWork(ItemStack queen) {
+ clearErrors();
+ if (queen == null) return true; // Reloaded the chunk ?
+ if (beeRoot.isMember(queen, EnumBeeType.PRINCESS.ordinal())) return true;
+ final IBee bee = beeRoot.getMember(queen);
+ for (IErrorState err : bee.getCanWork(this)) setCondition(true, err);
+ setCondition(!checkFlower(bee), EnumErrorCode.NO_FLOWER);
+ return !hasErrors();
+ }
+
+ private boolean canWork() {
+ clearErrors();
+ final EnumBeeType beeType = beeRoot.getType(getQueen());
+ if (beeType == EnumBeeType.PRINCESS) {
+ setCondition(!beeRoot.isDrone(getDrone()), EnumErrorCode.NO_DRONE);
+ return !hasErrors();
+ }
+ if (beeType == EnumBeeType.QUEEN) {
+ final IBee bee = beeRoot.getMember(getQueen());
+ for (IErrorState err : bee.getCanWork(this)) setCondition(true, err);
+ setCondition(!checkFlower(bee), EnumErrorCode.NO_FLOWER);
+ return !hasErrors();
+ } else {
+ setCondition(true, EnumErrorCode.NO_QUEEN);
+ return false;
+ }
+ }
+
+ // endregion
+
+ // region IBeeModifier
+
+ private float terrorityMod = 1f;
+ private float mutationMod = 1f;
+ private float lifespanMod = 1f;
+ private float productionMod = 2f;
+ private float floweringMod = 1f;
+ private float geneticDecayMod = 1f;
+ private float energyMod = 1f;
+ private boolean sealedMod = false;
+ private boolean selfLightedMod = false;
+ private boolean selfUnlightedMod = false;
+ private boolean sunlightSimulatedMod = false;
+ private BiomeGenBase biomeOverride = null;
+ private float humidityMod = 0f;
+ private float temperatureMod = 0f;
+ private boolean isAutomated = false;
+ private boolean isRetrievingPollen = false;
+ private int maxspeed = 0;
+
+ public void updateModifiers() {
+ final GT_ApiaryModifier mods = new GT_ApiaryModifier();
+ for (int i = 0; i < upgradeSlotCount; i++) {
+ final ItemStack s = getStackInSlot(upgradeSlot + i);
+ if (s == null) continue;
+ if (GT_ApiaryUpgrade.isUpgrade(s)) {
+ final GT_ApiaryUpgrade upgrade = GT_ApiaryUpgrade.getUpgrade(s);
+ upgrade.applyModifiers(mods, s);
+ }
+ }
+
+ terrorityMod = mods.territory;
+ mutationMod = mods.mutation;
+ lifespanMod = mods.lifespan;
+ productionMod = mods.production;
+ floweringMod = mods.flowering;
+ geneticDecayMod = mods.geneticDecay;
+ energyMod = mods.energy;
+ sealedMod = mods.isSealed;
+ selfLightedMod = mods.isSelfLighted;
+ selfUnlightedMod = mods.isSelfUnlighted;
+ sunlightSimulatedMod = mods.isSunlightSimulated;
+ biomeOverride = mods.biomeOverride;
+ humidityMod = mods.humidity;
+ temperatureMod = mods.temperature;
+ isAutomated = mods.isAutomated;
+ isRetrievingPollen = mods.isCollectingPollen;
+ maxspeed = mods.maxSpeed;
+
+ if (mLockedSpeed) mSpeed = maxspeed;
+ else mSpeed = Math.min(mSpeed, maxspeed);
+ }
+
+ /** Tries to move as much of [stack] into a possible upgrade slot */
+ public void addUpgrade(ItemStack stack) {
+ if (stack == null || !GT_ApiaryUpgrade.isUpgrade(stack)) return;
+
+ int amount = stack.stackSize;
+ for (int i = upgradeSlot; i < upgradeSlot + upgradeSlotCount; i++) {
+ if (!isItemValidForSlot(i, stack)) continue;
+
+ int maxStackSize = GT_ApiaryUpgrade.getUpgrade(stack)
+ .getMaxNumber();
+ ItemStack stackInSlot = getStackInSlot(i);
+
+ // Push into empty slot
+ if (stackInSlot == null) {
+ amount = Math.min(amount, maxStackSize);
+ setInventorySlotContents(i, stack.splitStack(amount));
+ return;
+ }
+
+ if (!GT_Utility.areStacksEqual(stack, stackInSlot)) continue;
+ amount = Math.max(Math.min(amount, maxStackSize - stackInSlot.stackSize), 0);
+ if (amount == 0) return;
+ stackInSlot.stackSize += amount;
+ stack.stackSize -= amount;
+ return;
+ }
+ }
+
+ /** Returns installed upgrade in slot 0 <= [index] < getMaxUpgradeCount() */
+ public ItemStack getUpgrade(int index) {
+ if (index < 0 || index >= upgradeSlotCount) return null;
+ return getStackInSlot(upgradeSlot + index);
+ }
+
+ /** Tries to remove [amount] or less of the upgrade installed in slot [index]. Returns the removed ItemStack */
+ public ItemStack removeUpgrade(int index, int amount) {
+ if (index < 0 || index >= upgradeSlotCount || amount <= 0) return null;
+
+ ItemStack stackInSlot = getUpgrade(index);
+ if (stackInSlot == null) return null;
+
+ amount = Math.min(amount, stackInSlot.stackSize);
+ ItemStack result = stackInSlot.splitStack(amount);
+ if (stackInSlot.stackSize <= 0) setInventorySlotContents(upgradeSlot + index, null);
+ return result;
+ }
+
+ public static int getMaxUpgradeCount() {
+ return upgradeSlotCount;
+ }
+
+ @Override
+ public float getTerritoryModifier(IBeeGenome iBeeGenome, float v) {
+ return Math.min(5, terrorityMod);
+ }
+
+ @Override
+ public float getMutationModifier(IBeeGenome iBeeGenome, IBeeGenome iBeeGenome1, float v) {
+ return mutationMod;
+ }
+
+ @Override
+ public float getLifespanModifier(IBeeGenome iBeeGenome, IBeeGenome iBeeGenome1, float v) {
+ return lifespanMod;
+ }
+
+ @Override
+ public float getProductionModifier(IBeeGenome iBeeGenome, float v) {
+ return productionMod;
+ }
+
+ @Override
+ public float getFloweringModifier(IBeeGenome iBeeGenome, float v) {
+ return floweringMod;
+ }
+
+ @Override
+ public float getGeneticDecay(IBeeGenome iBeeGenome, float v) {
+ return geneticDecayMod;
+ }
+
+ public float getEnergyModifier() {
+ return energyMod;
+ }
+
+ @Override
+ public boolean isSealed() {
+ return sealedMod;
+ }
+
+ @Override
+ public boolean isSelfLighted() {
+ return selfLightedMod;
+ }
+
+ @Override
+ public boolean isSelfUnlighted() {
+ return selfUnlightedMod;
+ }
+
+ @Override
+ public boolean isSunlightSimulated() {
+ return sunlightSimulatedMod;
+ }
+
+ @Override
+ public boolean isHellish() {
+ return getBiome() == BiomeGenBase.hell;
+ }
+
+ public int getMaxSpeed() {
+ return maxspeed;
+ }
+
+ // endregion
+
+ // region IBeeListener
+
+ @Override
+ public void wearOutEquipment(int i) {}
+
+ @Override
+ public void onQueenDeath() {}
+
+ @Override
+ public boolean onPollenRetrieved(IIndividual iIndividual) {
+ return false;
+ }
+
+ // endregion
+
+ static final IBeekeepingLogic dummylogic = new IBeekeepingLogic() {
+
+ @Override
+ public boolean canWork() {
+ return true;
+ }
+
+ @Override
+ public void doWork() {}
+
+ @Override
+ public void syncToClient() {}
+
+ @Override
+ public void syncToClient(EntityPlayerMP entityPlayerMP) {}
+
+ @Override
+ public int getBeeProgressPercent() {
+ return 0;
+ }
+
+ @Override
+ public boolean canDoBeeFX() {
+ return false;
+ }
+
+ @Override
+ public void doBeeFX() {}
+
+ @Override
+ public void readFromNBT(NBTTagCompound nbtTagCompound) {}
+
+ @Override
+ public void writeToNBT(NBTTagCompound nbtTagCompound) {}
+ };
+
+ private static final String POWER_SOURCE_POWER = "GT5U.machines.powersource.power",
+ CANCEL_PROCESS_TOOLTIP = "GT5U.machines.industrialapiary.cancel.tooltip",
+ SPEED_TOOLTIP = "GT5U.machines.industrialapiary.speed.tooltip",
+ SPEED_LOCKED_TOOLTIP = "GT5U.machines.industrialapiary.speedlocked.tooltip",
+ INFO_TOOLTIP = "GT5U.machines.industrialapiary.info.tooltip",
+ INFO_WITH_BEE_TOOLTIP = "GT5U.machines.industrialapiary.infoextended.tooltip",
+ UPGRADE_TOOLTIP = "GT5U.machines.industrialapiary.upgradeslot.tooltip",
+ AUTOQUEEN_TOOLTIP = "GT5U.machines.industrialapiary.autoqueen.tooltip";
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ builder
+ .widget(
+ new SlotWidget(new ApiarySlot(inventoryHandler, queen))
+ .setBackground(getGUITextureSet().getItemSlot(), GT_UITextures.OVERLAY_SLOT_BEE_QUEEN)
+ .setPos(36, 21))
+ .widget(
+ new SlotWidget(new ApiarySlot(inventoryHandler, drone))
+ .setBackground(getGUITextureSet().getItemSlot(), GT_UITextures.OVERLAY_SLOT_BEE_DRONE)
+ .setPos(36, 41))
+ .widget(
+ SlotGroup.ofItemHandler(inventoryHandler, 2)
+ .startFromSlot(7)
+ .endAtSlot(10)
+ .slotCreator(i -> new ApiarySlot(inventoryHandler, i))
+ .applyForWidget(
+ widget -> widget.setGTTooltip(() -> mTooltipCache.getData(UPGRADE_TOOLTIP))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY))
+ .build()
+ .setPos(61, 23));
+
+ super.addUIWidgets(builder, buildContext);
+
+ builder.widget(
+ new ButtonWidget().setOnClick((clickData, widget) -> cancelProcess())
+ .setBackground(GT_UITextures.BUTTON_STANDARD, GT_UITextures.OVERLAY_BUTTON_CROSS)
+ .setGTTooltip(() -> mTooltipCache.getData(CANCEL_PROCESS_TOOLTIP))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setPos(7, 26)
+ .setSize(18, 18))
+ .widget(
+ new CycleButtonWidget().setToggle(() -> mAutoQueen, x -> mAutoQueen = x)
+ .setTextureGetter(
+ i -> i == 0 ? GT_UITextures.OVERLAY_BUTTON_CROSS : GT_UITextures.OVERLAY_BUTTON_CHECKMARK)
+ .setGTTooltip(() -> mTooltipCache.getData(AUTOQUEEN_TOOLTIP))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setPos(7, 44)
+ .setSize(18, 18)
+ .setBackground(GT_UITextures.BUTTON_STANDARD, GT_UITextures.OVERLAY_SLOT_BEE_QUEEN))
+ .widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_INFORMATION)
+ .setGTTooltip(() -> {
+ final String energyreq = GT_Utility.formatNumbers(
+ (int) ((float) GT_MetaTileEntity_IndustrialApiary.baseEUtUsage * getEnergyModifier()
+ * getAcceleration()) + getAdditionalEnergyUsage());
+ final String Temp = StatCollector.translateToLocal(getTemperature().getName());
+ final String Hum = StatCollector.translateToLocal(getHumidity().getName());
+ if (getUsedQueen() != null
+ && BeeManager.beeRoot.isMember(getUsedQueen(), EnumBeeType.QUEEN.ordinal())) {
+ final IBee bee = BeeManager.beeRoot.getMember(getUsedQueen());
+ if (bee.isAnalyzed()) {
+ final IBeeGenome genome = bee.getGenome();
+ final IBeeModifier mod = BeeManager.beeRoot.getBeekeepingMode(getWorld())
+ .getBeeModifier();
+ final float tmod = getTerritoryModifier(null, 1f) * mod.getTerritoryModifier(null, 1f);
+ final int[] t = Arrays.stream(genome.getTerritory())
+ .map(i -> (int) ((float) i * tmod))
+ .toArray();
+ return mTooltipCache.getUncachedTooltipData(
+ INFO_WITH_BEE_TOOLTIP,
+ energyreq,
+ Temp,
+ Hum,
+ genome.getSpeed(),
+ getProductionModifier(null, 0f) + mod.getProductionModifier(null, 0f),
+ Math.round(
+ getFloweringModifier(null, 1f) * genome.getFlowering()
+ * mod.getFloweringModifier(null, 1f)),
+ Math.round(
+ getLifespanModifier(null, null, 1f) * genome.getLifespan()
+ * mod.getLifespanModifier(null, null, 1f)),
+ t[0],
+ t[1],
+ t[2]);
+ }
+ }
+ return mTooltipCache.getUncachedTooltipData(INFO_TOOLTIP, energyreq, Temp, Hum);
+ })
+ .attachSyncer(
+ new FakeSyncWidget.ItemStackSyncer(() -> usedQueen, val -> usedQueen = val),
+ builder,
+ (widget, val) -> widget.notifyTooltipChange())
+ .setPos(163, 5)
+ .setSize(7, 18))
+ .widget(new ButtonWidget().setOnClick((clickData, widget) -> {
+ if (clickData.mouseButton == 0) {
+ if (mLockedSpeed) return;
+ if (!clickData.shift) {
+ mSpeed++;
+ if (mSpeed > getMaxSpeed()) mSpeed = 0;
+ } else {
+ mSpeed--;
+ if (mSpeed < 0) mSpeed = getMaxSpeed();
+ }
+ } else if (clickData.mouseButton == 1) {
+ mLockedSpeed = !mLockedSpeed;
+ if (mLockedSpeed) mSpeed = getMaxSpeed();
+ }
+ })
+ .setGTTooltip(
+ () -> mTooltipCache.getUncachedTooltipData(
+ mLockedSpeed ? SPEED_LOCKED_TOOLTIP : SPEED_TOOLTIP,
+ getAcceleration(),
+ GT_Utility.formatNumbers(getAdditionalEnergyUsage())))
+ .attachSyncer(
+ new FakeSyncWidget.IntegerSyncer(() -> mSpeed, val -> mSpeed = val),
+ builder,
+ (widget, val) -> widget.notifyTooltipChange())
+ .attachSyncer(
+ new FakeSyncWidget.BooleanSyncer(() -> mLockedSpeed, val -> mLockedSpeed = val),
+ builder,
+ (widget, val) -> widget.notifyTooltipChange())
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setBackground(GT_UITextures.PICTURE_SQUARE_LIGHT_GRAY)
+ .setPos(25, 62)
+ .setSize(18, 18))
+ .widget(
+ new TextWidget("x").setDefaultColor(COLOR_TEXT_GRAY.get())
+ .setPos(30, 63))
+ .widget(
+ TextWidget.dynamicString(() -> String.valueOf(1 << mSpeed))
+ // mSpeed is already synced
+ .setSynced(false)
+ .setDefaultColor(COLOR_TEXT_GRAY.get())
+ .setPos(26, 72));
+ }
+
+ private static final FallbackableUITexture progressBarTexture = GT_UITextures
+ .fallbackableProgressbar("iapiary", GT_UITextures.PROGRESSBAR_ARROW);
+
+ @Override
+ protected BasicUIProperties getUIProperties() {
+ return super.getUIProperties().toBuilder()
+ .progressBarTexture(progressBarTexture)
+ .progressBarPos(new Pos2d(70, 3))
+ .build();
+ }
+
+ @Override
+ protected SlotWidget createItemInputSlot(int index, IDrawable[] backgrounds, Pos2d pos) {
+ // we have custom input slots
+ return null;
+ }
+
+ @Override
+ protected CycleButtonWidget createItemAutoOutputButton() {
+ return (CycleButtonWidget) super.createItemAutoOutputButton().setPos(7, 62);
+ }
+
+ @Override
+ protected CycleButtonWidget createFluidAutoOutputButton() {
+ return null;
+ }
+
+ @Override
+ protected SlotWidget createChargerSlot(int x, int y, String tooltipKey, Object[] tooltipArgs) {
+ return (SlotWidget) super.createChargerSlot(x, y, tooltipKey, tooltipArgs).setPos(79, 62);
+ }
+
+ @Override
+ protected DrawableWidget createErrorStatusArea(ModularWindow.Builder builder, IDrawable picture) {
+ return (DrawableWidget) super.createErrorStatusArea(builder, picture).setPos(100, 62)
+ .attachSyncer(
+ new FakeSyncWidget.ListSyncer<>(() -> Arrays.asList(mErrorStates.toArray(new IErrorState[0])), val -> {
+ mErrorStates.clear();
+ mErrorStates.addAll(new HashSet<>(val));
+ },
+ (buffer, val) -> buffer.writeShort(val.getID()),
+ buffer -> ForestryAPI.errorStateRegistry.getErrorState(buffer.readShort())),
+ builder,
+ (widget, val) -> widget.notifyTooltipChange());
+ }
+
+ @Override
+ protected List<String> getErrorDescriptions() {
+ if (!mErrorStates.isEmpty()) {
+ return mErrorStates.stream()
+ .map(state -> EnumChatFormatting.RED + StatCollector.translateToLocal("for." + state.getDescription()))
+ .collect(Collectors.toList());
+ } else if (mStuttering) {
+ return mTooltipCache
+ .getData(STALLED_STUTTERING_TOOLTIP, StatCollector.translateToLocal(POWER_SOURCE_POWER)).text;
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
+ @Override
+ protected List<String> getErrorDescriptionsShift() {
+ // Don't show shift tooltip of "Progress was lost"
+ // as this machine does not lose progress
+ return getErrorDescriptions();
+ }
+
+ private int getAcceleration() {
+ return 1 << mSpeed;
+ }
+
+ private int getAdditionalEnergyUsage() {
+ final int accelerated = getAcceleration();
+ int energyusage = 0;
+ if (accelerated == 2) energyusage = 32;
+ else if (accelerated > 2) energyusage = 32 * accelerated << (mSpeed - 2);
+ return energyusage;
+ }
+
+ private class ApiarySlot extends BaseSlot {
+
+ public ApiarySlot(IItemHandlerModifiable inventory, int index) {
+ super(inventory, index);
+ }
+
+ @Override
+ public boolean isItemValidPhantom(ItemStack stack) {
+ return super.isItemValidPhantom(stack) && getBaseMetaTileEntity().isItemValidForSlot(getSlotIndex(), stack);
+ }
+
+ @Override
+ public void onSlotChanged() {
+ super.onSlotChanged();
+ onInventoryUpdate(getSlotIndex());
+ }
+ }
+
+ private static final UIInfo<?, ?> IndustrialApiaryUI = GT_UIInfos.GTTileEntityUIFactory
+ .apply(GT_ModularUIContainer_IndustrialApiary::new);
+
+ private static class GT_ModularUIContainer_IndustrialApiary extends ModularUIContainer {
+
+ public GT_ModularUIContainer_IndustrialApiary(ModularUIContext context, ModularWindow mainWindow) {
+ super(context, mainWindow);
+ }
+
+ private final int playerInventorySlot = 36;
+
+ @Override
+ public ItemStack slotClick(int aSlotNumber, int aMouseclick, int aShifthold, EntityPlayer aPlayer) {
+ if (!(aSlotNumber >= playerInventorySlot + 2 && aSlotNumber < playerInventorySlot + 2 + 4))
+ return super.slotClick(aSlotNumber, aMouseclick, aShifthold, aPlayer);
+ if (aShifthold == 5) return null;
+ if (aShifthold != 0) return super.slotClick(aSlotNumber, aMouseclick, aShifthold, aPlayer);
+ if (aMouseclick > 1) return super.slotClick(aSlotNumber, aMouseclick, aShifthold, aPlayer);
+ final ItemStack s = aPlayer.inventory.getItemStack();
+ if (s == null) return super.slotClick(aSlotNumber, aMouseclick, aShifthold, aPlayer);
+ final Slot slot = getSlot(aSlotNumber);
+ final ItemStack slotStack = slot.getStack();
+ if (slotStack != null && !GT_Utility.areStacksEqual(slotStack, s)) return null; // super would replace item
+ if (slotStack == null && !slot.isItemValid(s))
+ return super.slotClick(aSlotNumber, aMouseclick, aShifthold, aPlayer);
+ if (!GT_ApiaryUpgrade.isUpgrade(s)) return super.slotClick(aSlotNumber, aMouseclick, aShifthold, aPlayer);
+ int max = GT_ApiaryUpgrade.getUpgrade(s)
+ .getMaxNumber();
+ if (slotStack != null) max = Math.max(0, max - slotStack.stackSize);
+ max = Math.min(max, s.stackSize);
+ if (max == 0) return null;
+ if (aMouseclick == 1) max = 1;
+ if (max == s.stackSize) return super.slotClick(aSlotNumber, aMouseclick, aShifthold, aPlayer);
+ final ItemStack newStack = s.splitStack(s.stackSize - max);
+ final ItemStack result = super.slotClick(aSlotNumber, aMouseclick, aShifthold, aPlayer);
+ aPlayer.inventory.setItemStack(newStack);
+ return result;
+ }
+
+ @Override
+ public ItemStack transferStackInSlot(EntityPlayer aPlayer, int aSlotIndex) {
+ final Slot s = getSlot(aSlotIndex);
+ if (s == null) return super.transferStackInSlot(aPlayer, aSlotIndex);
+ if (aSlotIndex >= playerInventorySlot) return super.transferStackInSlot(aPlayer, aSlotIndex);
+ final ItemStack aStack = s.getStack();
+ if (aStack == null) return super.transferStackInSlot(aPlayer, aSlotIndex);
+ if (!GT_ApiaryUpgrade.isUpgrade(aStack)) return super.transferStackInSlot(aPlayer, aSlotIndex);
+ for (int i = playerInventorySlot + 2; i < playerInventorySlot + 2 + 4; i++) {
+ final Slot iSlot = getSlot(i);
+ final ItemStack iStack = iSlot.getStack();
+ if (iStack == null) {
+ if (!iSlot.isItemValid(aStack)) continue;
+ } else {
+ if (!GT_Utility.areStacksEqual(aStack, iStack)) continue;
+ }
+ int max = GT_ApiaryUpgrade.getUpgrade(aStack)
+ .getMaxNumber();
+ if (iStack == null) {
+ max = Math.min(max, aStack.stackSize);
+ final ItemStack newstack = aStack.splitStack(max);
+ iSlot.putStack(newstack);
+ } else {
+ max = Math.max(0, max - iStack.stackSize);
+ max = Math.min(max, aStack.stackSize);
+ iStack.stackSize += max;
+ aStack.stackSize -= max;
+ iSlot.onSlotChanged();
+ }
+ if (aStack.stackSize == 0) s.putStack(null);
+ else s.onSlotChanged();
+ break;
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ super.getWailaBody(itemStack, currenttip, accessor, config);
+ final NBTTagCompound tag = accessor.getNBTData();
+ if (tag.hasKey("queen")) {
+ currenttip.add(
+ "Current Queen: " + EnumChatFormatting.GREEN + StatCollector.translateToLocal(tag.getString("queen")));
+ }
+ if (tag.hasKey("dummyProduction")) {
+ currenttip.add(
+ "Effective Production: " + EnumChatFormatting.AQUA
+ + String.format("b^0.52 * %.2f", tag.getFloat("dummyProduction")));
+ }
+ if (tag.hasKey("errors")) {
+ NBTTagCompound errorNbt = tag.getCompoundTag("errors");
+ for (int i = 0; i < errorNbt.getInteger("size"); i++) {
+ currenttip.add(
+ "Error: " + EnumChatFormatting.RED
+ + StatCollector.translateToLocal("for." + errorNbt.getString("e" + i)));
+ }
+ }
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ if (usedQueen != null) {
+ IBeeGenome genome = beeRoot.getMember(usedQueen)
+ .getGenome();
+ tag.setString(
+ "queen",
+ genome.getPrimary()
+ .getUnlocalizedName());
+ float prodModifier = getProductionModifier(genome, 0f);
+ prodModifier += beeRoot.getBeekeepingMode(world)
+ .getBeeModifier()
+ .getProductionModifier(genome, prodModifier);
+ float dummyProduction = 100f * Bee.getFinalChance(0.01f, genome.getSpeed(), prodModifier, 8f);
+ tag.setFloat("dummyProduction", dummyProduction);
+ }
+ if (hasErrors()) {
+ NBTTagCompound errorNbt = new NBTTagCompound();
+ int errorCounter = 0;
+ for (IErrorState error : mErrorStates) {
+ errorNbt.setString("e" + errorCounter++, error.getDescription());
+ }
+ errorNbt.setInteger("size", errorCounter);
+ tag.setTag("errors", errorNbt);
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Massfabricator.java b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Massfabricator.java
new file mode 100644
index 0000000000..7eaa8122c1
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Massfabricator.java
@@ -0,0 +1,241 @@
+package gregtech.common.tileentities.machines.basic;
+
+import static gregtech.api.enums.GT_Values.V;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_MASSFAB;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_MASSFAB_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_MASSFAB_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_MASSFAB_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MASSFAB;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MASSFAB_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MASSFAB_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MASSFAB_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_MASSFAB;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_MASSFAB_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_MASSFAB_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_MASSFAB_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_MASSFAB;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_MASSFAB_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_MASSFAB_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_MASSFAB_GLOW;
+
+import java.util.Arrays;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
+import net.minecraftforge.fluids.FluidStack;
+
+import com.google.common.primitives.Ints;
+
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.MachineType;
+import gregtech.api.enums.Materials;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine;
+import gregtech.api.objects.overclockdescriber.EUOverclockDescriber;
+import gregtech.api.objects.overclockdescriber.OverclockDescriber;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_OverclockCalculator;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.MethodsReturnNonnullByDefault;
+import gregtech.common.config.machinestats.ConfigMassFabricator;
+
+public class GT_MetaTileEntity_Massfabricator extends GT_MetaTileEntity_BasicMachine {
+
+ public static int sUUAperUUM = 1;
+ public static int sUUASpeedBonus = 4;
+ public static int sDurationMultiplier = 3215;
+ public static boolean sRequiresUUA = false;
+ public static int BASE_EUT = 256;
+ public static GT_Recipe nonUUARecipe;
+ public static GT_Recipe uuaRecipe;
+
+ public GT_MetaTileEntity_Massfabricator(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 8,
+ MachineType.MATTER_FABRICATOR.tooltipDescription(),
+ 1,
+ 1,
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_SIDE_MASSFAB_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_MASSFAB_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_SIDE_MASSFAB),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_MASSFAB_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_FRONT_MASSFAB_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MASSFAB_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_FRONT_MASSFAB),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MASSFAB_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_TOP_MASSFAB_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_MASSFAB_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_TOP_MASSFAB),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_MASSFAB_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_BOTTOM_MASSFAB_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_MASSFAB_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_BOTTOM_MASSFAB),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_MASSFAB_GLOW)
+ .glow()
+ .build()));
+ }
+
+ public GT_MetaTileEntity_Massfabricator(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 8, aDescription, aTextures, 1, 1);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Massfabricator(this.mName, this.mTier, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public boolean allowSelectCircuit() {
+ return true;
+ }
+
+ @Override
+ protected OverclockDescriber createOverclockDescriber() {
+ return new MassfabricatorOverclockDescriber(mTier, mAmperage);
+ }
+
+ @Override
+ public void onConfigLoad() {
+ super.onConfigLoad();
+ sDurationMultiplier = ConfigMassFabricator.durationMultiplier;
+ sUUAperUUM = ConfigMassFabricator.UUAPerUUM;
+ sUUASpeedBonus = ConfigMassFabricator.UUASpeedBonus;
+ sRequiresUUA = ConfigMassFabricator.requiresUUA;
+ Materials.UUAmplifier.mChemicalFormula = ("Mass Fabricator Eff/Speed Bonus: x" + sUUASpeedBonus);
+ }
+
+ @Override
+ public long maxAmperesIn() {
+ return 10;
+ }
+
+ @Override
+ public long maxEUStore() {
+ return V[mTier] * 512L;
+ }
+
+ @Override
+ public int checkRecipe() {
+ FluidStack tFluid = getDrainableStack();
+ if ((tFluid == null) || (tFluid.amount < getCapacity())) {
+ this.mOutputFluid = Materials.UUMatter.getFluid(1L);
+ calculateCustomOverclock(containsUUA(getFillableStack()) ? uuaRecipe : nonUUARecipe);
+ // In case recipe is too OP for that machine
+ if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1)
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ if (containsUUA(tFluid = getFillableStack())) {
+ tFluid.amount -= sUUAperUUM;
+ return FOUND_AND_SUCCESSFULLY_USED_RECIPE;
+ }
+ return sRequiresUUA || Arrays.stream(getAllInputs())
+ .anyMatch(s -> ItemList.Circuit_Integrated.isStackEqual(s, true, true))
+ ? FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS
+ : FOUND_AND_SUCCESSFULLY_USED_RECIPE;
+ }
+ return DID_NOT_FIND_RECIPE;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.massFabFakeRecipes;
+ }
+
+ @Override
+ public boolean isFluidInputAllowed(FluidStack aFluid) {
+ return aFluid.isFluidEqual(Materials.UUAmplifier.getFluid(1L));
+ }
+
+ @Override
+ public int getCapacity() {
+ return Math.max(sUUAperUUM, 1000);
+ }
+
+ private boolean containsUUA(FluidStack aFluid) {
+ return aFluid != null && aFluid.amount >= sUUAperUUM && aFluid.isFluidEqual(Materials.UUAmplifier.getFluid(1L));
+ }
+
+ @ParametersAreNonnullByDefault
+ @MethodsReturnNonnullByDefault
+ protected class MassfabricatorOverclockDescriber extends EUOverclockDescriber {
+
+ protected MassfabricatorOverclockDescriber(byte tier, int amperage) {
+ super(tier, amperage);
+ }
+
+ @Override
+ public GT_OverclockCalculator createCalculator(GT_OverclockCalculator template, GT_Recipe recipe) {
+ return super.createCalculator(template, recipe).setEUt(Ints.saturatedCast(V[tier] * amperage))
+ .setEUtIncreasePerOC(2.0)
+ .limitOverclockCount(tier - 1)
+ .setOneTickDiscount(false);
+ }
+
+ @Override
+ protected boolean shouldShowAmperage(GT_OverclockCalculator calculator) {
+ return true;
+ }
+
+ @Override
+ protected String getVoltageString(GT_OverclockCalculator calculator) {
+ // standard amperage calculation doesn't work here
+ return decorateWithOverclockLabel(GT_Utility.formatNumbers(V[mTier]) + " EU/t", calculator)
+ + GT_Utility.getTierNameWithParentheses(V[mTier]);
+ }
+
+ @Override
+ protected String getAmperageString(GT_OverclockCalculator calculator) {
+ int amperage = this.amperage;
+ int denominator = 1;
+ for (int i = 1; i < mTier; i++) {
+ amperage >>= 1;
+ if (amperage == 0) {
+ denominator <<= 1;
+ }
+ }
+ if (amperage > 0) {
+ return GT_Utility.formatNumbers(amperage);
+ } else {
+ return "1/" + denominator;
+ }
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_MicrowaveEnergyTransmitter.java b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_MicrowaveEnergyTransmitter.java
new file mode 100644
index 0000000000..575e98c5c6
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_MicrowaveEnergyTransmitter.java
@@ -0,0 +1,495 @@
+package gregtech.common.tileentities.machines.basic;
+
+import static gregtech.api.enums.GT_Values.V;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASINGS;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TELEPORTER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TELEPORTER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TELEPORTER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TELEPORTER_GLOW;
+
+import java.util.function.Consumer;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.world.World;
+import net.minecraftforge.common.DimensionManager;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import com.gtnewhorizons.modularui.api.NumberFormatMUI;
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Materials;
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.gui.modularui.GUITextureSet;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.modularui.IAddGregtechLogo;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IEnergyConnected;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.BaseMetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicTank;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.config.machinestats.ConfigMicrowaveEnergyTransmitter;
+import gregtech.common.config.machinestats.ConfigTeleporter;
+
+public class GT_MetaTileEntity_MicrowaveEnergyTransmitter extends GT_MetaTileEntity_BasicTank
+ implements IAddGregtechLogo, IAddUIWidgets {
+
+ private static boolean sInterDimensionalTeleportAllowed = true;
+ private static int mMaxLoss = 50;
+ private static int mMaxLossDistance = 10000;
+ private static boolean mPassiveEnergyUse = true;
+ public int mTargetX = 0;
+ public int mTargetY = 0;
+ public int mTargetZ = 0;
+ public int mTargetD = 0;
+ public boolean mDebug = false;
+ public boolean hasBlock = false;
+ public int tTargetX = 0;
+ public int tTargetY = 0;
+ public int tTargetZ = 0;
+ public int tTargetD = 0;
+ public TileEntity tTile = null;
+
+ public GT_MetaTileEntity_MicrowaveEnergyTransmitter(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 3,
+ new String[] { "Transmits Energy Wirelessly", "Use Nitrogen Plasma", "for Inter-dimensional transmission",
+ "0.004EU Loss per 100 Blocks" });
+ }
+
+ public GT_MetaTileEntity_MicrowaveEnergyTransmitter(String aName, int aTier, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, 3, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_MicrowaveEnergyTransmitter(String aName, int aTier, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, 3, aDescription, aTextures);
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ if (aBaseMetaTileEntity.isClientSide()) return true;
+ this.hasBlock = checkForBlock();
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_MicrowaveEnergyTransmitter(
+ this.mName,
+ this.mTier,
+ this.mDescriptionArray,
+ this.mTextures);
+ }
+
+ @Override
+ public String[] getInfoData() {
+ return new String[] { "Coordinates:",
+ "X: " + EnumChatFormatting.GREEN + GT_Utility.formatNumbers(this.mTargetX) + EnumChatFormatting.RESET,
+ "Y: " + EnumChatFormatting.GREEN + GT_Utility.formatNumbers(this.mTargetY) + EnumChatFormatting.RESET,
+ "Z: " + EnumChatFormatting.GREEN + GT_Utility.formatNumbers(this.mTargetZ) + EnumChatFormatting.RESET,
+ "Dimension: " + EnumChatFormatting.GREEN + this.mTargetD + EnumChatFormatting.RESET,
+ "Dimension Valid: " + (GT_Utility.isRealDimension(this.mTargetD)
+ ? EnumChatFormatting.GREEN + "Yes" + EnumChatFormatting.RESET
+ : EnumChatFormatting.RED + "No" + EnumChatFormatting.RESET),
+ "Dimension Registered: " + (DimensionManager.isDimensionRegistered(this.mTargetD)
+ ? EnumChatFormatting.GREEN + "Yes" + EnumChatFormatting.RESET
+ : EnumChatFormatting.RED + "No" + EnumChatFormatting.RESET) };
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side == ForgeDirection.DOWN) return new ITexture[] { MACHINE_CASINGS[mTier][colorIndex + 1] };
+ if (aActive) return new ITexture[] { MACHINE_CASINGS[mTier][colorIndex + 1],
+ TextureFactory.of(OVERLAY_TELEPORTER_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_TELEPORTER_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ return new ITexture[] { MACHINE_CASINGS[mTier][colorIndex + 1], TextureFactory.of(OVERLAY_TELEPORTER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TELEPORTER_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ if (mFluid != null) aNBT.setTag("mFluid", mFluid.writeToNBT(new NBTTagCompound()));
+ aNBT.setInteger("mTargetX", this.mTargetX);
+ aNBT.setInteger("mTargetY", this.mTargetY);
+ aNBT.setInteger("mTargetZ", this.mTargetZ);
+ aNBT.setInteger("mTargetD", this.mTargetD);
+ aNBT.setBoolean("mDebug", this.mDebug);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ mFluid = FluidStack.loadFluidStackFromNBT(aNBT.getCompoundTag("mFluid"));
+ this.mTargetX = aNBT.getInteger("mTargetX");
+ this.mTargetY = aNBT.getInteger("mTargetY");
+ this.mTargetZ = aNBT.getInteger("mTargetZ");
+ this.mTargetD = aNBT.getInteger("mTargetD");
+ this.mDebug = aNBT.getBoolean("mDebug");
+ }
+
+ @Override
+ public void onConfigLoad() {
+ sInterDimensionalTeleportAllowed = ConfigTeleporter.interDimensionalTPAllowed;
+ mMaxLoss = Math.max(ConfigMicrowaveEnergyTransmitter.maxLoss, 11);
+ mMaxLossDistance = ConfigMicrowaveEnergyTransmitter.maxLossDistance;
+ mPassiveEnergyUse = ConfigMicrowaveEnergyTransmitter.passiveEnergyUse;
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ if (aBaseMetaTileEntity.isServerSide()) {
+ if ((this.mTargetX == 0) && (this.mTargetY == 0) && (this.mTargetZ == 0) && (this.mTargetD == 0)) {
+ this.mTargetX = aBaseMetaTileEntity.getXCoord();
+ this.mTargetY = aBaseMetaTileEntity.getYCoord();
+ this.mTargetZ = aBaseMetaTileEntity.getZCoord();
+ this.mTargetD = aBaseMetaTileEntity.getWorld().provider.dimensionId;
+ }
+ this.hasBlock = checkForBlock();
+ }
+ }
+
+ public boolean checkForBlock() {
+ for (byte i = -5; i <= 5; i = (byte) (i + 1)) {
+ for (byte j = -5; j <= 5; j = (byte) (j + 1)) {
+ for (byte k = -5; k <= 5; k = (byte) (k + 1)) {
+ if (getBaseMetaTileEntity().getBlockOffset(i, j, k) == GregTech_API.sBlockMetal5
+ && getBaseMetaTileEntity().getMetaIDOffset(i, j, k) == 8) { // require osmiridium block
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean hasDimensionalTeleportCapability() {
+ return this.mDebug || (sInterDimensionalTeleportAllowed && (this.hasBlock
+ || mFluid != null && mFluid.isFluidEqual(Materials.Nitrogen.getPlasma(1)) && mFluid.amount >= 1000));
+ }
+
+ public boolean isDimensionalTeleportAvailable() {
+ return this.mDebug || (hasDimensionalTeleportCapability() && GT_Utility.isRealDimension(this.mTargetD)
+ && GT_Utility.isRealDimension(getBaseMetaTileEntity().getWorld().provider.dimensionId));
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (mFluid == null) {
+ mFluid = Materials.Nitrogen.getPlasma(0);
+ }
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ if (getBaseMetaTileEntity().isServerSide()) {
+ if (getBaseMetaTileEntity().getTimer() % 100L == 50L) {
+ this.hasBlock = checkForBlock();
+ }
+ if ((getBaseMetaTileEntity().isAllowedToWork()) && (getBaseMetaTileEntity().getRedstone())) {
+ if (getBaseMetaTileEntity().getStoredEU() > (V[mTier] * 16)) {
+ if (mPassiveEnergyUse) {
+ getBaseMetaTileEntity().decreaseStoredEnergyUnits(2L << (mTier - 1), false);
+ }
+ if (hasDimensionalTeleportCapability()
+ && this.mTargetD != getBaseMetaTileEntity().getWorld().provider.dimensionId
+ && mFluid.isFluidEqual(Materials.Nitrogen.getPlasma(1))) {
+ mFluid.amount--;
+ if (mFluid.amount < 1) {
+ mFluid = null;
+ }
+ }
+ if (tTargetD != mTargetD || tTargetX != mTargetX || tTargetY != mTargetY || tTargetZ != mTargetZ) {
+ tTargetD = mTargetD;
+ tTargetX = mTargetX;
+ tTargetY = mTargetY;
+ tTargetZ = mTargetZ;
+ if (this.mTargetD == getBaseMetaTileEntity().getWorld().provider.dimensionId) {
+ tTile = getBaseMetaTileEntity().getTileEntity(this.mTargetX, this.mTargetY, this.mTargetZ);
+ } else {
+ World tWorld = DimensionManager.getWorld(this.mTargetD);
+ if (tWorld != null) {
+ tTile = tWorld.getTileEntity(this.mTargetX, this.mTargetY, this.mTargetZ);
+ }
+ }
+ }
+ int tDistance = distanceCalculation();
+ if (tTile != null) {
+ if (tTile instanceof IEnergyConnected) {
+ long packetSize = V[mTier];
+ if (tTile instanceof IGregTechTileEntity) {
+ IMetaTileEntity mte = ((IGregTechTileEntity) tTile).getMetaTileEntity();
+ if (mte instanceof BaseMetaTileEntity) {
+ packetSize = ((BaseMetaTileEntity) mte).getMaxSafeInput();
+ }
+ }
+ long energyUse = 10;
+ if (mMaxLossDistance != 0) {
+ energyUse = GT_Utility
+ .safeInt(10L + (tDistance * Math.max(mMaxLoss - 10L, 0) / mMaxLossDistance));
+ }
+ energyUse = packetSize + ((V[mTier] * energyUse) / 100);
+ if (getBaseMetaTileEntity().isUniversalEnergyStored(energyUse)) {
+ if (((IEnergyConnected) tTile).injectEnergyUnits(ForgeDirection.UNKNOWN, packetSize, 1)
+ > 0) {
+ getBaseMetaTileEntity().decreaseStoredEnergyUnits(energyUse, false);
+ }
+ }
+ }
+ }
+ }
+ getBaseMetaTileEntity().setActive(true);
+ } else {
+ getBaseMetaTileEntity().setActive(false);
+ }
+ }
+ }
+
+ private int distanceCalculation() {
+ return Math.abs(
+ ((this.mTargetD != getBaseMetaTileEntity().getWorld().provider.dimensionId)
+ && (isDimensionalTeleportAvailable()) ? 100 : 1)
+ * (int) Math.sqrt(
+ Math.pow(getBaseMetaTileEntity().getXCoord() - this.mTargetX, 2.0D)
+ + Math.pow(getBaseMetaTileEntity().getYCoord() - this.mTargetY, 2.0D)
+ + Math.pow(getBaseMetaTileEntity().getZCoord() - this.mTargetZ, 2.0D)));
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return false;
+ }
+
+ @Override
+ public boolean isOverclockerUpgradable() {
+ return false;
+ }
+
+ @Override
+ public boolean isTransformerUpgradable() {
+ return false;
+ }
+
+ @Override
+ public boolean isElectric() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnetInput() {
+ return true;
+ }
+
+ @Override
+ public boolean isInputFacing(ForgeDirection side) {
+ return true;
+ }
+
+ @Override
+ public boolean isOutputFacing(ForgeDirection side) {
+ return false;
+ }
+
+ @Override
+ public boolean isTeleporterCompatible() {
+ return false;
+ }
+
+ @Override
+ public long getMinimumStoredEU() {
+ return V[mTier] * 16;
+ }
+
+ @Override
+ public long maxEUStore() {
+ return V[mTier] * 256;
+ }
+
+ @Override
+ public long maxEUInput() {
+ return V[mTier];
+ }
+
+ @Override
+ public long maxSteamStore() {
+ return maxEUStore();
+ }
+
+ @Override
+ public long maxAmperesIn() {
+ return 3;
+ }
+
+ @Override
+ public int getStackDisplaySlot() {
+ return 2;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public int getInputSlot() {
+ return 0;
+ }
+
+ @Override
+ public int getOutputSlot() {
+ return 0;
+ }
+
+ @Override
+ public int getCapacity() {
+ return 64000;
+ }
+
+ @Override
+ public boolean doesFillContainers() {
+ return false;
+ }
+
+ @Override
+ public boolean doesEmptyContainers() {
+ return false;
+ }
+
+ @Override
+ public boolean canTankBeFilled() {
+ return true;
+ }
+
+ @Override
+ public boolean canTankBeEmptied() {
+ return true;
+ }
+
+ @Override
+ public boolean displaysItemStack() {
+ return false;
+ }
+
+ @Override
+ public boolean displaysStackSize() {
+ return false;
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ return null;
+ }
+
+ protected static final NumberFormatMUI numberFormat = new NumberFormatMUI();
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ builder.widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_SCREEN_BLACK)
+ .setSize(90, 72)
+ .setPos(43, 4))
+ .widget(
+ new TextWidget().setStringSupplier(() -> "X: " + numberFormat.format(mTargetX))
+ .setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setPos(46, 8))
+ .widget(
+ new TextWidget().setStringSupplier(() -> "Y: " + numberFormat.format(mTargetY))
+ .setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setPos(46, 16))
+ .widget(
+ new TextWidget().setStringSupplier(() -> "Z: " + numberFormat.format(mTargetZ))
+ .setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setPos(46, 24))
+ .widget(
+ new TextWidget().setStringSupplier(() -> "Dim: " + numberFormat.format(mTargetD))
+ .setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setPos(46, 32))
+ .widget(
+ TextWidget.dynamicString(() -> "Dim Valid: " + (GT_Utility.isRealDimension(mTargetD) ? "Yes" : "No"))
+ .setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setEnabled(widget -> hasDimensionalTeleportCapability())
+ .setPos(46, 40))
+ .widget(new FakeSyncWidget.FluidStackSyncer(() -> mFluid, val -> mFluid = val));
+
+ addChangeNumberButtons(builder, GT_UITextures.OVERLAY_BUTTON_MINUS_LARGE, -512, -64, 7);
+ addChangeNumberButtons(builder, GT_UITextures.OVERLAY_BUTTON_MINUS_SMALL, -16, -1, 25);
+ addChangeNumberButtons(builder, GT_UITextures.OVERLAY_BUTTON_PLUS_SMALL, 16, 1, 133);
+ addChangeNumberButtons(builder, GT_UITextures.OVERLAY_BUTTON_PLUS_LARGE, 512, 64, 151);
+
+ addChangeNumberButton(
+ builder,
+ GT_UITextures.OVERLAY_BUTTON_MINUS_LARGE,
+ val -> mTargetD += val,
+ -16,
+ -8,
+ 7,
+ 58);
+ addChangeNumberButton(
+ builder,
+ GT_UITextures.OVERLAY_BUTTON_MINUS_SMALL,
+ val -> mTargetD += val,
+ -4,
+ -1,
+ 25,
+ 58);
+ addChangeNumberButton(builder, GT_UITextures.OVERLAY_BUTTON_PLUS_SMALL, val -> mTargetD += val, 4, 1, 133, 58);
+ addChangeNumberButton(builder, GT_UITextures.OVERLAY_BUTTON_PLUS_LARGE, val -> mTargetD += val, 16, 8, 151, 58);
+ }
+
+ private void addChangeNumberButtons(ModularWindow.Builder builder, IDrawable overlay, int addNumberShift,
+ int addNumber, int xPos) {
+ addChangeNumberButton(builder, overlay, val -> mTargetX += val, addNumberShift, addNumber, xPos, 4);
+ addChangeNumberButton(builder, overlay, val -> mTargetY += val, addNumberShift, addNumber, xPos, 22);
+ addChangeNumberButton(builder, overlay, val -> mTargetZ += val, addNumberShift, addNumber, xPos, 40);
+ }
+
+ private void addChangeNumberButton(ModularWindow.Builder builder, IDrawable overlay, Consumer<Integer> setter,
+ int addNumberShift, int addNumber, int xPos, int yPos) {
+ builder.widget(
+ new ButtonWidget()
+ .setOnClick((clickData, widget) -> setter.accept(clickData.shift ? addNumberShift : addNumber))
+ .setBackground(GT_UITextures.BUTTON_STANDARD, overlay)
+ .setSize(18, 18)
+ .setPos(xPos, yPos));
+ }
+
+ @Override
+ public GUITextureSet getGUITextureSet() {
+ return new GUITextureSet().setGregTechLogo(GT_UITextures.PICTURE_GT_LOGO_17x17_TRANSPARENT_GRAY);
+ }
+
+ @Override
+ public void addGregTechLogo(ModularWindow.Builder builder) {
+ builder.widget(
+ new DrawableWidget().setDrawable(getGUITextureSet().getGregTechLogo())
+ .setSize(17, 17)
+ .setPos(113, 56));
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Miner.java b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Miner.java
new file mode 100644
index 0000000000..b1ce994fb3
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Miner.java
@@ -0,0 +1,407 @@
+package gregtech.common.tileentities.machines.basic;
+
+import static gregtech.api.enums.GT_Values.V;
+import static gregtech.api.enums.GT_Values.debugBlockMiner;
+
+import java.util.ArrayList;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.ChunkPosition;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizons.modularui.api.drawable.FallbackableUITexture;
+
+import gregtech.api.enums.Textures;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine;
+import gregtech.api.recipe.BasicUIProperties;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Log;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.blocks.GT_Block_Ores_Abstract;
+import gregtech.common.blocks.GT_TileEntity_Ores;
+import gregtech.common.misc.GT_DrillingLogicDelegate;
+import gregtech.common.misc.GT_IDrillingLogicDelegateOwner;
+
+public class GT_MetaTileEntity_Miner extends GT_MetaTileEntity_BasicMachine
+ implements GT_IDrillingLogicDelegateOwner, IAddUIWidgets {
+
+ static final int[] RADIUS = { 8, 8, 16, 24, 32 }; // Miner radius per tier
+ static final int[] SPEED = { 160, 160, 80, 40, 20 }; // Miner cycle time per tier
+ static final int[] ENERGY = { 8, 8, 32, 128, 512 }; // Miner energy consumption per tier
+
+ /** Miner configured radius */
+ private int radiusConfig;
+ /** Found ore blocks cache of current drill depth */
+ private final ArrayList<ChunkPosition> oreBlockPositions = new ArrayList<>();
+
+ /** General pipe accessor */
+ private final GT_DrillingLogicDelegate pipe = new GT_DrillingLogicDelegate(this);
+
+ private final int mSpeed;
+
+ public GT_MetaTileEntity_Miner(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 1,
+ new String[] { "Digging ore instead of you", "Use Screwdriver to regulate work area",
+ "Use Soft Mallet to disable and retract the pipe",
+ String.format("%d EU/t, %d sec per block, no stuttering", ENERGY[aTier], SPEED[aTier] / 20),
+ String.format("Maximum work area %dx%d", (RADIUS[aTier] * 2 + 1), (RADIUS[aTier] * 2 + 1)),
+ String.format("Fortune bonus of %d", aTier) },
+ 2,
+ 2,
+ TextureFactory.of(
+ TextureFactory.of(new Textures.BlockIcons.CustomIcon("basicmachines/miner/OVERLAY_SIDE_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(new Textures.BlockIcons.CustomIcon("basicmachines/miner/OVERLAY_SIDE_ACTIVE_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(new Textures.BlockIcons.CustomIcon("basicmachines/miner/OVERLAY_SIDE")),
+ TextureFactory.builder()
+ .addIcon(new Textures.BlockIcons.CustomIcon("basicmachines/miner/OVERLAY_SIDE_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(new Textures.BlockIcons.CustomIcon("basicmachines/miner/OVERLAY_FRONT_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(new Textures.BlockIcons.CustomIcon("basicmachines/miner/OVERLAY_FRONT_ACTIVE_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(new Textures.BlockIcons.CustomIcon("basicmachines/miner/OVERLAY_FRONT")),
+ TextureFactory.builder()
+ .addIcon(new Textures.BlockIcons.CustomIcon("basicmachines/miner/OVERLAY_FRONT_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(new Textures.BlockIcons.CustomIcon("basicmachines/miner/OVERLAY_TOP_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(new Textures.BlockIcons.CustomIcon("basicmachines/miner/OVERLAY_TOP_ACTIVE_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(new Textures.BlockIcons.CustomIcon("basicmachines/miner/OVERLAY_TOP")),
+ TextureFactory.builder()
+ .addIcon(new Textures.BlockIcons.CustomIcon("basicmachines/miner/OVERLAY_TOP_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(new Textures.BlockIcons.CustomIcon("basicmachines/miner/OVERLAY_BOTTOM_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(new Textures.BlockIcons.CustomIcon("basicmachines/miner/OVERLAY_BOTTOM_ACTIVE_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(new Textures.BlockIcons.CustomIcon("basicmachines/miner/OVERLAY_BOTTOM")),
+ TextureFactory.builder()
+ .addIcon(new Textures.BlockIcons.CustomIcon("basicmachines/miner/OVERLAY_BOTTOM_GLOW"))
+ .glow()
+ .build()));
+ mSpeed = SPEED[aTier];
+ radiusConfig = RADIUS[mTier];
+ }
+
+ public GT_MetaTileEntity_Miner(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 1, aDescription, aTextures, 2, 2);
+ mSpeed = SPEED[aTier];
+ radiusConfig = RADIUS[mTier];
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Miner(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ pipe.findTipDepth();
+ fillOreList(aBaseMetaTileEntity);
+ }
+
+ @Override
+ protected boolean allowPutStackValidated(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return super.allowPutStackValidated(aBaseMetaTileEntity, aIndex, side, aStack) //
+ && aStack.getItem() == GT_DrillingLogicDelegate.MINING_PIPE_STACK.getItem();
+ }
+
+ /** Both output slots must be free to work */
+ public boolean hasFreeSpace() {
+ for (int i = getOutputSlot(); i < getOutputSlot() + 2; i++) {
+ if (mInventory[i] != null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ super.onScrewdriverRightClick(side, aPlayer, aX, aY, aZ);
+ if (side != getBaseMetaTileEntity().getFrontFacing() && side != mMainFacing) {
+ if (aPlayer.isSneaking()) {
+ if (radiusConfig >= 0) {
+ radiusConfig--;
+ }
+ if (radiusConfig < 0) {
+ radiusConfig = RADIUS[mTier];
+ }
+ } else {
+ if (radiusConfig <= RADIUS[mTier]) {
+ radiusConfig++;
+ }
+ if (radiusConfig > RADIUS[mTier]) {
+ radiusConfig = 0;
+ }
+ }
+
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ String.format(
+ "%s %dx%d",
+ StatCollector.translateToLocal("GT5U.machines.workareaset"),
+ (radiusConfig * 2 + 1),
+ (radiusConfig * 2 + 1)));
+
+ // Rebuild ore cache after change config
+ fillOreList(getBaseMetaTileEntity());
+ }
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+
+ if (!aBaseMetaTileEntity.isServerSide()) {
+ return;
+ }
+
+ // Pipe workaround
+ pipe.onOwnerPostTick(aBaseMetaTileEntity, aTick);
+
+ if (!aBaseMetaTileEntity.isAllowedToWork()) {
+ mMaxProgresstime = 0;
+ if (debugBlockMiner) {
+ GT_Log.out.println("MINER: Disabled");
+ }
+ return;
+ }
+
+ if (!hasFreeSpace()) {
+ mMaxProgresstime = 0;
+ if (debugBlockMiner) {
+ GT_Log.out.println("MINER: No free space");
+ }
+ return;
+ }
+
+ if (!aBaseMetaTileEntity.isUniversalEnergyStored((long) ENERGY[mTier] * (mSpeed - mProgresstime))) {
+ mMaxProgresstime = 0;
+ if (debugBlockMiner) {
+ GT_Log.out.println(
+ "MINER: Not enough energy yet, want " + (ENERGY[mTier] * mSpeed)
+ + " have "
+ + aBaseMetaTileEntity.getUniversalEnergyStored());
+ }
+ return;
+ }
+
+ /* Checks if machine are waiting new mining pipe item */
+ if (!pipe.canContinueDrilling(aTick)) {
+ mMaxProgresstime = 0;
+ return;
+ }
+
+ mMaxProgresstime = mSpeed;
+
+ aBaseMetaTileEntity.decreaseStoredEnergyUnits(ENERGY[mTier], true);
+
+ // Real working only when progress done. TODO some legacy code... refactorings needed
+ if (mProgresstime == mSpeed - 1) {
+ if (pipe.getTipDepth() == 0 || oreBlockPositions.isEmpty()) {
+ boolean descends = pipe.descent(aBaseMetaTileEntity);
+ if (descends) {
+ fillOreList(aBaseMetaTileEntity);
+ }
+ } else {
+ int x;
+ int y;
+ int z;
+ Block oreBlock;
+ boolean isOre;
+ do {
+ ChunkPosition oreBlockPos = oreBlockPositions.remove(0);
+ oreBlock = aBaseMetaTileEntity
+ .getBlockOffset(oreBlockPos.chunkPosX, oreBlockPos.chunkPosY, oreBlockPos.chunkPosZ);
+ x = aBaseMetaTileEntity.getXCoord() + oreBlockPos.chunkPosX;
+ y = aBaseMetaTileEntity.getYCoord() + oreBlockPos.chunkPosY;
+ z = aBaseMetaTileEntity.getZCoord() + oreBlockPos.chunkPosZ;
+ isOre = GT_Utility.isOre(
+ oreBlock,
+ aBaseMetaTileEntity.getWorld()
+ .getBlockMetadata(x, y, z));
+ } // someone else might have removed the block
+ while (!isOre && !oreBlockPositions.isEmpty());
+
+ if (isOre) {
+ pipe.mineBlock(aBaseMetaTileEntity, oreBlock, x, y, z);
+ }
+ }
+ }
+ }
+
+ /** Finds the ores in current drill Y level */
+ private void fillOreList(IGregTechTileEntity aBaseMetaTileEntity) {
+ if (pipe.getTipDepth() == 0) {
+ return;
+ }
+ oreBlockPositions.clear();
+ for (int z = -radiusConfig; z <= radiusConfig; ++z) {
+ for (int x = -radiusConfig; x <= radiusConfig; ++x) {
+ Block block = aBaseMetaTileEntity.getBlockOffset(x, pipe.getTipDepth(), z);
+ int blockMeta = aBaseMetaTileEntity.getMetaIDOffset(x, pipe.getTipDepth(), z);
+
+ // todo some weird checks. refactorings needed
+ if (block instanceof GT_Block_Ores_Abstract) {
+ TileEntity oreEntity = aBaseMetaTileEntity.getTileEntityOffset(x, pipe.getTipDepth(), z);
+ if (oreEntity instanceof GT_TileEntity_Ores && ((GT_TileEntity_Ores) oreEntity).mNatural) {
+ oreBlockPositions.add(new ChunkPosition(x, pipe.getTipDepth(), z));
+ }
+ } else if (GT_Utility.isOre(block, blockMeta)) {
+ oreBlockPositions.add(new ChunkPosition(x, pipe.getTipDepth(), z));
+ }
+ }
+ }
+ }
+
+ /** Pulls (or check can pull) items from an input slots. */
+ @Override
+ public boolean pullInputs(Item item, int count, boolean simulate) {
+ for (int i = 0; i < mInputSlotCount; i++) {
+ ItemStack stack = getInputAt(i);
+ if (stack != null && stack.getItem() == item && stack.stackSize >= count) {
+ if (simulate) {
+ return true;
+ }
+ stack.stackSize -= count;
+ if (stack.stackSize == 0) {
+ mInventory[getInputSlot() + i] = null;
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Pushes (or check can push) item to output slots. */
+ @Override
+ public boolean pushOutputs(ItemStack stack, int count, boolean simulate, boolean allowInputSlots) {
+ return allowInputSlots && pushOutput(getInputSlot(), getInputSlot() + mInputSlotCount, stack, count, simulate)
+ || pushOutput(getOutputSlot(), getOutputSlot() + mOutputItems.length, stack, count, simulate);
+ }
+
+ private boolean pushOutput(int startIndex, int endIndex, ItemStack stack, int count, boolean simulate) {
+ for (int i = startIndex; i < endIndex; i++) {
+ ItemStack slot = mInventory[i];
+ if (slot == null || slot.stackSize == 0) {
+ if (!simulate) {
+ ItemStack copy = stack.copy();
+ copy.stackSize = count;
+ mInventory[i] = copy;
+ }
+ return true;
+ } else if (GT_Utility.areStacksEqual(slot, stack) && slot.stackSize <= slot.getMaxStackSize() - count) {
+ if (!simulate) {
+ slot.stackSize += count;
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public long maxEUStore() {
+ return Math.max(V[mTier] * 64, 4096);
+ }
+
+ @Override
+ public void setItemNBT(NBTTagCompound aNBT) {
+ super.setItemNBT(aNBT);
+ aNBT.setInteger("radiusConfig", radiusConfig);
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setInteger("radiusConfig", radiusConfig);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ if (aNBT.hasKey("radiusConfig")) {
+ int newRadius = aNBT.getInteger("radiusConfig");
+ if (RADIUS[mTier] <= newRadius && newRadius > 0) {
+ radiusConfig = newRadius;
+ }
+ }
+ }
+
+ @Override
+ public String[] getInfoData() {
+ return new String[] {
+ String.format(
+ "%s%s%s",
+ EnumChatFormatting.BLUE,
+ StatCollector.translateToLocal("GT5U.machines.miner"),
+ EnumChatFormatting.RESET),
+ String.format(
+ "%s: %s%d%s %s",
+ StatCollector.translateToLocal("GT5U.machines.workarea"),
+ EnumChatFormatting.GREEN,
+ (radiusConfig * 2 + 1),
+ EnumChatFormatting.RESET,
+ StatCollector.translateToLocal("GT5U.machines.blocks")) };
+ }
+
+ @Override
+ public int getMachineTier() {
+ return mTier;
+ }
+
+ @Override
+ public int getMachineSpeed() {
+ return mSpeed;
+ }
+
+ public GT_DrillingLogicDelegate getPipe() {
+ return pipe;
+ }
+
+ private static final FallbackableUITexture progressBarTexture = GT_UITextures
+ .fallbackableProgressbar("miner", GT_UITextures.PROGRESSBAR_CANNER);
+
+ @Override
+ protected BasicUIProperties getUIProperties() {
+ return super.getUIProperties().toBuilder()
+ .progressBarTexture(progressBarTexture)
+ .build();
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_MonsterRepellent.java b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_MonsterRepellent.java
new file mode 100644
index 0000000000..fde78cfa2e
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_MonsterRepellent.java
@@ -0,0 +1,182 @@
+package gregtech.common.tileentities.machines.basic;
+
+import static gregtech.api.enums.GT_Values.V;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASINGS;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TELEPORTER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TELEPORTER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TELEPORTER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TELEPORTER_GLOW;
+
+import java.util.Arrays;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_TieredMachineBlock;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_SpawnEventHandler;
+
+public class GT_MetaTileEntity_MonsterRepellent extends GT_MetaTileEntity_TieredMachineBlock {
+
+ public int mRange = 16;
+
+ public GT_MetaTileEntity_MonsterRepellent(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 0,
+ "Repels nasty Creatures. Range: " + (4 + (12 * aTier))
+ + " unpowered / "
+ + (16 + (48 * aTier))
+ + " powered. Costs "
+ + (1L << (aTier * 2))
+ + " EU/t");
+ }
+
+ public GT_MetaTileEntity_MonsterRepellent(String aName, int aTier, int aInvSlotCount, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_MonsterRepellent(String aName, int aTier, int aInvSlotCount, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_MonsterRepellent(
+ this.mName,
+ this.mTier,
+ this.mInventory.length,
+ this.mDescriptionArray,
+ this.mTextures);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ if (sideDirection != ForgeDirection.UP) return new ITexture[] { MACHINE_CASINGS[mTier][colorIndex + 1] };
+ if (active) return new ITexture[] { MACHINE_CASINGS[mTier][colorIndex + 1],
+ TextureFactory.of(OVERLAY_TELEPORTER_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_TELEPORTER_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ return new ITexture[] { MACHINE_CASINGS[mTier][colorIndex + 1], TextureFactory.of(OVERLAY_TELEPORTER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TELEPORTER_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) {
+ if (aBaseMetaTileEntity.isAllowedToWork() && aBaseMetaTileEntity.isServerSide()) {
+ int[] tCoords = { aBaseMetaTileEntity.getXCoord(), aBaseMetaTileEntity.getYCoord(),
+ aBaseMetaTileEntity.getZCoord(), aBaseMetaTileEntity.getWorld().provider.dimensionId };
+ if ((aTimer % 600 == 0) && !GT_SpawnEventHandler.mobReps.contains(tCoords)) {
+ GT_SpawnEventHandler.mobReps.add(tCoords);
+ }
+ if (aBaseMetaTileEntity.isUniversalEnergyStored(getMinimumStoredEU())
+ && aBaseMetaTileEntity.decreaseStoredEnergyUnits(1L << (this.mTier * 2), false)) {
+ mRange = GT_SpawnEventHandler.getPoweredRepellentRange(mTier);
+ } else {
+ mRange = GT_SpawnEventHandler.getUnpoweredRepellentRange(mTier);
+ }
+ }
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ int[] tCoords = { aBaseMetaTileEntity.getXCoord(), aBaseMetaTileEntity.getYCoord(),
+ aBaseMetaTileEntity.getZCoord(), aBaseMetaTileEntity.getWorld().provider.dimensionId };
+ GT_SpawnEventHandler.mobReps.add(tCoords);
+ }
+
+ @Override
+ public void onRemoval() {
+ int[] tCoords = { this.getBaseMetaTileEntity()
+ .getXCoord(),
+ this.getBaseMetaTileEntity()
+ .getYCoord(),
+ this.getBaseMetaTileEntity()
+ .getZCoord(),
+ this.getBaseMetaTileEntity()
+ .getWorld().provider.dimensionId };
+ GT_SpawnEventHandler.mobReps.removeIf(coords -> Arrays.equals(coords, tCoords));
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return false;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnetInput() {
+ return true;
+ }
+
+ @Override
+ public boolean isInputFacing(ForgeDirection side) {
+ return true;
+ }
+
+ @Override
+ public boolean isTeleporterCompatible() {
+ return false;
+ }
+
+ @Override
+ public long getMinimumStoredEU() {
+ return 512L;
+ }
+
+ @Override
+ public long maxEUStore() {
+ return 512L + V[mTier] * 50;
+ }
+
+ @Override
+ public long maxEUInput() {
+ return V[mTier];
+ }
+
+ @Override
+ public long maxAmperesIn() {
+ return 2;
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ return null;
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {}
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {}
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_PotionBrewer.java b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_PotionBrewer.java
new file mode 100644
index 0000000000..8254c6440d
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_PotionBrewer.java
@@ -0,0 +1,225 @@
+package gregtech.common.tileentities.machines.basic;
+
+import net.minecraft.init.Items;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidRegistry;
+import net.minecraftforge.fluids.FluidStack;
+
+import gregtech.api.enums.MachineType;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_OreDictUnificator;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_PotionBrewer extends GT_MetaTileEntity_BasicMachine {
+
+ public GT_MetaTileEntity_PotionBrewer(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 1,
+ MachineType.BREWERY.tooltipDescription(),
+ 1,
+ 0,
+ TextureFactory.of(
+ TextureFactory.of(
+ new Textures.BlockIcons.CustomIcon("basicmachines/potionbrewer/OVERLAY_SIDE_POTIONBREWER_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(
+ new Textures.BlockIcons.CustomIcon(
+ "basicmachines/potionbrewer/OVERLAY_SIDE_POTIONBREWER_ACTIVE_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory
+ .of(new Textures.BlockIcons.CustomIcon("basicmachines/potionbrewer/OVERLAY_SIDE_POTIONBREWER")),
+ TextureFactory.builder()
+ .addIcon(
+ new Textures.BlockIcons.CustomIcon("basicmachines/potionbrewer/OVERLAY_SIDE_POTIONBREWER_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(
+ new Textures.BlockIcons.CustomIcon("basicmachines/potionbrewer/OVERLAY_FRONT_POTIONBREWER_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(
+ new Textures.BlockIcons.CustomIcon(
+ "basicmachines/potionbrewer/OVERLAY_FRONT_POTIONBREWER_ACTIVE_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory
+ .of(new Textures.BlockIcons.CustomIcon("basicmachines/potionbrewer/OVERLAY_FRONT_POTIONBREWER")),
+ TextureFactory.builder()
+ .addIcon(
+ new Textures.BlockIcons.CustomIcon(
+ "basicmachines/potionbrewer/OVERLAY_FRONT_POTIONBREWER_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(
+ new Textures.BlockIcons.CustomIcon("basicmachines/potionbrewer/OVERLAY_TOP_POTIONBREWER_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(
+ new Textures.BlockIcons.CustomIcon(
+ "basicmachines/potionbrewer/OVERLAY_TOP_POTIONBREWER_ACTIVE_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory
+ .of(new Textures.BlockIcons.CustomIcon("basicmachines/potionbrewer/OVERLAY_TOP_POTIONBREWER")),
+ TextureFactory.builder()
+ .addIcon(
+ new Textures.BlockIcons.CustomIcon("basicmachines/potionbrewer/OVERLAY_TOP_POTIONBREWER_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(
+ new Textures.BlockIcons.CustomIcon(
+ "basicmachines/potionbrewer/OVERLAY_BOTTOM_POTIONBREWER_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(
+ new Textures.BlockIcons.CustomIcon(
+ "basicmachines/potionbrewer/OVERLAY_BOTTOM_POTIONBREWER_ACTIVE_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory
+ .of(new Textures.BlockIcons.CustomIcon("basicmachines/potionbrewer/OVERLAY_BOTTOM_POTIONBREWER")),
+ TextureFactory.builder()
+ .addIcon(
+ new Textures.BlockIcons.CustomIcon(
+ "basicmachines/potionbrewer/OVERLAY_BOTTOM_POTIONBREWER_GLOW"))
+ .glow()
+ .build()));
+ }
+
+ public GT_MetaTileEntity_PotionBrewer(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 1, aDescription, aTextures, 1, 0);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_PotionBrewer(this.mName, this.mTier, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.brewingRecipes;
+ }
+
+ @Override
+ public int checkRecipe() {
+ int tCheck = super.checkRecipe();
+ if (tCheck != DID_NOT_FIND_RECIPE) {
+ return tCheck;
+ }
+
+ calculateOverclockedNess(4, 128);
+ // In case recipe is too OP for that machine
+ if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1)
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+
+ FluidStack aFluid = getFillableStack();
+ if ((getDrainableStack() == null) && (aFluid != null) && (getInputAt(0) != null)) {
+ String tInputName = aFluid.getFluid()
+ .getName();
+ if (tInputName.startsWith("potion.")) {
+ tInputName = tInputName.replaceFirst("potion.", "");
+ int tFirstDot = tInputName.indexOf('.') + 1;
+ String tModifier = tFirstDot <= 0 ? "" : tInputName.substring(tFirstDot);
+ if (!tModifier.isEmpty()) {
+ tInputName = tInputName.replaceFirst("." + tModifier, "");
+ }
+ if (GT_Utility.areStacksEqual(new ItemStack(Items.fermented_spider_eye, 1, 0), getInputAt(0))) {
+ return switch (tInputName) {
+ case "poison", "health", "waterbreathing" -> setOutput("potion.damage" + tModifier);
+ case "nightvision" -> setOutput("potion.invisibility" + tModifier);
+ case "fireresistance", "speed" -> setOutput("potion.slowness" + tModifier);
+ case "strength" -> setOutput("potion.weakness" + tModifier);
+ case "regen" -> setOutput("potion.poison" + tModifier);
+ default -> setOutput("potion.weakness");
+ };
+ }
+ if (GT_Utility.areStacksEqual(
+ GT_OreDictUnificator.get(OrePrefixes.dust, Materials.Glowstone, 1L),
+ getInputAt(0))) {
+ if (!tModifier.startsWith("strong")) {
+ return setOutput(
+ "potion." + tInputName + ".strong" + (tModifier.isEmpty() ? "" : "." + tModifier));
+ }
+ if (tModifier.startsWith("long")) {
+ return setOutput("potion." + tInputName + tModifier.replaceFirst("long", ""));
+ }
+ return setOutput("potion.thick");
+ }
+ if (GT_Utility.areStacksEqual(
+ GT_OreDictUnificator.get(OrePrefixes.dust, Materials.Redstone, 1L),
+ getInputAt(0))) {
+ if (!tModifier.startsWith("long")) {
+ return setOutput(
+ "potion." + tInputName + ".long" + (tModifier.isEmpty() ? "" : "." + tModifier));
+ }
+ if (tModifier.startsWith("strong")) {
+ return setOutput("potion." + tInputName + tModifier.replaceFirst("strong", ""));
+ }
+ return setOutput("potion.mundane");
+ }
+ if (GT_Utility.areStacksEqual(
+ GT_OreDictUnificator.get(OrePrefixes.dust, Materials.Gunpowder, 1L),
+ getInputAt(0))) {
+ if (!tInputName.endsWith(".splash")) {
+ return setOutput("potion." + tInputName + ".splash");
+ }
+ return setOutput("potion.mundane");
+ }
+ }
+ }
+ return 0;
+ }
+
+ private int setOutput(String aFluidName) {
+ if (getFillableStack().amount < 750) {
+ return 0;
+ }
+
+ this.mOutputFluid = FluidRegistry.getFluidStack(aFluidName, 750);
+ if (this.mOutputFluid == null) {
+ this.mOutputFluid = FluidRegistry.getFluidStack("potion.mundane", getFillableStack().amount);
+ }
+
+ getInputAt(0).stackSize -= 1;
+ getFillableStack().amount -= 750;
+ return 2;
+ }
+
+ @Override
+ public boolean allowPutStackValidated(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return super.allowPutStackValidated(aBaseMetaTileEntity, aIndex, side, aStack)
+ && getRecipeMap().containsInput(aStack);
+ }
+
+ @Override
+ public boolean isFluidInputAllowed(FluidStack aFluid) {
+ return (aFluid.getFluid()
+ .getName()
+ .startsWith("potion.")) || (super.isFluidInputAllowed(aFluid));
+ }
+
+ @Override
+ public int getCapacity() {
+ return getCapacityForTier(mTier);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Pump.java b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Pump.java
new file mode 100644
index 0000000000..eff998d405
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Pump.java
@@ -0,0 +1,846 @@
+package gregtech.common.tileentities.machines.basic;
+
+import static gregtech.api.enums.GT_Values.debugBlockPump;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.ChunkPosition;
+import net.minecraftforge.common.util.FakePlayer;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.IFluidBlock;
+
+import com.gtnewhorizons.modularui.api.drawable.FallbackableUITexture;
+
+import gregtech.api.enums.Textures;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.BaseTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine;
+import gregtech.api.recipe.BasicUIProperties;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Log;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.misc.GT_DrillingLogicDelegate;
+
+public class GT_MetaTileEntity_Pump extends GT_MetaTileEntity_BasicMachine {
+
+ private static final ItemStack MINING_PIPE = GT_ModHandler.getIC2Item("miningPipe", 0);
+
+ private static final ItemStack MINING_PIPE_ONE = GT_ModHandler.getIC2Item("miningPipe", 1);
+ private static final Block MINING_PIPE_BLOCK = GT_Utility.getBlockFromStack(MINING_PIPE);
+ private static final Block MINING_PIPE_TIP_BLOCK = GT_Utility
+ .getBlockFromStack(GT_ModHandler.getIC2Item("miningPipeTip", 0));
+
+ public static int getMaxDistanceForTier(int aTier) {
+ return (10 * ((int) Math.pow(1.6D, aTier)));
+ }
+
+ public static long getEuUsagePerTier(int aTier) {
+ return (16 * ((long) Math.pow(4, aTier)));
+ }
+
+ public ArrayDeque<ChunkPosition> mPumpList = new ArrayDeque<>();
+ public boolean wasPumping = false;
+ public int mPumpTimer = 0;
+ public int mPumpCountBelow = 0;
+ public Block mPrimaryPumpedBlock = null;
+ public Block mSecondaryPumpedBlock = null;
+
+ private int radiusConfig; // Pump configured radius
+ private boolean mRetractDone = false;
+
+ private boolean mDisallowRetract = true;
+
+ public GT_MetaTileEntity_Pump(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 1,
+ new String[] { "The best way to empty Oceans!",
+ getEuUsagePerTier(aTier) + " EU/operation, "
+ + GT_Utility.safeInt(160 / 20 / (long) Math.pow(2, aTier))
+ + " sec per bucket, no stuttering",
+ "Maximum pumping area: " + (getMaxDistanceForTier(aTier) * 2 + 1)
+ + "x"
+ + (getMaxDistanceForTier(aTier) * 2 + 1),
+ "Use Screwdriver to regulate pumping area", "Use Soft Mallet to disable and retract the pipe",
+ "Disable the bottom pump to retract the pipe!",
+ "Use Soldering Iron to auto retract the pipe when hitting a rock", },
+ 2,
+ 2,
+ TextureFactory.of(
+ TextureFactory.of(new Textures.BlockIcons.CustomIcon("basicmachines/pump/OVERLAY_SIDE_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(new Textures.BlockIcons.CustomIcon("basicmachines/pump/OVERLAY_SIDE_ACTIVE_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(new Textures.BlockIcons.CustomIcon("basicmachines/pump/OVERLAY_SIDE")),
+ TextureFactory.builder()
+ .addIcon(new Textures.BlockIcons.CustomIcon("basicmachines/pump/OVERLAY_SIDE_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(new Textures.BlockIcons.CustomIcon("basicmachines/pump/OVERLAY_FRONT_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(new Textures.BlockIcons.CustomIcon("basicmachines/pump/OVERLAY_FRONT_ACTIVE_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(new Textures.BlockIcons.CustomIcon("basicmachines/pump/OVERLAY_FRONT")),
+ TextureFactory.builder()
+ .addIcon(new Textures.BlockIcons.CustomIcon("basicmachines/pump/OVERLAY_FRONT_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(new Textures.BlockIcons.CustomIcon("basicmachines/pump/OVERLAY_TOP_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(new Textures.BlockIcons.CustomIcon("basicmachines/pump/OVERLAY_TOP_ACTIVE_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(new Textures.BlockIcons.CustomIcon("basicmachines/pump/OVERLAY_TOP")),
+ TextureFactory.builder()
+ .addIcon(new Textures.BlockIcons.CustomIcon("basicmachines/pump/OVERLAY_TOP_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(new Textures.BlockIcons.CustomIcon("basicmachines/pump/OVERLAY_BOTTOM_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(new Textures.BlockIcons.CustomIcon("basicmachines/pump/OVERLAY_BOTTOM_ACTIVE_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(new Textures.BlockIcons.CustomIcon("basicmachines/pump/OVERLAY_BOTTOM")),
+ TextureFactory.builder()
+ .addIcon(new Textures.BlockIcons.CustomIcon("basicmachines/pump/OVERLAY_BOTTOM_GLOW"))
+ .glow()
+ .build()));
+
+ radiusConfig = getMaxDistanceForTier(mTier);
+ }
+
+ public GT_MetaTileEntity_Pump(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 1, aDescription, aTextures, 2, 2);
+ radiusConfig = getMaxDistanceForTier(mTier);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Pump(this.mName, this.mTier, this.mDescriptionArray, this.mTextures);
+ }
+
+ private static final FallbackableUITexture progressBarTexture = GT_UITextures
+ .fallbackableProgressbar("pump", GT_UITextures.PROGRESSBAR_CANNER);
+
+ @Override
+ protected BasicUIProperties getUIProperties() {
+ return BasicUIProperties.builder()
+ .maxItemInputs(2)
+ .maxItemOutputs(2)
+ .slotOverlays((index, isFluid, isOutput, isSpecial) -> {
+ if (!isFluid && !isOutput && !isSpecial) {
+ return GT_UITextures.OVERLAY_SLOT_MINING_PIPE;
+ } else {
+ return null;
+ }
+ })
+ .maxFluidInputs(0)
+ .maxFluidOutputs(1)
+ .progressBarTexture(progressBarTexture)
+ .build();
+ }
+
+ @Override
+ public int getCapacity() {
+ return getCapacityForTier(mTier);
+ }
+
+ @Override
+ protected boolean allowPutStackValidated(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return super.allowPutStackValidated(aBaseMetaTileEntity, aIndex, side, aStack)
+ && aStack.getItem() == GT_DrillingLogicDelegate.MINING_PIPE_STACK.getItem();
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ boolean wasPumping = this.wasPumping || !this.mPumpList.isEmpty();
+ if (debugBlockPump) {
+ GT_Log.out.println(
+ "PUMP: NBT:Save - WasPumping - " + wasPumping
+ + " blocks ("
+ + this.mPrimaryPumpedBlock
+ + ", "
+ + this.mSecondaryPumpedBlock
+ + ")");
+ }
+ super.saveNBTData(aNBT);
+ aNBT.setString(
+ "mPumpedBlock1",
+ this.mPrimaryPumpedBlock == null ? "" : Block.blockRegistry.getNameForObject(this.mPrimaryPumpedBlock));
+ aNBT.setString(
+ "mPumpedBlock2",
+ this.mSecondaryPumpedBlock == null ? "" : Block.blockRegistry.getNameForObject(this.mSecondaryPumpedBlock));
+ aNBT.setBoolean("wasPumping", wasPumping);
+ aNBT.setInteger("radiusConfig", radiusConfig);
+ aNBT.setBoolean("mRetractDone", mRetractDone);
+ aNBT.setBoolean("mDisallowRetract", mDisallowRetract);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ this.wasPumping = aNBT.getBoolean("wasPumping");
+ if (aNBT.hasKey("radiusConfig")) this.radiusConfig = aNBT.getInteger("radiusConfig");
+ this.mPrimaryPumpedBlock = Block.getBlockFromName(aNBT.getString("mPumpedBlock1"));
+ this.mSecondaryPumpedBlock = Block.getBlockFromName(aNBT.getString("mPumpedBlock2"));
+ this.mRetractDone = aNBT.getBoolean("mRetractDone");
+ this.mDisallowRetract = aNBT.getBoolean("mDisallowRetract");
+
+ // Transition from old TE which was derived from GT_MetaTileEntity_Hatch
+ if (!aNBT.hasKey("mEUt")) {
+ // Output of old pump always faces up.
+ getBaseMetaTileEntity().setFrontFacing(ForgeDirection.UP);
+
+ // Automatic output on.
+ mFluidTransfer = true;
+
+ // Fluid was stored in the hatch, now needs to go to the output.
+ if (mFluid != null && mFluid.amount > 0) {
+ fluidOutputTank.fill(mFluid, true);
+ mFluid = null;
+ }
+
+ // Move pipes (or other things) from old slots to new ones.
+ if (mInventory[1] != null && mInventory[1].stackSize > 0) {
+ mInventory[getInputSlot() + 1] = mInventory[1];
+ mInventory[1] = null;
+ }
+
+ if (mInventory[0] != null && mInventory[0].stackSize > 0) {
+ mInventory[getInputSlot()] = mInventory[0];
+ mInventory[0] = null;
+ }
+ }
+
+ if (debugBlockPump) {
+ GT_Log.out.println(
+ "PUMP: NBT:Load - WasPumping - " + this.wasPumping
+ + "("
+ + aNBT.getString("mPumpedBlock1")
+ + ") "
+ + this.mPrimaryPumpedBlock);
+ }
+ }
+
+ @Override
+ public void setItemNBT(NBTTagCompound aNBT) {
+ super.setItemNBT(aNBT);
+ aNBT.setInteger("radiusConfig", radiusConfig);
+ aNBT.setBoolean("mDisallowRetract", mDisallowRetract);
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ super.onScrewdriverRightClick(side, aPlayer, aX, aY, aZ);
+
+ if (side == getBaseMetaTileEntity().getFrontFacing() || side == mMainFacing) {
+ // Configuring "input from output side allowed".
+ return;
+ }
+
+ int max = getMaxPumpableDistance();
+ if (aPlayer.isSneaking()) {
+ if (radiusConfig >= 0) {
+ radiusConfig--;
+ }
+ if (radiusConfig < 0) radiusConfig = max;
+ } else {
+ if (radiusConfig <= max) {
+ radiusConfig++;
+ }
+ if (radiusConfig > max) radiusConfig = 0;
+ }
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ StatCollector.translateToLocal("GT5U.machines.workareaset") + " "
+ + (radiusConfig * 2 + 1)
+ + "x"
+ + (radiusConfig * 2 + 1)); // TODO Add translation support
+
+ clearQueue(false);
+ }
+
+ @Override
+ public boolean onSolderingToolRightClick(ForgeDirection side, ForgeDirection wrenchingSide,
+ EntityPlayer entityPlayer, float aX, float aY, float aZ) {
+ if (super.onSolderingToolRightClick(side, wrenchingSide, entityPlayer, aX, aY, aZ)) return true;
+ mDisallowRetract = !mDisallowRetract;
+ GT_Utility.sendChatToPlayer(
+ entityPlayer,
+ StatCollector.translateToLocal(
+ mDisallowRetract ? "GT5U.machines.autoretract.disabled" : "GT5U.machines.autoretract.enabled"));
+ return true;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+
+ if (getBaseMetaTileEntity().isServerSide()) {
+ this.mPumpTimer -= 1;
+ if ((getBaseMetaTileEntity() instanceof BaseTileEntity)) {
+ ((BaseTileEntity) getBaseMetaTileEntity()).ignoreUnloadedChunks = false;
+ }
+ this.doTickProfilingInThisTick = true;
+ this.mPumpCountBelow = 0;
+
+ IGregTechTileEntity tTileEntity;
+ for (int i = 1; (i < 21)
+ && ((tTileEntity = getBaseMetaTileEntity()
+ .getIGregTechTileEntityAtSideAndDistance(ForgeDirection.DOWN, i)) != null)
+ && ((tTileEntity.getMetaTileEntity() instanceof GT_MetaTileEntity_Pump)); i++) {
+ // Apparently someone might stack 21 pumps on top of each other, so let's check for that
+ getBaseMetaTileEntity().setActive(tTileEntity.isActive());
+ this.mPumpCountBelow += 1;
+ // The more pumps we have stacked, the faster the ones below go
+ ((GT_MetaTileEntity_Pump) tTileEntity.getMetaTileEntity()).mPumpTimer -= 1;
+ ((GT_MetaTileEntity_Pump) tTileEntity.getMetaTileEntity()).mProgresstime += 1;
+ }
+ if (debugBlockPump && (this.mPumpCountBelow != 0)) {
+ GT_Log.out.println("PUMP: Detected " + this.mPumpCountBelow + " pumps below this pump.");
+ }
+ if (this.mPumpCountBelow <= 0) {
+ // Only the bottom most pump does anything
+ if (getBaseMetaTileEntity().isAllowedToWork()) {
+ mRetractDone = false;
+ if ((getBaseMetaTileEntity().isUniversalEnergyStored(this.getEuUsagePerAction()))
+ && (fluidOutputTank.getFluidAmount() + 1000 <= fluidOutputTank.getCapacity())) {
+ boolean tMovedOneDown = false;
+ if ((this.mPumpList.isEmpty()) && (getBaseMetaTileEntity().getTimer() % 100L == 0L)) {
+ if (!this.wasPumping) {
+ tMovedOneDown = moveOneDown();
+ if (!tMovedOneDown) {
+ if (canMoveDown(
+ getBaseMetaTileEntity().getXCoord(),
+ Math.max(getYOfPumpHead() - 1, 1),
+ getBaseMetaTileEntity().getZCoord())) {
+ if (debugBlockPump) {
+ GT_Log.out.println("PUMP: No pipe left. Idle for a little longer.");
+ }
+ this.mPumpTimer = 160;
+ } else {
+ getBaseMetaTileEntity().disableWorking();
+ if (debugBlockPump) {
+ GT_Log.out.println("PUMP: Can't move. Retracting in next few ticks");
+ }
+ }
+ } else if (debugBlockPump) {
+ GT_Log.out.println("PUMP: Moved down");
+ }
+ } else if (debugBlockPump) {
+ GT_Log.out.println("PUMP: Was pumping, didn't move down");
+ }
+ }
+ int x = getBaseMetaTileEntity().getXCoord(), z = getBaseMetaTileEntity().getZCoord();
+
+ if (!this.hasValidFluid()) {
+ // We don't have a valid block, let's try to find one
+ int y = getYOfPumpHead();
+
+ if (debugBlockPump && this.mPrimaryPumpedBlock != null) {
+ GT_Log.out.println(
+ "PUMP: Had an invalid pump block. Trying to find a fluid at Y: " + y
+ + " Previous blocks 1: "
+ + this.mPrimaryPumpedBlock
+ + " 2: "
+ + this.mSecondaryPumpedBlock);
+ }
+ // First look down
+ checkForFluidToPump(x, y - 1, z);
+
+ // Then look all around
+ checkForFluidToPump(x, y, z + 1);
+ checkForFluidToPump(x, y, z - 1);
+ checkForFluidToPump(x + 1, y, z);
+ checkForFluidToPump(x - 1, y, z);
+ this.clearQueue(false);
+
+ if (this.hasValidFluid()) {
+ // Don't move down and rebuild the queue if we now have a valid fluid
+ this.wasPumping = true;
+ }
+
+ } else if (getYOfPumpHead() < getBaseMetaTileEntity().getYCoord()) {
+ // We didn't just look for a block, and the pump head is below the pump
+ if ((tMovedOneDown) || this.wasPumping
+ || ((this.mPumpList.isEmpty()) && (getBaseMetaTileEntity().getTimer() % 200L == 100L))
+ || (getBaseMetaTileEntity().getTimer() % 72000L == 100L)) {
+ // Rebuild the list to pump if any of the following conditions are true:
+ // 1) We just moved down
+ // 2) We were previously pumping (and possibly just reloaded)
+ // 3) We have an empty queue and enough time has passed
+ // 4) A long while has passed
+ if (debugBlockPump) {
+ GT_Log.out.println(
+ "PUMP: Rebuilding pump list - Size " + this.mPumpList.size()
+ + " WasPumping: "
+ + this.wasPumping
+ + " Timer "
+ + getBaseMetaTileEntity().getTimer());
+ }
+ int yPump = getBaseMetaTileEntity().getYCoord() - 1, yHead = getYOfPumpHead();
+
+ this.rebuildPumpQueue(x, yPump, z, yHead);
+
+ if (debugBlockPump) {
+ GT_Log.out.println("PUMP: Rebuilt pump list - Size " + this.mPumpList.size());
+ }
+ }
+ if ((!tMovedOneDown) && (this.mPumpTimer <= 0)) {
+ while ((!this.mPumpList.isEmpty())) {
+ ChunkPosition pos = this.mPumpList.pollLast();
+ if (consumeFluid(pos.chunkPosX, pos.chunkPosY, pos.chunkPosZ)) {
+ // Keep trying until we consume something, or the list is empty
+ break;
+ }
+ }
+ this.mPumpTimer = GT_Utility.safeInt(160 / (long) Math.pow(2, this.mTier));
+ this.mPumpTimer = mPumpTimer == 0 ? 1 : mPumpTimer;
+
+ mMaxProgresstime = mPumpTimer;
+ mProgresstime = 0;
+ }
+ } else {
+ // We somehow have a valid fluid, but the head of the pump isn't below the pump. Perhaps
+ // someone broke some pipes
+ // -- Clear the queue and we should try to move down until we can find a valid fluid
+ this.clearQueue(false);
+ }
+ } else if (debugBlockPump) {
+ GT_Log.out.println("PUMP: Not enough energy? Free space?");
+ }
+ } else {
+ mMaxProgresstime = 0;
+
+ if (!mRetractDone && ((aTick % 5) == 0) && canOutput(MINING_PIPE_ONE)) {
+ // try retract if all of these conditions are met
+ // 1. not retracted yet
+ // 2. once per 5 tick
+ // 3. can hold retracted pipe in inventory
+ int tHeadY = getYOfPumpHead();
+ if (tHeadY < this.getBaseMetaTileEntity()
+ .getYCoord()) {
+ final int tXCoord = this.getBaseMetaTileEntity()
+ .getXCoord();
+ final int tZCoord = this.getBaseMetaTileEntity()
+ .getZCoord();
+ this.getBaseMetaTileEntity()
+ .getWorld()
+ .setBlockToAir(tXCoord, tHeadY, tZCoord);
+ if (tHeadY < this.getBaseMetaTileEntity()
+ .getYCoord() - 1) {
+ getBaseMetaTileEntity().getWorld()
+ .setBlock(tXCoord, tHeadY + 1, tZCoord, MINING_PIPE_TIP_BLOCK);
+ }
+
+ for (int i = 0; i < mOutputItems.length; ++i) {
+ if (aBaseMetaTileEntity.addStackToSlot(getOutputSlot() + i, MINING_PIPE_ONE.copy())) {
+ break;
+ }
+ }
+
+ if (debugBlockPump) {
+ GT_Log.out.println("PUMP: Retracted one pipe");
+ }
+ } else {
+ mRetractDone = true;
+ if (debugBlockPump) {
+ GT_Log.out.println("PUMP: Retract done");
+ }
+ }
+ }
+ }
+ if (!mDisallowRetract) getBaseMetaTileEntity().setActive(!this.mPumpList.isEmpty());
+ }
+ }
+ }
+
+ private int getMaxPumpableDistance() {
+ return getMaxDistanceForTier(this.mTier);
+ }
+
+ private long getEuUsagePerAction() {
+ return getEuUsagePerTier(this.mTier);
+ }
+
+ private boolean hasValidFluid() {
+ return mPrimaryPumpedBlock != null && mSecondaryPumpedBlock != null;
+ }
+
+ private boolean moveOneDown() {
+ boolean foundPipe = false;
+
+ for (int i = 0; i < mInputSlotCount; i++) {
+ ItemStack stack = getInputAt(i);
+ if (stack != null && GT_Utility.areStacksEqual(stack, MINING_PIPE) && stack.stackSize > 0) {
+ foundPipe = true;
+ break;
+ }
+ }
+
+ if (!foundPipe) {
+ // No mining pipes
+ if (debugBlockPump) {
+ GT_Log.out.println("PUMP: No mining pipes");
+ }
+ return false;
+ }
+
+ int yHead = getYOfPumpHead();
+ if (yHead <= 1) {
+ // Let's not punch through bedrock
+ if (debugBlockPump) {
+ GT_Log.out.println("PUMP: At bottom");
+ }
+ return false;
+ }
+
+ int x = getBaseMetaTileEntity().getXCoord(), z = getBaseMetaTileEntity().getZCoord();
+
+ Block aBlock = getBaseMetaTileEntity().getBlock(x, yHead - 1, z);
+ boolean canReplaceBlock = aBlock.isReplaceable(getBaseMetaTileEntity().getWorld(), x, yHead - 1, z);
+
+ // We specifically allow replacing water even if we can't consume it
+ // (e.g. pump holds a different fluid) to help avoid getting stuck on random water pockets.
+ if (!canReplaceBlock || (isFluid(aBlock) && !consumeFluid(x, yHead - 1, z) && !isWater(aBlock))) {
+ // Either we didn't consume a fluid, or it's a non-replaceable block, or it's water.
+ if (debugBlockPump) {
+ GT_Log.out.println("PUMP: Did not consume fluid, or non-replaceable block found");
+ }
+ return false;
+ }
+ // Try to set the block below us to a tip
+ if (!GT_Utility.setBlockByFakePlayer(
+ getFakePlayer(getBaseMetaTileEntity()),
+ x,
+ yHead - 1,
+ z,
+ MINING_PIPE_TIP_BLOCK,
+ 0,
+ false)) {
+ if (debugBlockPump) {
+ GT_Log.out.println("PUMP: Could not set block below to new tip");
+ }
+ return false;
+ }
+ // And change the previous block to a pipe -- as long as it isn't the pump itself!
+ if (yHead != getBaseMetaTileEntity().getYCoord()) {
+ getBaseMetaTileEntity().getWorld()
+ .setBlock(x, yHead, z, MINING_PIPE_BLOCK);
+ }
+
+ // Remove pipe from inputs.
+ foundPipe = false;
+
+ for (int i = 0; i < mInputSlotCount; i++) {
+ ItemStack stack = getInputAt(i);
+ if (stack != null && GT_Utility.areStacksEqual(stack, MINING_PIPE) && stack.stackSize > 0) {
+ foundPipe = true;
+ stack.stackSize -= 1;
+ if (stack.stackSize == 0) {
+ mInventory[getInputSlot() + i] = null;
+ }
+ break;
+ }
+ }
+
+ if (debugBlockPump) {
+ if (foundPipe) {
+ GT_Log.out.println("PUMP: Using 1 pipe");
+ } else {
+ GT_Log.err.println("PUMP: Lowered pipe but could not find pipe in input");
+ }
+ }
+
+ return true;
+ }
+
+ private int getYOfPumpHead() {
+ // Let's play find the pump head!
+
+ // TODO: Handle pipe|pipe|head|pipe|pipe
+ int y = getBaseMetaTileEntity().getYCoord() - 1, x = getBaseMetaTileEntity().getXCoord(),
+ z = getBaseMetaTileEntity().getZCoord();
+
+ while (y > 0) {
+ Block curBlock = getBaseMetaTileEntity().getBlock(x, y, z);
+ if (curBlock == MINING_PIPE_BLOCK) {
+ y--;
+ } else if (curBlock == MINING_PIPE_TIP_BLOCK) {
+ Block nextBlock = getBaseMetaTileEntity().getBlock(x, y - 1, z);
+ if (nextBlock == MINING_PIPE_BLOCK || nextBlock == MINING_PIPE_TIP_BLOCK) {
+ // We're running into an existing set of pipes -- Turn this block into a pipe and keep going
+ this.clearQueue(true);
+ getBaseMetaTileEntity().getWorld()
+ .setBlock(x, y, z, MINING_PIPE_BLOCK);
+ if (debugBlockPump) {
+ GT_Log.out.println("PUMP: Hit pipes already in place, trying to merge");
+ }
+ }
+ y--;
+
+ } else {
+ break;
+ }
+ }
+
+ if (getBaseMetaTileEntity().getBlock(x, y, z) != MINING_PIPE_TIP_BLOCK) {
+ if (y != getBaseMetaTileEntity().getYCoord() - 1
+ && getBaseMetaTileEntity().getBlock(x, y + 1, z) == MINING_PIPE_BLOCK) {
+ // We're below the pump at the bottom of the pipes, we haven't found a tip; make the previous pipe a
+ // tip!
+ this.clearQueue(true);
+ getBaseMetaTileEntity().getWorld()
+ .setBlock(x, y + 1, z, MINING_PIPE_TIP_BLOCK);
+ if (debugBlockPump) {
+ GT_Log.out.println("PUMP: Did not find a tip at bottom, setting last pipe as tip");
+ }
+ }
+ return y + 1;
+ }
+ return y;
+ }
+
+ private void clearQueue(boolean checkPumping) {
+ if (checkPumping) {
+ this.wasPumping = !this.mPumpList.isEmpty();
+ } else {
+ this.wasPumping = false;
+ }
+ this.mPumpList.clear();
+ }
+
+ private void rebuildPumpQueue(int aX, int yStart, int aZ, int yEnd) {
+ int mDist = this.radiusConfig;
+ doTickProfilingInThisTick = false;
+ ArrayDeque<ChunkPosition> fluidsToSearch = new ArrayDeque<>();
+ ArrayDeque<ChunkPosition> fluidsFound = new ArrayDeque<>();
+ Set<ChunkPosition> checked = new HashSet<>();
+ this.clearQueue(false);
+
+ for (int aY = yStart; this.mPumpList.isEmpty() && aY >= yEnd; aY--) {
+ // Start at the top (presumably the block below the pump), and work our way down to the end (presumably the
+ // location of the pump Head)
+ // and build up a queue of fluids to pump
+ fluidsToSearch.add(new ChunkPosition(aX, aY, aZ));
+
+ while (!fluidsToSearch.isEmpty()) {
+ for (ChunkPosition tPos : fluidsToSearch) {
+ // Look all around
+ if (tPos.chunkPosX < aX + mDist)
+ queueFluid(tPos.chunkPosX + 1, tPos.chunkPosY, tPos.chunkPosZ, fluidsFound, checked);
+ if (tPos.chunkPosX > aX - mDist)
+ queueFluid(tPos.chunkPosX - 1, tPos.chunkPosY, tPos.chunkPosZ, fluidsFound, checked);
+ if (tPos.chunkPosZ < aZ + mDist)
+ queueFluid(tPos.chunkPosX, tPos.chunkPosY, tPos.chunkPosZ + 1, fluidsFound, checked);
+ if (tPos.chunkPosZ > aZ - mDist)
+ queueFluid(tPos.chunkPosX, tPos.chunkPosY, tPos.chunkPosZ - 1, fluidsFound, checked);
+
+ // And then look up
+ queueFluid(tPos.chunkPosX, tPos.chunkPosY + 1, tPos.chunkPosZ, this.mPumpList, checked);
+ }
+ this.mPumpList.addAll(fluidsFound);
+ fluidsToSearch = fluidsFound;
+ fluidsFound = new ArrayDeque<>();
+ }
+
+ // Make sure we don't have the pipe location in the queue
+ this.mPumpList.remove(new ChunkPosition(aX, aY, aZ));
+ }
+ }
+
+ private boolean queueFluid(int aX, int aY, int aZ, ArrayDeque<ChunkPosition> fluidsFound,
+ Set<ChunkPosition> checked) {
+ // If we haven't already looked at this coordinate set, and it's not already in the list of fluids found, see if
+ // there is
+ // a valid fluid and add it to the fluids found
+ ChunkPosition tCoordinate = new ChunkPosition(aX, aY, aZ);
+ if (checked.add(tCoordinate) && !fluidsFound.contains(tCoordinate)) {
+ Block aBlock = getBaseMetaTileEntity().getBlock(aX, aY, aZ);
+ if ((this.mPrimaryPumpedBlock == aBlock) || (this.mSecondaryPumpedBlock == aBlock)) {
+ fluidsFound.addFirst(tCoordinate);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void checkForFluidToPump(int aX, int aY, int aZ) {
+ // If we don't currently have a valid fluid to pump, try pumping the fluid at the given coordinates
+ if (this.hasValidFluid()) return;
+
+ Block aBlock = getBaseMetaTileEntity().getBlock(aX, aY, aZ);
+ if (aBlock != null) {
+ if (isWater(aBlock)) {
+ this.mPrimaryPumpedBlock = Blocks.water;
+ this.mSecondaryPumpedBlock = Blocks.flowing_water;
+ return;
+ }
+ if (isLava(aBlock)) {
+ this.mPrimaryPumpedBlock = Blocks.lava;
+ this.mSecondaryPumpedBlock = Blocks.flowing_lava;
+ return;
+ }
+ if ((aBlock instanceof IFluidBlock)) {
+ this.mPrimaryPumpedBlock = aBlock;
+ this.mSecondaryPumpedBlock = aBlock;
+ return;
+ }
+ }
+ this.mPrimaryPumpedBlock = null;
+ this.mSecondaryPumpedBlock = null;
+ }
+
+ /** only check if block below can be replaced with pipe tip. pipe stockpile condition is ignored */
+ private boolean canMoveDown(int aX, int aY, int aZ) {
+ if (!GT_Utility.eraseBlockByFakePlayer(getFakePlayer(getBaseMetaTileEntity()), aX, aY, aZ, true)) return false;
+
+ Block aBlock = getBaseMetaTileEntity().getBlock(aX, aY, aZ);
+
+ return aBlock != null && aBlock.isReplaceable(getBaseMetaTileEntity().getWorld(), aX, aY, aZ);
+ }
+
+ private boolean consumeFluid(int aX, int aY, int aZ) {
+ // Try to consume a fluid at a location
+ // Returns true if something was consumed, otherwise false
+ if (!GT_Utility.eraseBlockByFakePlayer(getFakePlayer(getBaseMetaTileEntity()), aX, aY, aZ, true)) return false;
+
+ Block aBlock = getBaseMetaTileEntity().getBlock(aX, aY, aZ);
+ if (!isFluid(aBlock)) {
+ return false;
+ }
+
+ if (aBlock != null && ((this.mPrimaryPumpedBlock == aBlock) || (this.mSecondaryPumpedBlock == aBlock))) {
+ boolean isWaterOrLava = ((this.mPrimaryPumpedBlock == Blocks.water
+ || this.mPrimaryPumpedBlock == Blocks.lava));
+
+ if (isWaterOrLava && getBaseMetaTileEntity().getMetaID(aX, aY, aZ) != 0) {
+ // Water/Lava that isn't a source block - do nothing here, but set the block to air and consume energy
+ // below
+ if (debugBlockPump) {
+ GT_Log.out.println("PUMP: Water/Lava - Not a source block");
+ }
+
+ } else if (getDrainableStack() == null) {
+ // The pump has no internal fluid
+ if (this.mPrimaryPumpedBlock == Blocks.water) setDrainableStack(GT_ModHandler.getWater(1000L));
+ else if (this.mPrimaryPumpedBlock == Blocks.lava) setDrainableStack(GT_ModHandler.getLava(1000L));
+ else {
+ // Not water or lava; try to drain and set to air
+ setDrainableStack(
+ ((IFluidBlock) aBlock).drain(getBaseMetaTileEntity().getWorld(), aX, aY, aZ, true));
+ }
+
+ } else if (GT_ModHandler.isWater(getDrainableStack()) || GT_ModHandler.isLava(getDrainableStack())
+ || getDrainableStack().isFluidEqual(
+ ((IFluidBlock) aBlock).drain(getBaseMetaTileEntity().getWorld(), aX, aY, aZ, false))) {
+ if (!isWaterOrLava) {
+ // Only set Block to Air for non lava/water fluids
+ this.getBaseMetaTileEntity()
+ .getWorld()
+ .setBlockToAir(aX, aY, aZ);
+ }
+ getDrainableStack().amount += 1000;
+
+ } else {
+ if (debugBlockPump) {
+ GT_Log.out.println("PUMP: Couldn't consume " + aBlock);
+ }
+ // We didn't do anything
+ return false;
+ }
+
+ getBaseMetaTileEntity().decreaseStoredEnergyUnits(this.getEuUsagePerAction(), true);
+ getBaseMetaTileEntity().getWorld()
+ .setBlock(aX, aY, aZ, Blocks.air, 0, 2);
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean isWater(Block aBlock) {
+ return aBlock == Blocks.water || aBlock == Blocks.flowing_water;
+ }
+
+ private static boolean isLava(Block aBlock) {
+ return aBlock == Blocks.lava || aBlock == Blocks.flowing_lava;
+ }
+
+ private static boolean isFluid(Block aBlock) {
+ return isWater(aBlock) || isLava(aBlock) || aBlock instanceof IFluidBlock;
+ }
+
+ @Override
+ public ArrayList<String> getSpecialDebugInfo(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer,
+ int aLogLevel, ArrayList<String> aList) {
+ aList.addAll(
+ Arrays.asList(
+ EnumChatFormatting.BLUE + StatCollector.translateToLocal("GT5U.machines.pump")
+ + EnumChatFormatting.RESET,
+ StatCollector.translateToLocal("GT5U.machines.workarea") + ": "
+ + EnumChatFormatting.GREEN
+ + (radiusConfig * 2 + 1)
+ + EnumChatFormatting.RESET
+ + " "
+ + StatCollector.translateToLocal("GT5U.machines.blocks"),
+ "Primary pumping fluid: "
+ + (this.mPrimaryPumpedBlock != null ? this.mPrimaryPumpedBlock.getLocalizedName() : "None"),
+ "Secondary pumping fluid: "
+ + (this.mSecondaryPumpedBlock != null ? this.mSecondaryPumpedBlock.getLocalizedName() : "None"),
+ "Pumps below: " + mPumpCountBelow,
+ "Queue size: " + mPumpList.size(),
+ "Pump head at Y: " + getYOfPumpHead(),
+ "Pump timer: " + mPumpTimer,
+ "Meta Entity Timer: " + getBaseMetaTileEntity().getTimer()));
+ return aList;
+ }
+
+ private FakePlayer mFakePlayer = null;
+
+ protected FakePlayer getFakePlayer(IGregTechTileEntity aBaseTile) {
+ if (mFakePlayer == null) mFakePlayer = GT_Utility.getFakePlayer(aBaseTile);
+ mFakePlayer.setWorld(aBaseTile.getWorld());
+ mFakePlayer.setPosition(aBaseTile.getXCoord(), aBaseTile.getYCoord(), aBaseTile.getZCoord());
+ return mFakePlayer;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ return new String[] {
+ EnumChatFormatting.BLUE + StatCollector.translateToLocal("GT5U.machines.pump") + EnumChatFormatting.RESET,
+ StatCollector.translateToLocal("GT5U.machines.workarea") + ": "
+ + EnumChatFormatting.GREEN
+ + (radiusConfig * 2 + 1)
+ + EnumChatFormatting.RESET
+ + " "
+ + StatCollector.translateToLocal("GT5U.machines.blocks") };
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Replicator.java b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Replicator.java
new file mode 100644
index 0000000000..e9f3d98dd5
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Replicator.java
@@ -0,0 +1,130 @@
+package gregtech.common.tileentities.machines.basic;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.MachineType;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+
+public class GT_MetaTileEntity_Replicator extends GT_MetaTileEntity_BasicMachine {
+
+ public GT_MetaTileEntity_Replicator(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 1,
+ MachineType.REPLICATOR.tooltipDescription(),
+ 1,
+ 1,
+ TextureFactory.of(
+ TextureFactory
+ .of(new Textures.BlockIcons.CustomIcon("basicmachines/replicator/OVERLAY_SIDE_REPLICATOR_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(
+ new Textures.BlockIcons.CustomIcon(
+ "basicmachines/replicator/OVERLAY_SIDE_REPLICATOR_ACTIVE_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory
+ .of(new Textures.BlockIcons.CustomIcon("basicmachines/replicator/OVERLAY_SIDE_REPLICATOR")),
+ TextureFactory.builder()
+ .addIcon(
+ new Textures.BlockIcons.CustomIcon("basicmachines/replicator/OVERLAY_SIDE_REPLICATOR_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory
+ .of(new Textures.BlockIcons.CustomIcon("basicmachines/replicator/OVERLAY_FRONT_REPLICATOR_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(
+ new Textures.BlockIcons.CustomIcon(
+ "basicmachines/replicator/OVERLAY_FRONT_REPLICATOR_ACTIVE_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory
+ .of(new Textures.BlockIcons.CustomIcon("basicmachines/replicator/OVERLAY_FRONT_REPLICATOR")),
+ TextureFactory.builder()
+ .addIcon(
+ new Textures.BlockIcons.CustomIcon("basicmachines/replicator/OVERLAY_FRONT_REPLICATOR_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory
+ .of(new Textures.BlockIcons.CustomIcon("basicmachines/replicator/OVERLAY_TOP_REPLICATOR_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(
+ new Textures.BlockIcons.CustomIcon(
+ "basicmachines/replicator/OVERLAY_TOP_REPLICATOR_ACTIVE_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory
+ .of(new Textures.BlockIcons.CustomIcon("basicmachines/replicator/OVERLAY_TOP_REPLICATOR")),
+ TextureFactory.builder()
+ .addIcon(new Textures.BlockIcons.CustomIcon("basicmachines/replicator/OVERLAY_TOP_REPLICATOR_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(
+ new Textures.BlockIcons.CustomIcon("basicmachines/replicator/OVERLAY_BOTTOM_REPLICATOR_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(
+ new Textures.BlockIcons.CustomIcon(
+ "basicmachines/replicator/OVERLAY_BOTTOM_REPLICATOR_ACTIVE_GLOW"))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory
+ .of(new Textures.BlockIcons.CustomIcon("basicmachines/replicator/OVERLAY_BOTTOM_REPLICATOR")),
+ TextureFactory.builder()
+ .addIcon(
+ new Textures.BlockIcons.CustomIcon("basicmachines/replicator/OVERLAY_BOTTOM_REPLICATOR_GLOW"))
+ .glow()
+ .build()));
+ }
+
+ public GT_MetaTileEntity_Replicator(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 1, aDescription, aTextures, 1, 1);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Replicator(this.mName, this.mTier, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.replicatorRecipes;
+ }
+
+ @Override
+ protected boolean allowPutStackValidated(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return super.allowPutStackValidated(aBaseMetaTileEntity, aIndex, side, aStack)
+ && ItemList.Cell_Empty.isStackEqual(aStack);
+ }
+
+ @Override
+ public boolean isFluidInputAllowed(FluidStack aFluid) {
+ return aFluid.isFluidEqual(Materials.UUMatter.getFluid(1L));
+ }
+
+ @Override
+ public int getCapacity() {
+ return 3000;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_RockBreaker.java b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_RockBreaker.java
new file mode 100644
index 0000000000..e632f4eeda
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_RockBreaker.java
@@ -0,0 +1,169 @@
+package gregtech.common.tileentities.machines.basic;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_ROCK_BREAKER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_ROCK_BREAKER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_ROCK_BREAKER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_ROCK_BREAKER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ROCK_BREAKER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ROCK_BREAKER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ROCK_BREAKER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ROCK_BREAKER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_ROCK_BREAKER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_ROCK_BREAKER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_ROCK_BREAKER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_ROCK_BREAKER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_ROCK_BREAKER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_ROCK_BREAKER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_ROCK_BREAKER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_ROCK_BREAKER_GLOW;
+
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.enums.MachineType;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_OreDictUnificator;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_RockBreaker extends GT_MetaTileEntity_BasicMachine {
+
+ public GT_MetaTileEntity_RockBreaker(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 1,
+ MachineType.ROCKBREAKER.tooltipDescription(),
+ 1,
+ 1,
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_SIDE_ROCK_BREAKER_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_ROCK_BREAKER_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_SIDE_ROCK_BREAKER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_ROCK_BREAKER_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_FRONT_ROCK_BREAKER_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ROCK_BREAKER_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_FRONT_ROCK_BREAKER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ROCK_BREAKER_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_TOP_ROCK_BREAKER_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_ROCK_BREAKER_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_TOP_ROCK_BREAKER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_ROCK_BREAKER_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_BOTTOM_ROCK_BREAKER_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_ROCK_BREAKER_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_BOTTOM_ROCK_BREAKER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_ROCK_BREAKER_GLOW)
+ .glow()
+ .build()));
+ }
+
+ public GT_MetaTileEntity_RockBreaker(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 1, aDescription, aTextures, 1, 1);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_RockBreaker(this.mName, this.mTier, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.rockBreakerFakeRecipes;
+ }
+
+ @Override
+ protected boolean allowPutStackValidated(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return super.allowPutStackValidated(aBaseMetaTileEntity, aIndex, side, aStack)
+ && getRecipeMap().containsInput(aStack);
+ }
+
+ @Override
+ public boolean allowSelectCircuit() {
+ return true;
+ }
+
+ @Override
+ public int checkRecipe() {
+ IGregTechTileEntity aBaseMetaTileEntity = getBaseMetaTileEntity();
+ if ((aBaseMetaTileEntity.getBlockOffset(0, 0, 1) == Blocks.water)
+ || (aBaseMetaTileEntity.getBlockOffset(0, 0, -1) == Blocks.water)
+ || (aBaseMetaTileEntity.getBlockOffset(-1, 0, 0) == Blocks.water)
+ || (aBaseMetaTileEntity.getBlockOffset(1, 0, 0) == Blocks.water)) {
+ ItemStack tOutput = null;
+ if (aBaseMetaTileEntity.getBlockOffset(0, 1, 0) == Blocks.lava) {
+ tOutput = new ItemStack(Blocks.stone, 1);
+ } else if ((aBaseMetaTileEntity.getBlockOffset(0, 0, 1) == Blocks.lava)
+ || (aBaseMetaTileEntity.getBlockOffset(0, 0, -1) == Blocks.lava)
+ || (aBaseMetaTileEntity.getBlockOffset(-1, 0, 0) == Blocks.lava)
+ || (aBaseMetaTileEntity.getBlockOffset(1, 0, 0) == Blocks.lava)) {
+ tOutput = new ItemStack(Blocks.cobblestone, 1);
+ }
+ if (tOutput != null) {
+ if (GT_Utility.areStacksEqual(getStackInSlot(getCircuitSlot()), GT_Utility.getIntegratedCircuit(1))) {
+ if (GT_Utility.areStacksEqual(
+ getInputAt(0),
+ GT_OreDictUnificator.get(OrePrefixes.dust, Materials.Redstone, 1L))) {
+ tOutput = new ItemStack(Blocks.obsidian, 1);
+ if (canOutput(tOutput)) {
+ getInputAt(0).stackSize -= 1;
+ calculateOverclockedNess(30, 128);
+ // In case recipe is too OP for that machine
+ if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1)
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ this.mOutputItems[0] = tOutput;
+ return 2;
+ }
+ }
+ } else if (canOutput(tOutput)) {
+ calculateOverclockedNess(30, 16);
+ // In case recipe is too OP for that machine
+ if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1)
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ this.mOutputItems[0] = tOutput;
+ return 2;
+ }
+ }
+ }
+ return 0;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Scanner.java b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Scanner.java
new file mode 100644
index 0000000000..ba00be67f8
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Scanner.java
@@ -0,0 +1,425 @@
+package gregtech.common.tileentities.machines.basic;
+
+import static gregtech.api.enums.GT_Values.D1;
+import static gregtech.api.enums.Mods.GalacticraftCore;
+import static gregtech.api.enums.Mods.GalacticraftMars;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_SCANNER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_SCANNER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_SCANNER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_SCANNER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_SCANNER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_SCANNER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_SCANNER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_SCANNER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_SCANNER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_SCANNER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_SCANNER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_SCANNER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_SCANNER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_SCANNER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_SCANNER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_SCANNER_GLOW;
+import static gregtech.api.recipe.RecipeMaps.scannerFakeRecipes;
+
+import java.util.Objects;
+
+import net.minecraft.init.Items;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import forestry.api.genetics.AlleleManager;
+import forestry.api.genetics.IIndividual;
+import gregtech.GT_Mod;
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.MachineType;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine;
+import gregtech.api.objects.ItemData;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_AssemblyLineUtils;
+import gregtech.api.util.GT_Log;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_OreDictUnificator;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.items.behaviors.Behaviour_DataOrb;
+
+public class GT_MetaTileEntity_Scanner extends GT_MetaTileEntity_BasicMachine {
+
+ public GT_MetaTileEntity_Scanner(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 1,
+ MachineType.SCANNER.tooltipDescription(),
+ 1,
+ 1,
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_SIDE_SCANNER_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_SCANNER_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_SIDE_SCANNER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_SCANNER_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_FRONT_SCANNER_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_SCANNER_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_FRONT_SCANNER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_SCANNER_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_TOP_SCANNER_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_SCANNER_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_TOP_SCANNER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_SCANNER_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_BOTTOM_SCANNER_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_SCANNER_ACTIVE_GLOW)
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_BOTTOM_SCANNER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_SCANNER_GLOW)
+ .glow()
+ .build()));
+ }
+
+ public GT_MetaTileEntity_Scanner(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 1, aDescription, aTextures, 1, 1);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Scanner(this.mName, this.mTier, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public int checkRecipe() {
+ ItemStack aStack = getInputAt(0);
+ if (getOutputAt(0) != null) {
+ this.mOutputBlocked += 1;
+ } else if ((GT_Utility.isStackValid(aStack)) && (aStack.stackSize > 0)) {
+ if ((getFillableStack() != null) && (getFillableStack().containsFluid(Materials.Honey.getFluid(100L)))) {
+ try {
+ IIndividual tIndividual = AlleleManager.alleleRegistry.getIndividual(aStack);
+ if (tIndividual != null) {
+ if (tIndividual.analyze()) {
+ getFillableStack().amount -= 100;
+ this.mOutputItems[0] = GT_Utility.copyOrNull(aStack);
+ aStack.stackSize = 0;
+ NBTTagCompound tNBT = new NBTTagCompound();
+ tIndividual.writeToNBT(tNBT);
+ this.mOutputItems[0].setTagCompound(tNBT);
+ calculateOverclockedNess(2, 500);
+ // In case recipe is too OP for that machine
+ if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1)
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ return 2;
+ }
+ this.mOutputItems[0] = GT_Utility.copyOrNull(aStack);
+ aStack.stackSize = 0;
+ this.mMaxProgresstime = 1;
+ this.mEUt = 1;
+ return 2;
+ }
+ } catch (Throwable e) {
+ if (D1) {
+ e.printStackTrace(GT_Log.err);
+ }
+ }
+ }
+ if (ItemList.IC2_Crop_Seeds.isStackEqual(aStack, true, true)) {
+ NBTTagCompound tNBT = aStack.getTagCompound();
+ if (tNBT == null) {
+ tNBT = new NBTTagCompound();
+ }
+ if (tNBT.getByte("scan") < 4) {
+ tNBT.setByte("scan", (byte) 4);
+ calculateOverclockedNess(8, 160);
+ // In case recipe is too OP for that machine
+ if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1)
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ } else {
+ this.mMaxProgresstime = 1;
+ this.mEUt = 1;
+ }
+ aStack.stackSize -= 1;
+ this.mOutputItems[0] = GT_Utility.copyAmount(1, aStack);
+ assert this.mOutputItems[0] != null;
+ this.mOutputItems[0].setTagCompound(tNBT);
+ return 2;
+ }
+ if (ItemList.Tool_DataOrb.isStackEqual(getSpecialSlot(), false, true)) {
+ if (ItemList.Tool_DataOrb.isStackEqual(aStack, false, true)) {
+ aStack.stackSize -= 1;
+ this.mOutputItems[0] = GT_Utility.copyAmount(1, getSpecialSlot());
+ calculateOverclockedNess(30, 512);
+ // In case recipe is too OP for that machine
+ if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1)
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ return 2;
+ }
+ ItemData tData = GT_OreDictUnificator.getAssociation(aStack);
+ if ((tData != null) && ((tData.mPrefix == OrePrefixes.dust) || (tData.mPrefix == OrePrefixes.cell))
+ && (tData.mMaterial.mMaterial.mElement != null)
+ && (!tData.mMaterial.mMaterial.mElement.mIsIsotope)
+ && (tData.mMaterial.mMaterial != Materials.Magic)
+ && (tData.mMaterial.mMaterial.getMass() > 0L)) {
+ getSpecialSlot().stackSize -= 1;
+ aStack.stackSize -= 1;
+
+ this.mOutputItems[0] = ItemList.Tool_DataOrb.get(1L);
+ Behaviour_DataOrb.setDataTitle(this.mOutputItems[0], "Elemental-Scan");
+ Behaviour_DataOrb.setDataName(this.mOutputItems[0], tData.mMaterial.mMaterial.mElement.name());
+ calculateOverclockedNess(30, GT_Utility.safeInt(tData.mMaterial.mMaterial.getMass() * 8192L));
+ // In case recipe is too OP for that machine
+ if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1)
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ return 2;
+ }
+ }
+ if (ItemList.Tool_DataStick.isStackEqual(getSpecialSlot(), false, true)) {
+ if (ItemList.Tool_DataStick.isStackEqual(aStack, false, true)) {
+ aStack.stackSize -= 1;
+ this.mOutputItems[0] = GT_Utility.copyAmount(1, getSpecialSlot());
+ calculateOverclockedNess(30, 128);
+ // In case recipe is too OP for that machine
+ if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1)
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ return 2;
+ }
+ if (aStack.getItem() == Items.written_book) {
+ getSpecialSlot().stackSize -= 1;
+ aStack.stackSize -= 1;
+
+ this.mOutputItems[0] = GT_Utility.copyAmount(1, getSpecialSlot());
+ assert this.mOutputItems[0] != null;
+ this.mOutputItems[0].setTagCompound(aStack.getTagCompound());
+ calculateOverclockedNess(30, 128);
+ // In case recipe is too OP for that machine
+ if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1)
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ return 2;
+ }
+ if (aStack.getItem() == Items.filled_map) {
+ getSpecialSlot().stackSize -= 1;
+ aStack.stackSize -= 1;
+
+ this.mOutputItems[0] = GT_Utility.copyAmount(1, getSpecialSlot());
+ assert this.mOutputItems[0] != null;
+ this.mOutputItems[0].setTagCompound(
+ GT_Utility
+ .getNBTContainingShort(new NBTTagCompound(), "map_id", (short) aStack.getItemDamage()));
+ calculateOverclockedNess(30, 128);
+ // In case recipe is too OP for that machine
+ if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1)
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ return 2;
+ }
+
+ if ((aStack.getItem()
+ .getUnlocalizedName()
+ .contains("Schematic")
+ || aStack.getItem()
+ .getUnlocalizedName()
+ .contains("schematic"))
+ && !aStack.getItem()
+ .getUnlocalizedName()
+ .contains("Schematics")) {
+ if (mTier < 3) return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ String sTier = "";
+
+ int stackItemID = Item.getIdFromItem(aStack.getItem());
+ int stackItemDamage = aStack.getItemDamage();
+ if (stackItemID == Item.getIdFromItem(
+ Objects.requireNonNull(GT_ModHandler.getModItem(GalacticraftCore.ID, "item.schematic", 1L, 0))
+ .getItem())) {
+ if (stackItemDamage == 0 && aStack.toString()
+ .equals(
+ Objects
+ .requireNonNull(
+ GT_ModHandler.getModItem(GalacticraftCore.ID, "item.schematic", 1L, 0))
+ .copy()
+ .toString()))
+ sTier = "100";
+ else if (stackItemDamage == 1 && aStack.toString()
+ .equals(
+ Objects
+ .requireNonNull(
+ GT_ModHandler.getModItem(GalacticraftCore.ID, "item.schematic", 1L, 1))
+ .copy()
+ .toString()))
+ sTier = "2";
+ } else {
+ if (stackItemID == Item.getIdFromItem(
+ Objects
+ .requireNonNull(GT_ModHandler.getModItem(GalacticraftMars.ID, "item.schematic", 1L, 0))
+ .getItem())) {
+ if (stackItemDamage == 0 && aStack.toString()
+ .equals(
+ Objects
+ .requireNonNull(
+ GT_ModHandler.getModItem(GalacticraftMars.ID, "item.schematic", 1L, 0))
+ .copy()
+ .toString()))
+ sTier = "3";
+ else if (stackItemDamage == 1 && aStack.toString()
+ .equals(
+ Objects
+ .requireNonNull(
+ GT_ModHandler.getModItem(GalacticraftMars.ID, "item.schematic", 1L, 1))
+ .copy()
+ .toString()))
+ sTier = "101";
+ else if (stackItemDamage == 2 && aStack.toString()
+ .equals(
+ Objects
+ .requireNonNull(
+ GT_ModHandler.getModItem(GalacticraftMars.ID, "item.schematic", 1L, 2))
+ .copy()
+ .toString()))
+ sTier = "102";
+ } else if (aStack.getUnlocalizedName()
+ .matches(".*\\d+.*"))
+ sTier = aStack.getUnlocalizedName()
+ .split("(?<=\\D)(?=\\d)")[1].substring(0, 1);
+ else sTier = "1";
+ }
+
+ getSpecialSlot().stackSize -= 1;
+ aStack.stackSize -= 1;
+
+ this.mOutputItems[0] = GT_Utility.copyAmount(1, getSpecialSlot());
+ assert this.mOutputItems[0] != null;
+ this.mOutputItems[0].setTagCompound(
+ GT_Utility.getNBTContainingShort(new NBTTagCompound(), "rocket_tier", Short.parseShort(sTier)));
+
+ calculateOverclockedNess(480, 36000);
+ // In case recipe is too OP for that machine
+ if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1)
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ return 2;
+ }
+ }
+ if (getSpecialSlot() == null && ItemList.Tool_DataStick.isStackEqual(aStack, false, true)) {
+ if (GT_Utility.ItemNBT.getBookTitle(aStack)
+ .equals("Raw Prospection Data")) {
+ GT_Utility.ItemNBT.setBookTitle(aStack, "Analyzed Prospection Data");
+ GT_Utility.ItemNBT.convertProspectionData(aStack);
+ aStack.stackSize -= 1;
+
+ this.mOutputItems[0] = GT_Utility.copyAmount(1, aStack);
+ calculateOverclockedNess(30, 1000);
+ // In case recipe is too OP for that machine
+ if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1)
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ return 2;
+ }
+ }
+ if (ItemList.Tool_DataStick.isStackEqual(getSpecialSlot(), false, true)) {
+ for (GT_Recipe.GT_Recipe_AssemblyLine tRecipe : GT_Recipe.GT_Recipe_AssemblyLine.sAssemblylineRecipes) {
+ if (GT_Utility.areStacksEqual(tRecipe.mResearchItem, aStack, true)) {
+ boolean failScanner = true;
+ for (GT_Recipe scannerRecipe : scannerFakeRecipes.getAllRecipes()) {
+ if (GT_Utility.areStacksEqual(scannerRecipe.mInputs[0], aStack, true)) {
+ failScanner = false;
+ break;
+ }
+ }
+ if (failScanner) {
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ }
+
+ this.mOutputItems[0] = GT_Utility.copyAmount(1, getSpecialSlot());
+
+ // Use Assline Utils
+ if (GT_AssemblyLineUtils.setAssemblyLineRecipeOnDataStick(this.mOutputItems[0], tRecipe)) {
+ aStack.stackSize -= 1;
+ calculateOverclockedNess(30, tRecipe.mResearchTime);
+ // In case recipe is too OP for that machine
+ if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1)
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ getSpecialSlot().stackSize -= 1;
+ return 2;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (mProgresstime >= (mMaxProgresstime - 1)) {
+ if ((this.mOutputItems[0] != null) && (this.mOutputItems[0].getUnlocalizedName()
+ .equals("gt.metaitem.01.32707"))) {
+ GT_Mod.achievements.issueAchievement(
+ aBaseMetaTileEntity.getWorld()
+ .getPlayerEntityByName(aBaseMetaTileEntity.getOwnerName()),
+ "scanning");
+ }
+ }
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return scannerFakeRecipes;
+ }
+
+ @Override
+ public int getCapacity() {
+ return 1000;
+ }
+
+ @Override
+ protected boolean allowPutStackValidated(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return super.allowPutStackValidated(aBaseMetaTileEntity, aIndex, side, aStack)
+ && getRecipeMap().containsInput(aStack);
+ }
+
+ @Override
+ public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) {
+ super.startSoundLoop(aIndex, aX, aY, aZ);
+ if (aIndex == 1) {
+ GT_Utility.doSoundAtClient(SoundResource.IC2_MACHINES_MAGNETIZER_LOOP, 10, 1.0F, aX, aY, aZ);
+ }
+ }
+
+ @Override
+ public void startProcess() {
+ sendLoopStart((byte) 1);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Teleporter.java b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Teleporter.java
new file mode 100644
index 0000000000..9de00b4a0e
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_Teleporter.java
@@ -0,0 +1,601 @@
+package gregtech.common.tileentities.machines.basic;
+
+import static gregtech.api.enums.GT_Values.V;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASINGS;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TELEPORTER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TELEPORTER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TELEPORTER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TELEPORTER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TELEPORTER_SIDES;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TELEPORTER_SIDES_GLOW;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityHanging;
+import net.minecraft.entity.EntityLiving;
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.entity.boss.EntityDragonPart;
+import net.minecraft.entity.effect.EntityWeatherEffect;
+import net.minecraft.entity.item.EntityBoat;
+import net.minecraft.entity.item.EntityEnderCrystal;
+import net.minecraft.entity.item.EntityEnderEye;
+import net.minecraft.entity.item.EntityFireworkRocket;
+import net.minecraft.entity.item.EntityItem;
+import net.minecraft.entity.item.EntityMinecart;
+import net.minecraft.entity.item.EntityTNTPrimed;
+import net.minecraft.entity.item.EntityXPOrb;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.projectile.EntityArrow;
+import net.minecraft.entity.projectile.EntityFireball;
+import net.minecraft.entity.projectile.EntityFishHook;
+import net.minecraft.entity.projectile.EntityThrowable;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.AxisAlignedBB;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.world.World;
+import net.minecraftforge.common.DimensionManager;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import com.gtnewhorizons.modularui.api.NumberFormatMUI;
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.gui.modularui.GUITextureSet;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.modularui.IAddGregtechLogo;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicTank;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.config.machinestats.ConfigTeleporter;
+
+public class GT_MetaTileEntity_Teleporter extends GT_MetaTileEntity_BasicTank
+ implements IAddGregtechLogo, IAddUIWidgets {
+
+ private static boolean sInterDimensionalTeleportAllowed = true;
+ private static int sPassiveEnergyDrain = 2048;
+ private static int sPowerMultiplyer = 100;
+ private static double sFPowerMultiplyer = 1.0;
+ public int mTargetX = 0;
+ public int mTargetY = 0;
+ public int mTargetZ = 0;
+ public int mTargetD = Integer.MIN_VALUE;
+ public boolean mDebug = false;
+
+ public GT_MetaTileEntity_Teleporter(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 3,
+ new String[] { "Teleport long distances with this little device.", "Use a Dragon Egg or Nitrogen Plasma",
+ "for Inter-dimensional transmission" });
+ }
+
+ public GT_MetaTileEntity_Teleporter(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 3, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Teleporter(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 3, aDescription, aTextures);
+ }
+
+ private static float calculateWeight(Entity aEntity) {
+ if ((aEntity instanceof EntityFishHook)) {
+ return -1.0F;
+ }
+ if ((aEntity instanceof EntityDragonPart)) {
+ return -1.0F;
+ }
+ if ((aEntity instanceof EntityWeatherEffect)) {
+ return -1.0F;
+ }
+ if ((aEntity instanceof EntityPlayer tPlayer)) {
+ int tCount = 64;
+ for (int i = 0; i < 36; i++) {
+ if (tPlayer.inventory.getStackInSlot(i) != null) {
+ tCount += (tPlayer.inventory.getStackInSlot(i)
+ .getMaxStackSize() > 1 ? tPlayer.inventory.getStackInSlot(i).stackSize : 64);
+ }
+ }
+ for (int i = 0; i < 4; i++) {
+ if (tPlayer.inventory.armorInventory[i] != null) {
+ tCount += 256;
+ }
+ }
+ return Math.min(5.0F, tCount / 666.6F);
+ }
+ if (GT_Utility.getClassName(aEntity)
+ .equals("EntityItnt")) {
+ return 5.0F;
+ }
+ if (GT_Utility.getClassName(aEntity)
+ .equals("EntityNuke")) {
+ return 50.0F;
+ }
+ if ((aEntity instanceof EntityArrow)) {
+ return 0.001F;
+ }
+ if ((aEntity instanceof EntityBoat)) {
+ return 0.1F;
+ }
+ if ((aEntity instanceof EntityEnderCrystal)) {
+ return 2.0F;
+ }
+ if ((aEntity instanceof EntityEnderEye)) {
+ return 0.001F;
+ }
+ if ((aEntity instanceof EntityFireball)) {
+ return 0.001F;
+ }
+ if ((aEntity instanceof EntityFireworkRocket)) {
+ return 0.001F;
+ }
+ if ((aEntity instanceof EntityHanging)) {
+ return 0.005F;
+ }
+ if ((aEntity instanceof EntityItem)) {
+ return 0.001F;
+ }
+ if ((aEntity instanceof EntityLiving)) {
+ return 0.5F;
+ }
+ if ((aEntity instanceof EntityMinecart)) {
+ return 0.1F;
+ }
+ if ((aEntity instanceof EntityThrowable)) {
+ return 0.001F;
+ }
+ if ((aEntity instanceof EntityTNTPrimed)) {
+ return 5.0F;
+ }
+ if ((aEntity instanceof EntityXPOrb)) {
+ return 0.001F;
+ }
+ return -1.0F;
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ if (aBaseMetaTileEntity.isClientSide()) return true;
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Teleporter(this.mName, this.mTier, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return true;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ return new String[] { "Coordinates:",
+ "X: " + EnumChatFormatting.GREEN + GT_Utility.formatNumbers(this.mTargetX) + EnumChatFormatting.RESET,
+ "Y: " + EnumChatFormatting.GREEN + GT_Utility.formatNumbers(this.mTargetY) + EnumChatFormatting.RESET,
+ "Z: " + EnumChatFormatting.GREEN + GT_Utility.formatNumbers(this.mTargetZ) + EnumChatFormatting.RESET,
+ "Dimension: " + EnumChatFormatting.GREEN + this.mTargetD + EnumChatFormatting.RESET,
+ "Dimension Valid: " + (GT_Utility.isRealDimension(this.mTargetD)
+ ? EnumChatFormatting.GREEN + "Yes" + EnumChatFormatting.RESET
+ : EnumChatFormatting.RED + "No" + EnumChatFormatting.RESET),
+ "Dimension Registered: " + (DimensionManager.isDimensionRegistered(this.mTargetD)
+ ? EnumChatFormatting.GREEN + "Yes" + EnumChatFormatting.RESET
+ : EnumChatFormatting.RED + "No" + EnumChatFormatting.RESET) };
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side != this.getBaseMetaTileEntity()
+ .getFrontFacing())
+ return new ITexture[] { MACHINE_CASINGS[mTier][colorIndex + 1], TextureFactory.of(OVERLAY_TELEPORTER_SIDES),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TELEPORTER_SIDES_GLOW)
+ .glow()
+ .build() };
+ if (aActive) return new ITexture[] { MACHINE_CASINGS[mTier][colorIndex + 1],
+ TextureFactory.of(OVERLAY_TELEPORTER_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_TELEPORTER_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ return new ITexture[] { MACHINE_CASINGS[mTier][colorIndex + 1], TextureFactory.of(OVERLAY_TELEPORTER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TELEPORTER_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ if (mFluid != null) aNBT.setTag("mFluid", mFluid.writeToNBT(new NBTTagCompound()));
+ aNBT.setInteger("mTargetX", this.mTargetX);
+ aNBT.setInteger("mTargetY", this.mTargetY);
+ aNBT.setInteger("mTargetZ", this.mTargetZ);
+ aNBT.setInteger("mTargetD", this.mTargetD);
+ aNBT.setBoolean("mDebug", this.mDebug);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ mFluid = FluidStack.loadFluidStackFromNBT(aNBT.getCompoundTag("mFluid"));
+ this.mTargetX = aNBT.getInteger("mTargetX");
+ this.mTargetY = aNBT.getInteger("mTargetY");
+ this.mTargetZ = aNBT.getInteger("mTargetZ");
+ this.mTargetD = aNBT.getInteger("mTargetD");
+ this.mDebug = aNBT.getBoolean("mDebug");
+ }
+
+ @Override
+ public void onConfigLoad() {
+ sInterDimensionalTeleportAllowed = ConfigTeleporter.interDimensionalTPAllowed;
+ sPassiveEnergyDrain = ConfigTeleporter.passiveEnergyDrain;
+ sPowerMultiplyer = ConfigTeleporter.powerMultiplier;
+ sFPowerMultiplyer = sPowerMultiplyer / 100.0;
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ if (getBaseMetaTileEntity().isServerSide()) {
+ if ((this.mTargetX == 0) && (this.mTargetY == 0)
+ && (this.mTargetZ == 0)
+ && (this.mTargetD == Integer.MIN_VALUE)) {
+ this.mTargetX = aBaseMetaTileEntity.getXCoord();
+ this.mTargetY = aBaseMetaTileEntity.getYCoord();
+ this.mTargetZ = aBaseMetaTileEntity.getZCoord();
+ this.mTargetD = aBaseMetaTileEntity.getWorld().provider.dimensionId;
+ }
+ }
+ }
+
+ public boolean hasDimensionalTeleportCapability() {
+ return this.mDebug || sInterDimensionalTeleportAllowed;
+ }
+
+ public boolean isDimensionalTeleportAvailable() {
+ return this.mDebug || (hasDimensionalTeleportCapability() && GT_Utility.isRealDimension(this.mTargetD)
+ && GT_Utility.isRealDimension(getBaseMetaTileEntity().getWorld().provider.dimensionId));
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+
+ if (mFluid != null) { // Was if null -> Materials.Nitrogen.getPlasma(0);
+ mFluid = null;
+ }
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ if (getBaseMetaTileEntity().isServerSide()) {
+ if ((getBaseMetaTileEntity().isAllowedToWork()) && (getBaseMetaTileEntity().getRedstone())) {
+ if (getBaseMetaTileEntity().decreaseStoredEnergyUnits(sPassiveEnergyDrain, false)) {
+ int tDistance = distanceCalculation();
+ if (mInventory[0] != null) {
+ TileEntity tTile = null;
+ if (this.mTargetD == getBaseMetaTileEntity().getWorld().provider.dimensionId) {
+ tTile = getBaseMetaTileEntity().getTileEntity(this.mTargetX, this.mTargetY, this.mTargetZ);
+ } else {
+ World tWorld = DimensionManager.getWorld(this.mTargetD);
+ if (tWorld != null) {
+ tTile = tWorld.getTileEntity(this.mTargetX, this.mTargetY, this.mTargetZ);
+ }
+ }
+ if (tTile instanceof IInventory) {
+ int tStacksize = mInventory[0].stackSize;
+ GT_Utility.moveOneItemStack(
+ this,
+ tTile,
+ ForgeDirection.DOWN,
+ ForgeDirection.DOWN,
+ null,
+ false,
+ (byte) 64,
+ (byte) 1,
+ (byte) 64,
+ (byte) 1);
+ if (mInventory[0] == null || mInventory[0].stackSize < tStacksize) {
+ getBaseMetaTileEntity().decreaseStoredEnergyUnits(
+ (long) (Math.pow(tDistance, 1.5) * tDistance
+ * (tStacksize - (mInventory[0] == null ? 0 : mInventory[0].stackSize))
+ * sFPowerMultiplyer),
+ false);
+ }
+ }
+ }
+ List<Entity> entities_in_box = getBaseMetaTileEntity().getWorld()
+ .getEntitiesWithinAABB(
+ Entity.class,
+ AxisAlignedBB.getBoundingBox(
+ getBaseMetaTileEntity().getOffsetX(getBaseMetaTileEntity().getFrontFacing(), 2) - 1,
+ getBaseMetaTileEntity().getOffsetY(getBaseMetaTileEntity().getFrontFacing(), 2) - 1,
+ getBaseMetaTileEntity().getOffsetZ(getBaseMetaTileEntity().getFrontFacing(), 2) - 1,
+ getBaseMetaTileEntity().getOffsetX(getBaseMetaTileEntity().getFrontFacing(), 2) + 2,
+ getBaseMetaTileEntity().getOffsetY(getBaseMetaTileEntity().getFrontFacing(), 2) + 2,
+ getBaseMetaTileEntity().getOffsetZ(getBaseMetaTileEntity().getFrontFacing(), 2) + 2));
+
+ for (Object tObject : entities_in_box) {
+ if (((tObject instanceof Entity tEntity)) && (!((Entity) tObject).isDead)) {
+ if (getBaseMetaTileEntity().decreaseStoredEnergyUnits(
+ (long) (Math.pow(tDistance, 1.5) * calculateWeight(tEntity) * sFPowerMultiplyer),
+ false)) {
+
+ if (tEntity.ridingEntity != null) {
+ tEntity.mountEntity(null);
+ }
+ if (tEntity.riddenByEntity != null) {
+ tEntity.riddenByEntity.mountEntity(null);
+ }
+ if ((this.mTargetD == getBaseMetaTileEntity().getWorld().provider.dimensionId)
+ || (!isDimensionalTeleportAvailable())
+ || (!GT_Utility.moveEntityToDimensionAtCoords(
+ tEntity,
+ this.mTargetD,
+ this.mTargetX + 0.5D,
+ this.mTargetY + 0.5D,
+ this.mTargetZ + 0.5D))) {
+ if ((tEntity instanceof EntityLivingBase)) {
+ ((EntityLivingBase) tEntity).setPositionAndUpdate(
+ this.mTargetX + 0.5D,
+ this.mTargetY + 0.5D,
+ this.mTargetZ + 0.5D);
+ } else {
+ tEntity.setPosition(
+ this.mTargetX + 0.5D,
+ this.mTargetY + 0.5D,
+ this.mTargetZ + 0.5D);
+ }
+ }
+ }
+ }
+ }
+ }
+ getBaseMetaTileEntity().setActive(true);
+ } else {
+ getBaseMetaTileEntity().setActive(false);
+ }
+ }
+ }
+
+ private int distanceCalculation() {
+ return Math.abs(
+ ((this.mTargetD != getBaseMetaTileEntity().getWorld().provider.dimensionId)
+ && (isDimensionalTeleportAvailable())
+ ? 4000
+ : (int) Math.sqrt(
+ Math.pow(getBaseMetaTileEntity().getXCoord() - this.mTargetX, 2.0D)
+ + Math.pow(getBaseMetaTileEntity().getYCoord() - this.mTargetY, 2.0D)
+ + Math.pow(getBaseMetaTileEntity().getZCoord() - this.mTargetZ, 2.0D))));
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return false;
+ }
+
+ @Override
+ public boolean isOverclockerUpgradable() {
+ return false;
+ }
+
+ @Override
+ public boolean isTransformerUpgradable() {
+ return false;
+ }
+
+ @Override
+ public boolean isElectric() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnetInput() {
+ return true;
+ }
+
+ @Override
+ public boolean isInputFacing(ForgeDirection side) {
+ return true;
+ }
+
+ @Override
+ public boolean isOutputFacing(ForgeDirection side) {
+ return false;
+ }
+
+ @Override
+ public boolean isTeleporterCompatible() {
+ return false;
+ }
+
+ @Override
+ public long getMinimumStoredEU() {
+ return V[mTier] * 16;
+ }
+
+ @Override
+ public long maxEUStore() {
+ return 100000000;
+ }
+
+ @Override
+ public long maxEUInput() {
+ return V[mTier];
+ }
+
+ @Override
+ public long maxSteamStore() {
+ return maxEUStore();
+ }
+
+ @Override
+ public long maxAmperesIn() {
+ return 2;
+ }
+
+ @Override
+ public int getStackDisplaySlot() {
+ return 2;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public int getInputSlot() {
+ return 0;
+ }
+
+ @Override
+ public int getOutputSlot() {
+ return 0;
+ }
+
+ @Override
+ public int getCapacity() {
+ return 64000;
+ }
+
+ @Override
+ public boolean doesFillContainers() {
+ return false;
+ }
+
+ @Override
+ public boolean doesEmptyContainers() {
+ return false;
+ }
+
+ @Override
+ public boolean canTankBeFilled() {
+ return true;
+ }
+
+ @Override
+ public boolean canTankBeEmptied() {
+ return true;
+ }
+
+ @Override
+ public boolean displaysItemStack() {
+ return false;
+ }
+
+ @Override
+ public boolean displaysStackSize() {
+ return false;
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ return null;
+ }
+
+ protected static final NumberFormatMUI numberFormat = new NumberFormatMUI();
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ builder.widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_SCREEN_BLACK)
+ .setSize(90, 72)
+ .setPos(43, 4))
+ .widget(
+ new TextWidget().setStringSupplier(() -> "X: " + numberFormat.format(mTargetX))
+ .setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setPos(46, 8))
+ .widget(
+ new TextWidget().setStringSupplier(() -> "Y: " + numberFormat.format(mTargetY))
+ .setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setPos(46, 16))
+ .widget(
+ new TextWidget().setStringSupplier(() -> "Z: " + numberFormat.format(mTargetZ))
+ .setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setPos(46, 24))
+ .widget(
+ new TextWidget().setStringSupplier(() -> "Dim: " + numberFormat.format(mTargetD))
+ .setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setPos(46, 32))
+ .widget(
+ TextWidget.dynamicString(() -> "Dim Valid: " + (GT_Utility.isRealDimension(mTargetD) ? "Yes" : "No"))
+ .setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setEnabled(widget -> hasDimensionalTeleportCapability())
+ .setPos(46, 40))
+ .widget(new FakeSyncWidget.FluidStackSyncer(() -> mFluid, val -> mFluid = val));
+
+ addChangeNumberButtons(builder, GT_UITextures.OVERLAY_BUTTON_MINUS_LARGE, -512, -64, 7);
+ addChangeNumberButtons(builder, GT_UITextures.OVERLAY_BUTTON_MINUS_SMALL, -16, -1, 25);
+ addChangeNumberButtons(builder, GT_UITextures.OVERLAY_BUTTON_PLUS_SMALL, 16, 1, 133);
+ addChangeNumberButtons(builder, GT_UITextures.OVERLAY_BUTTON_PLUS_LARGE, 512, 64, 151);
+
+ addChangeNumberButton(
+ builder,
+ GT_UITextures.OVERLAY_BUTTON_MINUS_LARGE,
+ val -> mTargetD += val,
+ -16,
+ -8,
+ 7,
+ 58);
+ addChangeNumberButton(
+ builder,
+ GT_UITextures.OVERLAY_BUTTON_MINUS_SMALL,
+ val -> mTargetD += val,
+ -4,
+ -1,
+ 25,
+ 58);
+ addChangeNumberButton(builder, GT_UITextures.OVERLAY_BUTTON_PLUS_SMALL, val -> mTargetD += val, 4, 1, 133, 58);
+ addChangeNumberButton(builder, GT_UITextures.OVERLAY_BUTTON_PLUS_LARGE, val -> mTargetD += val, 16, 8, 151, 58);
+ }
+
+ private void addChangeNumberButtons(ModularWindow.Builder builder, IDrawable overlay, int addNumberShift,
+ int addNumber, int xPos) {
+ addChangeNumberButton(builder, overlay, val -> mTargetX += val, addNumberShift, addNumber, xPos, 4);
+ addChangeNumberButton(builder, overlay, val -> mTargetY += val, addNumberShift, addNumber, xPos, 22);
+ addChangeNumberButton(builder, overlay, val -> mTargetZ += val, addNumberShift, addNumber, xPos, 40);
+ }
+
+ private void addChangeNumberButton(ModularWindow.Builder builder, IDrawable overlay, Consumer<Integer> setter,
+ int addNumberShift, int addNumber, int xPos, int yPos) {
+ builder.widget(
+ new ButtonWidget()
+ .setOnClick((clickData, widget) -> setter.accept(clickData.shift ? addNumberShift : addNumber))
+ .setBackground(GT_UITextures.BUTTON_STANDARD, overlay)
+ .setSize(18, 18)
+ .setPos(xPos, yPos));
+ }
+
+ @Override
+ public GUITextureSet getGUITextureSet() {
+ return new GUITextureSet().setGregTechLogo(GT_UITextures.PICTURE_GT_LOGO_17x17_TRANSPARENT_GRAY);
+ }
+
+ @Override
+ public void addGregTechLogo(ModularWindow.Builder builder) {
+ builder.widget(
+ new DrawableWidget().setDrawable(getGUITextureSet().getGregTechLogo())
+ .setSize(17, 17)
+ .setPos(113, 56));
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_TurboCharger.java b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_TurboCharger.java
new file mode 100644
index 0000000000..a098e635f0
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_TurboCharger.java
@@ -0,0 +1,146 @@
+package gregtech.common.tileentities.machines.basic;
+
+import static gregtech.api.enums.GT_Values.V;
+
+import net.minecraft.item.ItemStack;
+
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.BaseMetaTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Utility;
+
+/**
+ * Created by danie_000 on 15.10.2016.
+ */
+public class GT_MetaTileEntity_TurboCharger extends GT_MetaTileEntity_Charger {
+
+ public GT_MetaTileEntity_TurboCharger(int aID, String aName, String aNameRegional, int aTier, String aDescription,
+ int aSlotCount) {
+ super(aID, aName, aNameRegional, aTier, aDescription, aSlotCount);
+ }
+
+ public GT_MetaTileEntity_TurboCharger(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures,
+ int aSlotCount) {
+ super(aName, aTier, aDescription, aTextures, aSlotCount);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_TurboCharger(mName, mTier, mDescriptionArray, mTextures, mInventory.length);
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ ITexture[][][] rTextures = new ITexture[2][17][];
+ for (byte b = -1; b < 16; b++) {
+ rTextures[0][b + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][b + 1] };
+ rTextures[1][b + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][b + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_OUT_POWER[mTier] };
+ }
+ return rTextures;
+ }
+
+ @Override
+ public long getMinimumStoredEU() {
+ return V[mTier] * 1536L * mInventory.length;
+ }
+
+ @Override
+ public long maxEUStore() {
+ return V[mTier] * 6144L * mInventory.length;
+ }
+
+ @Override
+ public long maxAmperesIn() {
+ return 16L * mInventory.length;
+ }
+
+ @Override
+ public long maxAmperesOut() {
+ return 4L * mInventory.length;
+ }
+
+ @Override
+ public long maxEUInput() {
+ return V[mTier];
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aBaseMetaTileEntity.isServerSide()) {
+
+ mCharge = aBaseMetaTileEntity.getStoredEU() / 2 > aBaseMetaTileEntity.getEUCapacity() / 3
+ || !aBaseMetaTileEntity.isAllowedToWork();
+ mDecharge = aBaseMetaTileEntity.getStoredEU() < aBaseMetaTileEntity.getEUCapacity() / 3
+ && aBaseMetaTileEntity.isAllowedToWork();
+ mBatteryCount = 0;
+ mChargeableCount = 0;
+ for (ItemStack tStack : mInventory) {
+ if (GT_ModHandler.isElectricItem(tStack, mTier)) {
+ if (GT_ModHandler.isChargerItem(tStack)) {
+ mBatteryCount++;
+ }
+ mChargeableCount++;
+ }
+ }
+
+ if (getBaseMetaTileEntity() instanceof BaseMetaTileEntity) {
+ BaseMetaTileEntity mBaseMetaTileEntity = (BaseMetaTileEntity) getBaseMetaTileEntity();
+ if (mBaseMetaTileEntity.getMetaTileEntity() instanceof MetaTileEntity) {
+ MetaTileEntity mMetaTileEntity = (MetaTileEntity) mBaseMetaTileEntity.getMetaTileEntity();
+ if (mMetaTileEntity.dechargerSlotCount() > 0
+ && mBaseMetaTileEntity.getStoredEU() < mBaseMetaTileEntity.getEUCapacity()) {
+ for (int i = mMetaTileEntity.dechargerSlotStartIndex(),
+ k = mMetaTileEntity.dechargerSlotCount() + i; i < k; i++) {
+ if (mMetaTileEntity.mInventory[i] != null
+ && mBaseMetaTileEntity.getStoredEU() < mBaseMetaTileEntity.getEUCapacity()) {
+ // CODE
+ mBaseMetaTileEntity.increaseStoredEnergyUnits(
+ GT_ModHandler.dischargeElectricItem(
+ mMetaTileEntity.mInventory[i],
+ GT_Utility.safeInt(
+ Math.min(
+ V[mTier] * 120,
+ mBaseMetaTileEntity.getEUCapacity()
+ - mBaseMetaTileEntity.getStoredEU())),
+ (int) Math.min(Integer.MAX_VALUE, mMetaTileEntity.getInputTier()),
+ true,
+ false,
+ false),
+ true);
+ if (mMetaTileEntity.mInventory[i].stackSize <= 0) {
+ mMetaTileEntity.mInventory[i] = null;
+ }
+ }
+ }
+ }
+ if (mMetaTileEntity.rechargerSlotCount() > 0 && mBaseMetaTileEntity.getStoredEU() > 0) {
+ for (int i = mMetaTileEntity.rechargerSlotStartIndex(),
+ k = mMetaTileEntity.rechargerSlotCount() + i; i < k; i++) {
+ if (mBaseMetaTileEntity.getStoredEU() > 0 && mMetaTileEntity.mInventory[i] != null) {
+ // CODE
+ mBaseMetaTileEntity
+ .decreaseStoredEU(
+ GT_ModHandler.chargeElectricItem(
+ mMetaTileEntity.mInventory[i],
+ GT_Utility
+ .safeInt(Math.min(V[mTier] * 120, mBaseMetaTileEntity.getStoredEU())),
+ (int) Math.min(Integer.MAX_VALUE, mMetaTileEntity.getOutputTier()),
+ true,
+ false),
+ true);
+ if (mMetaTileEntity.mInventory[i].stackSize <= 0) {
+ mMetaTileEntity.mInventory[i] = null;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_WorldAccelerator.java b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_WorldAccelerator.java
new file mode 100644
index 0000000000..1b0048f2ec
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/basic/GT_MetaTileEntity_WorldAccelerator.java
@@ -0,0 +1,470 @@
+package gregtech.common.tileentities.machines.basic;
+
+import static gregtech.api.enums.GT_Values.V;
+import static gregtech.api.enums.Mods.GregTech;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+
+import net.minecraft.block.Block;
+import net.minecraft.client.renderer.texture.IIconRegister;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import eu.usrv.yamcore.auxiliary.PlayerChatHelper;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_TieredMachineBlock;
+import gregtech.api.objects.GT_RenderedTexture;
+import gregtech.api.util.GT_Log;
+
+public class GT_MetaTileEntity_WorldAccelerator extends GT_MetaTileEntity_TieredMachineBlock {
+
+ // simple name is rather expensive to compute and it's not cached
+ // see https://stackoverflow.com/q/17369304
+ private static final ClassValue<String> simpleNameCache = new ClassValue<String>() {
+
+ @Override
+ protected String computeValue(Class<?> type) {
+ return type.getSimpleName();
+ }
+ };
+ private static final HashSet<Class<? extends TileEntity>> _mBlacklistedTiles = new HashSet<>();
+
+ public static boolean addTileToBlacklist(Class<? extends TileEntity> clazz) {
+ return _mBlacklistedTiles.add(clazz);
+ }
+
+ public static boolean addTileToBlacklist(TileEntity tileEntity) {
+ return _mBlacklistedTiles.add(tileEntity.getClass());
+ }
+
+ public static HashSet<Class<? extends TileEntity>> get_mBlacklistedTiles() {
+ return _mBlacklistedTiles;
+ }
+
+ private int _mRadiusTierOverride = -1;
+ private int _mSpeedTierOverride = -1;
+
+ private int getRadiusTierOverride() {
+ if (_mRadiusTierOverride == -1) _mRadiusTierOverride = mTier;
+ return _mRadiusTierOverride;
+ }
+
+ private int getSpeedTierOverride() {
+ if (_mSpeedTierOverride == -1) _mSpeedTierOverride = mTier;
+ return _mSpeedTierOverride;
+ }
+
+ private int incSpeedTierOverride() {
+ _mSpeedTierOverride = getSpeedTierOverride() + 1;
+ if (_mSpeedTierOverride > mTier) _mSpeedTierOverride = 1;
+
+ return _mSpeedTierOverride;
+ }
+
+ private int incRadiusTierOverride() {
+ // Make sure we get the Override value first, as we check it for initial -1
+ _mRadiusTierOverride = getRadiusTierOverride() + 1;
+ if (_mRadiusTierOverride > mTier) _mRadiusTierOverride = 1;
+
+ return _mRadiusTierOverride;
+ }
+
+ private byte mMode = 0; // 0: RandomTicks around 1: TileEntities with range 1
+ private static Textures.BlockIcons.CustomIcon _mGTIco_Norm_Idle;
+ private static Textures.BlockIcons.CustomIcon _mGTIco_Norm_Active;
+ private static Textures.BlockIcons.CustomIcon _mGTIco_TE_Idle;
+ private static Textures.BlockIcons.CustomIcon _mGTIco_TE_Active;
+ private static int[] mAccelerateStatic = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 512, 512, 512, 512, 512, 512 };
+ private static final int AMPERAGE_NORMAL = 3;
+ private static final int AMPERAGE_TE = 6;
+
+ @Override
+ public void registerIcons(IIconRegister aBlockIconRegister) {
+ super.registerIcons(aBlockIconRegister);
+ _mGTIco_Norm_Idle = new Textures.BlockIcons.CustomIcon("iconsets/OVERLAY_ACCELERATOR");
+ _mGTIco_Norm_Active = new Textures.BlockIcons.CustomIcon("iconsets/OVERLAY_ACCELERATOR_ACTIVE");
+ _mGTIco_TE_Idle = new Textures.BlockIcons.CustomIcon("iconsets/OVERLAY_ACCELERATOR_TE");
+ _mGTIco_TE_Active = new Textures.BlockIcons.CustomIcon("iconsets/OVERLAY_ACCELERATOR_TE_ACTIVE");
+ }
+
+ @SideOnly(Side.CLIENT)
+ @Override
+ public void onValueUpdate(byte aValue) {
+ mMode = aValue;
+ }
+
+ @Override
+ public byte getUpdateData() {
+ return mMode;
+ }
+
+ public GT_MetaTileEntity_WorldAccelerator(int pID, String pName, String pNameRegional, int pTier) {
+ super(pID, pName, pNameRegional, pTier, 0, "");
+ }
+
+ @Override
+ public String[] getDescription() {
+ return new String[] {
+ String
+ .format("Accelerating things (Max Radius: %d | Max Speed Bonus: x%d)", mTier, mAccelerateStatic[mTier]),
+ "Use a screwdriver to change mode, sneak to change Radius", "Use a wrench to change speed",
+ "To accelerate TileEntities, this machine has to be adjacent to it",
+ String.format("Normal mode consumes up to %s amperage, depending on radius", AMPERAGE_NORMAL),
+ String.format("TE mode consumes %s amperage", AMPERAGE_TE) };
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return true;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ List<String> tInfoDisplay = new ArrayList<>();
+
+ tInfoDisplay.add(String.format("Accelerator running in %s mode", mModeStr[mMode]));
+ tInfoDisplay.add(
+ String.format(
+ "Speed setting: [%d / %d]",
+ mAccelerateStatic[getSpeedTierOverride()],
+ mAccelerateStatic[mTier]));
+ tInfoDisplay.add(
+ String.format(
+ "Consuming %d EU/t",
+ getEnergyDemand(getSpeedTierOverride(), getRadiusTierOverride(), mMode == 1)));
+
+ // Don't show radius setting if in TE Mode
+ if (mMode == 0) tInfoDisplay.add(String.format("Radius setting: [%d / %d]", getRadiusTierOverride(), mTier));
+
+ return tInfoDisplay.toArray(new String[0]);
+ }
+
+ public GT_MetaTileEntity_WorldAccelerator(String pName, int pTier, int pInvSlotCount, String[] pDescription,
+ ITexture[][][] pTextures) {
+ super(pName, pTier, pInvSlotCount, pDescription, pTextures);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity pTileEntity) {
+ return new GT_MetaTileEntity_WorldAccelerator(mName, mTier, mInventory.length, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] pTextures) {
+ return null;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity pBaseMetaTileEntity, ForgeDirection side, ForgeDirection facing,
+ int colorIndex, boolean pActive, boolean pRedstone) {
+ if (mMode == 0) {
+ return new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][colorIndex + 1], side.offsetY != 0 ? null
+ : pActive ? new GT_RenderedTexture(_mGTIco_Norm_Active) : new GT_RenderedTexture(_mGTIco_Norm_Idle) };
+ } else {
+ return new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][colorIndex + 1], side.offsetY != 0 ? null
+ : pActive ? new GT_RenderedTexture(_mGTIco_TE_Active) : new GT_RenderedTexture(_mGTIco_TE_Idle) };
+ }
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity pBaseMetaTileEntity, int pIndex, ForgeDirection side,
+ ItemStack pStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity pBaseMetaTileEntity, int pIndex, ForgeDirection side,
+ ItemStack pStack) {
+ return false;
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound pNBT) {
+ pNBT.setByte("mAccelMode", mMode);
+
+ // SpeedOverride can never be larger than mTier; Which will never exceed 255, so it's safe to cast here
+ pNBT.setByte("mSpeed", (byte) getSpeedTierOverride());
+ pNBT.setByte("mRadius", (byte) getRadiusTierOverride());
+ }
+
+ public long getEnergyDemand(int pSpeedTier, int pRangeTier, boolean pIsAcceleratingTEs) {
+ // TE mode does not need to consider range setting
+ if (pIsAcceleratingTEs) return V[pSpeedTier] * AMPERAGE_TE;
+
+ // Include range setting into power calculation
+ float multiplier = 100.0F / (float) mTier * (float) pRangeTier / 100.0F;
+ long demand = V[pSpeedTier] * AMPERAGE_NORMAL;
+
+ float tDemand = demand * multiplier;
+
+ return (int) tDemand;
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound pNBT) {
+ mMode = pNBT.getByte("mAccelMode");
+
+ // Make sure we're not crashing with old Accelerator Machines
+ if (pNBT.hasKey("mSpeed")) _mSpeedTierOverride = pNBT.getByte("mSpeed");
+ if (pNBT.hasKey("mRadius")) _mRadiusTierOverride = pNBT.getByte("mRadius");
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer pPlayer) {
+ return true;
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return false;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnetInput() {
+ return true;
+ }
+
+ @Override
+ public boolean isInputFacing(ForgeDirection side) {
+ return true;
+ }
+
+ @Override
+ public boolean isTeleporterCompatible() {
+ return false;
+ }
+
+ @Override
+ public long getMinimumStoredEU() {
+ return 512;
+ }
+
+ @Override
+ public long maxEUStore() {
+ return 512 + V[mTier] * 50;
+ }
+
+ @Override
+ public long maxEUInput() {
+ return V[mTier];
+ }
+
+ @Override
+ public long maxAmperesIn() {
+ return 8;
+ }
+
+ private static String[] mModeStr = { "Blocks", "TileEntities" };
+
+ // This uses the Wrench as second tool to cycle speeds
+ @Override
+ public boolean onWrenchRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer pPlayer, float aX,
+ float aY, float aZ) {
+ incSpeedTierOverride();
+
+ markDirty();
+ PlayerChatHelper.SendInfo(
+ pPlayer,
+ String.format("Machine acceleration changed to x%d", mAccelerateStatic[getSpeedTierOverride()]));
+
+ return true;
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer pPlayer, float pX, float pY, float pZ) {
+ if (pPlayer.isSneaking()) {
+ if (mMode == 0) {
+ incRadiusTierOverride();
+
+ markDirty();
+ PlayerChatHelper
+ .SendInfo(pPlayer, String.format("Machine radius changed to %d Blocks", getRadiusTierOverride()));
+ } else PlayerChatHelper
+ .SendError(pPlayer, String.format("Can't change radius; Machine is in TileEntity Mode!"));
+ } else {
+ mMode = (byte) (mMode == 0x00 ? 0x01 : 0x00);
+ markDirty();
+ PlayerChatHelper.SendInfo(pPlayer, String.format("Switched mode to: %s", mModeStr[mMode]));
+ }
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity pBaseMetaTileEntity, long pTick) {
+ try {
+ if (!pBaseMetaTileEntity.isServerSide()) {
+ return;
+ }
+
+ long tEnergyDemand = getEnergyDemand(getSpeedTierOverride(), getRadiusTierOverride(), mMode == 1);
+
+ // Do we have enough energy to run? Or are we not allowed to run?
+ if (pBaseMetaTileEntity.getStoredEU() < tEnergyDemand || !pBaseMetaTileEntity.isAllowedToWork()) {
+ // Check if machine was active before
+ if (pBaseMetaTileEntity.isActive()) {
+ pBaseMetaTileEntity.setActive(false); // Then disable it now
+ }
+ } else {
+ // Continue to drain power
+ if (pBaseMetaTileEntity.decreaseStoredEnergyUnits(tEnergyDemand, false)) {
+ World tWorld = pBaseMetaTileEntity.getWorld();
+ // Limit the random ticks to once per second
+ if (mMode == 0) {
+ if (pTick % 20 == 0) {
+ doAccelerateNormalBlocks(pBaseMetaTileEntity, tWorld);
+ }
+ } else {
+ doAccelerateTileEntities(pBaseMetaTileEntity, tWorld);
+ }
+
+ } else {
+ // Energy drain failed. Disable machine
+ if (pBaseMetaTileEntity.isActive()) {
+ pBaseMetaTileEntity.setActive(false); // Then disable it now
+ }
+ }
+ }
+ } catch (Exception e) {
+ GT_Log.err.println("GT_MetaTileEntity_WorldAccelerator.onPostTick.crash\n" + e.getMessage());
+ }
+ }
+
+ private void doAccelerateTileEntities(IGregTechTileEntity pBaseMetaTileEntity, World pWorld) {
+ try {
+ if (!pBaseMetaTileEntity.isActive()) {
+ getBaseMetaTileEntity().setActive(true);
+ }
+
+ for (ForgeDirection tDir : ForgeDirection.VALID_DIRECTIONS) {
+ TileEntity tTile = pBaseMetaTileEntity.getTileEntityAtSide(tDir);
+ if (isTEBlackListed(tTile)) {
+ continue;
+ }
+
+ long tMaxTime = System.nanoTime() + 1000000;
+ for (int j = 0; j < mAccelerateStatic[getSpeedTierOverride()]; j++) {
+ tTile.updateEntity();
+ if (System.nanoTime() > tMaxTime) {
+ break;
+ }
+ }
+ }
+ } catch (Exception e) {
+ GT_Log.err.println("GT_MetaTileEntity_WorldAccelerator.doAccelerateTileEntities.crash\n" + e.getMessage());
+ }
+ }
+
+ // Inspired by ChromatiCraft's TileAccelerator
+ private boolean isTEBlackListed(TileEntity pTile) {
+ if (pTile == null) {
+ return true; // Obvious
+ }
+ if (!pTile.canUpdate()) {
+ return true; // Skip if TE can't update at all
+ }
+ if (pTile.isInvalid()) {
+ return true; // Obvious
+ }
+
+ String tSimpleClassName = simpleNameCache.get(pTile.getClass());
+ String tCanonicalName = pTile.getClass()
+ .getCanonicalName()
+ .toLowerCase();
+ if (tSimpleClassName.contains("conduit") || tSimpleClassName.contains("wire")
+ || tSimpleClassName.contains("cable")) {
+ return true;
+ }
+ if (tCanonicalName.contains("appeng") || tCanonicalName.contains(GregTech.ID)) // Don't accelerate ANY gregtech
+ // machines
+ {
+ return true;
+ }
+ if (tSimpleClassName.contains("solar") || tCanonicalName.contains("solar")) // Don't accelerate ANY solars
+ {
+ return true;
+ }
+
+ for (String tS : GT_Values.blacklistedTileEntiyClassNamesForWA) {
+ if (tCanonicalName.equalsIgnoreCase(tS)) {
+ return true;
+ }
+ }
+
+ return GT_MetaTileEntity_WorldAccelerator._mBlacklistedTiles.stream()
+ .map(Class::getCanonicalName)
+ .map(String::toLowerCase)
+ .anyMatch(tCanonicalName::equalsIgnoreCase);
+ }
+
+ /**
+ * Accelerate normal blocks. Eats some power and adds randomTicks to every block within its working area
+ * (Tier-Number = radius) This does only affect blocks that implement the "RandomTick" method; Which is mostly used
+ * for grass growth and plants.
+ *
+ * @param pBaseMetaTileEntity
+ */
+ private void doAccelerateNormalBlocks(IGregTechTileEntity pBaseMetaTileEntity, World pWorld) {
+ if (!pBaseMetaTileEntity.isActive()) {
+ getBaseMetaTileEntity().setActive(true);
+ }
+
+ Random rnd = new Random();
+ int tX = pBaseMetaTileEntity.getXCoord();
+ int tY = pBaseMetaTileEntity.getYCoord();
+ int tZ = pBaseMetaTileEntity.getZCoord();
+
+ int tX1 = tX - getRadiusTierOverride();
+ int tX2 = tX + getRadiusTierOverride();
+ int tY1 = Math.max(tY - getRadiusTierOverride(), 0); // Limit to bedrock
+ int tY2 = Math.min(tY + getRadiusTierOverride(), 255); // Limit to build height
+ int tZ1 = tZ - getRadiusTierOverride();
+ int tZ2 = tZ + getRadiusTierOverride();
+
+ for (int xi = tX1; xi <= tX2; xi++) {
+ for (int yi = tY1; yi <= tY2; yi++) {
+ for (int zi = tZ1; zi <= tZ2; zi++) {
+ tryTickBlock(pWorld, xi, yi, zi, rnd);
+ }
+ }
+ }
+ }
+
+ /**
+ * Send a tick to the target block
+ *
+ * @param pWorld
+ * @param pX
+ * @param pY
+ * @param pZ
+ * @param pRnd
+ */
+ private void tryTickBlock(World pWorld, int pX, int pY, int pZ, Random pRnd) {
+ try {
+ for (int j = 0; j < getSpeedTierOverride(); j++) {
+ Block tBlock = pWorld.getBlock(pX, pY, pZ);
+ if (tBlock.getTickRandomly()) {
+ tBlock.updateTick(pWorld, pX, pY, pZ, pRnd);
+ }
+ }
+ } catch (Exception e) {
+ GT_Log.err.println("GT_MetaTileEntity_WorldAccelerator.tryTickBlock.crash\n" + e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/long_distance/GT_MetaTileEntity_LongDistancePipelineBase.java b/src/main/java/gregtech/common/tileentities/machines/long_distance/GT_MetaTileEntity_LongDistancePipelineBase.java
new file mode 100644
index 0000000000..ff3048f4b2
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/long_distance/GT_MetaTileEntity_LongDistancePipelineBase.java
@@ -0,0 +1,415 @@
+/**
+ *
+ * Inspired/ported from GregTech 6 under the LGPL license
+ *
+ * Copyright (c) 2020 GregTech-6 Team
+ *
+ * This file is part of GregTech.
+ *
+ * GregTech is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * GregTech is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with GregTech. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+package gregtech.common.tileentities.machines.long_distance;
+
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASINGS;
+import static mcp.mobius.waila.api.SpecialChars.BLUE;
+import static mcp.mobius.waila.api.SpecialChars.GOLD;
+import static mcp.mobius.waila.api.SpecialChars.GREEN;
+import static mcp.mobius.waila.api.SpecialChars.RED;
+import static mcp.mobius.waila.api.SpecialChars.RESET;
+import static mcp.mobius.waila.api.SpecialChars.YELLOW;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.ChunkCoordinates;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.items.GT_Block_LongDistancePipe;
+import gregtech.api.metatileentity.BaseMetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicHull_NonElectric;
+import gregtech.api.util.GT_Utility;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public abstract class GT_MetaTileEntity_LongDistancePipelineBase extends GT_MetaTileEntity_BasicHull_NonElectric {
+
+ protected static final int INPUT_INDEX = 0;
+ protected static final int OUTPUT_INDEX = 1;
+ protected static final int SIDE_UP_DOWN_INDEX = 2;
+ protected static final int SIDE_LEFT_RIGHT_INDEX = 3;
+
+ public static int minimalDistancePoints = 64;
+
+ protected GT_MetaTileEntity_LongDistancePipelineBase mTarget = null;
+ // these two are updated by machine block update thread, so must be volatile
+ protected volatile GT_MetaTileEntity_LongDistancePipelineBase mSender = null;
+ protected volatile ChunkCoordinates mTargetPos = null;
+ protected GT_MetaTileEntity_LongDistancePipelineBase mTooCloseTarget = null, mTooCloseSender = null;
+
+ public GT_MetaTileEntity_LongDistancePipelineBase(int aID, String aName, String aNameRegional, int aTier,
+ String aDescription) {
+ super(aID, aName, aNameRegional, aTier, aDescription);
+ }
+
+ public GT_MetaTileEntity_LongDistancePipelineBase(String aName, int aTier, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ @Override
+ public String[] getDescription() {
+ return new String[] { "Only one Input and Output are allowed per pipeline",
+ "Only Input and Output have to be chunkloaded", "Transfer rate is solely limited by input rate",
+ "Minimum distance: " + minimalDistancePoints + " blocks" };
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ if (mTargetPos != null && mTarget != this) {
+ aNBT.setBoolean("target", true);
+ aNBT.setInteger("target.x", mTargetPos.posX);
+ aNBT.setInteger("target.y", mTargetPos.posY);
+ aNBT.setInteger("target.z", mTargetPos.posZ);
+ }
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ if (aNBT.hasKey("target")) {
+ mTargetPos = new ChunkCoordinates(
+ aNBT.getInteger("target.x"),
+ aNBT.getInteger("target.y"),
+ aNBT.getInteger("target.z"));
+ if (getDistanceToSelf(mTargetPos) < minimalDistancePoints) mTargetPos = null;
+ }
+ }
+
+ public boolean isSameClass(GT_MetaTileEntity_LongDistancePipelineBase other) {
+ return false;
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ if (aBaseMetaTileEntity.isClientSide()) return true;
+ ItemStack tCurrentItem = aPlayer.inventory.getCurrentItem();
+ if (tCurrentItem != null) {
+ if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sSoftHammerList)) {
+ scanPipes();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean isDead() {
+ return getBaseMetaTileEntity() == null || getBaseMetaTileEntity().isDead();
+ }
+
+ public boolean checkTarget() {
+ final IGregTechTileEntity gt_tile = getBaseMetaTileEntity();
+ if (gt_tile == null || !gt_tile.isAllowedToWork() || gt_tile.isClientSide()) return false;
+ World world = gt_tile.getWorld();
+ if (world == null) return false;
+
+ if (mTargetPos == null) {
+ // We don't have a target position, scan the pipes
+ scanPipes();
+ } else if (mTarget == null || mTarget.isDead()) {
+ // We don't have a target, or it's dead. Try checking the target position
+ mTarget = null;
+ if (world.blockExists(mTargetPos.posX, mTargetPos.posY, mTargetPos.posZ)) {
+ // Only check if the target position is loaded
+ TileEntity te = world.getTileEntity(mTargetPos.posX, mTargetPos.posY, mTargetPos.posZ);
+ final IMetaTileEntity tMeta;
+ if (te instanceof BaseMetaTileEntity
+ && ((tMeta = ((BaseMetaTileEntity) te)
+ .getMetaTileEntity()) instanceof GT_MetaTileEntity_LongDistancePipelineBase)
+ && isSameClass((GT_MetaTileEntity_LongDistancePipelineBase) tMeta)) {
+ // It's the right type!
+ mTarget = (GT_MetaTileEntity_LongDistancePipelineBase) tMeta;
+ } else if (te != null) {
+ // It isn't the right type, kill the target position
+ mTargetPos = null;
+ }
+ }
+ }
+ if (mTooCloseTarget != null && mTooCloseTarget.mSender == null) mTooCloseTarget.mTooCloseSender = this;
+ if (mTooCloseSender != null && (mTooCloseSender.isDead() || mTooCloseSender.mTarget != null))
+ mTooCloseSender = null;
+ if (mTarget == null || mTarget == this) return false;
+ if (mTarget.mSender == null || mTarget.mSender.isDead()
+ || mTarget.mSender.mTarget == null
+ || mTarget.mSender.mTarget.isDead()) {
+ mTarget.mSender = this;
+ mTarget.mTooCloseSender = null;
+ }
+
+ return mTarget.mSender == this;
+ }
+
+ @Override
+ public ArrayList<String> getSpecialDebugInfo(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer,
+ int aLogLevel, ArrayList<String> aList) {
+ if (mSender != null && !mSender.isDead() && mSender.mTarget == this) {
+ final ChunkCoordinates coords = mSender.getCoords();
+ aList.addAll(
+ Arrays.asList(
+ "Is Pipeline Output",
+ "Pipeline Input is at: X: " + coords.posX + " Y: " + coords.posY + " Z: " + coords.posZ));
+ } else {
+ aList.addAll(
+ Arrays.asList(
+ checkTarget() ? "Is connected to Pipeline Output" : "Pipeline Output is not connected/chunkloaded",
+ "Pipeline Output should be around: X: " + mTargetPos.posX
+ + " Y: "
+ + mTargetPos.posY
+ + " Z: "
+ + mTargetPos.posZ));
+ }
+
+ return aList;
+ }
+
+ // What meta should the pipes for this pipeline have
+ public abstract int getPipeMeta();
+
+ protected void scanPipes() {
+ if (mSender != null && !mSender.isDead() && mSender.mTarget == this) return;
+
+ // Check if we need to scan anything
+ final IGregTechTileEntity gtTile = getBaseMetaTileEntity();
+ if (gtTile == null) return;
+
+ final World world = gtTile.getWorld();
+ if (world == null) return;
+
+ mTargetPos = getCoords();
+ mTarget = this;
+ mSender = null;
+
+ // Start scanning from the output side
+ Block aBlock = gtTile.getBlockAtSide(gtTile.getBackFacing());
+
+ if (aBlock instanceof GT_Block_LongDistancePipe) {
+ byte aMetaData = gtTile.getMetaIDAtSide(gtTile.getBackFacing());
+ if (aMetaData != getPipeMeta()) return;
+
+ HashSet<ChunkCoordinates> tVisited = new HashSet<>(Collections.singletonList(getCoords())),
+ tWires = new HashSet<>();
+ Queue<ChunkCoordinates> tQueue = new LinkedList<>(
+ Collections.singletonList(getFacingOffset(gtTile, gtTile.getBackFacing())));
+
+ while (!tQueue.isEmpty()) {
+ final ChunkCoordinates aCoords = tQueue.poll();
+
+ if (world.getBlock(aCoords.posX, aCoords.posY, aCoords.posZ) == aBlock
+ && world.getBlockMetadata(aCoords.posX, aCoords.posY, aCoords.posZ) == aMetaData) {
+ // We've got another pipe/wire block
+ // TODO: Make sure it's the right type of pipe/wire via meta
+ ChunkCoordinates tCoords;
+ tWires.add(aCoords);
+
+ // For each direction, if we haven't already visited that coordinate, add it to the end of the
+ // queue
+ if (tVisited.add(tCoords = new ChunkCoordinates(aCoords.posX + 1, aCoords.posY, aCoords.posZ)))
+ tQueue.add(tCoords);
+ if (tVisited.add(tCoords = new ChunkCoordinates(aCoords.posX - 1, aCoords.posY, aCoords.posZ)))
+ tQueue.add(tCoords);
+ if (tVisited.add(tCoords = new ChunkCoordinates(aCoords.posX, aCoords.posY + 1, aCoords.posZ)))
+ tQueue.add(tCoords);
+ if (tVisited.add(tCoords = new ChunkCoordinates(aCoords.posX, aCoords.posY - 1, aCoords.posZ)))
+ tQueue.add(tCoords);
+ if (tVisited.add(tCoords = new ChunkCoordinates(aCoords.posX, aCoords.posY, aCoords.posZ + 1)))
+ tQueue.add(tCoords);
+ if (tVisited.add(tCoords = new ChunkCoordinates(aCoords.posX, aCoords.posY, aCoords.posZ - 1)))
+ tQueue.add(tCoords);
+ } else {
+ // It's not a block - let's see if it's a tile entity
+ TileEntity tTileEntity = world.getTileEntity(aCoords.posX, aCoords.posY, aCoords.posZ);
+ if (tTileEntity != gtTile && tTileEntity instanceof BaseMetaTileEntity
+ && ((BaseMetaTileEntity) tTileEntity)
+ .getMetaTileEntity() instanceof GT_MetaTileEntity_LongDistancePipelineBase tGtTile) {
+ if (isSameClass(tGtTile) && tWires.contains(
+ tGtTile.getFacingOffset(
+ (BaseMetaTileEntity) tTileEntity,
+ ((BaseMetaTileEntity) tTileEntity).getFrontFacing()))) {
+ // If it's the same class, and we've scanned a wire in front of it (the input side), we've
+ // found our target
+ // still need to check if it's distant enough
+ int distance = getDistanceToSelf(aCoords);
+ if (distance > minimalDistancePoints) {
+ mTarget = tGtTile;
+ mTargetPos = tGtTile.getCoords();
+ mTooCloseTarget = null;
+ return;
+ } else {
+ if (mTooCloseTarget == null) {
+ mTooCloseTarget = tGtTile;
+ }
+ }
+ }
+
+ // Remove this block from the visited because we might end up back here from another wire that
+ // IS connected to the
+ // input side
+ tVisited.remove(aCoords);
+ }
+ }
+ }
+ }
+ }
+
+ protected int getDistanceToSelf(ChunkCoordinates aCoords) {
+ return Math.abs(getBaseMetaTileEntity().getXCoord() - aCoords.posX)
+ + Math.abs(getBaseMetaTileEntity().getYCoord() - aCoords.posY) / 2
+ + Math.abs(getBaseMetaTileEntity().getZCoord() - aCoords.posZ);
+ }
+
+ public ChunkCoordinates getFacingOffset(IGregTechTileEntity gt_tile, ForgeDirection side) {
+ return new ChunkCoordinates(
+ gt_tile.getOffsetX(side, 1),
+ gt_tile.getOffsetY(side, 1),
+ gt_tile.getOffsetZ(side, 1));
+ }
+
+ public ChunkCoordinates getCoords() {
+ final IGregTechTileEntity gt_tile = getBaseMetaTileEntity();
+ return new ChunkCoordinates(gt_tile.getXCoord(), gt_tile.getYCoord(), gt_tile.getZCoord());
+ }
+
+ @Override
+ public void onMachineBlockUpdate() {
+ mTargetPos = null;
+ mSender = null;
+ }
+
+ @Override
+ public boolean shouldTriggerBlockUpdate() {
+ return true;
+ }
+
+ abstract public ITexture[] getTextureOverlays();
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ ITexture[][][] rTextures = new ITexture[4][17][];
+ ITexture[] overlays = getTextureOverlays();
+ for (int i = 0; i < rTextures[0].length; i++) {
+ rTextures[INPUT_INDEX][i] = new ITexture[] { MACHINE_CASINGS[mTier][i], overlays[INPUT_INDEX] };
+ rTextures[OUTPUT_INDEX][i] = new ITexture[] { MACHINE_CASINGS[mTier][i], overlays[OUTPUT_INDEX] };
+ rTextures[SIDE_UP_DOWN_INDEX][i] = new ITexture[] { MACHINE_CASINGS[mTier][i],
+ overlays[SIDE_UP_DOWN_INDEX] };
+ rTextures[SIDE_LEFT_RIGHT_INDEX][i] = new ITexture[] { MACHINE_CASINGS[mTier][i],
+ overlays[SIDE_LEFT_RIGHT_INDEX] };
+ }
+ return rTextures;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ colorIndex += 1;
+ if (sideDirection == facingDirection) return mTextures[INPUT_INDEX][colorIndex];
+ else if (sideDirection == facingDirection.getOpposite()) return mTextures[OUTPUT_INDEX][colorIndex];
+ else {
+ switch (facingDirection) {
+ case UP, DOWN -> {
+ return mTextures[SIDE_UP_DOWN_INDEX][colorIndex];
+ }
+ case NORTH -> {
+ switch (sideDirection) {
+ case DOWN, UP -> {
+ return mTextures[SIDE_UP_DOWN_INDEX][colorIndex];
+ }
+ case EAST, WEST -> {
+ return mTextures[SIDE_LEFT_RIGHT_INDEX][colorIndex];
+ }
+ default -> {}
+ }
+ }
+ case SOUTH -> {
+ switch (sideDirection) {
+ case DOWN, UP -> {
+ return mTextures[SIDE_UP_DOWN_INDEX][colorIndex];
+ }
+ case EAST, WEST -> {
+ return mTextures[SIDE_LEFT_RIGHT_INDEX][colorIndex];
+ }
+ default -> {}
+ }
+ }
+ case EAST, WEST -> {
+ return mTextures[SIDE_LEFT_RIGHT_INDEX][colorIndex];
+ }
+ default -> {}
+ }
+ }
+ return mTextures[INPUT_INDEX][colorIndex]; // dummy
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ final ForgeDirection facing = getBaseMetaTileEntity().getFrontFacing();
+ final ForgeDirection side = accessor.getSide();
+
+ final NBTTagCompound tag = accessor.getNBTData();
+ final boolean hasInput = tag.getBoolean("hasInput");
+ final boolean hasInputTooClose = tag.getBoolean("hasInputTooClose");
+ final boolean hasOutput = tag.getBoolean("hasOutput");
+ final boolean hasOutputTooClose = tag.getBoolean("hasOutputTooClose");
+
+ if (side == facing) currentTip.add(GOLD + "Pipeline Input" + RESET);
+ else if (side == facing.getOpposite()) currentTip.add(BLUE + "Pipeline Output" + RESET);
+ else currentTip.add("Pipeline Side");
+
+ if (!hasInput && !hasInputTooClose && !hasOutput && !hasOutputTooClose) {
+ currentTip.add(YELLOW + "Not connected" + RESET);
+ }
+
+ if (hasInput) currentTip.add(GREEN + "Connected to " + GOLD + "Input" + RESET);
+ else if (hasInputTooClose) currentTip.add(RED + "Connected Input too close" + RESET);
+ else if (hasOutput) currentTip.add(GREEN + "Connected to " + BLUE + "Output" + RESET);
+ else if (hasOutputTooClose) currentTip.add(RED + "Connected Output too close" + RESET);
+
+ super.getWailaBody(itemStack, currentTip, accessor, config);
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+
+ tag.setBoolean("hasInput", mSender != null);
+ tag.setBoolean("hasInputTooClose", mTooCloseSender != null);
+ tag.setBoolean("hasOutput", mTarget != null && mTarget != this);
+ tag.setBoolean("hasOutputTooClose", mTooCloseTarget != null);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/long_distance/GT_MetaTileEntity_LongDistancePipelineFluid.java b/src/main/java/gregtech/common/tileentities/machines/long_distance/GT_MetaTileEntity_LongDistancePipelineFluid.java
new file mode 100644
index 0000000000..70e295f3ad
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/long_distance/GT_MetaTileEntity_LongDistancePipelineFluid.java
@@ -0,0 +1,125 @@
+/**
+ *
+ * Inspired/ported from GregTech 6 under the LGPL license
+ *
+ * Copyright (c) 2020 GregTech-6 Team
+ *
+ * This file is part of GregTech.
+ *
+ * GregTech is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * GregTech is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with GregTech. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+package gregtech.common.tileentities.machines.long_distance;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPELINE_FLUID_BACK;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPELINE_FLUID_FRONT;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPELINE_FLUID_SIDE_LEFT_RIGHT;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPELINE_FLUID_SIDE_LEFT_RIGHT_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPELINE_FLUID_SIDE_UP_DOWN;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPELINE_FLUID_SIDE_UP_DOWN_GLOW;
+
+import net.minecraft.tileentity.TileEntity;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.FluidTankInfo;
+import net.minecraftforge.fluids.IFluidHandler;
+
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.render.TextureFactory;
+
+public class GT_MetaTileEntity_LongDistancePipelineFluid extends GT_MetaTileEntity_LongDistancePipelineBase {
+
+ static final FluidTankInfo[] emptyTank = { new FluidTankInfo(null, Integer.MAX_VALUE) };
+
+ public GT_MetaTileEntity_LongDistancePipelineFluid(int aID, String aName, String aNameRegional, int aTier) {
+ super(aID, aName, aNameRegional, aTier, "Sends fluids over long distances");
+ }
+
+ public GT_MetaTileEntity_LongDistancePipelineFluid(String aName, int aTier, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ @Override
+ public boolean isSameClass(GT_MetaTileEntity_LongDistancePipelineBase other) {
+ return other instanceof GT_MetaTileEntity_LongDistancePipelineFluid;
+ }
+
+ @Override
+ public int getPipeMeta() {
+ return 0;
+ }
+
+ public IFluidHandler getTank() {
+ final IGregTechTileEntity tTile = mTarget.getBaseMetaTileEntity();
+ TileEntity tankTile = tTile.getTileEntityAtSide(tTile.getBackFacing());
+ if (tankTile instanceof IFluidHandler) return (IFluidHandler) tankTile;
+ else return null;
+ }
+
+ @Override
+ public FluidTankInfo[] getTankInfo(ForgeDirection side) {
+ if (checkTarget()) {
+ final IFluidHandler tankTile = getTank();
+ if (tankTile != null) return tankTile.getTankInfo(side);
+ }
+
+ return emptyTank;
+ }
+
+ @Override
+ public int fill(ForgeDirection side, FluidStack aFluid, boolean aDoFill) {
+ if (checkTarget()) {
+ final IGregTechTileEntity tTile = mTarget.getBaseMetaTileEntity();
+ final IFluidHandler tankTile = getTank();
+ if (tankTile != null) return tankTile.fill(tTile.getFrontFacing(), aFluid, aDoFill);
+ }
+ return 0;
+ }
+
+ @Override
+ public FluidStack drain(ForgeDirection side, FluidStack aFluid, boolean aDoDrain) {
+ return null;
+ }
+
+ @Override
+ public FluidStack drain(ForgeDirection side, int aMaxDrain, boolean aDoDrain) {
+ return null;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_LongDistancePipelineFluid(mName, mTier, getDescription()[0], mTextures);
+ }
+
+ @Override
+ public ITexture[] getTextureOverlays() {
+ ITexture[] overlays = new ITexture[4];
+ overlays[INPUT_INDEX] = TextureFactory.of(OVERLAY_PIPELINE_FLUID_FRONT);
+ overlays[OUTPUT_INDEX] = TextureFactory.of(OVERLAY_PIPELINE_FLUID_BACK);
+ overlays[SIDE_UP_DOWN_INDEX] = TextureFactory.of(
+ TextureFactory.of(OVERLAY_PIPELINE_FLUID_SIDE_UP_DOWN),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_PIPELINE_FLUID_SIDE_UP_DOWN_GLOW)
+ .glow()
+ .build());
+ overlays[SIDE_LEFT_RIGHT_INDEX] = TextureFactory.of(
+ TextureFactory.of(OVERLAY_PIPELINE_FLUID_SIDE_LEFT_RIGHT),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_PIPELINE_FLUID_SIDE_LEFT_RIGHT_GLOW)
+ .glow()
+ .build());
+
+ return overlays;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/long_distance/GT_MetaTileEntity_LongDistancePipelineItem.java b/src/main/java/gregtech/common/tileentities/machines/long_distance/GT_MetaTileEntity_LongDistancePipelineItem.java
new file mode 100644
index 0000000000..e1510f4d3d
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/long_distance/GT_MetaTileEntity_LongDistancePipelineItem.java
@@ -0,0 +1,214 @@
+/**
+ *
+ * Inspired/ported from GregTech 6 under the LGPL license
+ *
+ * Copyright (c) 2020 GregTech-6 Team
+ *
+ * This file is part of GregTech.
+ *
+ * GregTech is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * GregTech is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with GregTech. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+package gregtech.common.tileentities.machines.long_distance;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPELINE_ITEM_BACK;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPELINE_ITEM_FRONT;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPELINE_ITEM_SIDE_LEFT_RIGHT;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPELINE_ITEM_SIDE_LEFT_RIGHT_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPELINE_ITEM_SIDE_UP_DOWN;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPELINE_ITEM_SIDE_UP_DOWN_GLOW;
+
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.ISidedInventory;
+import net.minecraft.item.ItemStack;
+import net.minecraft.tileentity.TileEntity;
+
+import gregtech.api.enums.GT_Values;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.render.TextureFactory;
+
+public class GT_MetaTileEntity_LongDistancePipelineItem extends GT_MetaTileEntity_LongDistancePipelineBase {
+
+ public GT_MetaTileEntity_LongDistancePipelineItem(int aID, String aName, String aNameRegional, int aTier) {
+ super(aID, aName, aNameRegional, aTier, "Sends Items over long distances");
+ }
+
+ public GT_MetaTileEntity_LongDistancePipelineItem(String aName, int aTier, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ @Override
+ public boolean isSameClass(GT_MetaTileEntity_LongDistancePipelineBase other) {
+ return other instanceof GT_MetaTileEntity_LongDistancePipelineItem;
+ }
+
+ @Override
+ public int getPipeMeta() {
+ return 1;
+ }
+
+ public IInventory getInventory() {
+ final IGregTechTileEntity tTile = mTarget.getBaseMetaTileEntity();
+ TileEntity invTile = tTile.getTileEntityAtSide(tTile.getBackFacing());
+ if (invTile instanceof IInventory) return (IInventory) invTile;
+ else return null;
+ }
+
+ @Override
+ public ItemStack decrStackSize(int aSlot, int aDecrement) {
+ if (checkTarget()) {
+ IInventory iInventory = getInventory();
+ if (iInventory != null) return iInventory.decrStackSize(aSlot, aDecrement);
+ }
+ return null;
+ }
+
+ @Override
+ public ItemStack getStackInSlotOnClosing(int aSlot) {
+ if (checkTarget()) {
+ IInventory iInventory = getInventory();
+ if (iInventory != null) return iInventory.getStackInSlotOnClosing(aSlot);
+ }
+ return null;
+ }
+
+ @Override
+ public ItemStack getStackInSlot(int aSlot) {
+ if (checkTarget()) {
+ IInventory iInventory = getInventory();
+ if (iInventory != null) return iInventory.getStackInSlot(aSlot);
+ }
+ return null;
+ }
+
+ @Override
+ public String getInventoryName() {
+ if (checkTarget()) {
+ IInventory iInventory = getInventory();
+ if (iInventory != null) return iInventory.getInventoryName();
+ }
+ return super.getInventoryName();
+ }
+
+ @Override
+ public int getSizeInventory() {
+ if (checkTarget()) {
+ IInventory iInventory = getInventory();
+ if (iInventory != null) return iInventory.getSizeInventory();
+ }
+ return 0;
+ }
+
+ @Override
+ public int getInventoryStackLimit() {
+ if (checkTarget()) {
+ IInventory iInventory = getInventory();
+ if (iInventory != null) return iInventory.getInventoryStackLimit();
+ }
+ return 0;
+ }
+
+ @Override
+ public void setInventorySlotContents(int aSlot, ItemStack aStack) {
+ if (checkTarget()) {
+ IInventory iInventory = getInventory();
+ if (iInventory != null) iInventory.setInventorySlotContents(aSlot, aStack);
+ }
+ }
+
+ @Override
+ public boolean hasCustomInventoryName() {
+ if (checkTarget()) {
+ IInventory iInventory = getInventory();
+ if (iInventory != null) return iInventory.hasCustomInventoryName();
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isItemValidForSlot(int aSlot, ItemStack aStack) {
+ if (checkTarget()) {
+ IInventory iInventory = getInventory();
+ if (iInventory != null) return iInventory.isItemValidForSlot(aSlot, aStack);
+ }
+ return false;
+ }
+
+ // // Relay Sided Inventories
+ //
+
+ @Override
+ public int[] getAccessibleSlotsFromSide(int ordinalSide) {
+ if (checkTarget()) {
+ final IGregTechTileEntity tTile = mTarget.getBaseMetaTileEntity();
+ final IInventory iInventory = getInventory();
+ if (iInventory instanceof ISidedInventory inv) return inv.getAccessibleSlotsFromSide(
+ tTile.getFrontFacing()
+ .ordinal());
+ if (iInventory != null) {
+ final int[] tReturn = new int[iInventory.getSizeInventory()];
+ for (int i = 0; i < tReturn.length; i++) tReturn[i] = i;
+ return tReturn;
+ }
+ }
+
+ return GT_Values.emptyIntArray;
+ }
+
+ @Override
+ public boolean canInsertItem(int aSlot, ItemStack aStack, int ordinalSide) {
+ if (checkTarget()) {
+ final IGregTechTileEntity tTile = mTarget.getBaseMetaTileEntity();
+ IInventory iInventory = getInventory();
+ if (iInventory instanceof ISidedInventory iSidedInventory) return iSidedInventory.canInsertItem(
+ aSlot,
+ aStack,
+ tTile.getFrontFacing()
+ .ordinal());
+ return iInventory != null;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean canExtractItem(int aSlot, ItemStack aStack, int ordinalSide) {
+ return false;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_LongDistancePipelineItem(mName, mTier, getDescription()[0], mTextures);
+ }
+
+ @Override
+ public ITexture[] getTextureOverlays() {
+ ITexture[] overlays = new ITexture[4];
+ overlays[INPUT_INDEX] = TextureFactory.of(OVERLAY_PIPELINE_ITEM_FRONT);
+ overlays[OUTPUT_INDEX] = TextureFactory.of(OVERLAY_PIPELINE_ITEM_BACK);
+ overlays[SIDE_UP_DOWN_INDEX] = TextureFactory.of(
+ TextureFactory.of(OVERLAY_PIPELINE_ITEM_SIDE_UP_DOWN),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_PIPELINE_ITEM_SIDE_UP_DOWN_GLOW)
+ .glow()
+ .build());
+ overlays[SIDE_LEFT_RIGHT_INDEX] = TextureFactory.of(
+ TextureFactory.of(OVERLAY_PIPELINE_ITEM_SIDE_LEFT_RIGHT),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_PIPELINE_ITEM_SIDE_LEFT_RIGHT_GLOW)
+ .glow()
+ .build());
+
+ return overlays;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_AbstractMultiFurnace.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_AbstractMultiFurnace.java
new file mode 100644
index 0000000000..0c91951e28
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_AbstractMultiFurnace.java
@@ -0,0 +1,48 @@
+package gregtech.common.tileentities.machines.multi;
+
+import net.minecraft.item.ItemStack;
+
+import gregtech.api.enums.HeatingCoilLevel;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase;
+
+public abstract class GT_MetaTileEntity_AbstractMultiFurnace<T extends GT_MetaTileEntity_AbstractMultiFurnace<T>>
+ extends GT_MetaTileEntity_ExtendedPowerMultiBlockBase<T> {
+
+ private HeatingCoilLevel mCoilLevel;
+
+ protected GT_MetaTileEntity_AbstractMultiFurnace(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ protected GT_MetaTileEntity_AbstractMultiFurnace(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ public HeatingCoilLevel getCoilLevel() {
+ return mCoilLevel;
+ }
+
+ public void setCoilLevel(HeatingCoilLevel aCoilLevel) {
+ mCoilLevel = aCoilLevel;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_AssemblyLine.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_AssemblyLine.java
new file mode 100644
index 0000000000..aa422505d7
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_AssemblyLine.java
@@ -0,0 +1,495 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.GT_Mod.GT_FML_LOGGER;
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ASSEMBLY_LINE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ASSEMBLY_LINE_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ASSEMBLY_LINE_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ASSEMBLY_LINE_GLOW;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_StructureUtility.ofHatchAdder;
+import static gregtech.api.util.GT_Utility.filterValidMTEs;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Textures;
+import gregtech.api.enums.Textures.BlockIcons;
+import gregtech.api.enums.VoidingMode;
+import gregtech.api.interfaces.IHatchElement;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_DataAccess;
+import gregtech.api.multitileentity.multiblock.casing.Glasses;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_AssemblyLineUtils;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Recipe.GT_Recipe_AssemblyLine;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.IGT_HatchAdder;
+
+public class GT_MetaTileEntity_AssemblyLine extends
+ GT_MetaTileEntity_ExtendedPowerMultiBlockBase<GT_MetaTileEntity_AssemblyLine> implements ISurvivalConstructable {
+
+ public ArrayList<GT_MetaTileEntity_Hatch_DataAccess> mDataAccessHatches = new ArrayList<>();
+ private static final String STRUCTURE_PIECE_FIRST = "first";
+ private static final String STRUCTURE_PIECE_LATER = "later";
+ private static final String STRUCTURE_PIECE_LAST = "last";
+ private static final IStructureDefinition<GT_MetaTileEntity_AssemblyLine> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_AssemblyLine>builder()
+ .addShape(
+ STRUCTURE_PIECE_FIRST,
+ transpose(new String[][] { { " ", "e", " " }, { "~", "l", "G" }, { "g", "m", "g" }, { "b", "i", "b" }, }))
+ .addShape(
+ STRUCTURE_PIECE_LATER,
+ transpose(new String[][] { { " ", "e", " " }, { "d", "l", "d" }, { "g", "m", "g" }, { "b", "I", "b" }, }))
+ .addShape(
+ STRUCTURE_PIECE_LAST,
+ transpose(new String[][] { { " ", "e", " " }, { "d", "l", "d" }, { "g", "m", "g" }, { "o", "i", "b" }, }))
+ .addElement('G', ofBlock(GregTech_API.sBlockCasings3, 10)) // grate machine casing
+ .addElement('l', ofBlock(GregTech_API.sBlockCasings2, 9)) // assembler machine casing
+ .addElement('m', ofBlock(GregTech_API.sBlockCasings2, 5)) // assembling line casing
+ .addElement('g', Glasses.chainAllGlasses())
+ .addElement(
+ 'e',
+ ofChain(
+ Energy.newAny(16, 1, ForgeDirection.UP, ForgeDirection.NORTH, ForgeDirection.SOUTH),
+ ofBlock(GregTech_API.sBlockCasings2, 0)))
+ .addElement(
+ 'd',
+ buildHatchAdder(GT_MetaTileEntity_AssemblyLine.class).atLeast(DataHatchElement.DataAccess)
+ .dot(2)
+ .casingIndex(42)
+ .allowOnly(ForgeDirection.NORTH)
+ .buildAndChain(GregTech_API.sBlockCasings3, 10))
+ .addElement(
+ 'b',
+ buildHatchAdder(GT_MetaTileEntity_AssemblyLine.class)
+ .atLeast(InputHatch, InputHatch, InputHatch, InputHatch, Maintenance)
+ .casingIndex(16)
+ .dot(3)
+ .allowOnly(ForgeDirection.DOWN)
+ .buildAndChain(
+ ofBlock(GregTech_API.sBlockCasings2, 0),
+ ofHatchAdder(GT_MetaTileEntity_AssemblyLine::addOutputToMachineList, 16, 4)))
+ .addElement(
+ 'I',
+ ofChain(
+ // all blocks nearby use solid steel casing, so let's use the texture of that
+ InputBus.newAny(16, 5, ForgeDirection.DOWN),
+ ofHatchAdder(GT_MetaTileEntity_AssemblyLine::addOutputToMachineList, 16, 4)))
+ .addElement('i', InputBus.newAny(16, 5, ForgeDirection.DOWN))
+ .addElement('o', OutputBus.newAny(16, 4, ForgeDirection.DOWN))
+ .build();
+
+ public GT_MetaTileEntity_AssemblyLine(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_AssemblyLine(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_AssemblyLine(this.mName);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Assembling Line")
+ .addInfo("Controller block for the Assembling Line")
+ .addInfo("Used to make complex machine parts (LuV+)")
+ .addInfo("Does not make Assembler items")
+ .addInfo("Recipe tier is at most Energy Hatch tier + 1.")
+ .addSeparator()
+ .beginVariableStructureBlock(5, 16, 4, 4, 3, 3, false) // ?
+ .addStructureInfo("From Bottom to Top, Left to Right")
+ .addStructureInfo(
+ "Layer 1 - Solid Steel Machine Casing, Input Bus (last can be Output Bus), Solid Steel Machine Casing")
+ .addStructureInfo(
+ "Layer 2 - Borosilicate Glass(any)/Warded Glass/Reinforced Glass, Assembling Line Casing, Reinforced Glass")
+ .addStructureInfo("Layer 3 - Grate Machine Casing, Assembler Machine Casing, Grate Machine Casing")
+ .addStructureInfo("Layer 4 - Empty, Solid Steel Machine Casing, Empty")
+ .addStructureInfo("Up to 16 repeating slices, each one allows for 1 more item in recipes")
+ .addController("Either Grate on layer 3 of the first slice")
+ .addEnergyHatch("Any layer 4 casing", 1)
+ .addMaintenanceHatch("Any layer 1 casing", 3)
+ .addInputBus("As specified on layer 1", 4, 5)
+ .addInputHatch("Any layer 1 casing", 3)
+ .addOutputBus("Replaces Input Bus on final slice or on any solid steel casing on layer 1", 4)
+ .addOtherStructurePart("Data Access Hatch", "Optional, next to controller", 2)
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ if (sideDirection == facingDirection) {
+ if (active) return new ITexture[] { BlockIcons.casingTexturePages[0][16], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ASSEMBLY_LINE_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ASSEMBLY_LINE_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { BlockIcons.casingTexturePages[0][16], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ASSEMBLY_LINE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ASSEMBLY_LINE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { Textures.BlockIcons.casingTexturePages[0][16] };
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.assemblylineVisualRecipes;
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ @NotNull
+ public CheckRecipeResult checkProcessing() {
+ if (GT_Values.D1) {
+ GT_FML_LOGGER.info("Start ALine recipe check");
+ }
+ CheckRecipeResult result = CheckRecipeResultRegistry.NO_DATA_STICKS;
+
+ ArrayList<ItemStack> tDataStickList = getDataItems(2);
+ if (tDataStickList.isEmpty()) {
+ return result;
+ }
+ if (GT_Values.D1) {
+ GT_FML_LOGGER.info("Stick accepted, " + tDataStickList.size() + " Data Sticks found");
+ }
+
+ int[] tStacks = new int[0];
+ FluidStack[] tFluids = new FluidStack[0];
+ long averageVoltage = getAverageInputVoltage();
+ long maxAmp = mEnergyHatches.size() <= 1 ? 1 : getMaxInputAmps();
+ int maxParallel = 1;
+ Map<GT_Utility.ItemId, ItemStack> inputsFromME = getStoredInputsFromME();
+ Map<Fluid, FluidStack> fluidsFromME = getStoredFluidsFromME();
+
+ for (ItemStack tDataStick : tDataStickList) {
+ GT_AssemblyLineUtils.LookupResult tLookupResult = GT_AssemblyLineUtils
+ .findAssemblyLineRecipeFromDataStick(tDataStick, false);
+
+ if (tLookupResult.getType() == GT_AssemblyLineUtils.LookupResultType.INVALID_STICK) {
+ result = CheckRecipeResultRegistry.NO_RECIPE;
+ continue;
+ }
+
+ GT_Recipe_AssemblyLine tRecipe = tLookupResult.getRecipe();
+ // Check if the recipe on the data stick is the current recipe for it's given output, if not we update it
+ // and continue to next.
+ if (tLookupResult.getType() != GT_AssemblyLineUtils.LookupResultType.VALID_STACK_AND_VALID_HASH) {
+ tRecipe = GT_AssemblyLineUtils.processDataStick(tDataStick);
+ if (tRecipe == null) {
+ result = CheckRecipeResultRegistry.NO_RECIPE;
+ continue;
+ }
+ }
+
+ // Void protection check.
+ if (!canOutputAll(new ItemStack[] { tRecipe.mOutput })) {
+ result = CheckRecipeResultRegistry.ITEM_OUTPUT_FULL;
+ continue;
+ }
+
+ // So here we check against the recipe found on the data stick.
+ // If we run into missing buses/hatches or bad inputs, we go to the next data stick.
+ // This check only happens if we have a valid up-to-date data stick.
+
+ // first validate we have enough input busses and input hatches for this recipe
+ if (mInputBusses.size() < tRecipe.mInputs.length || mInputHatches.size() < tRecipe.mFluidInputs.length) {
+ if (GT_Values.D1) {
+ GT_FML_LOGGER.info(
+ "Not enough sources: Need ({}, {}), has ({}, {})",
+ mInputBusses.size(),
+ tRecipe.mInputs.length,
+ mInputHatches.size(),
+ tRecipe.mFluidInputs.length);
+ }
+ result = CheckRecipeResultRegistry.NO_RECIPE;
+ continue;
+ }
+
+ // Check Inputs allign
+ int[] itemConsumptions = GT_Recipe_AssemblyLine.getItemConsumptionAmountArray(mInputBusses, tRecipe);
+ if (itemConsumptions == null || itemConsumptions.length == 0) {
+ result = CheckRecipeResultRegistry.NO_RECIPE;
+ continue;
+ }
+ maxParallel = (int) GT_Recipe_AssemblyLine
+ .maxParallelCalculatedByInputItems(mInputBusses, maxParallel, itemConsumptions, inputsFromME);
+ if (maxParallel <= 0) {
+ result = CheckRecipeResultRegistry.NO_RECIPE;
+ continue;
+ }
+ tStacks = itemConsumptions;
+
+ if (GT_Values.D1) {
+ GT_FML_LOGGER.info("All Items accepted");
+ }
+
+ // Check Fluid Inputs allign
+ if (tRecipe.mFluidInputs.length > 0) {
+ maxParallel = (int) GT_Recipe_AssemblyLine
+ .maxParallelCalculatedByInputFluids(mInputHatches, maxParallel, tRecipe.mFluidInputs, fluidsFromME);
+ if (maxParallel <= 0) {
+ result = CheckRecipeResultRegistry.NO_RECIPE;
+ continue;
+ }
+ tFluids = tRecipe.mFluidInputs;
+ }
+
+ if (GT_Values.D1) {
+ GT_FML_LOGGER.info("All fluids accepted");
+ }
+
+ if (GT_Values.D1) {
+ GT_FML_LOGGER.info("Check overclock");
+ }
+
+ // Recipe tier is limited to hatch tier + 1.
+ if (tRecipe.mEUt > averageVoltage * 4) {
+ result = CheckRecipeResultRegistry.insufficientPower(tRecipe.mEUt);
+ continue;
+ }
+
+ // Insufficient power check.
+ if (tRecipe.mEUt > maxAmp * averageVoltage) {
+ result = CheckRecipeResultRegistry.insufficientPower(tRecipe.mEUt);
+ continue;
+ }
+
+ calculateOverclockedNessMultiInternal(tRecipe.mEUt, tRecipe.mDuration, (int) maxAmp, averageVoltage, false);
+ // In case recipe is too OP for that machine
+ if (lEUt == Long.MAX_VALUE) {
+ if (GT_Values.D1) {
+ GT_FML_LOGGER.info("Recipe too OP");
+ }
+ result = CheckRecipeResultRegistry.POWER_OVERFLOW;
+ continue;
+ }
+
+ if (mMaxProgresstime == Integer.MAX_VALUE) {
+ if (GT_Values.D1) {
+ GT_FML_LOGGER.info("Recipe too OP");
+ }
+ result = CheckRecipeResultRegistry.DURATION_OVERFLOW;
+ continue;
+ }
+
+ if (GT_Values.D1) {
+ GT_FML_LOGGER.info("Find available recipe");
+ }
+ result = CheckRecipeResultRegistry.SUCCESSFUL;
+ mOutputItems = new ItemStack[] { tRecipe.mOutput };
+ break;
+ }
+
+ if (!result.wasSuccessful()) {
+ return result;
+ }
+
+ // Must be something wrong here...
+ if (tStacks.length == 0 || maxParallel <= 0) {
+ return CheckRecipeResultRegistry.INTERNAL_ERROR;
+ }
+
+ if (GT_Values.D1) {
+ GT_FML_LOGGER.info("All checked start consuming inputs");
+ }
+
+ GT_Recipe_AssemblyLine.consumeInputItems(mInputBusses, maxParallel, tStacks, inputsFromME);
+ GT_Recipe_AssemblyLine.consumeInputFluids(mInputHatches, maxParallel, tFluids, fluidsFromME);
+
+ if (this.lEUt > 0) {
+ this.lEUt = -this.lEUt;
+ }
+ this.mEfficiency = (10000 - (getIdealStatus() - getRepairStatus()) * 1000);
+ this.mEfficiencyIncrease = 10000;
+ updateSlots();
+ if (GT_Values.D1) {
+ GT_FML_LOGGER.info("Recipe successful");
+ }
+ return result;
+ }
+
+ @Override
+ public boolean onRunningTick(ItemStack aStack) {
+ for (GT_MetaTileEntity_Hatch_DataAccess hatch_dataAccess : mDataAccessHatches) {
+ hatch_dataAccess.setActive(true);
+ }
+ return super.onRunningTick(aStack);
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_AssemblyLine> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ mDataAccessHatches.clear();
+ if (!checkPiece(STRUCTURE_PIECE_FIRST, 0, 1, 0)) return false;
+ return checkMachine(true) || checkMachine(false);
+ }
+
+ private boolean checkMachine(boolean leftToRight) {
+ for (int i = 1; i < 16; i++) {
+ if (!checkPiece(STRUCTURE_PIECE_LATER, leftToRight ? -i : i, 1, 0)) return false;
+ if (!mOutputBusses.isEmpty())
+ return !mEnergyHatches.isEmpty() && mMaintenanceHatches.size() == 1 && mDataAccessHatches.size() <= 1;
+ }
+ return false;
+ }
+
+ /**
+ * @param state using bitmask, 1 for IntegratedCircuit, 2 for DataStick, 4 for DataOrb
+ */
+ private static boolean isCorrectDataItem(ItemStack aStack, int state) {
+ if ((state & 1) != 0 && ItemList.Circuit_Integrated.isStackEqual(aStack, true, true)) return true;
+ if ((state & 2) != 0 && ItemList.Tool_DataStick.isStackEqual(aStack, false, true)) return true;
+ return (state & 4) != 0 && ItemList.Tool_DataOrb.isStackEqual(aStack, false, true);
+ }
+
+ /**
+ * @param state using bitmask, 1 for IntegratedCircuit, 2 for DataStick, 4 for DataOrb
+ */
+ public ArrayList<ItemStack> getDataItems(int state) {
+ ArrayList<ItemStack> rList = new ArrayList<>();
+ if (GT_Utility.isStackValid(mInventory[1]) && isCorrectDataItem(mInventory[1], state)) {
+ rList.add(mInventory[1]);
+ }
+ for (GT_MetaTileEntity_Hatch_DataAccess tHatch : filterValidMTEs(mDataAccessHatches)) {
+ rList.addAll(tHatch.getInventoryItems(stack -> isCorrectDataItem(stack, state)));
+ }
+ return rList;
+ }
+
+ public boolean addDataAccessToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_DataAccess) {
+ ((GT_MetaTileEntity_Hatch) aMetaTileEntity).updateTexture(aBaseCasingIndex);
+ return mDataAccessHatches.add((GT_MetaTileEntity_Hatch_DataAccess) aMetaTileEntity);
+ }
+ return false;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_FIRST, stackSize, hintsOnly, 0, 1, 0);
+ int tLength = Math.min(stackSize.stackSize + 1, 16);
+ for (int i = 1; i < tLength; i++) {
+ buildPiece(STRUCTURE_PIECE_LATER, stackSize, hintsOnly, -i, 1, 0);
+ }
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ int build = survivialBuildPiece(STRUCTURE_PIECE_FIRST, stackSize, 0, 1, 0, elementBudget, env, false, true);
+ if (build >= 0) return build;
+ int tLength = Math.min(stackSize.stackSize + 1, 16);
+ for (int i = 1; i < tLength - 1; i++) {
+ build = survivialBuildPiece(STRUCTURE_PIECE_LATER, stackSize, -i, 1, 0, elementBudget, env, false, true);
+ if (build >= 0) return build;
+ }
+ return survivialBuildPiece(STRUCTURE_PIECE_LAST, stackSize, 1 - tLength, 1, 0, elementBudget, env, false, true);
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ @Override
+ public Set<VoidingMode> getAllowedVoidingModes() {
+ return VoidingMode.ITEM_ONLY_MODES;
+ }
+
+ private enum DataHatchElement implements IHatchElement<GT_MetaTileEntity_AssemblyLine> {
+
+ DataAccess;
+
+ @Override
+ public List<? extends Class<? extends IMetaTileEntity>> mteClasses() {
+ return Collections.singletonList(GT_MetaTileEntity_Hatch_DataAccess.class);
+ }
+
+ @Override
+ public IGT_HatchAdder<GT_MetaTileEntity_AssemblyLine> adder() {
+ return GT_MetaTileEntity_AssemblyLine::addDataAccessToMachineList;
+ }
+
+ @Override
+ public long count(GT_MetaTileEntity_AssemblyLine t) {
+ return t.mDataAccessHatches.size();
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_BrickedBlastFurnace.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_BrickedBlastFurnace.java
new file mode 100644
index 0000000000..a9cb7708cf
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_BrickedBlastFurnace.java
@@ -0,0 +1,155 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static gregtech.api.util.GT_Waila.getMachineProgressString;
+
+import java.util.List;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import org.lwjgl.input.Keyboard;
+
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.SteamVariant;
+import gregtech.api.enums.Textures;
+import gregtech.api.enums.Textures.BlockIcons;
+import gregtech.api.interfaces.ISecondaryDescribable;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public class GT_MetaTileEntity_BrickedBlastFurnace extends GT_MetaTileEntity_PrimitiveBlastFurnace
+ implements ISecondaryDescribable {
+
+ private static final ITexture[] FACING_SIDE = { TextureFactory.of(Textures.BlockIcons.MACHINE_CASING_DENSEBRICKS) };
+ private static final ITexture[] FACING_FRONT = {
+ TextureFactory.of(Textures.BlockIcons.MACHINE_CASING_BRICKEDBLASTFURNACE_INACTIVE) };
+ private static final ITexture[] FACING_ACTIVE = {
+ TextureFactory.of(Textures.BlockIcons.MACHINE_CASING_BRICKEDBLASTFURNACE_ACTIVE), TextureFactory.builder()
+ .addIcon(BlockIcons.MACHINE_CASING_BRICKEDBLASTFURNACE_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ private GT_Multiblock_Tooltip_Builder tooltipBuilder;
+
+ public GT_MetaTileEntity_BrickedBlastFurnace(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_BrickedBlastFurnace(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public String[] getDescription() {
+ return getCurrentDescription();
+ }
+
+ @Override
+ public boolean isDisplaySecondaryDescription() {
+ return Keyboard.isKeyDown(Keyboard.KEY_LSHIFT);
+ }
+
+ public String[] getPrimaryDescription() {
+ return getTooltip().getInformation();
+ }
+
+ public String[] getSecondaryDescription() {
+ return getTooltip().getStructureInformation();
+ }
+
+ protected GT_Multiblock_Tooltip_Builder getTooltip() {
+ if (tooltipBuilder == null) {
+ tooltipBuilder = new GT_Multiblock_Tooltip_Builder();
+ tooltipBuilder.addMachineType("Blast Furnace")
+ .addInfo("Controller Block for the Bricked Blast Furnace")
+ .addInfo("Usable for Steel and general Pyrometallurgy")
+ .addInfo("Has a useful interface, unlike other gregtech multis")
+ .addPollutionAmount(GT_Mod.gregtechproxy.mPollutionPrimitveBlastFurnacePerSecond)
+ .addSeparator()
+ .beginStructureBlock(3, 4, 3, true)
+ .addController("Front center")
+ .addOtherStructurePart("Firebricks", "Everything except the controller")
+ .addStructureInfo("The top block is also empty")
+ .addStructureInfo("You can share the walls of GT multis, so")
+ .addStructureInfo("each additional one costs less, up to 4")
+ .toolTipFinisher("Gregtech");
+ }
+ return tooltipBuilder;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side == aFacing) {
+ return aActive ? FACING_ACTIVE : FACING_FRONT;
+ }
+ return FACING_SIDE;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_BrickedBlastFurnace(this.mName);
+ }
+
+ @Override
+ protected Block getCasingBlock() {
+ return GregTech_API.sBlockCasings4;
+ }
+
+ @Override
+ protected int getCasingMetaID() {
+ return 15;
+ }
+
+ @Override
+ public String getName() {
+ return "Bricked Blast Furnace";
+ }
+
+ @Override
+ public SteamVariant getSteamVariant() {
+ return SteamVariant.PRIMITIVE;
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ super.getWailaBody(itemStack, currenttip, accessor, config);
+ if (!this.getBaseMetaTileEntity()
+ .isInvalidTileEntity()) {
+ NBTTagCompound nbt = accessor.getNBTData();
+ currenttip.add(
+ getMachineProgressString(
+ this.getBaseMetaTileEntity()
+ .isActive(),
+ nbt.getInteger("mMaxProgressTime"),
+ nbt.getInteger("mProgressTime")));
+ }
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ if (!this.getBaseMetaTileEntity()
+ .isInvalidTileEntity()) {
+ tag.setInteger("mProgressTime", this.getProgresstime());
+ tag.setInteger("mMaxProgressTime", this.maxProgresstime());
+ }
+ }
+
+ @Override
+ public String[] getStructureDescription(ItemStack stackSize) {
+ return getTooltip().getStructureHint();
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_Charcoal_Pit.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_Charcoal_Pit.java
new file mode 100644
index 0000000000..3a22c4ee99
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_Charcoal_Pit.java
@@ -0,0 +1,312 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ROCK_BREAKER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ROCK_BREAKER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ROCK_BREAKER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ROCK_BREAKER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.casingTexturePages;
+import static gregtech.api.objects.XSTR.XSTR_INSTANCE;
+
+import java.util.ArrayList;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.block.Block;
+import net.minecraft.block.material.Material;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.world.ChunkPosition;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.oredict.OreDictionary;
+
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.enums.ParticleFX;
+import gregtech.api.interfaces.ISecondaryDescribable;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_TooltipMultiBlockBase;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.WorldSpawnedEventBuilder;
+import gregtech.common.GT_Pollution;
+
+public class GT_MetaTileEntity_Charcoal_Pit extends GT_MetaTileEntity_TooltipMultiBlockBase
+ implements ISecondaryDescribable {
+
+ private boolean running = false;
+
+ public GT_MetaTileEntity_Charcoal_Pit(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_Charcoal_Pit(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return (facing.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0;
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ // No GUI, do not capture right-click so it does not interfere when placing logs
+ return false;
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Nonnull
+ @Override
+ public CheckRecipeResult checkProcessing() {
+ if (!checkRecursiveBlocks()) {
+ mEfficiency = 0;
+ mEfficiencyIncrease = 0;
+ mMaxProgresstime = 0;
+ running = false;
+ return CheckRecipeResultRegistry.NO_RECIPE;
+ }
+
+ if (mEfficiency == 0) {
+ mEfficiency = 10000;
+ mEfficiencyIncrease = 10000;
+ mMaxProgresstime = Math.max(1, mMaxProgresstime);
+
+ // adds all the pollution at once when the recipe starts
+ GT_Pollution.addPollution(getBaseMetaTileEntity(), mMaxProgresstime * getPollutionPerTick(null));
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ } else {
+ mEfficiency = 0;
+ mEfficiencyIncrease = 0;
+ mMaxProgresstime = 0;
+ return CheckRecipeResultRegistry.NO_RECIPE;
+ }
+ }
+
+ private boolean checkRecursiveBlocks() {
+ ArrayList<ChunkPosition> tList1 = new ArrayList<>();
+ ArrayList<ChunkPosition> tList2 = new ArrayList<>();
+
+ Block tBlock = getBaseMetaTileEntity().getBlockOffset(0, -1, 0);
+ if (isWoodLog(tBlock, getBaseMetaTileEntity().getMetaIDOffset(0, -1, 0))) {
+ tList2.add(new ChunkPosition(0, -1, 0));
+ } else return false;
+ while (!tList2.isEmpty()) {
+ ChunkPosition tPos = tList2.get(0);
+ tList2.remove(0);
+ if (!checkAllBlockSides(tPos.chunkPosX, tPos.chunkPosY, tPos.chunkPosZ, tList1, tList2)) {
+ return false;
+ }
+ }
+ if (running) {
+ for (ChunkPosition tPos : tList1) {
+ if (isWoodLog(
+ getBaseMetaTileEntity().getBlockOffset(tPos.chunkPosX, tPos.chunkPosY, tPos.chunkPosZ),
+ getBaseMetaTileEntity().getMetaIDOffset(tPos.chunkPosX, tPos.chunkPosY, tPos.chunkPosZ)))
+ getBaseMetaTileEntity().getWorld()
+ .setBlock(
+ getBaseMetaTileEntity().getXCoord() + tPos.chunkPosX,
+ getBaseMetaTileEntity().getYCoord() + tPos.chunkPosY,
+ getBaseMetaTileEntity().getZCoord() + tPos.chunkPosZ,
+ GregTech_API.sBlockReinforced,
+ 4,
+ 3);
+ }
+ running = false;
+ return false;
+ } else {
+ mMaxProgresstime = (int) Math.sqrt(tList1.size() * 240000);
+ }
+ running = true;
+ return true;
+ }
+
+ private boolean isWoodLog(Block log, int meta) {
+ for (int id : OreDictionary.getOreIDs(new ItemStack(log, 1, meta))) {
+ if (OreDictionary.getOreName(id)
+ .equals("logWood")) return true;
+ }
+ String tTool = log.getHarvestTool(meta);
+ return OrePrefixes.log.contains(new ItemStack(log, 1, meta)) && ("axe".equals(tTool))
+ && (log.getMaterial() == Material.wood);
+ }
+
+ private boolean checkAllBlockSides(int aX, int aY, int aZ, ArrayList<? super ChunkPosition> aList1,
+ ArrayList<? super ChunkPosition> aList2) {
+ boolean expandToChunkXPos = false;
+ boolean expandToChunkXNeg = false;
+ boolean expandToChunkYPos = false;
+ boolean expandToChunkYNeg = false;
+ boolean expandToChunkZPos = false;
+ boolean expandToChunkZNeg = false;
+
+ Block blockXPos = getBaseMetaTileEntity().getBlockOffset(aX + 1, aY, aZ);
+ if (aX + 1 < 6 && (isWoodLog(blockXPos, getBaseMetaTileEntity().getMetaIDOffset(aX + 1, aY, aZ)))) {
+ if (!aList1.contains(new ChunkPosition(aX + 1, aY, aZ))
+ && (!aList2.contains(new ChunkPosition(aX + 1, aY, aZ)))) expandToChunkXPos = true;
+ } else if (!(blockXPos == Blocks.dirt || blockXPos == Blocks.grass)) {
+ return false;
+ }
+
+ Block blockXNeg = getBaseMetaTileEntity().getBlockOffset(aX - 1, aY, aZ);
+ if (aX - 1 > -6 && (isWoodLog(blockXNeg, getBaseMetaTileEntity().getMetaIDOffset(aX - 1, aY, aZ)))) {
+ if (!aList1.contains(new ChunkPosition(aX - 1, aY, aZ))
+ && (!aList2.contains(new ChunkPosition(aX - 1, aY, aZ)))) expandToChunkXNeg = true;
+ } else if (!(blockXNeg == Blocks.dirt || blockXNeg == Blocks.grass)) {
+ return false;
+ }
+
+ Block blockYPos = getBaseMetaTileEntity().getBlockOffset(aX, aY + 1, aZ);
+ if (aY + 1 < 1 && (isWoodLog(blockYPos, getBaseMetaTileEntity().getMetaIDOffset(aX, aY + 1, aZ)))) {
+ if (!aList1.contains(new ChunkPosition(aX, aY + 1, aZ))
+ && (!aList2.contains(new ChunkPosition(aX, aY + 1, aZ)))) expandToChunkYPos = true;
+ } else if (!(blockYPos == Blocks.dirt || blockYPos == Blocks.grass
+ || (aX == 0 && aY == -1 && aZ == 0 && blockYPos == GregTech_API.sBlockMachines))) {
+ return false;
+ }
+
+ Block blockYNeg = getBaseMetaTileEntity().getBlockOffset(aX, aY - 1, aZ);
+ if (aY - 1 > -6 && (isWoodLog(blockYNeg, getBaseMetaTileEntity().getMetaIDOffset(aX, aY - 1, aZ)))) {
+ if (!aList1.contains(new ChunkPosition(aX, aY - 1, aZ))
+ && (!aList2.contains(new ChunkPosition(aX, aY - 1, aZ)))) expandToChunkYNeg = true;
+ } else if (blockYNeg != Blocks.brick_block) {
+ return false;
+ }
+
+ Block blockZPos = getBaseMetaTileEntity().getBlockOffset(aX, aY, aZ + 1);
+ if (aZ + 1 < 6 && (isWoodLog(blockZPos, getBaseMetaTileEntity().getMetaIDOffset(aX, aY, aZ + 1)))) {
+ if (!aList1.contains(new ChunkPosition(aX, aY, aZ + 1))
+ && (!aList2.contains(new ChunkPosition(aX, aY, aZ + 1)))) expandToChunkZPos = true;
+ } else if (!(blockZPos == Blocks.dirt || blockZPos == Blocks.grass)) {
+ return false;
+ }
+
+ Block blockZNeg = getBaseMetaTileEntity().getBlockOffset(aX, aY, aZ - 1);
+ if (aZ - 1 > -6 && (isWoodLog(blockZNeg, getBaseMetaTileEntity().getMetaIDOffset(aX, aY, aZ - 1)))) {
+ if (!aList1.contains(new ChunkPosition(aX, aY, aZ - 1))
+ && (!aList2.contains(new ChunkPosition(aX, aY, aZ - 1)))) expandToChunkZNeg = true;
+ } else if (!(blockZNeg == Blocks.dirt || blockZNeg == Blocks.grass)) {
+ return false;
+ }
+ aList1.add(new ChunkPosition(aX, aY, aZ));
+ if (expandToChunkXPos) aList2.add(new ChunkPosition(aX + 1, aY, aZ));
+ if (expandToChunkXNeg) aList2.add(new ChunkPosition(aX - 1, aY, aZ));
+ if (expandToChunkYPos) aList2.add(new ChunkPosition(aX, aY + 1, aZ));
+ if (expandToChunkYNeg) aList2.add(new ChunkPosition(aX, aY - 1, aZ));
+ if (expandToChunkZPos) aList2.add(new ChunkPosition(aX, aY, aZ + 1));
+ if (expandToChunkZNeg) aList2.add(new ChunkPosition(aX, aY, aZ - 1));
+ return true;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getPollutionPerSecond(ItemStack aStack) {
+ return GT_Mod.gregtechproxy.mPollutionCharcoalPitPerSecond;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return null;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Charcoal_Pit(mName);
+ }
+
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Charcoal Pile Igniter")
+ .addInfo("Controller for the Charcoal Pit")
+ .addInfo("Converts Logs into Brittle Charcoal blocks")
+ .addInfo("Will automatically start when valid")
+ .addPollutionAmount(getPollutionPerSecond(null))
+ .addSeparator()
+ .beginVariableStructureBlock(3, 13, 3, 7, 3, 13, false)
+ .addStructureInfo("Can be up to 13x7x13 in size, including the dirt; shape doesn't matter")
+ .addOtherStructurePart("Controller", "Top layer, directly touching a wood log")
+ .addOtherStructurePart("Dirt/Grass", "Top and middle layers, covering wood logs")
+ .addOtherStructurePart("Bricks", "Bottom layer, under all wood logs")
+ .addOtherStructurePart("Wood Logs", "Up to 5 layers, inside the previously mentioned blocks")
+ .addStructureInfo("No air between logs allowed.")
+ .addStructureInfo(
+ "All logs must be within 6 x/z of the controller, so it must be dead-center for a full 11x11 square of wood.")
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side == ForgeDirection.UP) {
+ if (aActive) return new ITexture[] { casingTexturePages[0][10],
+ TextureFactory.of(OVERLAY_FRONT_ROCK_BREAKER_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ROCK_BREAKER_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ return new ITexture[] { casingTexturePages[0][10], TextureFactory.of(OVERLAY_FRONT_ROCK_BREAKER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ROCK_BREAKER_GLOW)
+ .glow()
+ .build(), };
+ }
+ return new ITexture[] { casingTexturePages[0][10] };
+ }
+
+ @Override
+ public boolean polluteEnvironment(int aPollutionLevel) {
+ // Do nothing and don't choke on pollution. This is fine because we add
+ // all the pollution at once when the recipe starts
+ return true;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) {
+ super.onPostTick(aBaseMetaTileEntity, aTimer);
+ if ((aBaseMetaTileEntity.isClientSide()) && (aBaseMetaTileEntity.isActive())) {
+
+ new WorldSpawnedEventBuilder.ParticleEventBuilder().setMotion(0D, 0.3D, 0D)
+ .setIdentifier(ParticleFX.LARGE_SMOKE)
+ .setPosition(
+ aBaseMetaTileEntity.getOffsetX(ForgeDirection.UP, 1) + XSTR_INSTANCE.nextFloat(),
+ aBaseMetaTileEntity.getOffsetY(ForgeDirection.UP, 1),
+ aBaseMetaTileEntity.getOffsetZ(ForgeDirection.UP, 1) + XSTR_INSTANCE.nextFloat())
+ .setWorld(getBaseMetaTileEntity().getWorld())
+ .run();
+ }
+ }
+
+ @Override
+ public boolean getDefaultHasMaintenanceChecks() {
+ return false;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_Cleanroom.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_Cleanroom.java
new file mode 100644
index 0000000000..b0b9cdd078
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_Cleanroom.java
@@ -0,0 +1,515 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static gregtech.api.enums.GT_Values.debugCleanroom;
+import static gregtech.api.enums.Textures.BlockIcons.BLOCK_PLASCRETE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_CLEANROOM;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_CLEANROOM_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_CLEANROOM_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_CLEANROOM_GLOW;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.block.Block;
+import net.minecraft.item.ItemStack;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.world.World;
+import net.minecraftforge.common.config.ConfigCategory;
+import net.minecraftforge.common.config.Configuration;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizon.structurelib.StructureLibAPI;
+import com.gtnewhorizon.structurelib.alignment.constructable.IConstructable;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.TierEU;
+import gregtech.api.interfaces.ICleanroom;
+import gregtech.api.interfaces.ICleanroomReceiver;
+import gregtech.api.interfaces.ISecondaryDescribable;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicHull;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_TooltipMultiBlockBase;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.recipe.check.SimpleCheckRecipeResult;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Log;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+
+public class GT_MetaTileEntity_Cleanroom extends GT_MetaTileEntity_TooltipMultiBlockBase
+ implements IConstructable, ISecondaryDescribable, ICleanroom {
+
+ private final Set<ICleanroomReceiver> cleanroomReceivers = new HashSet<>();
+ private int mHeight = -1;
+
+ public GT_MetaTileEntity_Cleanroom(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_Cleanroom(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Cleanroom(mName);
+ }
+
+ @Override
+ public int getCleanness() {
+ return mEfficiency;
+ }
+
+ @Override
+ public boolean isValidCleanroom() {
+ return isValid() && mMachine;
+ }
+
+ @Override
+ public void pollute() {
+ mEfficiency = 0;
+ mWrench = false;
+ mScrewdriver = false;
+ mSoftHammer = false;
+ mHardHammer = false;
+ mSolderingTool = false;
+ mCrowbar = false;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Cleanroom")
+ .addInfo("Controller block for the Cleanroom")
+ .addInfo("Consumes 40 EU/t when first turned on")
+ .addInfo("and 4 EU/t once at 100% efficiency")
+ .addInfo("If you use an LV energy hatch, it will actually accept 2A instead of just 1A.")
+ .addInfo(
+ "MV+ energy hatches just accept 1A as usual. For HV+ the cleanroom will overclock and gain efficiency faster.")
+ .addInfo("Time required to reach full efficiency is proportional to")
+ .addInfo("the height of empty space within")
+ .addInfo("Machines that cause pollution aren't allowed to be put in.")
+ .addSeparator()
+ .beginVariableStructureBlock(3, 15, 4, 15, 3, 15, true)
+ .addController("Top center")
+ .addCasingInfoRange("Plascrete", 20, 1007, false)
+ .addStructureInfo(
+ GT_Values.cleanroomGlass
+ + "% of the Plascrete can be replaced with Reinforced Glass (not counting the top layer)")
+ .addStructureInfo(
+ "Other material can be used in place of Plascrete, even in higher percentages. See config for detail")
+ .addOtherStructurePart("Filter Machine Casing", "Top besides controller and edges")
+ .addEnergyHatch("Any casing except top layer. Exactly one.")
+ .addMaintenanceHatch("Any casing except top layer")
+ .addStructureInfo("0-2x Reinforced Door (keep closed or efficiency will reduce)")
+ .addStructureInfo("Up to 1 Elevator, Rotating Elevator, and Travel Anchor each")
+ .addStructureInfo("Up to 10 Machine Hulls for Item & Energy transfer through walls")
+ .addStructureInfo("You can also use Diodes for more power")
+ .addStructureInfo("Diodes also count towards 10 Machine Hulls count limit")
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public String[] getStructureDescription(ItemStack itemStack) {
+ return new String[] { "The base can be rectangular." };
+ }
+
+ @Nonnull
+ @Override
+ public CheckRecipeResult checkProcessing() {
+ mEfficiencyIncrease = 100;
+ final long inputVoltage = getMaxInputVoltage();
+
+ // only allow LV+ energy hatches
+ if (inputVoltage < TierEU.LV) {
+ return CheckRecipeResultRegistry.insufficientPower(40);
+ }
+
+ // use the standard overclock mechanism to determine duration and estimate a maximum consumption
+ // if the cleanroom is powered by an LV energy hatch, it will actually accept 2A instead of just 1A.
+ calculateOverclockedNessMultiInternal(
+ 40,
+ 45 * Math.max(1, mHeight - 1),
+ inputVoltage == TierEU.LV ? 2 : 1,
+ inputVoltage,
+ false);
+ // negate it to trigger the special energy consumption function. divide by 10 to get the actual final
+ // consumption.
+ mEUt /= -10;
+ return SimpleCheckRecipeResult.ofSuccess("cleanroom_running");
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return (facing.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ int x = 1;
+ int z = 1;
+ int y = 1;
+ int mDoorCount = 0;
+ int mHullCount = 0;
+ int mPlascreteCount = 0;
+ final HashMap<String, Integer> otherBlocks = new HashMap<>();
+ boolean doorState = false;
+ this.mUpdate = 100;
+ cleanroomReceivers.forEach(r -> r.setCleanroom(null));
+ cleanroomReceivers.clear();
+
+ if (debugCleanroom) {
+ GT_Log.out.println("Cleanroom: Checking machine");
+ }
+ for (int i = 1; i < 8; i++) {
+ final Block tBlock = aBaseMetaTileEntity.getBlockOffset(i, 0, 0);
+ final int tMeta = aBaseMetaTileEntity.getMetaIDOffset(i, 0, 0);
+ if (tBlock != GregTech_API.sBlockCasings3 || tMeta != 11) {
+ if (tBlock == GregTech_API.sBlockReinforced || tMeta == 2) {
+ x = i;
+ break;
+ } else {
+ if (debugCleanroom) {
+ GT_Log.out.println("Cleanroom: Unable to detect room X edge?");
+ }
+ return false;
+ }
+ }
+ }
+ for (int i = 1; i < 8; i++) {
+ final Block tBlock = aBaseMetaTileEntity.getBlockOffset(0, 0, i);
+ final int tMeta = aBaseMetaTileEntity.getMetaIDOffset(0, 0, i);
+ if (tBlock != GregTech_API.sBlockCasings3 || tMeta != 11) {
+ if (tBlock == GregTech_API.sBlockReinforced || tMeta == 2) {
+ z = i;
+ break;
+ } else {
+ if (debugCleanroom) {
+ GT_Log.out.println("Cleanroom: Unable to detect room Z edge?");
+ }
+ return false;
+ }
+ }
+ }
+ // detect rectangular area of filters
+ for (int i = -x + 1; i < x; i++) {
+ for (int j = -z + 1; j < z; j++) {
+ if (i == 0 && j == 0) continue;
+ final Block tBlock = aBaseMetaTileEntity.getBlockOffset(i, 0, j);
+ final int tMeta = aBaseMetaTileEntity.getMetaIDOffset(i, 0, j);
+ if (tBlock != GregTech_API.sBlockCasings3 && tMeta != 11) {
+ if (debugCleanroom) {
+ GT_Log.out.println("Cleanroom: This is not a filter.");
+ }
+ return false;
+ }
+ }
+ }
+
+ for (int i = -1; i > -16; i--) {
+ final Block tBlock = aBaseMetaTileEntity.getBlockOffset(x, i, z);
+ final int tMeta = aBaseMetaTileEntity.getMetaIDOffset(x, i, z);
+ if (tBlock != GregTech_API.sBlockReinforced || tMeta != 2) {
+ y = i + 1;
+ break;
+ }
+ }
+ if (y > -2) {
+ if (debugCleanroom) {
+ GT_Log.out.println("Cleanroom: Room not tall enough?");
+ }
+ return false;
+ }
+ for (int dX = -x; dX <= x; dX++) {
+ for (int dZ = -z; dZ <= z; dZ++) {
+ for (int dY = 0; dY >= y; dY--) {
+ if (dX == -x || dX == x || dY == 0 || dY == y || dZ == -z || dZ == z) {
+ Block tBlock = aBaseMetaTileEntity.getBlockOffset(dX, dY, dZ);
+ int tMeta = aBaseMetaTileEntity.getMetaIDOffset(dX, dY, dZ);
+ if (dY == 0) { // TOP
+ if (dX == -x || dX == x || dZ == -z || dZ == z) { // Top Border
+ if (tBlock != GregTech_API.sBlockReinforced || tMeta != 2) {
+ if (debugCleanroom) {
+ GT_Log.out.println("Cleanroom: Non reinforced block on top edge? tMeta != 2");
+ }
+ return false;
+ }
+ } else if (dX != 0 || dZ != 0) { // Top Inner exclude center
+ if (tBlock != GregTech_API.sBlockCasings3 || tMeta != 11) {
+ if (debugCleanroom) {
+ GT_Log.out.println(
+ "Cleanroom: Non reinforced block on top face interior? tMeta != 11");
+ }
+ return false;
+ }
+ }
+ } else if (tBlock == GregTech_API.sBlockReinforced && tMeta == 2) {
+ mPlascreteCount++;
+ } else {
+ final IGregTechTileEntity tTileEntity = aBaseMetaTileEntity
+ .getIGregTechTileEntityOffset(dX, dY, dZ);
+ if ((!this.addMaintenanceToMachineList(tTileEntity, 210))
+ && (!this.addEnergyInputToMachineList(tTileEntity, 210))) {
+ if (tBlock instanceof ic2.core.block.BlockIC2Door) {
+ if ((tMeta & 8) == 0) {
+ // let's not fiddle with bits anymore.
+ if (Math.abs(dZ) < z) // on side parallel to z axis
+ doorState = tMeta == 1 || tMeta == 3 || tMeta == 4 || tMeta == 6;
+ else if (Math.abs(dX) < x) // on side parallel to x axis
+ doorState = tMeta == 0 || tMeta == 2 || tMeta == 5 || tMeta == 7;
+ // corners ignored
+ }
+ mDoorCount++;
+ } else {
+ if (tTileEntity != null) {
+ final IMetaTileEntity aMetaTileEntity = tTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) {
+ if (debugCleanroom) {
+ GT_Log.out.println("Cleanroom: Missing block? Not a aMetaTileEntity");
+ }
+ return false;
+ }
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_BasicHull) {
+ mHullCount++;
+ } else {
+ if (debugCleanroom) {
+ GT_Log.out.println(
+ "Cleanroom: Incorrect GT block? " + tBlock.getUnlocalizedName());
+ }
+ return false;
+ }
+ } else {
+ String key = tBlock.getUnlocalizedName() + ":" + tMeta;
+ if (config.containsKey(key)) { // check with meta first
+ otherBlocks.compute(key, (k, v) -> v == null ? 1 : v + 1);
+ } else {
+ key = tBlock.getUnlocalizedName();
+ if (config.containsKey(key)) {
+ otherBlocks.compute(key, (k, v) -> v == null ? 1 : v + 1);
+ } else {
+ if (debugCleanroom) {
+ GT_Log.out.println(
+ "Cleanroom: not allowed block " + tBlock.getUnlocalizedName());
+ }
+ return false;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (this.mMaintenanceHatches.size() != 1 || this.mEnergyHatches.size() != 1
+ || mDoorCount > 4
+ || mHullCount > 10) {
+ if (debugCleanroom) {
+ GT_Log.out.println("Cleanroom: Incorrect number of doors, hulls, or hatches.");
+ }
+ return false;
+ }
+ if (mPlascreteCount < 20) {
+ if (debugCleanroom) {
+ GT_Log.out.println("Cleanroom: Could not find 20 Plascrete.");
+ }
+ return false;
+ }
+ final float ratio = (((float) mPlascreteCount) / 100f);
+ for (Map.Entry<String, Integer> e : otherBlocks.entrySet()) {
+ final ConfigEntry ce = config.get(e.getKey());
+ if (ce.allowedCount > 0) { // count has priority
+ if (e.getValue() > ce.allowedCount) {
+ if (debugCleanroom) {
+ GT_Log.out.println("Cleanroom: Absolute count too high for a block.");
+ }
+ return false;
+ }
+ } else if (e.getValue() > ratio * ce.percentage) {
+ if (debugCleanroom) {
+ GT_Log.out.println("Cleanroom: Relative count too high for a block.");
+ }
+ return false;
+ }
+ }
+
+ setCleanroomReceivers(x, y, z, aBaseMetaTileEntity);
+
+ if (doorState) {
+ this.mEfficiency = Math.max(0, this.mEfficiency - 200);
+ }
+ for (final ForgeDirection tSide : ForgeDirection.VALID_DIRECTIONS) {
+ final byte t = (byte) Math.max(1, (byte) (15 / (10000f / this.mEfficiency)));
+ aBaseMetaTileEntity.setInternalOutputRedstoneSignal(tSide, t);
+ }
+ this.mHeight = -y;
+ if (debugCleanroom) {
+ GT_Log.out.println("Cleanroom: Check successful.");
+ }
+ return true;
+ }
+
+ private void setCleanroomReceivers(int x, int y, int z, IGregTechTileEntity aBaseMetaTileEntity) {
+ for (int dX = -x + 1; dX <= x - 1; dX++) {
+ for (int dZ = -z + 1; dZ <= z - 1; dZ++) for (int dY = -1; dY >= y + 1; dY--) {
+ TileEntity tTileEntity = aBaseMetaTileEntity.getTileEntityOffset(dX, dY, dZ);
+ if (tTileEntity instanceof ICleanroomReceiver receiver) {
+ receiver.setCleanroom(this);
+ cleanroomReceivers.add(receiver);
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean allowGeneralRedstoneOutput() {
+ return true;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ if ((sideDirection.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) != 0) {
+ return new ITexture[] { TextureFactory.of(BLOCK_PLASCRETE), active
+ ? TextureFactory.of(
+ TextureFactory.of(OVERLAY_TOP_CLEANROOM_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_CLEANROOM_ACTIVE_GLOW)
+ .glow()
+ .build())
+ : TextureFactory.of(
+ TextureFactory.of(OVERLAY_TOP_CLEANROOM),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_CLEANROOM_GLOW)
+ .glow()
+ .build()) };
+ }
+ return new ITexture[] { TextureFactory.of(BLOCK_PLASCRETE) };
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public void construct(ItemStack itemStack, boolean b) {
+ int i = Math.min(itemStack.stackSize, 7);
+ IGregTechTileEntity baseEntity = this.getBaseMetaTileEntity();
+ World world = baseEntity.getWorld();
+ int x = baseEntity.getXCoord();
+ int y = baseEntity.getYCoord();
+ int z = baseEntity.getZCoord();
+ int yoff = Math.max(i * 2, 3);
+ for (int X = x - i; X <= x + i; X++) for (int Y = y; Y >= y - yoff; Y--) for (int Z = z - i; Z <= z + i; Z++) {
+ if (X == x && Y == y && Z == z) continue;
+ if (X == x - i || X == x + i || Z == z - i || Z == z + i || Y == y - yoff) {
+ if (b) StructureLibAPI.hintParticle(world, X, Y, Z, GregTech_API.sBlockReinforced, 2);
+ else world.setBlock(X, Y, Z, GregTech_API.sBlockReinforced, 2, 2);
+ } else if (Y == y) {
+ if (b) StructureLibAPI.hintParticle(world, X, Y, Z, GregTech_API.sBlockCasings3, 11);
+ else world.setBlock(X, Y, Z, GregTech_API.sBlockCasings3, 11, 2);
+ }
+ }
+ }
+
+ private static class ConfigEntry {
+
+ final int percentage;
+ final int allowedCount;
+
+ ConfigEntry(int percentage, int count) {
+ this.percentage = percentage;
+ this.allowedCount = count;
+ }
+ }
+
+ private static final HashMap<String, ConfigEntry> config = new HashMap<>();
+ private static final String category = "cleanroom_allowed_blocks";
+
+ private static void setDefaultConfigValues(Configuration cfg) {
+ cfg.get("cleanroom_allowed_blocks.manaGlass", "Name", "tile.manaGlass");
+ cfg.get("cleanroom_allowed_blocks.manaGlass", "Percentage", 50);
+ cfg.get("cleanroom_allowed_blocks.elfGlass", "Name", "tile.elfGlass");
+ cfg.get("cleanroom_allowed_blocks.elfGlass", "Percentage", 50);
+ cfg.get("cleanroom_allowed_blocks.reinforced_glass", "Name", "blockAlloyGlass");
+ cfg.get("cleanroom_allowed_blocks.reinforced_glass", "Percentage", 5);
+ cfg.get("cleanroom_allowed_blocks.bw_reinforced_glass_0", "Name", "BW_GlasBlocks");
+ cfg.get("cleanroom_allowed_blocks.bw_reinforced_glass_0", "Percentage", 50);
+ cfg.get("cleanroom_allowed_blocks.bw_reinforced_glass_0", "Meta", 0);
+ cfg.get("cleanroom_allowed_blocks.bw_reinforced_glass", "Name", "BW_GlasBlocks");
+ cfg.get("cleanroom_allowed_blocks.bw_reinforced_glass", "Percentage", 100);
+ cfg.get("cleanroom_allowed_blocks.elevator", "Name", "tile.openblocks.elevator");
+ cfg.get("cleanroom_allowed_blocks.elevator", "Count", 1);
+ cfg.get("cleanroom_allowed_blocks.travel_anchor", "Name", "tile.blockTravelAnchor");
+ cfg.get("cleanroom_allowed_blocks.travel_anchor", "Count", 1);
+ cfg.get("cleanroom_allowed_blocks.warded_glass", "Name", "tile.blockCosmeticOpaque");
+ cfg.get("cleanroom_allowed_blocks.warded_glass", "Meta", 2);
+ cfg.get("cleanroom_allowed_blocks.warded_glass", "Percentage", 50);
+ cfg.save();
+ }
+
+ public static void loadConfig(Configuration cfg) {
+ if (!cfg.hasCategory(category)) setDefaultConfigValues(cfg);
+ for (ConfigCategory cc : cfg.getCategory(category)
+ .getChildren()) {
+ final String name = cc.get("Name")
+ .getString();
+ if (cc.containsKey("Count")) {
+ if (cc.containsKey("Meta")) config.put(
+ name + ":"
+ + cc.get("Meta")
+ .getInt(),
+ new ConfigEntry(
+ 0,
+ cc.get("Count")
+ .getInt()));
+ else config.put(
+ name,
+ new ConfigEntry(
+ 0,
+ cc.get("Count")
+ .getInt()));
+ } else if (cc.containsKey("Percentage")) {
+ if (cc.containsKey("Meta")) config.put(
+ name + ":"
+ + cc.get("Meta")
+ .getInt(),
+ new ConfigEntry(
+ cc.get("Percentage")
+ .getInt(),
+ 0));
+ else config.put(
+ name,
+ new ConfigEntry(
+ cc.get("Percentage")
+ .getInt(),
+ 0));
+ }
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ConcreteBackfiller1.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ConcreteBackfiller1.java
new file mode 100644
index 0000000000..e61cfc0feb
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ConcreteBackfiller1.java
@@ -0,0 +1,53 @@
+package gregtech.common.tileentities.machines.multi;
+
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+
+public class GT_MetaTileEntity_ConcreteBackfiller1 extends GT_MetaTileEntity_ConcreteBackfillerBase {
+
+ public GT_MetaTileEntity_ConcreteBackfiller1(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_ConcreteBackfiller1(String aName) {
+ super(aName);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ return createTooltip("Concrete Backfiller");
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_ConcreteBackfiller1(mName);
+ }
+
+ @Override
+ protected ItemList getCasingBlockItem() {
+ return ItemList.Casing_SolidSteel;
+ }
+
+ @Override
+ protected Materials getFrameMaterial() {
+ return Materials.Steel;
+ }
+
+ @Override
+ protected int getCasingTextureIndex() {
+ return 16;
+ }
+
+ @Override
+ protected int getRadius() {
+ return 16;
+ }
+
+ @Override
+ protected int getMinTier() {
+ return 2;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ConcreteBackfiller2.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ConcreteBackfiller2.java
new file mode 100644
index 0000000000..c175d73cb1
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ConcreteBackfiller2.java
@@ -0,0 +1,54 @@
+package gregtech.common.tileentities.machines.multi;
+
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+
+public class GT_MetaTileEntity_ConcreteBackfiller2 extends GT_MetaTileEntity_ConcreteBackfillerBase {
+
+ public GT_MetaTileEntity_ConcreteBackfiller2(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_ConcreteBackfiller2(String aName) {
+ super(aName);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ return createTooltip("Advanced Concrete Backfiller");
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_ConcreteBackfiller2(mName);
+ }
+
+ @Override
+ protected ItemList getCasingBlockItem() {
+ return ItemList.Casing_StableTitanium;
+ }
+
+ @Override
+ protected Materials getFrameMaterial() {
+ return Materials.Titanium;
+ }
+
+ @Override
+ protected int getCasingTextureIndex() {
+ return 50;
+ }
+
+ @Override
+ protected int getRadius() {
+ int tConfig = getTotalConfigValue() * 2;
+ return tConfig >= 128 ? 128 : tConfig <= 0 ? 64 : tConfig;
+ }
+
+ @Override
+ protected int getMinTier() {
+ return 4;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ConcreteBackfillerBase.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ConcreteBackfillerBase.java
new file mode 100644
index 0000000000..3ad37f4c37
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ConcreteBackfillerBase.java
@@ -0,0 +1,265 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_Values.VN;
+import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY;
+
+import java.util.List;
+
+import net.minecraft.block.Block;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.StatCollector;
+import net.minecraftforge.fluids.IFluidBlock;
+
+import com.google.common.collect.ImmutableList;
+import com.gtnewhorizons.modularui.api.NumberFormatMUI;
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.math.Pos2d;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.DynamicPositionedColumn;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Materials;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.gui.widgets.GT_LockedWhileActiveButton;
+import gregtech.api.interfaces.IHatchElement;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.shutdown.ShutDownReasonRegistry;
+
+public abstract class GT_MetaTileEntity_ConcreteBackfillerBase extends GT_MetaTileEntity_DrillerBase {
+
+ private int mLastXOff = 0, mLastZOff = 0;
+
+ /** Used to drive the readout in the GUI for the backfiller's current y-level. */
+ private int clientYHead;
+
+ protected boolean mLiquidEnabled = true;
+
+ private static boolean isWater(Block aBlock) {
+ return aBlock == Blocks.water || aBlock == Blocks.flowing_water;
+ }
+
+ private static boolean isLava(Block aBlock) {
+ return aBlock == Blocks.lava || aBlock == Blocks.flowing_lava;
+ }
+
+ private static boolean isFluid(Block aBlock) {
+ return isWater(aBlock) || isLava(aBlock) || aBlock instanceof IFluidBlock;
+ }
+
+ public GT_MetaTileEntity_ConcreteBackfillerBase(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ initRecipeResults();
+ }
+
+ public GT_MetaTileEntity_ConcreteBackfillerBase(String aName) {
+ super(aName);
+ initRecipeResults();
+ }
+
+ private void initRecipeResults() {
+ addResultMessage(STATE_UPWARD, true, "backfiller_working");
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setBoolean("liquidenabled", mLiquidEnabled);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ if (aNBT.hasKey("liquidenabled")) mLiquidEnabled = aNBT.getBoolean("liquidenabled");
+ }
+
+ protected GT_Multiblock_Tooltip_Builder createTooltip(String aStructureName) {
+ String casings = getCasingBlockItem().get(0)
+ .getDisplayName();
+
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Concrete Backfiller")
+ .addInfo("Controller Block for the " + aStructureName)
+ .addInfo("Will fill in areas below it with light concrete. This goes through walls")
+ .addInfo("Use it to remove any spawning locations beneath your base to reduce lag")
+ .addInfo("Will pull back the pipes after it finishes that layer")
+ .addInfo("Radius is " + getRadius() + " blocks")
+ .addSeparator()
+ .beginStructureBlock(3, 7, 3, false)
+ .addController("Front bottom")
+ .addOtherStructurePart(casings, "form the 3x1x3 Base")
+ .addOtherStructurePart(casings, "1x3x1 pillar above the center of the base (2 minimum total)")
+ .addOtherStructurePart(getFrameMaterial().mName + " Frame Boxes", "Each pillar's side and 1x3x1 on top")
+ .addEnergyHatch("1x " + VN[getMinTier()] + "+, Any base casing", 1)
+ .addMaintenanceHatch("Any base casing", 1)
+ .addInputBus("Mining Pipes, optional, any base casing", 1)
+ .addInputHatch("GT Concrete, any base casing", 1)
+ .addOutputBus("Mining Pipes, optional, any base casing", 1)
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ protected abstract int getRadius();
+
+ @Override
+ protected boolean checkHatches() {
+ return !mMaintenanceHatches.isEmpty() && !mInputHatches.isEmpty() && mEnergyHatches.size() == 1;
+ }
+
+ @Override
+ protected List<IHatchElement<? super GT_MetaTileEntity_DrillerBase>> getAllowedHatches() {
+ return ImmutableList.of(InputHatch, InputBus, Maintenance, Energy);
+ }
+
+ @Override
+ protected void setElectricityStats() {
+ this.mEfficiency = getCurrentEfficiency(null);
+ this.mEfficiencyIncrease = 10000;
+ int tier = Math.max(1, GT_Utility.getTier(getMaxInputVoltage()));
+ this.mEUt = -6 * (1 << (tier << 1));
+ this.mMaxProgresstime = (workState == STATE_UPWARD ? 240 : 80) / (1 << tier);
+ this.mMaxProgresstime = Math.max(1, this.mMaxProgresstime);
+ }
+
+ @Override
+ protected boolean workingUpward(ItemStack aStack, int xDrill, int yDrill, int zDrill, int xPipe, int zPipe,
+ int yHead, int oldYHead) {
+ if (isRefillableBlock(xPipe, yHead - 1, zPipe)) return tryRefillBlock(xPipe, yHead - 1, zPipe);
+ int radius = getRadius();
+ if (mLastXOff == 0 && mLastZOff == 0) {
+ mLastXOff = -radius;
+ mLastZOff = -radius;
+ }
+ if (yHead != yDrill) {
+ for (int i = mLastXOff; i <= radius; i++) {
+ for (int j = (i == mLastXOff ? mLastZOff : -radius); j <= radius; j++) {
+ if (isRefillableBlock(xPipe + i, yHead, zPipe + j)) {
+ mLastXOff = i;
+ mLastZOff = j;
+ return tryRefillBlock(xPipe + i, yHead, zPipe + j);
+ }
+ }
+ }
+ }
+
+ if (tryPickPipe()) {
+ mLastXOff = 0;
+ mLastZOff = 0;
+ return true;
+ } else {
+ workState = STATE_DOWNWARD;
+ stopMachine(ShutDownReasonRegistry.NONE);
+ setShutdownReason(StatCollector.translateToLocal("GT5U.gui.text.backfiller_finished"));
+ return false;
+ }
+ }
+
+ private boolean isRefillableBlock(int aX, int aY, int aZ) {
+ IGregTechTileEntity aBaseTile = getBaseMetaTileEntity();
+ Block aBlock = aBaseTile.getBlock(aX, aY, aZ);
+ if (!aBlock.isAir(aBaseTile.getWorld(), aX, aY, aZ)) {
+ if (mLiquidEnabled) {
+ if (!isFluid(aBlock)) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ if (aBlock.getMaterial()
+ .isSolid()) {
+ return false;
+ }
+ return GT_Utility
+ .setBlockByFakePlayer(getFakePlayer(aBaseTile), aX, aY, aZ, GregTech_API.sBlockConcretes, 8, true);
+ }
+
+ private boolean tryRefillBlock(int aX, int aY, int aZ) {
+ if (!tryConsumeFluid()) {
+ setRuntimeFailureReason(CheckRecipeResultRegistry.BACKFILLER_NO_CONCRETE);
+ return false;
+ }
+ getBaseMetaTileEntity().getWorld()
+ .setBlock(aX, aY, aZ, GregTech_API.sBlockConcretes, 8, 3);
+ return true;
+ }
+
+ private boolean tryConsumeFluid() {
+ if (!depleteInput(Materials.Concrete.getMolten(144L))) {
+ mMaxProgresstime = 0;
+ return false;
+ }
+ return true;
+ }
+
+ protected static final NumberFormatMUI numberFormat = new NumberFormatMUI();
+
+ @Override
+ protected void drawTexts(DynamicPositionedColumn screenElements, SlotWidget inventorySlot) {
+ super.drawTexts(screenElements, inventorySlot);
+ screenElements
+ .widget(
+ TextWidget
+ .dynamicString(
+ () -> StatCollector.translateToLocalFormatted(
+ "GT5U.gui.text.backfiller_current_area",
+ numberFormat.format(clientYHead)))
+ .setSynced(false)
+ .setTextAlignment(Alignment.CenterLeft)
+ .setEnabled(widget -> getBaseMetaTileEntity().isActive() && workState == STATE_UPWARD))
+ .widget(new FakeSyncWidget.IntegerSyncer(this::getYHead, newInt -> clientYHead = newInt))
+ .widget(new FakeSyncWidget.IntegerSyncer(() -> workState, newInt -> workState = newInt));
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ super.addUIWidgets(builder, buildContext);
+ final int BUTTON_Y_LEVEL = 91;
+
+ builder.widget(
+ new GT_LockedWhileActiveButton(this.getBaseMetaTileEntity(), builder)
+ .setOnClick((clickData, widget) -> mLiquidEnabled = !mLiquidEnabled)
+ .setPlayClickSound(true)
+ .setBackground(() -> {
+ if (mLiquidEnabled) {
+ return new IDrawable[] { GT_UITextures.BUTTON_STANDARD_PRESSED,
+ GT_UITextures.OVERLAY_BUTTON_LIQUIDMODE };
+ }
+ return new IDrawable[] { GT_UITextures.BUTTON_STANDARD,
+ GT_UITextures.OVERLAY_BUTTON_LIQUIDMODE_OFF };
+ })
+ .attachSyncer(
+ new FakeSyncWidget.BooleanSyncer(() -> mLiquidEnabled, newBoolean -> mLiquidEnabled = newBoolean),
+ builder,
+ (widget, val) -> widget.notifyTooltipChange())
+ .dynamicTooltip(
+ () -> ImmutableList.of(
+ StatCollector.translateToLocal(
+ mLiquidEnabled ? "GT5U.gui.button.liquid_filling_ON"
+ : "GT5U.gui.button.liquid_filling_OFF")))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setPos(new Pos2d(100, BUTTON_Y_LEVEL))
+ .setSize(16, 16));
+ int left = 98;
+ for (ButtonWidget button : getAdditionalButtons(builder, buildContext)) {
+ button.setPos(new Pos2d(left, BUTTON_Y_LEVEL))
+ .setSize(16, 16);
+ builder.widget(button);
+ left += 18;
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_DieselEngine.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_DieselEngine.java
new file mode 100644
index 0000000000..6c77dc7f0d
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_DieselEngine.java
@@ -0,0 +1,416 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.lazy;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.enums.GT_HatchElement.Dynamo;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.Muffler;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_DIESEL_ENGINE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_DIESEL_ENGINE_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_DIESEL_ENGINE_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_DIESEL_ENGINE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.casingTexturePages;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_Utility.filterValidMTEs;
+
+import java.util.ArrayList;
+
+import net.minecraft.block.Block;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Materials;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_EnhancedMultiBlockBase;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Dynamo;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Muffler;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.recipe.check.SimpleCheckRecipeResult;
+import gregtech.api.recipe.maps.FuelBackend;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_DieselEngine
+ extends GT_MetaTileEntity_EnhancedMultiBlockBase<GT_MetaTileEntity_DieselEngine> implements ISurvivalConstructable {
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final ClassValue<IStructureDefinition<GT_MetaTileEntity_DieselEngine>> STRUCTURE_DEFINITION = new ClassValue<>() {
+
+ @Override
+ protected IStructureDefinition<GT_MetaTileEntity_DieselEngine> computeValue(Class<?> type) {
+ return StructureDefinition.<GT_MetaTileEntity_DieselEngine>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ transpose(
+ new String[][] { { "---", "iii", "chc", "chc", "ccc", }, { "---", "i~i", "hgh", "hgh", "cdc", },
+ { "---", "iii", "chc", "chc", "ccc", }, }))
+ .addElement('i', lazy(t -> ofBlock(t.getIntakeBlock(), t.getIntakeMeta())))
+ .addElement('c', lazy(t -> ofBlock(t.getCasingBlock(), t.getCasingMeta())))
+ .addElement('g', lazy(t -> ofBlock(t.getGearboxBlock(), t.getGearboxMeta())))
+ .addElement('d', lazy(t -> Dynamo.newAny(t.getCasingTextureIndex(), 2)))
+ .addElement(
+ 'h',
+ lazy(
+ t -> buildHatchAdder(GT_MetaTileEntity_DieselEngine.class)
+ .atLeast(InputHatch, InputHatch, InputHatch, Muffler, Maintenance)
+ .casingIndex(t.getCasingTextureIndex())
+ .dot(1)
+ .buildAndChain(t.getCasingBlock(), t.getCasingMeta())))
+ .build();
+ }
+ };
+ protected int fuelConsumption = 0;
+ protected int fuelValue = 0;
+ protected int fuelRemaining = 0;
+ protected boolean boostEu = false;
+
+ public GT_MetaTileEntity_DieselEngine(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_DieselEngine(String aName) {
+ super(aName);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Combustion Generator")
+ .addInfo("Controller block for the Large Combustion Engine")
+ .addInfo("Supply Diesel Fuels and 1000L of Lubricant per hour to run")
+ .addInfo("Supply 40L/s of Oxygen to boost output (optional)")
+ .addInfo("Default: Produces 2048EU/t at 100% fuel efficiency")
+ .addInfo("Boosted: Produces 6144EU/t at 150% fuel efficiency")
+ .addInfo("You need to wait for it to reach 300% to output full power")
+ .addPollutionAmount(getPollutionPerSecond(null))
+ .addSeparator()
+ .beginStructureBlock(3, 3, 4, false)
+ .addController("Front center")
+ .addCasingInfoRange("Stable Titanium Machine Casing", 16, 22, false)
+ .addOtherStructurePart("Titanium Gear Box Machine Casing", "Inner 2 blocks")
+ .addOtherStructurePart("Engine Intake Machine Casing", "8x, ring around controller")
+ .addStructureInfo("Engine Intake Casings must not be obstructed in front (only air blocks)")
+ .addDynamoHatch("Back center", 2)
+ .addMaintenanceHatch("One of the casings next to a Gear Box", 1)
+ .addMufflerHatch("Top middle back, above the rear Gear Box", 1)
+ .addInputHatch("Diesel Fuel, next to a Gear Box", 1)
+ .addInputHatch("Lubricant, next to a Gear Box", 1)
+ .addInputHatch("Oxygen, optional, next to a Gear Box", 1)
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side == aFacing) {
+ if (aActive) return new ITexture[] { casingTexturePages[0][50], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_DIESEL_ENGINE_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_DIESEL_ENGINE_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { casingTexturePages[0][50], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_DIESEL_ENGINE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_DIESEL_ENGINE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { casingTexturePages[0][50] };
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return getMaxEfficiency(aStack) > 0;
+ }
+
+ @Override
+ public RecipeMap<FuelBackend> getRecipeMap() {
+ return RecipeMaps.dieselFuels;
+ }
+
+ @Override
+ protected boolean filtersFluid() {
+ return false;
+ }
+
+ /**
+ * The nominal energy output This can be further multiplied by {@link #getMaxEfficiency(ItemStack)} when boosted
+ */
+ protected int getNominalOutput() {
+ return 2048;
+ }
+
+ protected Materials getBooster() {
+ return Materials.Oxygen;
+ }
+
+ /**
+ * x times fuel will be consumed when boosted This will however NOT increase power output Go tweak
+ * {@link #getMaxEfficiency(ItemStack)} and {@link #getNominalOutput()} instead
+ */
+ protected int getBoostFactor() {
+ return 2;
+ }
+
+ /**
+ * x times of additive will be consumed when boosted
+ */
+ protected int getAdditiveFactor() {
+ return 1;
+ }
+
+ /**
+ * Efficiency will increase by this amount every tick
+ */
+ protected int getEfficiencyIncrease() {
+ return 15;
+ }
+
+ @Override
+ @NotNull
+ public CheckRecipeResult checkProcessing() {
+ ArrayList<FluidStack> tFluids = getStoredFluids();
+
+ // fast track lookup
+ if (!tFluids.isEmpty()) {
+ double boostedFuelValue = 0;
+ double boostedOutput = 0;
+ double extraFuelFraction = 0;
+ for (FluidStack tFluid : tFluids) {
+ GT_Recipe tRecipe = getRecipeMap().getBackend()
+ .findFuel(tFluid);
+ if (tRecipe == null) continue;
+ fuelValue = tRecipe.mSpecialValue;
+
+ FluidStack tLiquid = tFluid.copy();
+ if (boostEu) {
+ boostedFuelValue = GT_Utility.safeInt((long) (fuelValue * 1.5));
+ boostedOutput = getNominalOutput() * 3;
+
+ fuelConsumption = tLiquid.amount = getBoostFactor() * getNominalOutput() / fuelValue;
+
+ // HOG consumption rate is normally 1 L/t, when it's supposed to be around 1.64 L/t
+ // This code increases fuel consumption by 1 at random, but with a weighted chance
+ if (boostedFuelValue * 2 > boostedOutput) {
+ extraFuelFraction = boostedOutput / boostedFuelValue;
+ extraFuelFraction = extraFuelFraction - (int) extraFuelFraction;
+ double rand = Math.random();
+ if (rand < extraFuelFraction) {
+ tLiquid.amount += 1;
+ }
+ }
+
+ } else {
+ fuelConsumption = tLiquid.amount = getNominalOutput() / fuelValue;
+ }
+
+ // Deplete that amount
+ if (!depleteInput(tLiquid)) return CheckRecipeResultRegistry.NO_FUEL_FOUND;
+ boostEu = depleteInput(getBooster().getGas(2L * getAdditiveFactor()));
+
+ // Check to prevent burning HOG without consuming it, if not boosted
+ if (!boostEu && fuelValue > getNominalOutput()) {
+ return SimpleCheckRecipeResult.ofFailure("fuel_quality_too_high");
+ }
+
+ // Deplete Lubricant. 1000L should = 1 hour of runtime (if baseEU = 2048)
+ if ((mRuntime % 72 == 0 || mRuntime == 0)
+ && !depleteInput(Materials.Lubricant.getFluid((boostEu ? 2L : 1L) * getAdditiveFactor())))
+ return SimpleCheckRecipeResult.ofFailure("no_lubricant");
+
+ fuelRemaining = tFluid.amount; // Record available fuel
+ this.mEUt = mEfficiency < 2000 ? 0 : getNominalOutput(); // Output 0 if startup is less than 20%
+ this.mProgresstime = 1;
+ this.mMaxProgresstime = 1;
+ this.mEfficiencyIncrease = getEfficiencyIncrease();
+ return CheckRecipeResultRegistry.GENERATING;
+ }
+ }
+ this.mEUt = 0;
+ this.mEfficiency = 0;
+ return CheckRecipeResultRegistry.NO_FUEL_FOUND;
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_DieselEngine> getStructureDefinition() {
+ return STRUCTURE_DEFINITION.get(getClass());
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ return checkPiece(STRUCTURE_PIECE_MAIN, 1, 1, 1) && !mMufflerHatches.isEmpty()
+ && mMaintenanceHatches.size() == 1;
+ }
+
+ public Block getCasingBlock() {
+ return GregTech_API.sBlockCasings4;
+ }
+
+ public byte getCasingMeta() {
+ return 2;
+ }
+
+ public Block getIntakeBlock() {
+ return GregTech_API.sBlockCasings4;
+ }
+
+ public byte getIntakeMeta() {
+ return 13;
+ }
+
+ public Block getGearboxBlock() {
+ return GregTech_API.sBlockCasings2;
+ }
+
+ public byte getGearboxMeta() {
+ return 4;
+ }
+
+ public byte getCasingTextureIndex() {
+ return 50;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_DieselEngine(this.mName);
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 1;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return boostEu ? 30000 : 10000;
+ }
+
+ @Override
+ public int getPollutionPerSecond(ItemStack aStack) {
+ return GT_Mod.gregtechproxy.mPollutionLargeCombustionEnginePerSecond;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ int mPollutionReduction = 0;
+ for (GT_MetaTileEntity_Hatch_Muffler tHatch : filterValidMTEs(mMufflerHatches)) {
+ mPollutionReduction = Math.max(tHatch.calculatePollutionReduction(100), mPollutionReduction);
+ }
+
+ long storedEnergy = 0;
+ long maxEnergy = 0;
+ for (GT_MetaTileEntity_Hatch_Dynamo tHatch : filterValidMTEs(mDynamoHatches)) {
+ storedEnergy += tHatch.getBaseMetaTileEntity()
+ .getStoredEU();
+ maxEnergy += tHatch.getBaseMetaTileEntity()
+ .getEUCapacity();
+ }
+
+ return new String[] { EnumChatFormatting.BLUE + "Diesel Engine" + EnumChatFormatting.RESET,
+ StatCollector.translateToLocal("GT5U.multiblock.energy") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(storedEnergy)
+ + EnumChatFormatting.RESET
+ + " EU / "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(maxEnergy)
+ + EnumChatFormatting.RESET
+ + " EU",
+ getIdealStatus() == getRepairStatus()
+ ? EnumChatFormatting.GREEN + StatCollector.translateToLocal("GT5U.turbine.maintenance.false")
+ + EnumChatFormatting.RESET
+ : EnumChatFormatting.RED + StatCollector.translateToLocal("GT5U.turbine.maintenance.true")
+ + EnumChatFormatting.RESET,
+ StatCollector.translateToLocal("GT5U.engine.output") + ": "
+ + EnumChatFormatting.RED
+ + GT_Utility.formatNumbers(((long) mEUt * mEfficiency / 10000))
+ + EnumChatFormatting.RESET
+ + " EU/t",
+ StatCollector.translateToLocal("GT5U.engine.consumption") + ": "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(fuelConsumption)
+ + EnumChatFormatting.RESET
+ + " L/t",
+ StatCollector.translateToLocal("GT5U.engine.value") + ": "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(fuelValue)
+ + EnumChatFormatting.RESET
+ + " EU/L",
+ StatCollector.translateToLocal("GT5U.turbine.fuel") + ": "
+ + EnumChatFormatting.GOLD
+ + GT_Utility.formatNumbers(fuelRemaining)
+ + EnumChatFormatting.RESET
+ + " L",
+ StatCollector.translateToLocal("GT5U.engine.efficiency") + ": "
+ + EnumChatFormatting.YELLOW
+ + (mEfficiency / 100F)
+ + EnumChatFormatting.YELLOW
+ + " %",
+ StatCollector.translateToLocal("GT5U.multiblock.pollution") + ": "
+ + EnumChatFormatting.GREEN
+ + mPollutionReduction
+ + EnumChatFormatting.RESET
+ + " %" };
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return true;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 1, 1, 1);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 1, 1, 1, elementBudget, env, false, true);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_DistillationTower.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_DistillationTower.java
new file mode 100644
index 0000000000..1f5fec10f3
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_DistillationTower.java
@@ -0,0 +1,369 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.isAir;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.onElementPass;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_DISTILLATION_TOWER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_DISTILLATION_TOWER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_DISTILLATION_TOWER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_DISTILLATION_TOWER_GLOW;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_StructureUtility.ofHatchAdder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import com.gtnewhorizon.structurelib.alignment.IAlignmentLimits;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.Textures;
+import gregtech.api.enums.Textures.BlockIcons;
+import gregtech.api.interfaces.IHatchElement;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.fluid.IFluidStore;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_EnhancedMultiBlockBase;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Output;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_Output_ME;
+
+public class GT_MetaTileEntity_DistillationTower extends
+ GT_MetaTileEntity_EnhancedMultiBlockBase<GT_MetaTileEntity_DistillationTower> implements ISurvivalConstructable {
+
+ protected static final int CASING_INDEX = 49;
+ protected static final String STRUCTURE_PIECE_BASE = "base";
+ protected static final String STRUCTURE_PIECE_LAYER = "layer";
+ protected static final String STRUCTURE_PIECE_LAYER_HINT = "layerHint";
+ protected static final String STRUCTURE_PIECE_TOP_HINT = "topHint";
+ private static final IStructureDefinition<GT_MetaTileEntity_DistillationTower> STRUCTURE_DEFINITION;
+
+ static {
+ IHatchElement<GT_MetaTileEntity_DistillationTower> layeredOutputHatch = OutputHatch
+ .withCount(GT_MetaTileEntity_DistillationTower::getCurrentLayerOutputHatchCount)
+ .withAdder(GT_MetaTileEntity_DistillationTower::addLayerOutputHatch);
+ STRUCTURE_DEFINITION = StructureDefinition.<GT_MetaTileEntity_DistillationTower>builder()
+ .addShape(STRUCTURE_PIECE_BASE, transpose(new String[][] { { "b~b", "bbb", "bbb" }, }))
+ .addShape(STRUCTURE_PIECE_LAYER, transpose(new String[][] { { "lll", "lcl", "lll" }, }))
+ .addShape(STRUCTURE_PIECE_LAYER_HINT, transpose(new String[][] { { "lll", "l-l", "lll" }, }))
+ .addShape(STRUCTURE_PIECE_TOP_HINT, transpose(new String[][] { { "LLL", "LLL", "LLL" }, }))
+ .addElement(
+ 'b',
+ ofChain(
+ buildHatchAdder(GT_MetaTileEntity_DistillationTower.class)
+ .atLeast(Energy, OutputBus, InputHatch, InputBus, Maintenance)
+ .casingIndex(CASING_INDEX)
+ .dot(1)
+ .build(),
+ onElementPass(
+ GT_MetaTileEntity_DistillationTower::onCasingFound,
+ ofBlock(GregTech_API.sBlockCasings4, 1))))
+ .addElement(
+ 'l',
+ ofChain(
+ buildHatchAdder(GT_MetaTileEntity_DistillationTower.class).atLeast(layeredOutputHatch)
+ .casingIndex(CASING_INDEX)
+ .dot(2)
+ .disallowOnly(ForgeDirection.UP, ForgeDirection.DOWN)
+ .build(),
+ ofHatchAdder(GT_MetaTileEntity_DistillationTower::addEnergyInputToMachineList, CASING_INDEX, 2),
+ ofHatchAdder(GT_MetaTileEntity_DistillationTower::addLayerOutputHatch, CASING_INDEX, 2),
+ ofHatchAdder(GT_MetaTileEntity_DistillationTower::addMaintenanceToMachineList, CASING_INDEX, 2),
+ onElementPass(
+ GT_MetaTileEntity_DistillationTower::onCasingFound,
+ ofBlock(GregTech_API.sBlockCasings4, 1))))
+ // hint element only used in top layer
+ .addElement(
+ 'L',
+ buildHatchAdder(GT_MetaTileEntity_DistillationTower.class).atLeast(layeredOutputHatch)
+ .casingIndex(CASING_INDEX)
+ .dot(2)
+ .disallowOnly(ForgeDirection.UP)
+ .buildAndChain(GregTech_API.sBlockCasings4, 1))
+ .addElement(
+ 'c',
+ ofChain(
+ onElementPass(
+ t -> t.onTopLayerFound(false),
+ ofHatchAdder(GT_MetaTileEntity_DistillationTower::addOutputToMachineList, CASING_INDEX, 3)),
+ onElementPass(
+ t -> t.onTopLayerFound(false),
+ ofHatchAdder(
+ GT_MetaTileEntity_DistillationTower::addMaintenanceToMachineList,
+ CASING_INDEX,
+ 3)),
+ onElementPass(t -> t.onTopLayerFound(true), ofBlock(GregTech_API.sBlockCasings4, 1)),
+ isAir()))
+ .build();
+ }
+
+ protected final List<List<GT_MetaTileEntity_Hatch_Output>> mOutputHatchesByLayer = new ArrayList<>();
+ protected int mHeight;
+ protected int mCasing;
+ protected boolean mTopLayerFound;
+
+ public GT_MetaTileEntity_DistillationTower(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_DistillationTower(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_DistillationTower(this.mName);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Distillery")
+ .addInfo("Controller block for the Distillation Tower")
+ .addInfo("Fluids are only put out at the correct height")
+ .addInfo("The correct height equals the slot number in the NEI recipe")
+ .addSeparator()
+ .beginVariableStructureBlock(3, 3, 3, 12, 3, 3, true)
+ .addController("Front bottom")
+ .addOtherStructurePart("Clean Stainless Steel Machine Casing", "7 x h - 5 (minimum)")
+ .addEnergyHatch("Any casing except top centre", 1, 2)
+ .addMaintenanceHatch("Any casing", 1, 2, 3)
+ .addInputHatch("Any bottom layer casing", 1)
+ .addOutputBus("Any bottom layer casing", 1)
+ .addOutputHatch("2-11x Output Hatches (At least one per layer except bottom layer)", 2, 3)
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ if (sideDirection == facingDirection) {
+ if (active) return new ITexture[] { BlockIcons.getCasingTextureForId(CASING_INDEX), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_DISTILLATION_TOWER_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_DISTILLATION_TOWER_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { BlockIcons.getCasingTextureForId(CASING_INDEX), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_DISTILLATION_TOWER)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_DISTILLATION_TOWER_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX) };
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.distillationTowerRecipes;
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic();
+ }
+
+ protected void onCasingFound() {
+ mCasing++;
+ }
+
+ protected void onTopLayerFound(boolean aIsCasing) {
+ mTopLayerFound = true;
+ if (aIsCasing) onCasingFound();
+ }
+
+ protected int getCurrentLayerOutputHatchCount() {
+ return mOutputHatchesByLayer.size() < mHeight || mHeight <= 0 ? 0
+ : mOutputHatchesByLayer.get(mHeight - 1)
+ .size();
+ }
+
+ protected boolean addLayerOutputHatch(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null || aTileEntity.isDead()
+ || !(aTileEntity.getMetaTileEntity() instanceof GT_MetaTileEntity_Hatch_Output tHatch)) return false;
+ while (mOutputHatchesByLayer.size() < mHeight) mOutputHatchesByLayer.add(new ArrayList<>());
+ tHatch.updateTexture(aBaseCasingIndex);
+ return mOutputHatchesByLayer.get(mHeight - 1)
+ .add(tHatch);
+ }
+
+ @Override
+ public List<? extends IFluidStore> getFluidOutputSlots(FluidStack[] toOutput) {
+ return getFluidOutputSlotsByLayer(toOutput, mOutputHatchesByLayer);
+ }
+
+ @Override
+ protected IAlignmentLimits getInitialAlignmentLimits() {
+ // don't rotate a freaking tower, it won't work
+ return (d, r, f) -> (d.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0 && r.isNotRotated()
+ && !f.isVerticallyFliped();
+ }
+
+ @Override
+ public boolean isRotationChangeAllowed() {
+ return false;
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_DistillationTower> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ // reset
+ mOutputHatchesByLayer.forEach(List::clear);
+ mHeight = 1;
+ mTopLayerFound = false;
+ mCasing = 0;
+
+ // check base
+ if (!checkPiece(STRUCTURE_PIECE_BASE, 1, 0, 0)) return false;
+
+ // check each layer
+ while (mHeight < 12) {
+ if (!checkPiece(STRUCTURE_PIECE_LAYER, 1, mHeight, 0)) {
+ return false;
+ }
+ if (mOutputHatchesByLayer.size() < mHeight || mOutputHatchesByLayer.get(mHeight - 1)
+ .isEmpty())
+ // layer without output hatch
+ return false;
+ if (mTopLayerFound) {
+ break;
+ }
+ // not top
+ mHeight++;
+ }
+
+ // validate final invariants... (actual height is mHeight+1)
+ return mCasing >= 7 * (mHeight + 1) - 5 && mHeight + 1 >= 3
+ && mTopLayerFound
+ && mMaintenanceHatches.size() == 1;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ protected void addFluidOutputs(FluidStack[] mOutputFluids2) {
+ for (int i = 0; i < mOutputFluids2.length && i < mOutputHatchesByLayer.size(); i++) {
+ final FluidStack fluidStack = mOutputFluids2[i];
+ if (fluidStack == null) continue;
+ FluidStack tStack = fluidStack.copy();
+ if (!dumpFluid(mOutputHatchesByLayer.get(i), tStack, true))
+ dumpFluid(mOutputHatchesByLayer.get(i), tStack, false);
+ }
+ }
+
+ @Override
+ public boolean canDumpFluidToME() {
+ // All fluids can be dumped to ME only if each layer contains a ME Output Hatch.
+ return this.mOutputHatchesByLayer.stream()
+ .allMatch(
+ tLayerOutputHatches -> tLayerOutputHatches.stream()
+ .anyMatch(
+ tHatch -> (tHatch instanceof GT_MetaTileEntity_Hatch_Output_ME tMEHatch)
+ && (tMEHatch.canAcceptFluid())));
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_BASE, stackSize, hintsOnly, 1, 0, 0);
+ int tTotalHeight = Math.min(12, stackSize.stackSize + 2); // min 2 output layer, so at least 1 + 2 height
+ for (int i = 1; i < tTotalHeight - 1; i++) {
+ buildPiece(STRUCTURE_PIECE_LAYER_HINT, stackSize, hintsOnly, 1, i, 0);
+ }
+ buildPiece(STRUCTURE_PIECE_TOP_HINT, stackSize, hintsOnly, 1, tTotalHeight - 1, 0);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ mHeight = 0;
+ int built = survivialBuildPiece(STRUCTURE_PIECE_BASE, stackSize, 1, 0, 0, elementBudget, env, false, true);
+ if (built >= 0) return built;
+ int tTotalHeight = Math.min(12, stackSize.stackSize + 2); // min 2 output layer, so at least 1 + 2 height
+ for (int i = 1; i < tTotalHeight - 1; i++) {
+ mHeight = i;
+ built = survivialBuildPiece(
+ STRUCTURE_PIECE_LAYER_HINT,
+ stackSize,
+ 1,
+ i,
+ 0,
+ elementBudget,
+ env,
+ false,
+ true);
+ if (built >= 0) return built;
+ }
+ mHeight = tTotalHeight - 1;
+ return survivialBuildPiece(
+ STRUCTURE_PIECE_TOP_HINT,
+ stackSize,
+ 1,
+ tTotalHeight - 1,
+ 0,
+ elementBudget,
+ env,
+ false,
+ true);
+ }
+
+ @Override
+ protected SoundResource getProcessStartSound() {
+ return SoundResource.GT_MACHINES_DISTILLERY_LOOP;
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_DrillerBase.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_DrillerBase.java
new file mode 100644
index 0000000000..702185c9bd
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_DrillerBase.java
@@ -0,0 +1,952 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.lazy;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.Muffler;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.GT_Values.W;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ORE_DRILL;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ORE_DRILL_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ORE_DRILL_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ORE_DRILL_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.getCasingTextureForId;
+import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_StructureUtility.ofFrame;
+import static gregtech.api.util.GT_Utility.filterValidMTEs;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.ChunkCoordIntPair;
+import net.minecraftforge.common.util.FakePlayer;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.google.common.collect.ImmutableList;
+import com.gtnewhorizon.structurelib.alignment.IAlignmentLimits;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.math.Pos2d;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.DynamicPositionedColumn;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.gui.widgets.GT_LockedWhileActiveButton;
+import gregtech.api.interfaces.IChunkLoader;
+import gregtech.api.interfaces.IHatchElement;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_EnhancedMultiBlockBase;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_DataAccess;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Energy;
+import gregtech.api.objects.GT_ChunkManager;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.recipe.check.SimpleCheckRecipeResult;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.IGT_HatchAdder;
+import gregtech.api.util.shutdown.ShutDownReasonRegistry;
+
+public abstract class GT_MetaTileEntity_DrillerBase
+ extends GT_MetaTileEntity_EnhancedMultiBlockBase<GT_MetaTileEntity_DrillerBase>
+ implements IChunkLoader, ISurvivalConstructable {
+
+ private static final ItemStack miningPipe = GT_ModHandler.getIC2Item("miningPipe", 0);
+ private static final ItemStack miningPipeTip = GT_ModHandler.getIC2Item("miningPipeTip", 0);
+ private static final Block miningPipeBlock = GT_Utility.getBlockFromStack(miningPipe);
+ private static final Block miningPipeTipBlock = GT_Utility.getBlockFromStack(miningPipeTip);
+ protected static final String STRUCTURE_PIECE_MAIN = "main";
+ protected static final ClassValue<IStructureDefinition<GT_MetaTileEntity_DrillerBase>> STRUCTURE_DEFINITION = new ClassValue<>() {
+
+ @Override
+ protected IStructureDefinition<GT_MetaTileEntity_DrillerBase> computeValue(Class<?> type) {
+ return StructureDefinition.<GT_MetaTileEntity_DrillerBase>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ transpose(
+ new String[][] { { " ", " f ", " " }, { " ", " f ", " " }, { " ", " f ", " " },
+ { " f ", "fcf", " f " }, { " f ", "fcf", " f " }, { " f ", "fcf", " f " },
+ { "b~b", "bbb", "bbb" }, }))
+ .addElement('f', lazy(t -> ofFrame(t.getFrameMaterial())))
+ .addElement(
+ 'c',
+ lazy(
+ t -> ofBlock(
+ t.getCasingBlockItem()
+ .getBlock(),
+ t.getCasingBlockItem()
+ .get(0)
+ .getItemDamage())))
+ .addElement(
+ 'b',
+ lazy(
+ t -> buildHatchAdder(GT_MetaTileEntity_DrillerBase.class).atLeastList(t.getAllowedHatches())
+ .adder(GT_MetaTileEntity_DrillerBase::addToMachineList)
+ .casingIndex(t.casingTextureIndex)
+ .dot(1)
+ .buildAndChain(
+ t.getCasingBlockItem()
+ .getBlock(),
+ t.getCasingBlockItem()
+ .get(0)
+ .getItemDamage())))
+ .build();
+ }
+ };
+
+ private Block casingBlock;
+ private int casingMeta;
+ private int frameMeta;
+ protected int casingTextureIndex;
+ protected boolean isPickingPipes;
+
+ private ForgeDirection back;
+
+ private int xDrill, yDrill, zDrill, xPipe, zPipe, yHead;
+
+ protected int getXDrill() {
+ return xDrill;
+ }
+
+ protected int getZDrill() {
+ return zDrill;
+ }
+
+ protected int getYHead() {
+ return yHead;
+ }
+
+ protected int workState;
+ protected static final int STATE_DOWNWARD = 0, STATE_AT_BOTTOM = 1, STATE_UPWARD = 2, STATE_ABORT = 3;
+
+ protected boolean mChunkLoadingEnabled = true;
+ protected ChunkCoordIntPair mCurrentChunk = null;
+ protected boolean mWorkChunkNeedsReload = true;
+
+ /** Stores default result messages for success/failures of each work state. */
+ private final Map<ResultRegistryKey, CheckRecipeResult> resultRegistry = new HashMap<>();
+
+ /** Allows inheritors to supply custom runtime failure messages. */
+ private CheckRecipeResult runtimeFailure = null;
+ private CheckRecipeResult lastRuntimeFailure = null;
+
+ /** Allows inheritors to supply custom shutdown failure messages. */
+ private @NotNull String shutdownReason = "";
+
+ /** Allows inheritors to suppress wiping the last error if the machine is forcibly turned off. */
+ protected boolean suppressErrorWipe = false;
+
+ public GT_MetaTileEntity_DrillerBase(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ initFields();
+ }
+
+ public GT_MetaTileEntity_DrillerBase(String aName) {
+ super(aName);
+ initFields();
+ }
+
+ private void initFields() {
+ casingBlock = getCasingBlockItem().getBlock();
+ casingMeta = getCasingBlockItem().get(0)
+ .getItemDamage();
+ int frameId = 4096 + getFrameMaterial().mMetaItemSubID;
+ frameMeta = GregTech_API.METATILEENTITIES[frameId] != null
+ ? GregTech_API.METATILEENTITIES[frameId].getTileEntityBaseType()
+ : W;
+ casingTextureIndex = getCasingTextureIndex();
+ workState = STATE_DOWNWARD;
+
+ // Inheritors can overwrite these to add custom operating messages.
+ addResultMessage(STATE_DOWNWARD, true, "deploying_pipe");
+ addResultMessage(STATE_DOWNWARD, false, "extracting_pipe");
+ addResultMessage(STATE_AT_BOTTOM, true, "drilling");
+ addResultMessage(STATE_AT_BOTTOM, false, "no_mining_pipe");
+ addResultMessage(STATE_UPWARD, true, "retracting_pipe");
+ addResultMessage(STATE_UPWARD, false, "drill_generic_finished");
+ addResultMessage(STATE_ABORT, true, "retracting_pipe");
+ addResultMessage(STATE_ABORT, false, "drill_retract_pipes_finished");
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ if (sideDirection == facingDirection) {
+ if (active) return new ITexture[] { getCasingTextureForId(casingTextureIndex), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ORE_DRILL_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ORE_DRILL_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { getCasingTextureForId(casingTextureIndex), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ORE_DRILL)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ORE_DRILL_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { getCasingTextureForId(casingTextureIndex) };
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setInteger("workState", workState);
+ aNBT.setBoolean("chunkLoadingEnabled", mChunkLoadingEnabled);
+ aNBT.setBoolean("isChunkloading", mCurrentChunk != null);
+ if (mCurrentChunk != null) {
+ aNBT.setInteger("loadedChunkXPos", mCurrentChunk.chunkXPos);
+ aNBT.setInteger("loadedChunkZPos", mCurrentChunk.chunkZPos);
+ }
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ workState = aNBT.getInteger("workState");
+ if (aNBT.hasKey("isPickingPipes"))
+ workState = aNBT.getBoolean("isPickingPipes") ? STATE_UPWARD : STATE_DOWNWARD;
+ if (aNBT.hasKey("chunkLoadingEnabled")) mChunkLoadingEnabled = aNBT.getBoolean("chunkLoadingEnabled");
+ if (aNBT.getBoolean("isChunkloading")) {
+ mCurrentChunk = new ChunkCoordIntPair(
+ aNBT.getInteger("loadedChunkXPos"),
+ aNBT.getInteger("loadedChunkZPos"));
+ }
+ }
+
+ @Override
+ public boolean onSolderingToolRightClick(ForgeDirection side, ForgeDirection wrenchingSide,
+ EntityPlayer entityPlayer, float aX, float aY, float aZ) {
+ if (side == getBaseMetaTileEntity().getFrontFacing()) {
+ mChunkLoadingEnabled = !mChunkLoadingEnabled;
+ GT_Utility.sendChatToPlayer(
+ entityPlayer,
+ mChunkLoadingEnabled ? GT_Utility.trans("502", "Mining chunk loading enabled")
+ : GT_Utility.trans("503", "Mining chunk loading disabled"));
+ return true;
+ }
+ return super.onSolderingToolRightClick(side, wrenchingSide, entityPlayer, aX, aY, aZ);
+ }
+
+ @Override
+ public void onRemoval() {
+ if (mChunkLoadingEnabled) GT_ChunkManager.releaseTicket((TileEntity) getBaseMetaTileEntity());
+ super.onRemoval();
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ if (aBaseMetaTileEntity.isServerSide() && mCurrentChunk != null
+ && !mWorkChunkNeedsReload
+ && !aBaseMetaTileEntity.isAllowedToWork()) {
+ // if machine has stopped, stop chunkloading
+ GT_ChunkManager.releaseTicket((TileEntity) aBaseMetaTileEntity);
+ mWorkChunkNeedsReload = true;
+ }
+ }
+
+ protected boolean tryPickPipe() {
+ if (yHead == yDrill) return isPickingPipes = false;
+ if (tryOutputPipe()) {
+ if (checkBlockAndMeta(xPipe, yHead + 1, zPipe, miningPipeBlock, W)) getBaseMetaTileEntity().getWorld()
+ .setBlock(xPipe, yHead + 1, zPipe, miningPipeTipBlock);
+ getBaseMetaTileEntity().getWorld()
+ .setBlockToAir(xPipe, yHead, zPipe);
+ return isPickingPipes = true;
+ }
+ return isPickingPipes = false;
+ }
+
+ /**
+ * @return 0 for succeeded, 1 for invalid block, 2 for not having mining pipes, 3 for event canceled.
+ */
+ protected int tryLowerPipeState() {
+ return tryLowerPipeState(false);
+ }
+
+ /**
+ * @return 0 for succeeded, 1 for invalid block, 2 for not having mining pipes, 3 for event canceled.
+ */
+ protected int tryLowerPipeState(boolean isSimulating) {
+ if (!isHasMiningPipes()) return 2;
+ switch (canLowerPipe()) {
+ case 1 -> {
+ return 1;
+ }
+ case 2 -> {
+ return 3;
+ }
+ }
+
+ Block b = getBaseMetaTileEntity().getBlock(xPipe, yHead - 1, zPipe);
+ if (b != miningPipeTipBlock && !GT_Utility.setBlockByFakePlayer(
+ getFakePlayer(getBaseMetaTileEntity()),
+ xPipe,
+ yHead - 1,
+ zPipe,
+ miningPipeTipBlock,
+ 0,
+ isSimulating)) return 3;
+ if (!isSimulating) {
+ if (yHead != yDrill) getBaseMetaTileEntity().getWorld()
+ .setBlock(xPipe, yHead, zPipe, miningPipeBlock);
+ if (b != miningPipeBlock && b != miningPipeTipBlock) getBaseMetaTileEntity().decrStackSize(1, 1);
+ }
+
+ return 0;
+ }
+
+ private void putMiningPipesFromInputsInController() {
+ int maxPipes = miningPipe.getMaxStackSize();
+ if (isHasMiningPipes(maxPipes)) return;
+
+ ItemStack pipes = getStackInSlot(1);
+ if (pipes != null && !pipes.isItemEqual(miningPipe)) return;
+ for (ItemStack storedItem : getStoredInputs()) {
+ if (!storedItem.isItemEqual(miningPipe)) continue;
+
+ if (pipes == null) {
+ setInventorySlotContents(1, GT_Utility.copyOrNull(miningPipe));
+ pipes = getStackInSlot(1);
+ }
+
+ if (pipes.stackSize == maxPipes) break;
+
+ int needPipes = maxPipes - pipes.stackSize;
+ int transferPipes = Math.min(storedItem.stackSize, needPipes);
+
+ pipes.stackSize += transferPipes;
+ storedItem.stackSize -= transferPipes;
+ }
+ updateSlots();
+ }
+
+ private boolean tryOutputPipe() {
+ if (!getBaseMetaTileEntity().addStackToSlot(1, GT_Utility.copyAmount(1, miningPipe)))
+ mOutputItems = new ItemStack[] { GT_Utility.copyAmount(1, miningPipe) };
+ return true;
+ }
+
+ /**
+ * @return 0 for available, 1 for invalid block, 2 for event canceled.
+ */
+ protected int canLowerPipe() {
+ IGregTechTileEntity aBaseTile = getBaseMetaTileEntity();
+ if (yHead > 0 && GT_Utility.getBlockHardnessAt(aBaseTile.getWorld(), xPipe, yHead - 1, zPipe) >= 0) {
+ return GT_Utility.eraseBlockByFakePlayer(getFakePlayer(aBaseTile), xPipe, yHead - 1, zPipe, true) ? 0 : 2;
+ }
+ return 1;
+ }
+
+ protected boolean reachingVoidOrBedrock() {
+ return yHead <= 0 || checkBlockAndMeta(xPipe, yHead - 1, zPipe, Blocks.bedrock, W);
+ }
+
+ private boolean isHasMiningPipes() {
+ return isHasMiningPipes(1);
+ }
+
+ private boolean isHasMiningPipes(int minCount) {
+ ItemStack pipe = getStackInSlot(1);
+ return pipe != null && pipe.stackSize > minCount - 1 && pipe.isItemEqual(miningPipe);
+ }
+
+ /**
+ * @deprecated Readded for compability
+ * @return if no pipes are present
+ */
+ @Deprecated
+ protected boolean waitForPipes() {
+ return !isHasMiningPipes();
+ }
+
+ private boolean isEnergyEnough() {
+ long requiredEnergy = 512 + getMaxInputVoltage() * 4;
+ for (GT_MetaTileEntity_Hatch_Energy energyHatch : mEnergyHatches) {
+ requiredEnergy -= energyHatch.getEUVar();
+ if (requiredEnergy <= 0) return true;
+ }
+ return false;
+ }
+
+ protected boolean workingDownward(ItemStack aStack, int xDrill, int yDrill, int zDrill, int xPipe, int zPipe,
+ int yHead, int oldYHead) {
+ switch (tryLowerPipeState()) {
+ case 2 -> {
+ mMaxProgresstime = 0;
+ setRuntimeFailureReason(CheckRecipeResultRegistry.MISSING_MINING_PIPE);
+ return false;
+ }
+ case 3 -> {
+ workState = STATE_UPWARD;
+ return true;
+ }
+ case 1 -> {
+ workState = STATE_AT_BOTTOM;
+ return true;
+ }
+ default -> {
+ return true;
+ }
+ }
+ }
+
+ protected boolean workingAtBottom(ItemStack aStack, int xDrill, int yDrill, int zDrill, int xPipe, int zPipe,
+ int yHead, int oldYHead) {
+ if (tryLowerPipeState(true) == 0) {
+ workState = STATE_DOWNWARD;
+ return true;
+ }
+ workState = STATE_UPWARD;
+ return true;
+ }
+
+ protected boolean workingUpward(ItemStack aStack, int xDrill, int yDrill, int zDrill, int xPipe, int zPipe,
+ int yHead, int oldYHead) {
+ if (tryPickPipe()) {
+ return true;
+ } else {
+ workState = STATE_DOWNWARD;
+ stopMachine(ShutDownReasonRegistry.NONE);
+ return false;
+ }
+ }
+
+ /** Called once when the abort button is clicked. Use to perform any needed cleanup (e.g. unloading chunks.) */
+ protected void onAbort() {}
+
+ protected void abortDrilling() {
+ if (workState != STATE_ABORT) {
+ workState = STATE_ABORT;
+ onAbort();
+ setShutdownReason("");
+
+ if (!isAllowedToWork()) {
+ enableWorking();
+ }
+ }
+ }
+
+ // This is a distinct state from workingUpward, because some inheritors (like concrete backfiller) operate
+ // exclusively on the workingUpward phase. It also allows for more distinct status messages.
+ protected boolean workingToAbortOperation(@NotNull ItemStack aStack, int xDrill, int yDrill, int zDrill, int xPipe,
+ int zPipe, int yHead, int oldYHead) {
+ if (tryPickPipe()) {
+ return true;
+ } else {
+ workState = STATE_DOWNWARD;
+ stopMachine(ShutDownReasonRegistry.NONE);
+ return false;
+ }
+ }
+
+ @Override
+ public void enableWorking() {
+ super.enableWorking();
+ shutdownReason = "";
+ }
+
+ @Override
+ public void onDisableWorking() {
+ if (suppressErrorWipe) {
+ suppressErrorWipe = false;
+ } else {
+ super.onDisableWorking();
+ }
+ }
+
+ @Override
+ @NotNull
+ public CheckRecipeResult checkProcessing() {
+ ItemStack controllerStack = getControllerSlot();
+ // Public pipe actions
+ setElectricityStats();
+ int oldYHead = yHead;
+ if (!checkPipesAndSetYHead()) {
+ stopMachine(ShutDownReasonRegistry.NONE);
+ return SimpleCheckRecipeResult.ofFailure("no_mining_pipe");
+ } else if (!isEnergyEnough()) {
+ stopMachine(ShutDownReasonRegistry.NONE);
+ return SimpleCheckRecipeResult.ofFailure("not_enough_energy");
+ }
+ putMiningPipesFromInputsInController();
+
+ final boolean wasSuccessful;
+ switch (workState) {
+ case STATE_DOWNWARD -> wasSuccessful = workingDownward(
+ controllerStack,
+ xDrill,
+ yDrill,
+ zDrill,
+ xPipe,
+ zPipe,
+ yHead,
+ oldYHead);
+ case STATE_AT_BOTTOM -> wasSuccessful = workingAtBottom(
+ controllerStack,
+ xDrill,
+ yDrill,
+ zDrill,
+ xPipe,
+ zPipe,
+ yHead,
+ oldYHead);
+ case STATE_UPWARD -> wasSuccessful = workingUpward(
+ controllerStack,
+ xDrill,
+ yDrill,
+ zDrill,
+ xPipe,
+ zPipe,
+ yHead,
+ oldYHead);
+ case STATE_ABORT -> wasSuccessful = workingToAbortOperation(
+ controllerStack,
+ xDrill,
+ yDrill,
+ zDrill,
+ xPipe,
+ zPipe,
+ yHead,
+ oldYHead);
+ default -> wasSuccessful = false;
+ }
+
+ if (runtimeFailure == null) {
+ if (wasSuccessful) {
+ lastRuntimeFailure = null;
+ }
+
+ return resultRegistry.getOrDefault(
+ new ResultRegistryKey(workState, wasSuccessful),
+ SimpleCheckRecipeResult.ofFailure("no_mining_pipe"));
+ } else {
+ final CheckRecipeResult result;
+ result = lastRuntimeFailure = runtimeFailure;
+ runtimeFailure = null;
+ return result;
+ }
+ }
+
+ /**
+ * Allow drills to set a specific failure reason specific to their situation. E.g.: out of drilling fluid.
+ * Should be used when the machine doesn't turn off due to the failure.
+ *
+ * @param newFailureReason A new failure reason
+ */
+ protected void setRuntimeFailureReason(@NotNull CheckRecipeResult newFailureReason) {
+ runtimeFailure = newFailureReason;
+ }
+
+ /**
+ * Gets a reason for why the drill turned off, for use in UIs and such.
+ *
+ * @return A reason, or empty if the machine is active or there is no message set yet.
+ */
+ @NotNull
+ protected Optional<String> getFailureReason() {
+ if (getBaseMetaTileEntity().isActive()) {
+ return Optional.empty();
+ }
+
+ if (!shutdownReason.isEmpty()) {
+ return Optional.of(shutdownReason);
+ }
+
+ return Optional.ofNullable(lastRuntimeFailure)
+ .map(CheckRecipeResult::getDisplayString);
+ }
+
+ /**
+ * Sets a line in the UI to explain why the drill shut down. E.g.: operation finished.
+ * Should be used when the machine has been turned off due to an operating issue or completion.
+ *
+ * @param newReason The reason for the machine shutdown
+ */
+ protected void setShutdownReason(@NotNull String newReason) {
+ shutdownReason = newReason;
+ }
+
+ @Override
+ protected IAlignmentLimits getInitialAlignmentLimits() {
+ return (d, r, f) -> (d.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0 && r.isNotRotated()
+ && !f.isVerticallyFliped();
+ }
+
+ @Override
+ public boolean isRotationChangeAllowed() {
+ return false;
+ }
+
+ @Override
+ public final IStructureDefinition<GT_MetaTileEntity_DrillerBase> getStructureDefinition() {
+ return STRUCTURE_DEFINITION.get(getClass());
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ updateCoordinates();
+ return checkPiece(STRUCTURE_PIECE_MAIN, 1, 6, 0) && checkHatches()
+ && GT_Utility.getTier(getMaxInputVoltage()) >= getMinTier()
+ && mMaintenanceHatches.size() == 1;
+ }
+
+ private void updateCoordinates() {
+ xDrill = getBaseMetaTileEntity().getXCoord();
+ yDrill = getBaseMetaTileEntity().getYCoord();
+ zDrill = getBaseMetaTileEntity().getZCoord();
+ back = getBaseMetaTileEntity().getBackFacing();
+ xPipe = xDrill + back.offsetX;
+ zPipe = zDrill + back.offsetZ;
+ }
+
+ private boolean checkPipesAndSetYHead() {
+ yHead = yDrill - 1;
+ while (checkBlockAndMeta(xPipe, yHead, zPipe, miningPipeBlock, W)) yHead--; // skip pipes
+ // is pipe tip OR is controller layer
+ if (checkBlockAndMeta(xPipe, yHead, zPipe, miningPipeTipBlock, W) || ++yHead == yDrill) return true;
+ // pipe column is broken - try fix
+ getBaseMetaTileEntity().getWorld()
+ .setBlock(xPipe, yHead, zPipe, miningPipeTipBlock);
+ return true;
+ }
+
+ private boolean checkBlockAndMeta(int x, int y, int z, Block block, int meta) {
+ return (meta == W || getBaseMetaTileEntity().getMetaID(x, y, z) == meta)
+ && getBaseMetaTileEntity().getBlock(x, y, z) == block;
+ }
+
+ private FakePlayer mFakePlayer = null;
+
+ protected FakePlayer getFakePlayer(IGregTechTileEntity aBaseTile) {
+ if (mFakePlayer == null) mFakePlayer = GT_Utility.getFakePlayer(aBaseTile);
+ mFakePlayer.setWorld(aBaseTile.getWorld());
+ mFakePlayer.setPosition(aBaseTile.getXCoord(), aBaseTile.getYCoord(), aBaseTile.getZCoord());
+ return mFakePlayer;
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ protected abstract ItemList getCasingBlockItem();
+
+ protected abstract Materials getFrameMaterial();
+
+ protected abstract int getCasingTextureIndex();
+
+ protected abstract int getMinTier();
+
+ protected abstract boolean checkHatches();
+
+ protected abstract void setElectricityStats();
+
+ public int getTotalConfigValue() {
+ int config = 0;
+ ArrayList<ItemStack> tCircuitList = getDataItems(1);
+ for (ItemStack tCircuit : tCircuitList) config += tCircuit.getItemDamage();
+ return config;
+ }
+
+ public ArrayList<GT_MetaTileEntity_Hatch_DataAccess> mDataAccessHatches = new ArrayList<>();
+
+ /**
+ * @param state using bitmask, 1 for IntegratedCircuit, 2 for DataStick, 4 for DataOrb
+ */
+ private boolean isCorrectDataItem(ItemStack aStack, int state) {
+ if ((state & 1) != 0 && ItemList.Circuit_Integrated.isStackEqual(aStack, true, true)) return true;
+ if ((state & 2) != 0 && ItemList.Tool_DataStick.isStackEqual(aStack, false, true)) return true;
+ return (state & 4) != 0 && ItemList.Tool_DataOrb.isStackEqual(aStack, false, true);
+ }
+
+ /**
+ * @param state using bitmask, 1 for IntegratedCircuit, 2 for DataStick, 4 for DataOrb
+ */
+ public ArrayList<ItemStack> getDataItems(int state) {
+ ArrayList<ItemStack> rList = new ArrayList<>();
+ if (GT_Utility.isStackValid(mInventory[1]) && isCorrectDataItem(mInventory[1], state)) {
+ rList.add(mInventory[1]);
+ }
+ for (GT_MetaTileEntity_Hatch_DataAccess tHatch : filterValidMTEs(mDataAccessHatches)) {
+ for (int i = 0; i < tHatch.getBaseMetaTileEntity()
+ .getSizeInventory(); i++) {
+ if (tHatch.getBaseMetaTileEntity()
+ .getStackInSlot(i) != null && isCorrectDataItem(
+ tHatch.getBaseMetaTileEntity()
+ .getStackInSlot(i),
+ state))
+ rList.add(
+ tHatch.getBaseMetaTileEntity()
+ .getStackInSlot(i));
+ }
+ }
+ return rList;
+ }
+
+ @Override
+ public boolean addToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ return super.addToMachineList(aTileEntity, aBaseCasingIndex)
+ || addDataAccessToMachineList(aTileEntity, aBaseCasingIndex);
+ }
+
+ public boolean addDataAccessToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_DataAccess) {
+ ((GT_MetaTileEntity_Hatch) aMetaTileEntity).updateTexture((byte) aBaseCasingIndex);
+ return mDataAccessHatches.add((GT_MetaTileEntity_Hatch_DataAccess) aMetaTileEntity);
+ }
+ return false;
+ }
+
+ @Override
+ public ChunkCoordIntPair getActiveChunk() {
+ return mCurrentChunk;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 1, 6, 0);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 1, 6, 0, elementBudget, env, false, true);
+ }
+
+ @Override
+ protected void drawTexts(DynamicPositionedColumn screenElements, SlotWidget inventorySlot) {
+ super.drawTexts(screenElements, inventorySlot);
+ screenElements.widget(
+ TextWidget.dynamicString(() -> shutdownReason)
+ .setSynced(false)
+ .setTextAlignment(Alignment.CenterLeft)
+ .setEnabled(widget -> !(getBaseMetaTileEntity().isActive() || shutdownReason.isEmpty())))
+ .widget(new FakeSyncWidget.StringSyncer(() -> shutdownReason, newString -> shutdownReason = newString));
+ }
+
+ @Override
+ protected boolean showRecipeTextInGUI() {
+ return false;
+ }
+
+ /**
+ * Adds additional buttons to the main button row. You do not need to set the position.
+ *
+ * @param builder Only use to attach SyncWidgets.
+ * @param buildContext Context for things like the player.
+ */
+ protected List<ButtonWidget> getAdditionalButtons(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ super.addUIWidgets(builder, buildContext);
+ final int BUTTON_Y_LEVEL = 91;
+
+ builder.widget(
+ new GT_LockedWhileActiveButton(this.getBaseMetaTileEntity(), builder)
+ .setOnClick((clickData, widget) -> mChunkLoadingEnabled = !mChunkLoadingEnabled)
+ .setPlayClickSound(true)
+ .setBackground(() -> {
+ if (mChunkLoadingEnabled) {
+ return new IDrawable[] { GT_UITextures.BUTTON_STANDARD_PRESSED,
+ GT_UITextures.OVERLAY_BUTTON_CHUNK_LOADING };
+ }
+ return new IDrawable[] { GT_UITextures.BUTTON_STANDARD,
+ GT_UITextures.OVERLAY_BUTTON_CHUNK_LOADING_OFF };
+ })
+ .attachSyncer(
+ new FakeSyncWidget.BooleanSyncer(
+ () -> mChunkLoadingEnabled,
+ newBoolean -> mChunkLoadingEnabled = newBoolean),
+ builder,
+ (widget, val) -> widget.notifyTooltipChange())
+ .dynamicTooltip(
+ () -> ImmutableList.of(
+ StatCollector.translateToLocal(
+ mChunkLoadingEnabled ? "GT5U.gui.button.chunk_loading_on"
+ : "GT5U.gui.button.chunk_loading_off")))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setPos(new Pos2d(80, BUTTON_Y_LEVEL))
+ .setSize(16, 16))
+ .widget(
+ new ButtonWidget().setOnClick((clickData, widget) -> abortDrilling())
+ .setPlayClickSound(true)
+ .setBackground(() -> {
+ if (workState == STATE_ABORT) {
+ return new IDrawable[] { GT_UITextures.BUTTON_STANDARD_PRESSED,
+ GT_UITextures.OVERLAY_BUTTON_RETRACT_PIPE, GT_UITextures.OVERLAY_BUTTON_LOCKED };
+ }
+ return new IDrawable[] { GT_UITextures.BUTTON_STANDARD,
+ GT_UITextures.OVERLAY_BUTTON_RETRACT_PIPE };
+ })
+ .attachSyncer(
+ new FakeSyncWidget.IntegerSyncer(() -> workState, (newInt) -> workState = newInt),
+ builder,
+ (widget, integer) -> widget.notifyTooltipChange())
+ .dynamicTooltip(
+ () -> ImmutableList.of(
+ StatCollector.translateToLocalFormatted(
+ workState == STATE_ABORT ? "GT5U.gui.button.drill_retract_pipes_active"
+ : "GT5U.gui.button.drill_retract_pipes")))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setPos(new Pos2d(174, 112))
+ .setSize(16, 16));
+
+ int left = 98;
+ for (ButtonWidget button : getAdditionalButtons(builder, buildContext)) {
+ button.setPos(new Pos2d(left, BUTTON_Y_LEVEL))
+ .setSize(16, 16);
+ builder.widget(button);
+ left += 18;
+ }
+ }
+
+ protected List<IHatchElement<? super GT_MetaTileEntity_DrillerBase>> getAllowedHatches() {
+ return ImmutableList.of(
+ InputHatch,
+ OutputHatch,
+ InputBus,
+ OutputBus,
+ Muffler,
+ Maintenance,
+ Energy,
+ DataHatchElement.DataAccess);
+ }
+
+ protected enum DataHatchElement implements IHatchElement<GT_MetaTileEntity_DrillerBase> {
+
+ DataAccess;
+
+ @Override
+ public List<? extends Class<? extends IMetaTileEntity>> mteClasses() {
+ return Collections.singletonList(GT_MetaTileEntity_Hatch_DataAccess.class);
+ }
+
+ @Override
+ public IGT_HatchAdder<GT_MetaTileEntity_DrillerBase> adder() {
+ return GT_MetaTileEntity_DrillerBase::addDataAccessToMachineList;
+ }
+
+ @Override
+ public long count(GT_MetaTileEntity_DrillerBase t) {
+ return t.mDataAccessHatches.size();
+ }
+ }
+
+ /**
+ * Sets or overrides the {@link CheckRecipeResult} for a given work state
+ *
+ * @param state A work state like {@link #STATE_DOWNWARD}.
+ * @param result A previously registered recipe result.
+ */
+ protected void addResultMessage(final int state, @NotNull final CheckRecipeResult result) {
+ resultRegistry.put(new ResultRegistryKey(state, result.wasSuccessful()), result);
+ }
+
+ /**
+ * Sets or overrides the {@link CheckRecipeResult} for a given work state and operation success type.
+ *
+ * @param state A work state like {@link #STATE_DOWNWARD}.
+ * @param wasSuccessful Whether the operation was successful.
+ * @param resultKey An I18N key for the message.
+ */
+ protected void addResultMessage(final int state, final boolean wasSuccessful, @NotNull final String resultKey) {
+ addResultMessage(
+ state,
+ wasSuccessful ? SimpleCheckRecipeResult.ofSuccess(resultKey)
+ : SimpleCheckRecipeResult.ofFailure(resultKey));
+ }
+
+ @SuppressWarnings("ClassCanBeRecord")
+ private final static class ResultRegistryKey {
+
+ private final int state;
+ private final boolean successful;
+
+ public ResultRegistryKey(final int state, final boolean successful) {
+ this.state = state;
+ this.successful = successful;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof ResultRegistryKey other)) {
+ return false;
+ }
+
+ return (state == other.state && successful == other.successful);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(state, successful);
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ElectricBlastFurnace.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ElectricBlastFurnace.java
new file mode 100644
index 0000000000..49b2cc6998
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ElectricBlastFurnace.java
@@ -0,0 +1,401 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.Muffler;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.GT_Values.VN;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ELECTRIC_BLAST_FURNACE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ELECTRIC_BLAST_FURNACE_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ELECTRIC_BLAST_FURNACE_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ELECTRIC_BLAST_FURNACE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.casingTexturePages;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_StructureUtility.ofCoil;
+import static gregtech.api.util.GT_Utility.filterValidMTEs;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.HeatingCoilLevel;
+import gregtech.api.enums.Materials;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.fluid.IFluidStore;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Energy;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Muffler;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Output;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_OverclockCalculator;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_ElectricBlastFurnace extends
+ GT_MetaTileEntity_AbstractMultiFurnace<GT_MetaTileEntity_ElectricBlastFurnace> implements ISurvivalConstructable {
+
+ private int mHeatingCapacity = 0;
+ protected final ArrayList<GT_MetaTileEntity_Hatch_Output> mPollutionOutputHatches = new ArrayList<>();
+ protected final FluidStack[] pollutionFluidStacks = { Materials.CarbonDioxide.getGas(1000),
+ Materials.CarbonMonoxide.getGas(1000), Materials.SulfurDioxide.getGas(1000) };
+
+ protected static final int CASING_INDEX = 11;
+ protected static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final IStructureDefinition<GT_MetaTileEntity_ElectricBlastFurnace> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_ElectricBlastFurnace>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ transpose(
+ new String[][] { { "ttt", "tmt", "ttt" }, { "CCC", "C-C", "CCC" }, { "CCC", "C-C", "CCC" },
+ { "b~b", "bbb", "bbb" } }))
+ .addElement(
+ 't',
+ buildHatchAdder(GT_MetaTileEntity_ElectricBlastFurnace.class)
+ .atLeast(
+ OutputHatch.withAdder(GT_MetaTileEntity_ElectricBlastFurnace::addOutputHatchToTopList)
+ .withCount(t -> t.mPollutionOutputHatches.size()))
+ .casingIndex(CASING_INDEX)
+ .dot(1)
+ .buildAndChain(GregTech_API.sBlockCasings1, CASING_INDEX))
+ .addElement('m', Muffler.newAny(CASING_INDEX, 2))
+ .addElement(
+ 'C',
+ ofCoil(
+ GT_MetaTileEntity_ElectricBlastFurnace::setCoilLevel,
+ GT_MetaTileEntity_ElectricBlastFurnace::getCoilLevel))
+ .addElement(
+ 'b',
+ buildHatchAdder(GT_MetaTileEntity_ElectricBlastFurnace.class)
+ .atLeast(InputHatch, OutputHatch, InputBus, OutputBus, Maintenance, Energy)
+ .casingIndex(CASING_INDEX)
+ .dot(1)
+ .buildAndChain(GregTech_API.sBlockCasings1, CASING_INDEX))
+ .build();
+
+ public GT_MetaTileEntity_ElectricBlastFurnace(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_ElectricBlastFurnace(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_ElectricBlastFurnace(this.mName);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Blast Furnace")
+ .addInfo("Controller block for the Electric Blast Furnace")
+ .addInfo("You can use some fluids to reduce recipe time. Place the circuit in the Input Bus")
+ .addInfo("Each 900K over the min. Heat required reduces power consumption by 5% (multiplicatively)")
+ .addInfo("Each 1800K over the min. Heat allows for an overclock to be upgraded to a perfect overclock.")
+ .addInfo("That means the EBF will reduce recipe time by a factor 4 instead of 2 (giving 100% efficiency).")
+ .addInfo("Additionally gives +100K for every tier past MV")
+ .addPollutionAmount(getPollutionPerSecond(null))
+ .addSeparator()
+ .beginStructureBlock(3, 4, 3, true)
+ .addController("Front bottom")
+ .addCasingInfoRange("Heat Proof Machine Casing", 0, 15, false)
+ .addOtherStructurePart("Heating Coils", "Two middle Layers")
+ .addEnergyHatch("Any bottom layer casing", 3)
+ .addMaintenanceHatch("Any bottom layer casing", 3)
+ .addMufflerHatch("Top middle", 2)
+ .addInputBus("Any bottom layer casing", 3)
+ .addInputHatch("Any bottom layer casing", 3)
+ .addOutputBus("Any bottom layer casing", 3)
+ .addOutputHatch("Fluid outputs, Any bottom layer casing")
+ .addOutputHatch("Pollution gases (CO2/CO/SO2), Any top layer casing", 1)
+ .addStructureInfo("Pollution gas output amount scales with Muffler Hatch tier")
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side == aFacing) {
+ if (aActive) return new ITexture[] { casingTexturePages[0][CASING_INDEX], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ELECTRIC_BLAST_FURNACE_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ELECTRIC_BLAST_FURNACE_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { casingTexturePages[0][CASING_INDEX], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ELECTRIC_BLAST_FURNACE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ELECTRIC_BLAST_FURNACE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { casingTexturePages[0][CASING_INDEX] };
+ }
+
+ @Override
+ public int getPollutionPerSecond(ItemStack aStack) {
+ return GT_Mod.gregtechproxy.mPollutionEBFPerSecond;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.blastFurnaceRecipes;
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_ElectricBlastFurnace> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic() {
+
+ @Nonnull
+ @Override
+ protected GT_OverclockCalculator createOverclockCalculator(@Nonnull GT_Recipe recipe) {
+ return super.createOverclockCalculator(recipe).setRecipeHeat(recipe.mSpecialValue)
+ .setMachineHeat(mHeatingCapacity)
+ .setHeatOC(true)
+ .setHeatDiscount(true);
+ }
+
+ @Override
+ protected @Nonnull CheckRecipeResult validateRecipe(@Nonnull GT_Recipe recipe) {
+ return recipe.mSpecialValue <= mHeatingCapacity ? CheckRecipeResultRegistry.SUCCESSFUL
+ : CheckRecipeResultRegistry.insufficientHeat(recipe.mSpecialValue);
+ }
+ };
+ }
+
+ public boolean addOutputHatchToTopList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Output) {
+ ((GT_MetaTileEntity_Hatch) aMetaTileEntity).updateTexture(aBaseCasingIndex);
+ return mPollutionOutputHatches.add((GT_MetaTileEntity_Hatch_Output) aMetaTileEntity);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ this.mHeatingCapacity = 0;
+
+ setCoilLevel(HeatingCoilLevel.None);
+
+ mPollutionOutputHatches.clear();
+
+ if (!checkPiece(STRUCTURE_PIECE_MAIN, 1, 3, 0)) return false;
+
+ if (getCoilLevel() == HeatingCoilLevel.None) return false;
+
+ if (mMaintenanceHatches.size() != 1) return false;
+
+ this.mHeatingCapacity = (int) getCoilLevel().getHeat() + 100 * (GT_Utility.getTier(getMaxInputVoltage()) - 2);
+ return true;
+ }
+
+ @Override
+ public boolean addOutput(FluidStack aLiquid) {
+ if (aLiquid == null) return false;
+ FluidStack tLiquid = aLiquid.copy();
+ ArrayList<GT_MetaTileEntity_Hatch_Output> tOutputHatches;
+ if (isPollutionFluid(tLiquid)) {
+ tOutputHatches = this.mPollutionOutputHatches;
+ multiplyPollutionFluidAmount(tLiquid);
+ } else {
+ tOutputHatches = this.mOutputHatches;
+ }
+ return dumpFluid(tOutputHatches, tLiquid, true) || dumpFluid(tOutputHatches, tLiquid, false);
+ }
+
+ protected boolean isPollutionFluid(@Nullable FluidStack fluidStack) {
+ if (fluidStack == null) return false;
+ for (FluidStack pollutionFluidStack : pollutionFluidStacks) {
+ if (!fluidStack.isFluidEqual(pollutionFluidStack)) continue;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public List<? extends IFluidStore> getFluidOutputSlots(FluidStack[] toOutput) {
+ if (Arrays.stream(toOutput)
+ .anyMatch(this::isPollutionFluid)) {
+ return filterValidMTEs(mPollutionOutputHatches);
+ }
+ return filterValidMTEs(mOutputHatches);
+ }
+
+ /**
+ * @return 100 -> all released to air, 0 -> all dumped to hatch
+ */
+ public int getPollutionReduction() {
+ int reduction = 100;
+ for (GT_MetaTileEntity_Hatch_Muffler tHatch : filterValidMTEs(mMufflerHatches)) {
+ reduction = Math.min(tHatch.calculatePollutionReduction(100), reduction);
+ }
+ return reduction;
+ }
+
+ protected void multiplyPollutionFluidAmount(@Nonnull FluidStack fluid) {
+ fluid.amount = fluid.amount * Math.min(100 - getPollutionReduction(), 100) / 100;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ int mPollutionReduction = getPollutionReduction();
+ long storedEnergy = 0;
+ long maxEnergy = 0;
+ for (GT_MetaTileEntity_Hatch_Energy tHatch : filterValidMTEs(mEnergyHatches)) {
+ storedEnergy += tHatch.getBaseMetaTileEntity()
+ .getStoredEU();
+ maxEnergy += tHatch.getBaseMetaTileEntity()
+ .getEUCapacity();
+ }
+
+ return new String[] {
+ StatCollector.translateToLocal("GT5U.multiblock.Progress") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(mProgresstime / 20)
+ + EnumChatFormatting.RESET
+ + " s / "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(mMaxProgresstime / 20)
+ + EnumChatFormatting.RESET
+ + " s",
+ StatCollector.translateToLocal("GT5U.multiblock.energy") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(storedEnergy)
+ + EnumChatFormatting.RESET
+ + " EU / "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(maxEnergy)
+ + EnumChatFormatting.RESET
+ + " EU",
+ StatCollector.translateToLocal("GT5U.multiblock.usage") + ": "
+ + EnumChatFormatting.RED
+ + GT_Utility.formatNumbers(-lEUt)
+ + EnumChatFormatting.RESET
+ + " EU/t",
+ StatCollector.translateToLocal("GT5U.multiblock.mei") + ": "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(getMaxInputVoltage())
+ + EnumChatFormatting.RESET
+ + " EU/t(*2A) "
+ + StatCollector.translateToLocal("GT5U.machines.tier")
+ + ": "
+ + EnumChatFormatting.YELLOW
+ + VN[GT_Utility.getTier(getMaxInputVoltage())]
+ + EnumChatFormatting.RESET,
+ StatCollector.translateToLocal("GT5U.multiblock.problems") + ": "
+ + EnumChatFormatting.RED
+ + (getIdealStatus() - getRepairStatus())
+ + EnumChatFormatting.RESET
+ + " "
+ + StatCollector.translateToLocal("GT5U.multiblock.efficiency")
+ + ": "
+ + EnumChatFormatting.YELLOW
+ + mEfficiency / 100.0F
+ + EnumChatFormatting.RESET
+ + " %",
+ StatCollector.translateToLocal("GT5U.EBF.heat") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(mHeatingCapacity)
+ + EnumChatFormatting.RESET
+ + " K",
+ StatCollector.translateToLocal("GT5U.multiblock.pollution") + ": "
+ + EnumChatFormatting.GREEN
+ + mPollutionReduction
+ + EnumChatFormatting.RESET
+ + " %" };
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 1, 3, 0);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 1, 3, 0, elementBudget, env, false, true);
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ inputSeparation = !inputSeparation;
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ StatCollector.translateToLocal("GT5U.machines.separatebus") + " " + inputSeparation);
+ }
+
+ @Override
+ public void loadNBTData(final NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ if (aNBT.hasKey("isBussesSeparate")) {
+ // backward compatibility
+ inputSeparation = aNBT.getBoolean("isBussesSeparate");
+ }
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsInputSeparation() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsBatchMode() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ExtremeDieselEngine.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ExtremeDieselEngine.java
new file mode 100644
index 0000000000..dace58a492
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ExtremeDieselEngine.java
@@ -0,0 +1,237 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_EXTREME_DIESEL_ENGINE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_EXTREME_DIESEL_ENGINE_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_EXTREME_DIESEL_ENGINE_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_EXTREME_DIESEL_ENGINE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.casingTexturePages;
+import static gregtech.api.util.GT_Utility.filterValidMTEs;
+
+import net.minecraft.block.Block;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Materials;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Dynamo;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Muffler;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.maps.FuelBackend;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_ExtremeDieselEngine extends GT_MetaTileEntity_DieselEngine {
+
+ public GT_MetaTileEntity_ExtremeDieselEngine(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_ExtremeDieselEngine(String aName) {
+ super(aName);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Combustion Generator")
+ .addInfo("Controller block for the Extreme Combustion Engine")
+ .addInfo("Supply high rating fuel and 8000L of Lubricant per hour to run")
+ .addInfo("Supply 40L/s of Liquid Oxygen to boost output (optional)")
+ .addInfo("Default: Produces 10900EU/t at 100% fuel efficiency")
+ .addInfo("Boosted: Produces 32700EU/t at 150% fuel efficiency")
+ .addInfo("You need to wait for it to reach 300% to output full power")
+ .addPollutionAmount(getPollutionPerSecond(null))
+ .addSeparator()
+ .beginStructureBlock(3, 3, 4, false)
+ .addController("Front center")
+ .addCasingInfoRange("Robust Tungstensteel Machine Casing", 16, 22, false)
+ .addOtherStructurePart("Titanium Gear Box Machine Casing", "Inner 2 blocks")
+ .addOtherStructurePart("Extreme Engine Intake Machine Casing", "8x, ring around controller")
+ .addStructureInfo("Extreme Engine Intake Casings must not be obstructed in front (only air blocks)")
+ .addDynamoHatch("Back center", 2)
+ .addMaintenanceHatch("One of the casings next to a Gear Box", 1)
+ .addMufflerHatch("Top middle back, above the rear Gear Box", 1)
+ .addInputHatch("HOG, next to a Gear Box", 1)
+ .addInputHatch("Lubricant, next to a Gear Box", 1)
+ .addInputHatch("Liquid Oxygen, optional, next to a Gear Box", 1)
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public RecipeMap<FuelBackend> getRecipeMap() {
+ return RecipeMaps.extremeDieselFuels;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side == aFacing) {
+ if (aActive) return new ITexture[] { casingTexturePages[0][60], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_EXTREME_DIESEL_ENGINE_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_EXTREME_DIESEL_ENGINE_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { casingTexturePages[0][60], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_EXTREME_DIESEL_ENGINE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_EXTREME_DIESEL_ENGINE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { casingTexturePages[0][60] };
+ }
+
+ @Override
+ public Block getCasingBlock() {
+ return GregTech_API.sBlockCasings4;
+ }
+
+ @Override
+ public byte getCasingMeta() {
+ return 0;
+ }
+
+ @Override
+ public Block getIntakeBlock() {
+ return GregTech_API.sBlockCasings8;
+ }
+
+ @Override
+ public byte getIntakeMeta() {
+ return 4;
+ }
+
+ @Override
+ public Block getGearboxBlock() {
+ return GregTech_API.sBlockCasings2;
+ }
+
+ @Override
+ public byte getGearboxMeta() {
+ return 4;
+ }
+
+ @Override
+ public byte getCasingTextureIndex() {
+ return 60;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_ExtremeDieselEngine(this.mName);
+ }
+
+ @Override
+ protected int getNominalOutput() {
+ return 10900;
+ }
+
+ @Override
+ protected int getBoostFactor() {
+ return 2;
+ }
+
+ @Override
+ protected Materials getBooster() {
+ return Materials.LiquidOxygen;
+ }
+
+ @Override
+ protected int getAdditiveFactor() {
+ return 1;
+ }
+
+ @Override
+ protected int getEfficiencyIncrease() {
+ return 20;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return boostEu ? 30000 : 10000;
+ }
+
+ @Override
+ public int getPollutionPerSecond(ItemStack aStack) {
+ return GT_Mod.gregtechproxy.mPollutionExtremeCombustionEnginePerSecond;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ int mPollutionReduction = 0;
+ for (GT_MetaTileEntity_Hatch_Muffler tHatch : filterValidMTEs(mMufflerHatches)) {
+ mPollutionReduction = Math.max(tHatch.calculatePollutionReduction(100), mPollutionReduction);
+ }
+
+ long storedEnergy = 0;
+ long maxEnergy = 0;
+ for (GT_MetaTileEntity_Hatch_Dynamo tHatch : filterValidMTEs(mDynamoHatches)) {
+ storedEnergy += tHatch.getBaseMetaTileEntity()
+ .getStoredEU();
+ maxEnergy += tHatch.getBaseMetaTileEntity()
+ .getEUCapacity();
+ }
+
+ return new String[] { EnumChatFormatting.BLUE + "Extreme Diesel Engine" + EnumChatFormatting.RESET,
+ StatCollector.translateToLocal("GT5U.multiblock.energy") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(storedEnergy)
+ + EnumChatFormatting.RESET
+ + " EU / "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(maxEnergy)
+ + EnumChatFormatting.RESET
+ + " EU",
+ getIdealStatus() == getRepairStatus()
+ ? EnumChatFormatting.GREEN + StatCollector.translateToLocal("GT5U.turbine.maintenance.false")
+ + EnumChatFormatting.RESET
+ : EnumChatFormatting.RED + StatCollector.translateToLocal("GT5U.turbine.maintenance.true")
+ + EnumChatFormatting.RESET,
+ StatCollector.translateToLocal("GT5U.engine.output") + ": "
+ + EnumChatFormatting.RED
+ + GT_Utility.formatNumbers((long) -mEUt * mEfficiency / 10000)
+ + EnumChatFormatting.RESET
+ + " EU/t",
+ StatCollector.translateToLocal("GT5U.engine.consumption") + ": "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(fuelConsumption)
+ + EnumChatFormatting.RESET
+ + " L/t",
+ StatCollector.translateToLocal("GT5U.engine.value") + ": "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(fuelValue)
+ + EnumChatFormatting.RESET
+ + " EU/L",
+ StatCollector.translateToLocal("GT5U.turbine.fuel") + ": "
+ + EnumChatFormatting.GOLD
+ + GT_Utility.formatNumbers(fuelRemaining)
+ + EnumChatFormatting.RESET
+ + " L",
+ StatCollector.translateToLocal("GT5U.engine.efficiency") + ": "
+ + EnumChatFormatting.YELLOW
+ + (mEfficiency / 100F)
+ + EnumChatFormatting.YELLOW
+ + " %",
+ StatCollector.translateToLocal("GT5U.multiblock.pollution") + ": "
+ + EnumChatFormatting.GREEN
+ + mPollutionReduction
+ + EnumChatFormatting.RESET
+ + " %" };
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_FusionComputer.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_FusionComputer.java
new file mode 100644
index 0000000000..8edd2dea4e
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_FusionComputer.java
@@ -0,0 +1,675 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.lazy;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_FUSION_GLASS;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_FUSION_GLASS_YELLOW;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_FUSION_GLASS_YELLOW_GLOW;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_StructureUtility.filterByMTETier;
+import static gregtech.api.util.GT_Utility.filterValidMTEs;
+
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.block.Block;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.StatCollector;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.google.common.collect.ImmutableMap;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+import com.gtnewhorizons.modularui.api.NumberFormatMUI;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.ProgressBar;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import gregtech.GT_Mod;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.Textures;
+import gregtech.api.enums.VoidingMode;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.gui.modularui.GUITextureSet;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.interfaces.tileentity.IOverclockDescriptionProvider;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_EnhancedMultiBlockBase;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Energy;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Input;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Output;
+import gregtech.api.objects.GT_ItemStack;
+import gregtech.api.objects.overclockdescriber.FusionOverclockDescriber;
+import gregtech.api.objects.overclockdescriber.OverclockDescriber;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_OverclockCalculator;
+import gregtech.api.util.GT_ParallelHelper;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.shutdown.ShutDownReason;
+import gregtech.api.util.shutdown.ShutDownReasonRegistry;
+import gregtech.common.tileentities.machines.multi.drone.GT_MetaTileEntity_Hatch_DroneDownLink;
+
+public abstract class GT_MetaTileEntity_FusionComputer
+ extends GT_MetaTileEntity_EnhancedMultiBlockBase<GT_MetaTileEntity_FusionComputer>
+ implements ISurvivalConstructable, IAddUIWidgets, IOverclockDescriptionProvider {
+
+ private final OverclockDescriber overclockDescriber;
+
+ public static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final ClassValue<IStructureDefinition<GT_MetaTileEntity_FusionComputer>> STRUCTURE_DEFINITION = new ClassValue<>() {
+
+ @Override
+ protected IStructureDefinition<GT_MetaTileEntity_FusionComputer> computeValue(Class<?> type) {
+ return StructureDefinition.<GT_MetaTileEntity_FusionComputer>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ transpose(
+ new String[][] {
+ { " ", " ihi ", " hh hh ", " h h ",
+ " h h ", " h h ", " i i ", " h h ",
+ " i i ", " h h ", " h h ", " h h ",
+ " hh hh ", " ihi ", " ", },
+ { " xhx ", " hhccchh ", " eccxdxcce ", " eceh hece ",
+ " hce ech ", " hch hch ", "xcx xcx", "hch hch",
+ "xcx xcx", " hch hch ", " hce ech ", " eceh hece ",
+ " eccx~xcce ", " hhccchh ", " xhx ", },
+ { " ", " ihi ", " hh hh ", " h h ",
+ " h h ", " h h ", " i i ", " h h ",
+ " i i ", " h h ", " h h ", " h h ",
+ " hh hh ", " ihi ", " ", } }))
+ .addElement('c', lazy(t -> ofBlock(t.getFusionCoil(), t.getFusionCoilMeta())))
+ .addElement('h', lazy(t -> ofBlock(t.getCasing(), t.getCasingMeta())))
+ .addElement(
+ 'i',
+ lazy(
+ t -> buildHatchAdder(GT_MetaTileEntity_FusionComputer.class)
+ .atLeast(
+ ImmutableMap.of(InputHatch.withAdder(GT_MetaTileEntity_FusionComputer::addInjector), 1))
+ .hatchItemFilterAnd(t2 -> filterByMTETier(t2.tier(), Integer.MAX_VALUE))
+ .casingIndex(53)
+ .dot(1)
+ .buildAndChain(t.getCasing(), t.getCasingMeta())))
+ .addElement(
+ 'e',
+ lazy(
+ t -> buildHatchAdder(GT_MetaTileEntity_FusionComputer.class).atLeast(
+ ImmutableMap.of(Energy.withAdder(GT_MetaTileEntity_FusionComputer::addEnergyInjector), 16))
+ .hatchItemFilterAnd(t2 -> filterByMTETier(t2.tier(), Integer.MAX_VALUE))
+ .casingIndex(53)
+ .dot(2)
+ .buildAndChain(t.getCasing(), t.getCasingMeta())))
+ .addElement(
+ 'x',
+ lazy(
+ t -> buildHatchAdder(GT_MetaTileEntity_FusionComputer.class)
+ .atLeast(OutputHatch.withAdder(GT_MetaTileEntity_FusionComputer::addExtractor))
+ .hatchItemFilterAnd(t2 -> filterByMTETier(t2.tier(), Integer.MAX_VALUE))
+ .casingIndex(53)
+ .dot(3)
+ .buildAndChain(t.getCasing(), t.getCasingMeta())))
+ .addElement(
+ 'd',
+ lazy(
+ t -> buildHatchAdder(GT_MetaTileEntity_FusionComputer.class)
+ .adder(GT_MetaTileEntity_FusionComputer::addDroneHatch)
+ .hatchId(9401)
+ .casingIndex(53)
+ .dot(4)
+ .buildAndChain(t.getCasing(), t.getCasingMeta())))
+ .build();
+ }
+ };
+ public GT_Recipe mLastRecipe;
+ public long mEUStore;
+
+ static {
+ Textures.BlockIcons.setCasingTextureForId(
+ 52,
+ TextureFactory.of(
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_FUSION_GLASS_YELLOW)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(MACHINE_CASING_FUSION_GLASS_YELLOW_GLOW)
+ .extFacing()
+ .glow()
+ .build()));
+ }
+
+ public GT_MetaTileEntity_FusionComputer(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ this.overclockDescriber = createOverclockDescriber();
+ }
+
+ public GT_MetaTileEntity_FusionComputer(String aName) {
+ super(aName);
+ this.overclockDescriber = createOverclockDescriber();
+ }
+
+ protected OverclockDescriber createOverclockDescriber() {
+ return new FusionOverclockDescriber((byte) tier(), capableStartupCanonical());
+ }
+
+ @Nonnull
+ @Override
+ public OverclockDescriber getOverclockDescriber() {
+ return overclockDescriber;
+ }
+
+ public abstract int tier();
+
+ @Override
+ public abstract long maxEUStore();
+
+ /**
+ * Unlike {@link #maxEUStore()}, this provides theoretical limit of startup EU, without considering the amount of
+ * hatches nor the room for extra energy. Intended for simulation.
+ */
+ public abstract long capableStartupCanonical();
+
+ @Override
+ public abstract MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity);
+
+ @Override
+ public boolean allowCoverOnSide(ForgeDirection side, GT_ItemStack aStack) {
+
+ return side != getBaseMetaTileEntity().getFrontFacing();
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ if (mEUt > 0) {
+ mEUt = -mEUt;
+ }
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_FusionComputer> getStructureDefinition() {
+ return STRUCTURE_DEFINITION.get(getClass());
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addController("Fusion Reactor")
+ .addInfo("Some kind of fusion reactor, maybe")
+ .addSeparator()
+ .addInfo("Some kind of fusion reactor, maybe")
+ .addStructureInfo("Should probably be built similar to other fusions")
+ .addStructureInfo("See controller tooltip for details")
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ return checkPiece(STRUCTURE_PIECE_MAIN, 7, 1, 12) && !mOutputHatches.isEmpty() && !mEnergyHatches.isEmpty();
+ }
+
+ private boolean addEnergyInjector(IGregTechTileEntity aBaseMetaTileEntity, int aBaseCasingIndex) {
+ IMetaTileEntity aMetaTileEntity = aBaseMetaTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (!(aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Energy tHatch)) return false;
+ if (tHatch.mTier < tier()) return false;
+ tHatch.updateTexture(aBaseCasingIndex);
+ return mEnergyHatches.add(tHatch);
+ }
+
+ private boolean addInjector(IGregTechTileEntity aBaseMetaTileEntity, int aBaseCasingIndex) {
+ IMetaTileEntity aMetaTileEntity = aBaseMetaTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (!(aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Input tHatch)) return false;
+ if (tHatch.getTierForStructure() < tier()) return false;
+ tHatch.updateTexture(aBaseCasingIndex);
+ tHatch.mRecipeMap = getRecipeMap();
+ return mInputHatches.add(tHatch);
+ }
+
+ private boolean addExtractor(IGregTechTileEntity aBaseMetaTileEntity, int aBaseCasingIndex) {
+ if (aBaseMetaTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aBaseMetaTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (!(aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Output tHatch)) return false;
+ if (tHatch.getTierForStructure() < tier()) return false;
+ tHatch.updateTexture(aBaseCasingIndex);
+ return mOutputHatches.add(tHatch);
+ }
+
+ private boolean addDroneHatch(IGregTechTileEntity aBaseMetaTileEntity, int aBaseCasingIndex) {
+ if (aBaseMetaTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aBaseMetaTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (!(aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_DroneDownLink tHatch)) return false;
+ tHatch.updateTexture(aBaseCasingIndex);
+ return addToMachineList(aBaseMetaTileEntity, aBaseCasingIndex);
+ }
+
+ public abstract Block getCasing();
+
+ public abstract int getCasingMeta();
+
+ public abstract Block getFusionCoil();
+
+ public abstract int getFusionCoilMeta();
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side == aFacing) return new ITexture[] { TextureFactory.builder()
+ .addIcon(MACHINE_CASING_FUSION_GLASS)
+ .extFacing()
+ .build(), getTextureOverlay() };
+ if (aActive) return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(52) };
+ return new ITexture[] { TextureFactory.builder()
+ .addIcon(MACHINE_CASING_FUSION_GLASS)
+ .extFacing()
+ .build() };
+ }
+
+ /**
+ * @return The list of textures overlay
+ */
+ public abstract ITexture getTextureOverlay();
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.fusionRecipes;
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic() {
+
+ @NotNull
+ @Override
+ protected GT_ParallelHelper createParallelHelper(@NotNull GT_Recipe recipe) {
+ // When the fusion first loads and is still processing, it does the recipe check without consuming.
+ return super.createParallelHelper(recipe).setConsumption(!mRunningOnLoad);
+ }
+
+ @NotNull
+ @Override
+ protected GT_OverclockCalculator createOverclockCalculator(@NotNull GT_Recipe recipe) {
+ return overclockDescriber.createCalculator(super.createOverclockCalculator(recipe), recipe);
+ }
+
+ @NotNull
+ @Override
+ protected CheckRecipeResult validateRecipe(@NotNull GT_Recipe recipe) {
+ if (!mRunningOnLoad && recipe.mSpecialValue > maxEUStore()) {
+ return CheckRecipeResultRegistry.insufficientStartupPower(recipe.mSpecialValue);
+ }
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ }
+
+ @NotNull
+ @Override
+ public CheckRecipeResult process() {
+ CheckRecipeResult result = super.process();
+ if (mRunningOnLoad) mRunningOnLoad = false;
+ turnCasingActive(result.wasSuccessful());
+ if (result.wasSuccessful()) {
+ mLastRecipe = lastRecipe;
+ } else {
+ mLastRecipe = null;
+ }
+ return result;
+ }
+ };
+ }
+
+ @Override
+ protected void setProcessingLogicPower(ProcessingLogic logic) {
+ logic.setAvailableVoltage(GT_Values.V[tier()]);
+ logic.setAvailableAmperage(1);
+ logic.setAmperageOC(false);
+ }
+
+ public boolean turnCasingActive(boolean status) {
+ if (this.mEnergyHatches != null) {
+ for (GT_MetaTileEntity_Hatch_Energy hatch : this.mEnergyHatches) {
+ hatch.updateTexture(status ? 52 : 53);
+ }
+ }
+ if (this.mOutputHatches != null) {
+ for (GT_MetaTileEntity_Hatch_Output hatch : this.mOutputHatches) {
+ hatch.updateTexture(status ? 52 : 53);
+ }
+ }
+ if (this.mInputHatches != null) {
+ for (GT_MetaTileEntity_Hatch_Input hatch : this.mInputHatches) {
+ hatch.updateTexture(status ? 52 : 53);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aBaseMetaTileEntity.isServerSide()) {
+ if (mEfficiency < 0) mEfficiency = 0;
+ if (mRunningOnLoad && checkMachine(aBaseMetaTileEntity, mInventory[1])) {
+ this.mEUStore = aBaseMetaTileEntity.getStoredEU();
+ checkRecipe();
+ }
+ if (mUpdated) {
+ mUpdate = 50;
+ mUpdated = false;
+ }
+ if (--mUpdate == 0 || --mStartUpCheck == 0) {
+ checkStructure(true, aBaseMetaTileEntity);
+ }
+ if (mStartUpCheck < 0) {
+ if (mMachine) {
+ this.mEUStore = aBaseMetaTileEntity.getStoredEU();
+ if (this.mEnergyHatches != null) {
+ for (GT_MetaTileEntity_Hatch_Energy tHatch : filterValidMTEs(mEnergyHatches)) {
+ long energyToMove = GT_Values.V[tier()] / 16;
+ if (aBaseMetaTileEntity.getStoredEU() + energyToMove < maxEUStore()
+ && tHatch.getBaseMetaTileEntity()
+ .decreaseStoredEnergyUnits(energyToMove, false)) {
+ aBaseMetaTileEntity.increaseStoredEnergyUnits(energyToMove, true);
+ }
+ }
+ }
+ if (this.mEUStore <= 0 && mMaxProgresstime > 0) {
+ stopMachine(ShutDownReasonRegistry.POWER_LOSS);
+ }
+ if (mMaxProgresstime > 0) {
+ this.getBaseMetaTileEntity()
+ .decreaseStoredEnergyUnits(-mEUt, true);
+ if (mMaxProgresstime > 0 && ++mProgresstime >= mMaxProgresstime) {
+ if (mOutputItems != null)
+ for (ItemStack tStack : mOutputItems) if (tStack != null) addOutput(tStack);
+ if (mOutputFluids != null)
+ for (FluidStack tStack : mOutputFluids) if (tStack != null) addOutput(tStack);
+ mEfficiency = Math
+ .max(0, Math.min(mEfficiency + mEfficiencyIncrease, getMaxEfficiency(mInventory[1])));
+ mOutputItems = null;
+ mProgresstime = 0;
+ mMaxProgresstime = 0;
+ mEfficiencyIncrease = 0;
+ if (mOutputFluids != null && mOutputFluids.length > 0) {
+ try {
+ GT_Mod.achievements.issueAchivementHatchFluid(
+ aBaseMetaTileEntity.getWorld()
+ .getPlayerEntityByName(aBaseMetaTileEntity.getOwnerName()),
+ mOutputFluids[0]);
+ } catch (Exception ignored) {}
+ }
+ this.mEUStore = aBaseMetaTileEntity.getStoredEU();
+ if (aBaseMetaTileEntity.isAllowedToWork()) checkRecipe();
+ }
+ } else {
+ if (aTick % 100 == 0 || aBaseMetaTileEntity.hasWorkJustBeenEnabled()
+ || aBaseMetaTileEntity.hasInventoryBeenModified()) {
+ turnCasingActive(mMaxProgresstime > 0);
+ if (aBaseMetaTileEntity.isAllowedToWork()) {
+ this.mEUStore = aBaseMetaTileEntity.getStoredEU();
+ if (checkRecipe()) {
+ if (this.mEUStore < this.mLastRecipe.mSpecialValue + this.mEUt) {
+ stopMachine(ShutDownReasonRegistry.POWER_LOSS);
+ }
+ aBaseMetaTileEntity
+ .decreaseStoredEnergyUnits(this.mLastRecipe.mSpecialValue + this.mEUt, true);
+ }
+ }
+ if (mMaxProgresstime <= 0) mEfficiency = Math.max(0, mEfficiency - 1000);
+ }
+ }
+ } else if (aBaseMetaTileEntity.isAllowedToWork()) {
+ this.mLastRecipe = null;
+ stopMachine(ShutDownReasonRegistry.STRUCTURE_INCOMPLETE);
+ }
+ }
+ aBaseMetaTileEntity
+ .setErrorDisplayID((aBaseMetaTileEntity.getErrorDisplayID() & ~127) | (mMachine ? 0 : 64));
+ aBaseMetaTileEntity.setActive(mMaxProgresstime > 0);
+ } else {
+ doActivitySound(getActivitySoundLoop());
+ }
+ }
+
+ @Override
+ public boolean drainEnergyInput(long aEU) {
+ return false;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public void stopMachine(@NotNull ShutDownReason reason) {
+ super.stopMachine(reason);
+ turnCasingActive(false);
+ }
+
+ @Override
+ public String[] getInfoData() {
+ String tier = tier() == 6 ? EnumChatFormatting.RED + "I" + EnumChatFormatting.RESET
+ : tier() == 7 ? EnumChatFormatting.YELLOW + "II" + EnumChatFormatting.RESET
+ : tier() == 8 ? EnumChatFormatting.GRAY + "III" + EnumChatFormatting.RESET : "IV";
+ float plasmaOut = 0;
+ int powerRequired = 0;
+ if (this.mLastRecipe != null) {
+ powerRequired = this.mLastRecipe.mEUt;
+ if (this.mLastRecipe.getFluidOutput(0) != null) {
+ plasmaOut = (float) this.mLastRecipe.getFluidOutput(0).amount / (float) this.mLastRecipe.mDuration;
+ }
+ }
+
+ return new String[] { EnumChatFormatting.BLUE + "Fusion Reactor MK " + EnumChatFormatting.RESET + tier,
+ StatCollector.translateToLocal("GT5U.fusion.req") + ": "
+ + EnumChatFormatting.RED
+ + GT_Utility.formatNumbers(powerRequired)
+ + EnumChatFormatting.RESET
+ + "EU/t",
+ StatCollector.translateToLocal("GT5U.multiblock.energy") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(mEUStore)
+ + EnumChatFormatting.RESET
+ + " EU / "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(maxEUStore())
+ + EnumChatFormatting.RESET
+ + " EU",
+ StatCollector.translateToLocal("GT5U.fusion.plasma") + ": "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(plasmaOut)
+ + EnumChatFormatting.RESET
+ + "L/t" };
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return true;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 7, 1, 12);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 7, 1, 12, elementBudget, env, false, true);
+ }
+
+ @SideOnly(Side.CLIENT)
+ @Override
+ protected ResourceLocation getActivitySoundLoop() {
+ return SoundResource.GT_MACHINES_FUSION_LOOP.resourceLocation;
+ }
+
+ @Override
+ public boolean doesBindPlayerInventory() {
+ return false;
+ }
+
+ @Override
+ public void addGregTechLogo(ModularWindow.Builder builder) {
+ builder.widget(
+ new DrawableWidget().setDrawable(getGUITextureSet().getGregTechLogo())
+ .setSize(17, 17)
+ .setPos(155, 145));
+ }
+
+ @Override
+ public GUITextureSet getGUITextureSet() {
+ return new GUITextureSet().setMainBackground(GT_UITextures.BACKGROUND_FUSION_COMPUTER);
+ }
+
+ @Override
+ public int getGUIWidth() {
+ return 176;
+ }
+
+ @Override
+ public int getGUIHeight() {
+ return 166;
+ }
+
+ protected static final NumberFormatMUI numberFormat = new NumberFormatMUI();
+ protected long clientEU;
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ builder
+ .widget(
+ new TextWidget(GT_Utility.trans("138", "Incomplete Structure.")).setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setEnabled(widget -> !mMachine)
+ .setPos(10, 8))
+ .widget(new FakeSyncWidget.BooleanSyncer(() -> mMachine, val -> mMachine = val))
+ .widget(
+ new TextWidget("Hit with Soft Mallet to (re-)start the Machine if it doesn't start.")
+ .setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setTextAlignment(Alignment.Center)
+ .setEnabled(
+ widget -> getBaseMetaTileEntity().getErrorDisplayID() == 0
+ && !getBaseMetaTileEntity().isActive())
+ .setPos(-getGUIWidth() / 2, 170)
+ .setSize(getGUIWidth() * 2, 9))
+ .widget(
+ new FakeSyncWidget.IntegerSyncer(
+ () -> getBaseMetaTileEntity().getErrorDisplayID(),
+ val -> getBaseMetaTileEntity().setErrorDisplayID(val)))
+ .widget(
+ new FakeSyncWidget.BooleanSyncer(
+ () -> getBaseMetaTileEntity().isActive(),
+ val -> getBaseMetaTileEntity().setActive(val)))
+ .widget(
+ new TextWidget("Running perfectly.").setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setTextAlignment(Alignment.Center)
+ .setEnabled(
+ widget -> getBaseMetaTileEntity().getErrorDisplayID() == 0
+ && getBaseMetaTileEntity().isActive())
+ .setPos(0, 170)
+ .setSize(getGUIWidth(), 9))
+ .widget(
+ new FakeSyncWidget.IntegerSyncer(
+ () -> getBaseMetaTileEntity().getErrorDisplayID(),
+ val -> getBaseMetaTileEntity().setErrorDisplayID(val)))
+ .widget(
+ new ProgressBar()
+ .setProgress(
+ () -> (float) getBaseMetaTileEntity().getStoredEU() / getBaseMetaTileEntity().getEUCapacity())
+ .setDirection(ProgressBar.Direction.RIGHT)
+ .setTexture(GT_UITextures.PROGRESSBAR_STORED_EU, 147)
+ .setPos(5, 156)
+ .setSize(147, 5))
+ .widget(new TextWidget().setStringSupplier(() -> {
+ if (clientEU > 160_000_000L && clientEU < 160_010_000L) {
+ clientEU = 160_000_000L;
+ }
+ if (clientEU > 320_000_000L && clientEU < 320_010_000L) {
+ clientEU = 320_000_000L;
+ }
+ if (clientEU > 640_000_000L && clientEU < 640_010_000L) {
+ clientEU = 640_000_000L;
+ }
+ if (clientEU > 5_120_000_000L && clientEU < 5_120_080_000L) {
+ clientEU = 5_120_000_000L;
+ }
+ return numberFormat.format(clientEU) + " EU";
+ })
+ .setDefaultColor(COLOR_TEXT_RED.get())
+ .setTextAlignment(Alignment.Center)
+ .setScale(0.5f)
+ .setPos(5, 157)
+ .setSize(147, 5))
+ .widget(new FakeSyncWidget.LongSyncer(() -> getBaseMetaTileEntity().getStoredEU(), val -> clientEU = val))
+ .widget(
+ new ButtonWidget().setNEITransferRect(
+ RecipeMaps.fusionRecipes.getFrontend()
+ .getUIProperties().neiTransferRectId)
+ .setBackground(GT_UITextures.BUTTON_STANDARD, GT_UITextures.OVERLAY_BUTTON_NEI)
+ .setPos(154, 4)
+ .setSize(18, 18));
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ @Override
+ public Set<VoidingMode> getAllowedVoidingModes() {
+ return VoidingMode.FLUID_ONLY_MODES;
+ }
+
+ @Override
+ public boolean getDefaultHasMaintenanceChecks() {
+ return false;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_FusionComputer1.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_FusionComputer1.java
new file mode 100644
index 0000000000..e3a2432bf9
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_FusionComputer1.java
@@ -0,0 +1,103 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FUSION1;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FUSION1_GLOW;
+
+import net.minecraft.block.Block;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+
+public class GT_MetaTileEntity_FusionComputer1 extends GT_MetaTileEntity_FusionComputer {
+
+ private static final ITexture textureOverlay = TextureFactory.of(
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FUSION1)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FUSION1_GLOW)
+ .extFacing()
+ .glow()
+ .build());
+
+ public GT_MetaTileEntity_FusionComputer1(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_FusionComputer1(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public int tier() {
+ return 6;
+ }
+
+ @Override
+ public long maxEUStore() {
+ return 160003000L * (Math.min(16, this.mEnergyHatches.size())) / 16L;
+ }
+
+ @Override
+ public long capableStartupCanonical() {
+ return 160_000_000;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_FusionComputer1(mName);
+ }
+
+ @Override
+ public Block getCasing() {
+ return GregTech_API.sBlockCasings1;
+ }
+
+ @Override
+ public int getCasingMeta() {
+ return 6;
+ }
+
+ @Override
+ public Block getFusionCoil() {
+ return GregTech_API.sBlockCasings1;
+ }
+
+ @Override
+ public int getFusionCoilMeta() {
+ return 15;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Fusion Reactor")
+ .addInfo("It's over 9000!!!")
+ .addInfo("Controller block for the Fusion Reactor Mk I")
+ .addInfo("2048EU/t and 10M EU capacity per Energy Hatch")
+ .addInfo("If the recipe has a startup cost greater than the")
+ .addInfo("number of energy hatches * cap, you can't do it")
+ .addSeparator()
+ .beginStructureBlock(15, 3, 15, false)
+ .addController("See diagram when placed")
+ .addCasingInfoRange("LuV Machine Casing", 79, 123, false)
+ .addStructureInfo("Cover the coils with casing")
+ .addOtherStructurePart("Superconducting Coil Block", "Center part of the ring")
+ .addEnergyHatch("1-16, Specified casings", 2)
+ .addInputHatch("2-16, Specified casings", 1)
+ .addOutputHatch("1-16, Specified casings", 3)
+ .addStructureInfo("ALL Hatches must be LuV or better")
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public ITexture getTextureOverlay() {
+ return textureOverlay;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_FusionComputer2.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_FusionComputer2.java
new file mode 100644
index 0000000000..0b1f10158b
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_FusionComputer2.java
@@ -0,0 +1,103 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FUSION2;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FUSION2_GLOW;
+
+import net.minecraft.block.Block;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+
+public class GT_MetaTileEntity_FusionComputer2 extends GT_MetaTileEntity_FusionComputer {
+
+ private static final ITexture textureOverlay = TextureFactory.of(
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FUSION2)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FUSION2_GLOW)
+ .extFacing()
+ .glow()
+ .build());
+
+ public GT_MetaTileEntity_FusionComputer2(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_FusionComputer2(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public int tier() {
+ return 7;
+ }
+
+ @Override
+ public long maxEUStore() {
+ return 320006000L * (Math.min(16, this.mEnergyHatches.size())) / 16L;
+ }
+
+ @Override
+ public long capableStartupCanonical() {
+ return 320_000_000;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_FusionComputer2(mName);
+ }
+
+ @Override
+ public Block getCasing() {
+ return GregTech_API.sBlockCasings4;
+ }
+
+ @Override
+ public int getCasingMeta() {
+ return 6;
+ }
+
+ @Override
+ public Block getFusionCoil() {
+ return GregTech_API.sBlockCasings4;
+ }
+
+ @Override
+ public int getFusionCoilMeta() {
+ return 7;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Fusion Reactor")
+ .addInfo("It's over 9000!!!")
+ .addInfo("Controller block for the Fusion Reactor Mk II")
+ .addInfo("8192EU/t and 20M EU capacity per Energy Hatch")
+ .addInfo("If the recipe has a startup cost greater than the")
+ .addInfo("number of energy hatches * cap, you can't do it")
+ .addSeparator()
+ .beginStructureBlock(15, 3, 15, false)
+ .addController("See diagram when placed")
+ .addCasingInfoRange("Fusion Machine Casing", 79, 123, false)
+ .addStructureInfo("Cover the coils with casing")
+ .addOtherStructurePart("Fusion Coil Block", "Center part of the ring")
+ .addEnergyHatch("1-16, Specified casings", 2)
+ .addInputHatch("2-16, Specified casings", 1)
+ .addOutputHatch("1-16, Specified casings", 3)
+ .addStructureInfo("ALL Hatches must be ZPM or better")
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public ITexture getTextureOverlay() {
+ return textureOverlay;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_FusionComputer3.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_FusionComputer3.java
new file mode 100644
index 0000000000..64d92ccc99
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_FusionComputer3.java
@@ -0,0 +1,103 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FUSION3;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FUSION3_GLOW;
+
+import net.minecraft.block.Block;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+
+public class GT_MetaTileEntity_FusionComputer3 extends GT_MetaTileEntity_FusionComputer {
+
+ private static final ITexture textureOverlay = TextureFactory.of(
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FUSION3)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FUSION3_GLOW)
+ .extFacing()
+ .glow()
+ .build());
+
+ public GT_MetaTileEntity_FusionComputer3(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_FusionComputer3(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public int tier() {
+ return 8;
+ }
+
+ @Override
+ public long maxEUStore() {
+ return 640010000L * (Math.min(16, this.mEnergyHatches.size())) / 16L;
+ }
+
+ @Override
+ public long capableStartupCanonical() {
+ return 640_000_000;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_FusionComputer3(mName);
+ }
+
+ @Override
+ public Block getCasing() {
+ return GregTech_API.sBlockCasings4;
+ }
+
+ @Override
+ public int getCasingMeta() {
+ return 8;
+ }
+
+ @Override
+ public Block getFusionCoil() {
+ return GregTech_API.sBlockCasings4;
+ }
+
+ @Override
+ public int getFusionCoilMeta() {
+ return 7;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Fusion Reactor")
+ .addInfo("A SUN DOWN ON EARTH")
+ .addInfo("Controller block for the Fusion Reactor Mk III")
+ .addInfo("32768EU/t and 40M EU capacity per Energy Hatch")
+ .addInfo("If the recipe has a startup cost greater than the")
+ .addInfo("number of energy hatches * cap, you can't do it")
+ .addSeparator()
+ .beginStructureBlock(15, 3, 15, false)
+ .addController("See diagram when placed")
+ .addCasingInfoRange("Fusion Machine Casing Mk II", 79, 123, false)
+ .addStructureInfo("Cover the coils with casing")
+ .addOtherStructurePart("Fusion Coil Block", "Center part of the ring")
+ .addEnergyHatch("1-16, Specified casings", 2)
+ .addInputHatch("2-16, Specified casings", 1)
+ .addOutputHatch("1-16, Specified casings", 3)
+ .addStructureInfo("ALL Hatches must be UV or better")
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public ITexture getTextureOverlay() {
+ return textureOverlay;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_HeatExchanger.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_HeatExchanger.java
new file mode 100644
index 0000000000..46c9c712ce
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_HeatExchanger.java
@@ -0,0 +1,400 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.onElementPass;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_HEAT_EXCHANGER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_HEAT_EXCHANGER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_HEAT_EXCHANGER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_HEAT_EXCHANGER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.casingTexturePages;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidRegistry;
+import net.minecraftforge.fluids.FluidStack;
+
+import com.gtnewhorizon.structurelib.alignment.IAlignmentLimits;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_EnhancedMultiBlockBase;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Input;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Output;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.registries.LHECoolantRegistry;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Log;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_HeatExchanger extends
+ GT_MetaTileEntity_EnhancedMultiBlockBase<GT_MetaTileEntity_HeatExchanger> implements ISurvivalConstructable {
+
+ private int dryHeatCounter = 0; // Counts up to dryHeatMaximum to check for explosion conditions
+ private static final int dryHeatMaximum = 2000; // 2000 ticks = 100 seconds
+ private static final int CASING_INDEX = 50;
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final IStructureDefinition<GT_MetaTileEntity_HeatExchanger> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_HeatExchanger>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ transpose(
+ new String[][] { { "ccc", "cCc", "ccc" }, { "ccc", "cPc", "ccc" }, { "ccc", "cPc", "ccc" },
+ { "c~c", "cHc", "ccc" }, }))
+ .addElement('P', ofBlock(GregTech_API.sBlockCasings2, 14))
+ .addElement(
+ 'C',
+ OutputHatch.withAdder(GT_MetaTileEntity_HeatExchanger::addColdFluidOutputToMachineList)
+ .withCount(t -> t.mOutputColdFluidHatch.isValid() ? 1 : 0)
+ .newAny(CASING_INDEX, 3))
+ .addElement(
+ 'H',
+ InputHatch.withAdder(GT_MetaTileEntity_HeatExchanger::addHotFluidInputToMachineList)
+ .withCount(t -> t.mInputHotFluidHatch.isValid() ? 1 : 0)
+ .newAny(CASING_INDEX, 3))
+ .addElement(
+ 'c',
+ buildHatchAdder(GT_MetaTileEntity_HeatExchanger.class)
+ .atLeast(InputBus, InputHatch, OutputBus, OutputHatch, Maintenance)
+ .casingIndex(CASING_INDEX)
+ .dot(1)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_HeatExchanger::onCasingAdded,
+ ofBlock(GregTech_API.sBlockCasings4, (byte) 2))))
+ .build();
+ public static float penalty_per_config = 0.015f; // penalize 1.5% efficiency per circuitry level (1-25)
+
+ private GT_MetaTileEntity_Hatch_Input mInputHotFluidHatch;
+ private GT_MetaTileEntity_Hatch_Output mOutputColdFluidHatch;
+ private boolean superheated = false;
+ private int superheated_threshold = 0;
+ /**
+ * How much more steam we can make without draining real water. Unit is (1L/GT_Values.STEAM_PER_WATER)
+ */
+ private int steamBudget;
+
+ private int mCasingAmount;
+
+ public GT_MetaTileEntity_HeatExchanger(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_HeatExchanger(String aName) {
+ super(aName);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Heat Exchanger")
+ .addInfo("Controller Block for the Large Heat Exchanger")
+ .addInfo("More complicated than a Fusion Reactor. Seriously")
+ .addInfo("Inputs are Hot Coolant or Lava")
+ .addInfo("Outputs Coolant or Pahoehoe Lava and SH Steam/Steam")
+ .addInfo("Read the wiki article to understand how it works")
+ .addInfo("Then go to the Discord to understand the wiki")
+ .addSeparator()
+ .beginStructureBlock(3, 4, 3, false)
+ .addController("Front bottom")
+ .addCasingInfoRange("Stable Titanium Machine Casing", 20, 32, false)
+ .addOtherStructurePart("Titanium Pipe Casing", "Center 2 blocks")
+ .addMaintenanceHatch("Any casing", 1)
+ .addInputHatch("Hot fluid, bottom center", 2)
+ .addInputHatch("Distilled water, any casing", 1)
+ .addOutputHatch("Cold fluid, top center", 3)
+ .addOutputHatch("Steam/SH Steam, any casing", 1)
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ superheated = aNBT.getBoolean("superheated");
+ steamBudget = aNBT.getInteger("steamBudget");
+ super.loadNBTData(aNBT);
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ aNBT.setBoolean("superheated", superheated);
+ aNBT.setInteger("steamBudget", steamBudget);
+ super.saveNBTData(aNBT);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side == aFacing) {
+ if (aActive) return new ITexture[] { casingTexturePages[0][CASING_INDEX], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_HEAT_EXCHANGER_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_HEAT_EXCHANGER_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { casingTexturePages[0][CASING_INDEX], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_HEAT_EXCHANGER)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_HEAT_EXCHANGER_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { casingTexturePages[0][CASING_INDEX] };
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ protected IAlignmentLimits getInitialAlignmentLimits() {
+ return (d, r, f) -> !r.isUpsideDown() && !f.isVerticallyFliped();
+ }
+
+ @Override
+ @Nonnull
+ public CheckRecipeResult checkProcessing() {
+ if (mInputHotFluidHatch.getFluid() == null) return CheckRecipeResultRegistry.NO_RECIPE;
+
+ int fluidAmountToConsume = mInputHotFluidHatch.getFluidAmount(); // how much fluid is in hatch
+
+ superheated_threshold = 4000; // default: must have 4000L per second to generate superheated steam
+ float efficiency = 1f; // default: operate at 100% efficiency with no integrated circuitry
+ int shs_reduction_per_config = 150; // reduce threshold 150L/s per circuitry level (1-25)
+ float steam_output_multiplier = 20f; // default: multiply output by 4 * 10 (boosted x5)
+ float penalty = 0.0f; // penalty to apply to output based on circuitry level (1-25).
+
+ // Do we have an integrated circuit with a valid configuration?
+ if (mInventory[1] != null && mInventory[1].getUnlocalizedName()
+ .startsWith("gt.integrated_circuit")) {
+ int circuit_config = mInventory[1].getItemDamage();
+ if (circuit_config >= 1 && circuit_config <= 25) {
+ // If so, apply the penalty and reduce the threshold.
+ penalty = (circuit_config - 1) * penalty_per_config;
+ superheated_threshold -= shs_reduction_per_config * (circuit_config - 1);
+ }
+ }
+
+ efficiency -= penalty;
+
+ var coolant = LHECoolantRegistry.getCoolant(
+ mInputHotFluidHatch.getFluid()
+ .getFluid());
+
+ if (coolant == null) {
+ superheated_threshold = 0;
+ return CheckRecipeResultRegistry.NO_RECIPE;
+ } else {
+ steam_output_multiplier *= coolant.steamMultiplier;
+ superheated_threshold *= coolant.superheatedThreshold;
+ }
+
+ // set the internal superheated flag if we have
+ // enough hot fluid. Used in the onRunningTick method.
+ superheated = fluidAmountToConsume >= superheated_threshold;
+
+ // Don't consume too much hot fluid per second
+ fluidAmountToConsume = Math.min(fluidAmountToConsume, superheated_threshold * 2);
+
+ mInputHotFluidHatch.drain(fluidAmountToConsume, true);
+ mOutputColdFluidHatch.fill(coolant.getColdFluid(fluidAmountToConsume), true);
+
+ this.mMaxProgresstime = 20;
+ this.mEUt = (int) (fluidAmountToConsume * steam_output_multiplier * efficiency);
+ this.mEfficiencyIncrease = 80;
+
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ }
+
+ private int useWater(int steam) {
+ steamBudget -= steam;
+ int usage = -Math.min(0, Math.floorDiv(steamBudget, GT_Values.STEAM_PER_WATER));
+ // still subtract, because usage will be a negative number
+ steamBudget += usage * GT_Values.STEAM_PER_WATER;
+ return usage;
+ }
+
+ @Override
+ public boolean onRunningTick(ItemStack aStack) {
+ if (this.mEUt > 0) {
+ int tGeneratedEU = (int) (this.mEUt * 2L * this.mEfficiency / 10000L); // APPROXIMATELY how much steam to
+ // generate.
+ if (tGeneratedEU > 0) {
+
+ if (superheated) tGeneratedEU /= 2; // We produce half as much superheated steam if necessary
+
+ int distilledConsumed = useWater(tGeneratedEU); // how much distilled water to consume
+ // tGeneratedEU = distilledConsumed * 160; // EXACTLY how much steam to generate, producing a perfect
+ // 1:160 ratio with distilled water consumption
+
+ FluidStack distilledStack = GT_ModHandler.getDistilledWater(distilledConsumed);
+ startRecipeProcessing();
+ if (depleteInput(distilledStack)) // Consume the distilled water
+ {
+ if (superheated) {
+ addOutput(FluidRegistry.getFluidStack("ic2superheatedsteam", tGeneratedEU)); // Generate
+ // superheated
+ // steam
+ } else {
+ addOutput(GT_ModHandler.getSteam(tGeneratedEU)); // Generate regular steam
+ }
+ dryHeatCounter = 0;
+ } else {
+ if (dryHeatCounter < dryHeatMaximum) {
+ dryHeatCounter += 1;
+ } else {
+ GT_Log.exp.println(this.mName + " was too hot and had no more Distilled Water!");
+ explodeMultiblock(); // Generate crater
+ }
+ }
+ endRecipeProcessing();
+ }
+ return true;
+ }
+ return true;
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_HeatExchanger> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ private void onCasingAdded() {
+ mCasingAmount++;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ mOutputColdFluidHatch = null;
+ mInputHotFluidHatch = null;
+ mCasingAmount = 0;
+ return checkPiece(STRUCTURE_PIECE_MAIN, 1, 3, 0) && mCasingAmount >= 20 && mMaintenanceHatches.size() == 1;
+ }
+
+ public boolean addColdFluidOutputToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Output) {
+ ((GT_MetaTileEntity_Hatch) aMetaTileEntity).updateTexture(aBaseCasingIndex);
+ mOutputColdFluidHatch = (GT_MetaTileEntity_Hatch_Output) aMetaTileEntity;
+ return true;
+ }
+ return false;
+ }
+
+ public boolean addHotFluidInputToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Input) {
+ ((GT_MetaTileEntity_Hatch) aMetaTileEntity).updateTexture(aBaseCasingIndex);
+ ((GT_MetaTileEntity_Hatch_Input) aMetaTileEntity).mRecipeMap = getRecipeMap();
+ mInputHotFluidHatch = (GT_MetaTileEntity_Hatch_Input) aMetaTileEntity;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_HeatExchanger(this.mName);
+ }
+
+ @Override
+ public String[] getInfoData() {
+ return new String[] {
+ StatCollector.translateToLocal("GT5U.multiblock.Progress") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(mProgresstime / 20)
+ + EnumChatFormatting.RESET
+ + " s / "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(mMaxProgresstime / 20)
+ + EnumChatFormatting.RESET
+ + " s",
+ StatCollector.translateToLocal("GT5U.multiblock.usage") + " "
+ + StatCollector.translateToLocal("GT5U.LHE.steam")
+ + ": "
+ + (superheated ? EnumChatFormatting.RED : EnumChatFormatting.YELLOW)
+ + GT_Utility.formatNumbers(superheated ? -2L * mEUt : -mEUt)
+ + EnumChatFormatting.RESET
+ + " EU/t",
+ StatCollector.translateToLocal("GT5U.multiblock.problems") + ": "
+ + EnumChatFormatting.RED
+ + (getIdealStatus() - getRepairStatus())
+ + EnumChatFormatting.RESET
+ + " "
+ + StatCollector.translateToLocal("GT5U.multiblock.efficiency")
+ + ": "
+ + EnumChatFormatting.YELLOW
+ + mEfficiency / 100.0F
+ + EnumChatFormatting.RESET
+ + " %",
+ StatCollector.translateToLocal("GT5U.LHE.superheated") + ": "
+ + (superheated ? EnumChatFormatting.RED : EnumChatFormatting.BLUE)
+ + superheated
+ + EnumChatFormatting.RESET,
+ StatCollector.translateToLocal("GT5U.LHE.superheated") + " "
+ + StatCollector.translateToLocal("GT5U.LHE.threshold")
+ + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(superheated_threshold)
+ + EnumChatFormatting.RESET };
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 1, 3, 0);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 1, 3, 0, elementBudget, env, false, true);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ImplosionCompressor.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ImplosionCompressor.java
new file mode 100644
index 0000000000..a1968b05d4
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ImplosionCompressor.java
@@ -0,0 +1,163 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_IMPLOSION_COMPRESSOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_IMPLOSION_COMPRESSOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_IMPLOSION_COMPRESSOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_IMPLOSION_COMPRESSOR_GLOW;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizon.structurelib.structure.IStructureElement;
+
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.Textures;
+import gregtech.api.enums.Textures.BlockIcons;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_CubicMultiBlockBase;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+
+public class GT_MetaTileEntity_ImplosionCompressor
+ extends GT_MetaTileEntity_CubicMultiBlockBase<GT_MetaTileEntity_ImplosionCompressor> {
+
+ public GT_MetaTileEntity_ImplosionCompressor(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_ImplosionCompressor(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_ImplosionCompressor(this.mName);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Implosion Compressor")
+ .addInfo("Explosions are fun")
+ .addInfo("Controller block for the Implosion Compressor")
+ .addPollutionAmount(getPollutionPerSecond(null))
+ .addSeparator()
+ .beginStructureBlock(3, 3, 3, true)
+ .addController("Front center")
+ .addCasingInfoRange("Solid Steel Machine Casing", 16, 24, false)
+ .addStructureInfo("Casings can be replaced with Explosion Warning Signs")
+ .addEnergyHatch("Any casing", 1)
+ .addMaintenanceHatch("Any casing", 1)
+ .addMufflerHatch("Any casing", 1)
+ .addInputBus("Any casing", 1)
+ .addOutputBus("Any casing", 1)
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public boolean addToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ return super.addToMachineList(aTileEntity, aBaseCasingIndex)
+ || addMufflerToMachineList(aTileEntity, aBaseCasingIndex);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side == aFacing) {
+ if (aActive) return new ITexture[] { BlockIcons.casingTexturePages[0][16], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_IMPLOSION_COMPRESSOR_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_IMPLOSION_COMPRESSOR_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { BlockIcons.casingTexturePages[0][16], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_IMPLOSION_COMPRESSOR)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_IMPLOSION_COMPRESSOR_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { Textures.BlockIcons.casingTexturePages[0][16] };
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.implosionRecipes;
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic();
+ }
+
+ @Override
+ protected int getTimeBetweenProcessSounds() {
+ return 10;
+ }
+
+ @Override
+ protected SoundResource getProcessStartSound() {
+ return SoundResource.RANDOM_EXPLODE;
+ }
+
+ @Override
+ protected IStructureElement<GT_MetaTileEntity_CubicMultiBlockBase<?>> getCasingElement() {
+ return ofChain(ofBlock(GregTech_API.sBlockCasings2, 0), ofBlock(GregTech_API.sBlockCasings3, 4));
+ }
+
+ @Override
+ protected int getHatchTextureIndex() {
+ return 16;
+ }
+
+ @Override
+ protected int getRequiredCasingCount() {
+ return 16;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getPollutionPerSecond(ItemStack aStack) {
+ return GT_Mod.gregtechproxy.mPollutionImplosionCompressorPerSecond;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_IndustrialElectromagneticSeparator.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_IndustrialElectromagneticSeparator.java
new file mode 100644
index 0000000000..a3e65ce057
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_IndustrialElectromagneticSeparator.java
@@ -0,0 +1,467 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.*;
+import static gregtech.api.enums.GT_HatchElement.*;
+import static gregtech.api.enums.GT_Values.AuthorFourIsTheNumber;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_EMS;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_EMS_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_EMS_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_EMS_GLOW;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_StructureUtility.ofFrame;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.Textures;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_MagHatch;
+import gregtech.api.multitileentity.multiblock.casing.Glasses;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.recipe.check.SimpleCheckRecipeResult;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.blocks.GT_Block_Casings10;
+import gregtech.common.items.GT_MetaGenerated_Item_01;
+import gtPlusPlus.core.util.minecraft.PlayerUtils;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public class GT_MetaTileEntity_IndustrialElectromagneticSeparator
+ extends GT_MetaTileEntity_ExtendedPowerMultiBlockBase<GT_MetaTileEntity_IndustrialElectromagneticSeparator>
+ implements ISurvivalConstructable {
+
+ public enum MagnetTiers {
+
+ Iron(8, 0.8F, 1.1F, false),
+ Steel(24, 0.75F, 1.25F, false),
+ Neodymium(48, 0.7F, 1.5F, false),
+ Samarium(96, 0.6F, 2F, false),
+ Tengam(256, 0.5F, 2.5F, true);
+
+ final int maxParallel;
+ final float euModifier, speedBoost;
+ final boolean supportsExotic;
+
+ MagnetTiers(int maxParallel, float euModifier, float speedBoost, boolean supportsExotic) {
+ this.maxParallel = maxParallel;
+ this.euModifier = euModifier;
+ this.speedBoost = 1F / speedBoost;
+ this.supportsExotic = supportsExotic;
+ }
+
+ public static String buildMagnetTooltip(MagnetTiers m) {
+ String tooltip = "Used in Magnetic Flux Exhibitor/n " + EnumChatFormatting.LIGHT_PURPLE
+ + "Speed: +"
+ + Math.round((1F / m.speedBoost * 100) - 100)
+ + "%/n "
+ + EnumChatFormatting.DARK_PURPLE
+ + "EU Usage: "
+ + Math.round(m.euModifier * 100)
+ + "%/n "
+ + EnumChatFormatting.AQUA
+ + "Parallel: "
+ + m.maxParallel;
+
+ if (m.supportsExotic) tooltip = tooltip + "/n "
+ + EnumChatFormatting.BOLD
+ + EnumChatFormatting.GREEN
+ + "Can Use Multiamp Hatches";
+
+ return tooltip;
+ }
+ }
+
+ final int MIN_CASING = 64;
+ private GT_MetaTileEntity_MagHatch mMagHatch = null;
+ private MagnetTiers magnetTier = null;
+
+ private static final int MACHINEMODE_SEPARATOR = 0;
+ private static final int MACHINEMODE_POLARIZER = 1;
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final IStructureDefinition<GT_MetaTileEntity_IndustrialElectromagneticSeparator> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_IndustrialElectromagneticSeparator>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ (new String[][] { { " CCC ", " ", " ", " ", " ", " B~B " },
+ { " CC CC ", " ", " BBB ", " AAA ", " BEB ", " BBBBB " },
+ { "CC CC", " ", " BBBBB ", " A A ", " BBBBB ", "BBBBBBB" },
+ { "C C", "C C", "CBBBBBC", "CA C AC", "CBBBBBC", "BBBBBBB" },
+ { "CC CC", " ", " BBBBB ", " A A ", " BBBBB ", "BBBBBBB" },
+ { " CC CC ", " ", " BBB ", " AAA ", " BBB ", " BBBBB " },
+ { " CCC ", " C ", " C ", " C ", " C ", " BBB " } }))
+ .addElement('A', Glasses.chainAllGlasses())
+ .addElement(
+ 'B',
+ ofChain(
+ buildHatchAdder(GT_MetaTileEntity_IndustrialElectromagneticSeparator.class)
+ .atLeast(InputBus, OutputBus, Maintenance, Energy.or(ExoticEnergy))
+ .casingIndex(((GT_Block_Casings10) GregTech_API.sBlockCasings10).getTextureIndex(0))
+ .dot(1)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_IndustrialElectromagneticSeparator::onCasingAdded,
+ ofBlock(GregTech_API.sBlockCasings10, 0)))))
+ .addElement('C', ofFrame(Materials.NeodymiumMagnetic))
+ .addElement(
+ 'E',
+ buildHatchAdder(GT_MetaTileEntity_IndustrialElectromagneticSeparator.class)
+ .adder(GT_MetaTileEntity_IndustrialElectromagneticSeparator::addMagHatch)
+ .hatchClass(GT_MetaTileEntity_MagHatch.class)
+ .casingIndex(((GT_Block_Casings10) GregTech_API.sBlockCasings10).getTextureIndex(0))
+ .dot(2)
+ .build())
+ .build();
+
+ public GT_MetaTileEntity_IndustrialElectromagneticSeparator(final int aID, final String aName,
+ final String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_IndustrialElectromagneticSeparator(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_IndustrialElectromagneticSeparator> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_IndustrialElectromagneticSeparator(this.mName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ ITexture[] rTexture;
+ if (side == aFacing) {
+ if (aActive) {
+ rTexture = new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings10, 0)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_EMS_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_EMS_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ } else {
+ rTexture = new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings10, 0)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_EMS)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_EMS_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ } else {
+ rTexture = new ITexture[] { Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings10, 0)) };
+ }
+ return rTexture;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Electromagnetic Separator/Polarizer")
+ .addInfo("Controller Block for the Magnetic Flux Exhibitor")
+ .addInfo("Use screwdriver to switch mode")
+ .addInfo("Insert an electromagnet into the electromagnet housing to use")
+ .addInfo("Better electromagnets give further bonuses")
+ .addInfo("With Tengam electromagnet, multiamp (NOT laser) hatches are allowed")
+ .addInfo(
+ AuthorFourIsTheNumber + EnumChatFormatting.GRAY
+ + " & "
+ + EnumChatFormatting.GOLD
+ + "Ba"
+ + EnumChatFormatting.LIGHT_PURPLE
+ + "ps")
+ .addSeparator()
+ .beginStructureBlock(7, 6, 7, false)
+ .addController("Front Center")
+ .addCasingInfoMin("MagTech Casings", MIN_CASING, false)
+ .addOtherStructurePart("Any glass", "x12")
+ .addOtherStructurePart("Magnetic Neodymium Frame Box", "x37")
+ .addOtherStructurePart("Electromagnet Housing", "1 Block Above/Behind Controller", 2)
+ .addInputBus("Any Casing", 1)
+ .addOutputBus("Any Casing", 1)
+ .addEnergyHatch("Any Casing", 1)
+ .addMaintenanceHatch("Any Casing", 1)
+ .toolTipFinisher("GregTech");
+ return tt;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 3, 5, 0);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 3, 5, 0, elementBudget, env, false, true);
+ }
+
+ private int mCasingAmount;
+
+ private void onCasingAdded() {
+ mCasingAmount++;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ mCasingAmount = 0;
+ mMagHatch = null;
+ mExoticEnergyHatches.clear();
+ mEnergyHatches.clear();
+
+ if (!checkPiece(STRUCTURE_PIECE_MAIN, 3, 5, 0)) return false;
+ if (mCasingAmount < MIN_CASING) return false;
+ if (mMagHatch == null) return false;
+
+ // If there are exotic hatches, ensure there is only 1, and it is not laser. Only multiamp allowed
+ if (!mExoticEnergyHatches.isEmpty()) {
+ if (!mEnergyHatches.isEmpty()) return false;
+ if (mExoticEnergyHatches.size() > 1) return false;
+ if (mExoticEnergyHatches.get(0)
+ .maxWorkingAmperesIn() > 64) return false;
+ }
+
+ // All checks passed!
+ return true;
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic() {
+
+ @NotNull
+ @Override
+ protected CheckRecipeResult validateRecipe(@Nonnull GT_Recipe recipe) {
+ findMagnet();
+ if (magnetTier != null) {
+ if (!mExoticEnergyHatches.isEmpty() && !magnetTier.supportsExotic)
+ return SimpleCheckRecipeResult.ofFailure("electromagnet_insufficient");
+ euModifier = magnetTier.euModifier;
+ speedBoost = magnetTier.speedBoost;
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ }
+ return SimpleCheckRecipeResult.ofFailure("electromagnet_missing");
+ }
+ }.setMaxParallelSupplier(this::getMaxParallels);
+ }
+
+ private int getMaxParallels() {
+ if (magnetTier != null) return magnetTier.maxParallel;
+ return 0;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return (machineMode == MACHINEMODE_POLARIZER) ? RecipeMaps.polarizerRecipes
+ : RecipeMaps.electroMagneticSeparatorRecipes;
+ }
+
+ @Nonnull
+ @Override
+ public Collection<RecipeMap<?>> getAvailableRecipeMaps() {
+ return Arrays.asList(RecipeMaps.polarizerRecipes, RecipeMaps.electroMagneticSeparatorRecipes);
+ }
+
+ @Override
+ public int getRecipeCatalystPriority() {
+ return -10;
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ if (aNBT.hasKey("polarizerMode")) {
+ machineMode = aNBT.getBoolean("polarizerMode") ? MACHINEMODE_POLARIZER : MACHINEMODE_SEPARATOR;
+ aNBT.removeTag("polarizerMode");
+ }
+ super.loadNBTData(aNBT);
+ }
+
+ @Override
+ public boolean supportsMachineModeSwitch() {
+ return true;
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ setMachineMode(nextMachineMode());
+ PlayerUtils.messagePlayer(
+ aPlayer,
+ String.format(StatCollector.translateToLocal("GT5U.MULTI_MACHINE_CHANGE"), getMachineModeName()));
+ }
+
+ @Override
+ public void setMachineModeIcons() {
+ machineModeIcons.add(GT_UITextures.OVERLAY_BUTTON_MACHINEMODE_SEPARATOR);
+ machineModeIcons.add(GT_UITextures.OVERLAY_BUTTON_MACHINEMODE_POLARIZER);
+ }
+
+ @Override
+ public String getMachineModeName() {
+ return StatCollector.translateToLocal("GT5U.INDUSTRIAL_ELECTROMAGNETIC_SEPARATOR.mode." + machineMode);
+ }
+
+ @Override
+ public void clearHatches() {
+ super.clearHatches();
+ mExoticEnergyHatches.clear();
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ tag.setInteger("mode", machineMode);
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ super.getWailaBody(itemStack, currentTip, accessor, config);
+ final NBTTagCompound tag = accessor.getNBTData();
+ currentTip.add(
+ StatCollector.translateToLocal("GT5U.machines.oreprocessor1") + " "
+ + EnumChatFormatting.WHITE
+ + StatCollector
+ .translateToLocal("GT5U.INDUSTRIAL_ELECTROMAGNETIC_SEPARATOR.mode." + tag.getInteger("mode"))
+ + EnumChatFormatting.RESET);
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsBatchMode() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsInputSeparation() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsSingleRecipeLocking() {
+ return true;
+ }
+
+ @Override
+ protected void setProcessingLogicPower(ProcessingLogic logic) {
+ // This fix works for normal energy hatches, preventing over-paralleling with 1 energy hatch
+ // However, it does not work with multiamp. MuTEs can't come soon enough.
+
+ if (mExoticEnergyHatches.isEmpty()) {
+ logic.setAvailableVoltage(GT_Utility.roundUpVoltage(this.getMaxInputVoltage()));
+ logic.setAvailableAmperage(1L);
+ } else super.setProcessingLogicPower(logic);
+ }
+
+ private void findMagnet() {
+ magnetTier = null;
+ if (mMagHatch != null) {
+ ItemStack aSlot = mMagHatch.getStackInSlot(0);
+ if (aSlot != null) {
+ switch (aSlot.getItemDamage()) {
+ case 32345 -> magnetTier = MagnetTiers.Iron;
+ case 32346 -> magnetTier = MagnetTiers.Steel;
+ case 32347 -> magnetTier = MagnetTiers.Neodymium;
+ case 32348 -> magnetTier = MagnetTiers.Samarium;
+ case 32349 -> magnetTier = MagnetTiers.Tengam;
+ default -> magnetTier = null;
+ }
+ }
+
+ }
+ }
+
+ public static boolean isValidElectromagnet(ItemStack aMagnet) {
+ return aMagnet != null && aMagnet.getItem() instanceof GT_MetaGenerated_Item_01
+ && aMagnet.getItemDamage() >= 32345
+ && aMagnet.getItemDamage() <= 32349;
+ }
+
+ private boolean addMagHatch(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity != null) {
+ final IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_MagHatch) {
+ ((GT_MetaTileEntity_MagHatch) aMetaTileEntity).updateTexture(aBaseCasingIndex);
+ if (mMagHatch == null) {
+ mMagHatch = (GT_MetaTileEntity_MagHatch) aMetaTileEntity;
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_IndustrialExtractor.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_IndustrialExtractor.java
new file mode 100644
index 0000000000..e10b497b6b
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_IndustrialExtractor.java
@@ -0,0 +1,293 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.*;
+import static gregtech.api.enums.GT_HatchElement.*;
+import static gregtech.api.enums.GT_Values.AuthorFourIsTheNumber;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_INDUSTRIAL_EXTRACTOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_INDUSTRIAL_EXTRACTOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_INDUSTRIAL_EXTRACTOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_INDUSTRIAL_EXTRACTOR_GLOW;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+
+import java.util.List;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import org.apache.commons.lang3.tuple.Pair;
+
+import com.google.common.collect.ImmutableList;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.blocks.GT_Block_Casings4;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public class GT_MetaTileEntity_IndustrialExtractor
+ extends GT_MetaTileEntity_ExtendedPowerMultiBlockBase<GT_MetaTileEntity_IndustrialExtractor>
+ implements ISurvivalConstructable {
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final IStructureDefinition<GT_MetaTileEntity_IndustrialExtractor> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_IndustrialExtractor>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ (new String[][] { { " A ", " BBB ", "AB~BA", " BBB " }, { " A ", " BBB ", "AB BA", " BBB " },
+ { " A ", " BBB ", "AB BA", " BBB " }, { " A ", " BBB ", "ABBBA", " BBB " },
+ { " A ", " A ", "AAAAA", " " } }))
+ .addElement(
+ 'B',
+ buildHatchAdder(GT_MetaTileEntity_IndustrialExtractor.class)
+ .atLeast(InputBus, OutputBus, Maintenance, Energy)
+ .casingIndex(((GT_Block_Casings4) GregTech_API.sBlockCasings4).getTextureIndex(1))
+ .dot(1)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_IndustrialExtractor::onCasingAdded,
+ ofBlock(GregTech_API.sBlockCasings4, 1))))
+ .addElement(
+ 'A',
+ ofBlocksTiered(
+ GT_MetaTileEntity_IndustrialExtractor::getItemPipeTierFromMeta,
+ ImmutableList.of(
+ Pair.of(GregTech_API.sBlockCasings11, 0),
+ Pair.of(GregTech_API.sBlockCasings11, 1),
+ Pair.of(GregTech_API.sBlockCasings11, 2),
+ Pair.of(GregTech_API.sBlockCasings11, 3),
+ Pair.of(GregTech_API.sBlockCasings11, 4),
+ Pair.of(GregTech_API.sBlockCasings11, 5),
+ Pair.of(GregTech_API.sBlockCasings11, 6),
+ Pair.of(GregTech_API.sBlockCasings11, 7)),
+ -2,
+ GT_MetaTileEntity_IndustrialExtractor::setItemPipeTier,
+ GT_MetaTileEntity_IndustrialExtractor::getItemPipeTier))
+ .build();
+
+ private int itemPipeTier = 0;
+
+ private static Integer getItemPipeTierFromMeta(Block block, Integer metaID) {
+ if (block != GregTech_API.sBlockCasings11) return -1;
+ if (metaID < 0 || metaID > 7) return -1;
+ return metaID + 1;
+ }
+
+ private void setItemPipeTier(int tier) {
+ itemPipeTier = tier;
+ }
+
+ private int getItemPipeTier() {
+ return itemPipeTier;
+ }
+
+ public GT_MetaTileEntity_IndustrialExtractor(final int aID, final String aName, final String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_IndustrialExtractor(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_IndustrialExtractor> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_IndustrialExtractor(this.mName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ ITexture[] rTexture;
+ if (side == aFacing) {
+ if (aActive) {
+ rTexture = new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings4, 1)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_INDUSTRIAL_EXTRACTOR_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_INDUSTRIAL_EXTRACTOR_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ } else {
+ rTexture = new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings4, 1)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_INDUSTRIAL_EXTRACTOR)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_INDUSTRIAL_EXTRACTOR_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ } else {
+ rTexture = new ITexture[] { Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings4, 1)) };
+ }
+ return rTexture;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Extractor")
+ .addInfo("Controller Block for the Dissection Apparatus")
+ .addInfo("200% faster than single block machines of the same voltage")
+ .addInfo("Only uses 85% of the EU/t normally required")
+ .addInfo("Gains 8 parallels per tier of Item Pipe Casing")
+ .addInfo(AuthorFourIsTheNumber)
+ .addSeparator()
+ .beginStructureBlock(5, 4, 5, true)
+ .addController("Front Center")
+ .addCasingInfoMin("Stainless Steel Machine Casing", 24, false)
+ .addCasingInfoExactly("Item Pipe Casing", 19, true)
+ .addInputBus("Any Stainless Steel Casing", 1)
+ .addOutputBus("Any Stainless Steel Casing", 1)
+ .addEnergyHatch("Any Stainless Steel Casing", 1)
+ .addMaintenanceHatch("Any Stainless Steel Casing", 1)
+ .toolTipFinisher("GregTech");
+ return tt;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 2, 2, 0);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 2, 2, 0, elementBudget, env, false, true);
+ }
+
+ private int mCasingAmount;
+
+ private void onCasingAdded() {
+ mCasingAmount++;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ mCasingAmount = 0;
+ itemPipeTier = -2;
+
+ if (!checkPiece(STRUCTURE_PIECE_MAIN, 2, 2, 0)) return false;
+ return mCasingAmount >= 24;
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic().setSpeedBonus(1F / 3F)
+ .setMaxParallelSupplier(this::getMaxParallelRecipes)
+ .setEuModifier(0.85F);
+ }
+
+ public int getMaxParallelRecipes() {
+ return 8 * itemPipeTier;
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ tag.setInteger("maxParallelRecipes", getMaxParallelRecipes());
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ super.getWailaBody(itemStack, currentTip, accessor, config);
+ final NBTTagCompound tag = accessor.getNBTData();
+ currentTip.add(
+ StatCollector.translateToLocal("GT5U.multiblock.parallelism") + ": "
+ + EnumChatFormatting.WHITE
+ + tag.getInteger("maxParallelRecipes"));
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.extractorRecipes;
+ }
+
+ @Override
+ public int getRecipeCatalystPriority() {
+ return -10;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsBatchMode() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsInputSeparation() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsSingleRecipeLocking() {
+ return true;
+ }
+
+ @Override
+ protected void setProcessingLogicPower(ProcessingLogic logic) {
+ logic.setAvailableVoltage(GT_Utility.roundUpVoltage(this.getMaxInputVoltage()));
+ logic.setAvailableAmperage(1L);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_IndustrialLaserEngraver.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_IndustrialLaserEngraver.java
new file mode 100644
index 0000000000..50c55d11a5
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_IndustrialLaserEngraver.java
@@ -0,0 +1,615 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.*;
+import static gregtech.api.enums.GT_HatchElement.*;
+import static gregtech.api.enums.GT_Values.AuthorFourIsTheNumber;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ENGRAVER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ENGRAVER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ENGRAVER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ENGRAVER_GLOW;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_StructureUtility.ofFrame;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.github.bartimaeusnek.bartworks.API.BorosilicateGlass;
+import com.github.technus.tectech.thing.metaTileEntity.hatch.GT_MetaTileEntity_Hatch_DynamoTunnel;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.GTVoltageIndex;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.recipe.check.SimpleCheckRecipeResult;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_OreDictUnificator;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.LaserRenderingUtil;
+import gregtech.common.blocks.GT_Block_Casings10;
+import gregtech.common.tileentities.render.TileLaser;
+import gtPlusPlus.core.util.minecraft.PlayerUtils;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public class GT_MetaTileEntity_IndustrialLaserEngraver
+ extends GT_MetaTileEntity_ExtendedPowerMultiBlockBase<GT_MetaTileEntity_IndustrialLaserEngraver>
+ implements ISurvivalConstructable {
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final IStructureDefinition<GT_MetaTileEntity_IndustrialLaserEngraver> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_IndustrialLaserEngraver>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ (new String[][] { { " f ", " ", " ", " ", " a~a " },
+ { " fsf ", " g ", " g ", "a g a", "aaraa" }, { "faaaf", "f f", "f f", "a a", "aaaaa" },
+ { "aaaaa", "a a a", "a a a", "a a a", "aaaaa" }, { "aaaaa", "aaaaa", "aaaaa", "aaaaa", "aaaaa" } }))
+ .addElement(
+ 'a',
+ buildHatchAdder(GT_MetaTileEntity_IndustrialLaserEngraver.class)
+ .atLeast(InputBus, OutputBus, InputHatch, OutputHatch, Maintenance, Energy)
+ .casingIndex(((GT_Block_Casings10) GregTech_API.sBlockCasings10).getTextureIndex(1))
+ .dot(1)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_IndustrialLaserEngraver::onCasingAdded,
+ ofBlock(GregTech_API.sBlockCasings10, 1))))
+ .addElement('f', ofFrame(Materials.TungstenSteel))
+ .addElement(
+ 'g',
+ BorosilicateGlass
+ .ofBoroGlass((byte) 0, (byte) 1, Byte.MAX_VALUE, (te, t) -> te.glassTier = t, te -> te.glassTier))
+ .addElement(
+ 'r',
+ LaserRenderingUtil.ofBlockAdder(
+ GT_MetaTileEntity_IndustrialLaserEngraver::laserRendererAdder,
+ GregTech_API.sLaserRender,
+ 0))
+ .addElement(
+ 's',
+ buildHatchAdder(GT_MetaTileEntity_IndustrialLaserEngraver.class)
+ .adder(GT_MetaTileEntity_IndustrialLaserEngraver::addLaserSource)
+ .hatchClass(GT_MetaTileEntity_Hatch_DynamoTunnel.class)
+ .casingIndex(((GT_Block_Casings10) GregTech_API.sBlockCasings10).getTextureIndex(1))
+ .dot(3)
+ .build())
+ .build();
+
+ protected TileLaser renderer;
+ private byte glassTier = 0;
+ private GT_MetaTileEntity_Hatch_DynamoTunnel laserSource = null;
+ private int laserAmps = 0;
+ private int laserTier = 0;
+ private String tierName = "LV";
+
+ private boolean addLaserSource(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity != null) {
+ final IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_DynamoTunnel) {
+ laserSource = (GT_MetaTileEntity_Hatch_DynamoTunnel) aMetaTileEntity;
+ laserSource.updateTexture(aBaseCasingIndex);
+ // Snap the laser source toward the plate. Player can rotate it if they want after but this will look
+ // nicer
+ switch (getRotation()) {
+ case NORMAL -> laserSource.getBaseMetaTileEntity()
+ .setFrontFacing(ForgeDirection.DOWN);
+ case UPSIDE_DOWN -> laserSource.getBaseMetaTileEntity()
+ .setFrontFacing(ForgeDirection.UP);
+ case CLOCKWISE -> laserSource.getBaseMetaTileEntity()
+ .setFrontFacing(getDirection().getRotation(ForgeDirection.UP));
+ default -> laserSource.getBaseMetaTileEntity()
+ .setFrontFacing(getDirection().getRotation(ForgeDirection.DOWN));
+ }
+ // Cube root the amperage to get the parallels
+ laserAmps = (int) Math.cbrt(laserSource.maxAmperesOut());
+ laserTier = (int) laserSource.getOutputTier();
+ tierName = GT_Values.VN[laserTier];
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean laserRendererAdder(Block block, int meta, World world, int x, int y, int z) {
+ if (block != GregTech_API.sLaserRender || world == null) {
+ return false;
+ }
+ TileEntity te = world.getTileEntity(x, y, z);
+ if (te instanceof TileLaser) {
+ renderer = (TileLaser) te;
+ renderer.setRotationFields(getDirection(), getRotation(), getFlip());
+ return true;
+ }
+ return false;
+ }
+
+ public GT_MetaTileEntity_IndustrialLaserEngraver(final int aID, final String aName, final String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_IndustrialLaserEngraver(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_IndustrialLaserEngraver> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_IndustrialLaserEngraver(this.mName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ ITexture[] rTexture;
+ if (side == aFacing) {
+ if (aActive) {
+ rTexture = new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings10, 1)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ENGRAVER_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ENGRAVER_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ } else {
+ rTexture = new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings10, 1)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ENGRAVER)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ENGRAVER_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ } else {
+ rTexture = new ITexture[] { Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings10, 1)) };
+ }
+ return rTexture;
+ }
+
+ private boolean stopAllRendering = false;
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ stopAllRendering = !stopAllRendering;
+ if (stopAllRendering) {
+ PlayerUtils.messagePlayer(aPlayer, "Rendering off");
+ if (renderer != null) renderer.setShouldRender(false);
+ } else PlayerUtils.messagePlayer(aPlayer, "Rendering on");
+ }
+
+ @Override
+ public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer,
+ float aX, float aY, float aZ, ItemStack aTool) {
+ if (aPlayer.isSneaking()) {
+ batchMode = !batchMode;
+ if (batchMode) {
+ GT_Utility.sendChatToPlayer(aPlayer, StatCollector.translateToLocal("misc.BatchModeTextOn"));
+ } else {
+ GT_Utility.sendChatToPlayer(aPlayer, StatCollector.translateToLocal("misc.BatchModeTextOff"));
+ }
+ return true;
+ } else {
+ if (renderer != null) {
+ renderer.realism = !renderer.realism;
+ PlayerUtils.messagePlayer(aPlayer, "Toggling realism!");
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void onDisableWorking() {
+ if (renderer != null) renderer.setShouldRender(false);
+ super.onDisableWorking();
+ }
+
+ @Override
+ public void onBlockDestroyed() {
+ if (renderer != null) renderer.setShouldRender(false);
+ super.onBlockDestroyed();
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Laser Engraver")
+ .addInfo("Controller Block for the Hyper-Intensity Laser Engraver")
+ .addInfo("200% faster than single block machines of the same voltage")
+ .addInfo("Uses 80% of the EU normally required")
+ .addInfo("Laser source hatch determines maximum recipe tier and parallels")
+ .addInfo("Parallels equal to the cube root of laser source amperage input")
+ .addInfo("Glass tier determines maximum laser source tier")
+ .addInfo("Only accepts borosilicate glass (no, really)")
+ .addInfo("UMV glass accepts all laser source hatches")
+ .addInfo("Use screwdriver to disable laser rendering")
+ .addInfo("Use wire cutter to toggle realism mode if you hate angled lasers")
+ .addInfo(AuthorFourIsTheNumber)
+ .addSeparator()
+ .beginStructureBlock(5, 5, 5, false)
+ .addController("Front Center")
+ .addCasingInfoMin("Laser Containment Casing", 45, false)
+ .addCasingInfoExactly("Tungstensteel Frame Box", 9, false)
+ .addOtherStructurePart("Laser Resistant Plate", "x1")
+ .addOtherStructurePart("Borosilicate Glass", "x3")
+ .addOtherStructurePart("Laser Source Hatch", "x1", 3)
+ .addInputBus("Any Casing", 1)
+ .addInputHatch("Any Casing", 1)
+ .addOutputBus("Any Casing", 1)
+ .addOutputHatch("Any Casing", 1)
+ .addEnergyHatch("Any Casing", 1)
+ .addMaintenanceHatch("Any Casing", 1)
+ .toolTipFinisher("GregTech");
+ return tt;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 2, 4, 0);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 2, 4, 0, elementBudget, env, false, true);
+ }
+
+ private int mCasingAmount;
+
+ private void onCasingAdded() {
+ mCasingAmount++;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ mCasingAmount = 0;
+ mEnergyHatches.clear();
+
+ if (!checkPiece(STRUCTURE_PIECE_MAIN, 2, 4, 0)) return false;
+ if (mCasingAmount < 45) return false;
+ if (laserSource == null) return false;
+ if (renderer == null) return false;
+ if (glassTier < GTVoltageIndex.UMV && laserSource.mTier > glassTier) return false;
+
+ return true;
+ }
+
+ private static String getUniqueIdentifier(ItemStack is) {
+ return is.getItem()
+ .getUnlocalizedName() + is.getItemDamage();
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic() {
+
+ @NotNull
+ @Override
+ protected CheckRecipeResult validateRecipe(@Nonnull GT_Recipe recipe) {
+ if (laserTier < GTVoltageIndex.UXV && recipe.mEUt > GT_Values.V[laserTier]) {
+ return SimpleCheckRecipeResult.ofFailure("laser_insufficient");
+ }
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ }
+
+ @NotNull
+ @Override
+ protected CheckRecipeResult onRecipeStart(@NotNull GT_Recipe recipe) {
+ Colors c = Colors.Red;
+ for (int i = 0; i < recipe.mInputs.length; i++) {
+ String uid = getUniqueIdentifier(recipe.mInputs[i]);
+ if (lensColors.containsKey(uid)) {
+ c = lensColors.get(uid);
+ }
+ }
+ if (renderer != null) {
+ renderer.setColors(c.r, c.g, c.b);
+ if (!stopAllRendering) {
+ renderer.setShouldRender(true);
+ }
+ }
+ return super.onRecipeStart(recipe);
+ }
+
+ @Override
+ public ProcessingLogic clear() {
+ if (renderer != null) renderer.setShouldRender(false);
+ return super.clear();
+ }
+ }.setSpeedBonus(1F / 3F)
+ .setEuModifier(0.8F)
+ .setMaxParallelSupplier(this::getMaxParallelRecipes);
+ }
+
+ @Override
+ public boolean isRotationChangeAllowed() {
+ return true;
+ }
+
+ private int getMaxParallelRecipes() {
+ return laserAmps;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.laserEngraverRecipes;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public int getRecipeCatalystPriority() {
+ return -10;
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsBatchMode() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsInputSeparation() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsSingleRecipeLocking() {
+ return true;
+ }
+
+ @Override
+ protected void setProcessingLogicPower(ProcessingLogic logic) {
+ logic.setAvailableVoltage(GT_Utility.roundUpVoltage(this.getMaxInputVoltage()));
+ logic.setAvailableAmperage(1L);
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ aNBT.setBoolean("stopAllRendering", stopAllRendering);
+ super.saveNBTData(aNBT);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ if (aNBT.hasKey("stopAllRendering")) {
+ stopAllRendering = aNBT.getBoolean("stopAllRendering");
+ }
+ super.loadNBTData(aNBT);
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ tag.setInteger("laserAmps", laserAmps);
+ tag.setString("tierName", tierName);
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ super.getWailaBody(itemStack, currentTip, accessor, config);
+ final NBTTagCompound tag = accessor.getNBTData();
+ currentTip.add(
+ StatCollector.translateToLocal("GT5U.multiblock.parallelism") + ": "
+ + EnumChatFormatting.WHITE
+ + tag.getInteger("laserAmps")
+ + EnumChatFormatting.RESET);
+ currentTip.add(
+ StatCollector.translateToLocal("GT5U.multiblock.maxtier") + ": "
+ + EnumChatFormatting.WHITE
+ + tag.getString("tierName")
+ + EnumChatFormatting.RESET);
+ }
+
+ private enum Colors {
+
+ White(1, 1, 1),
+ Red(1, 0, 0),
+ Green(0, 1, 0),
+ Blue(0, 0, 1),
+ Yellow(1, 1, 0),
+ Purple(1, 0, 1),
+ Cyan(0, 1, 1),
+ Orange(1, 0.5F, 0),
+ Black(0, 0, 0),
+ Brown(0.647F, 0.164F, 0.164F);
+
+ final float r, g, b;
+
+ Colors(float r, float g, float b) {
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ }
+ }
+
+ private static final Map<String, Colors> lensColors;
+ static {
+ lensColors = new HashMap<>();
+
+ // Black lenses
+ lensColors
+ .put(getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.Forcicium, 1)), Colors.Black);
+ lensColors.put(
+ getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.Forcillium, 1)),
+ Colors.Black);
+ lensColors.put(
+ getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.InfusedEntropy, 1)),
+ Colors.Black);
+
+ // White lenses
+ lensColors.put(
+ getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.NetherStar, 1)),
+ Colors.White);
+ lensColors
+ .put(getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.Dilithium, 1)), Colors.White);
+ lensColors
+ .put(getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.Glass, 1)), Colors.White);
+ lensColors
+ .put(getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.Diamond, 1)), Colors.Cyan);
+ lensColors.put(
+ getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.InfusedOrder, 1)),
+ Colors.White);
+
+ // Green lenses
+ lensColors
+ .put(getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.Emerald, 1)), Colors.Green);
+ lensColors
+ .put(getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.Olivine, 1)), Colors.Green);
+ lensColors.put(
+ getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.GreenSapphire, 1)),
+ Colors.Green);
+ lensColors.put(
+ getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.InfusedEarth, 1)),
+ Colors.Green);
+
+ // Red lenses
+ lensColors.put(getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.Ruby, 1)), Colors.Red);
+ lensColors
+ .put(getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.Firestone, 1)), Colors.Red);
+ lensColors
+ .put(getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.Jasper, 1)), Colors.Red);
+ lensColors
+ .put(getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.FoolsRuby, 1)), Colors.Red);
+ lensColors
+ .put(getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.GarnetRed, 1)), Colors.Red);
+ lensColors
+ .put(getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.InfusedFire, 1)), Colors.Red);
+
+ // Blue lenses
+ lensColors
+ .put(getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.BlueTopaz, 1)), Colors.Blue);
+ lensColors.put(getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.Opal, 1)), Colors.Blue);
+ lensColors.put(
+ getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.InfusedWater, 1)),
+ Colors.Blue);
+
+ // Yellow lenses
+ lensColors.put(
+ getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.GarnetYellow, 1)),
+ Colors.Yellow);
+ lensColors
+ .put(getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.Force, 1)), Colors.Yellow);
+ lensColors.put(
+ getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.InfusedAir, 1)),
+ Colors.Yellow);
+
+ // Purple lenses
+ lensColors
+ .put(getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.Amethyst, 1)), Colors.Purple);
+ lensColors.put(
+ getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.Tanzanite, 1)),
+ Colors.Purple);
+ lensColors
+ .put(getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.Sapphire, 1)), Colors.Purple);
+
+ // Cyan lenses
+ lensColors
+ .put(getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.EnderEye, 1)), Colors.Cyan);
+ lensColors
+ .put(getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.EnderPearl, 1)), Colors.Cyan);
+
+ // Orange lenses
+ lensColors
+ .put(getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.Topaz, 1)), Colors.Orange);
+ lensColors
+ .put(getUniqueIdentifier(GT_OreDictUnificator.get(OrePrefixes.lens, Materials.Amber, 1)), Colors.Orange);
+
+ // Time to manually define a bunch of lenses based on id
+ lensColors.put("gt.bwMetaGeneratedlens1", Colors.Yellow);
+ lensColors.put("gt.bwMetaGeneratedlens4", Colors.White);
+ lensColors.put("gt.bwMetaGeneratedlens5", Colors.Black);
+ lensColors.put("gt.bwMetaGeneratedlens7", Colors.Green);
+ lensColors.put("gt.bwMetaGeneratedlens8", Colors.Green);
+ lensColors.put("gt.bwMetaGeneratedlens9", Colors.White);
+ lensColors.put("gt.bwMetaGeneratedlens19", Colors.Red);
+ lensColors.put("gt.bwMetaGeneratedlens20", Colors.White);
+ lensColors.put("gt.bwMetaGeneratedlens21", Colors.Brown);
+ lensColors.put("gt.bwMetaGeneratedlens22", Colors.Orange);
+ lensColors.put("gt.bwMetaGeneratedlens23", Colors.Black);
+ lensColors.put("gt.bwMetaGeneratedlens24", Colors.White);
+ lensColors.put("gt.bwMetaGeneratedlens25", Colors.Green);
+ lensColors.put("gt.bwMetaGeneratedlens35", Colors.Yellow);
+ lensColors.put("gt.bwMetaGeneratedlens36", Colors.Purple);
+ lensColors.put("gt.bwMetaGeneratedlens43", Colors.Green);
+ lensColors.put("gt.bwMetaGeneratedlens89", Colors.Green);
+ lensColors.put("gt.bwMetaGeneratedlens91", Colors.Purple);
+ lensColors.put("gt.bwMetaGeneratedlens10023", Colors.Red);
+ lensColors.put("gt.bwMetaGeneratedlens11499", Colors.Green);
+ lensColors.put("gt.bwMetaGeneratedlens11358", Colors.Red);
+ lensColors.put("MU-metaitem.0132140", Colors.Purple);
+ lensColors.put("MU-metaitem.0132140", Colors.Purple);
+
+ //
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_IntegratedOreFactory.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_IntegratedOreFactory.java
new file mode 100644
index 0000000000..4dd9d6485e
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_IntegratedOreFactory.java
@@ -0,0 +1,829 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.Muffler;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PROCESSING_ARRAY;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PROCESSING_ARRAY_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PROCESSING_ARRAY_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PROCESSING_ARRAY_GLOW;
+import static gregtech.api.enums.TickTime.SECOND;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_StructureUtility.ofFrame;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.stream.Collectors;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.oredict.OreDictionary;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.gtnewhorizon.structurelib.alignment.IAlignmentLimits;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase;
+import gregtech.api.multitileentity.multiblock.casing.Glasses;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_OverclockCalculator;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public class GT_MetaTileEntity_IntegratedOreFactory
+ extends GT_MetaTileEntity_ExtendedPowerMultiBlockBase<GT_MetaTileEntity_IntegratedOreFactory>
+ implements ISurvivalConstructable {
+
+ private static final int CASING_INDEX1 = 183;
+ private static final int CASING_INDEX2 = 49;
+ private static final int MAX_PARA = 1024;
+ private static final long RECIPE_EUT = 30;
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final IStructureDefinition<GT_MetaTileEntity_IntegratedOreFactory> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_IntegratedOreFactory>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ transpose(
+ new String[][] {
+ { " ", " ", " WWW ", " WWW ", " ", " " },
+ { " ", " sss ", " sppps", " sppps", " sss ", " " },
+ { " ", " sss ", " s s", " s s", " sss ", " " },
+ { " ", " sss ", " sppps", " sppps", " sss ", " " },
+ { " ", " sss ", " s s", " s s", " sss ", " " },
+ { " ", " sss ", " sppps", " sppps", " sss ", " " },
+ { "iiiiii ", "iIIIIiisssi", "iIIIIis s", "iIIIIis s", "iIIIIiisssi", "iiiiii " },
+ { "iggggi ", "gt t isssi", "g xx sppps", "g xx sppps", "gt t isssi", "iggggi " },
+ { "iggggi ", "gt t isssi", "g xx s s", "g xx s s", "gt t isssi", "iggggi " },
+ { "iggggi ", "gt t is~si", "g xx spppO", "g xx spppO", "gt t isssi", "iggggi " },
+ { "iggggi ", "gt t isssi", "g xx s O", "g xx s O", "gt t isssi", "iggggi " },
+ { "EEEEEE ", "EEEEEEEEEEE", "EEEEEEEEEEE", "EEEEEEEEEEE", "EEEEEEEEEEE", "EEEEEE " } }))
+ .addElement('i', ofBlock(GregTech_API.sBlockCasings8, 7))
+ .addElement('s', ofBlock(GregTech_API.sBlockCasings4, 1))
+ .addElement('g', Glasses.chainAllGlasses())
+ .addElement('x', ofBlock(GregTech_API.sBlockCasings2, 3))
+ .addElement('p', ofBlock(GregTech_API.sBlockCasings2, 15))
+ .addElement('t', ofFrame(Materials.TungstenSteel))
+ .addElement(
+ 'E',
+ buildHatchAdder(GT_MetaTileEntity_IntegratedOreFactory.class).atLeast(Energy, Maintenance)
+ .casingIndex(CASING_INDEX1)
+ .dot(1)
+ .buildAndChain(GregTech_API.sBlockCasings8, 7))
+ .addElement(
+ 'I',
+ buildHatchAdder(GT_MetaTileEntity_IntegratedOreFactory.class).atLeast(InputBus)
+ .casingIndex(CASING_INDEX1)
+ .dot(2)
+ .buildAndChain(GregTech_API.sBlockCasings8, 7))
+ .addElement(
+ 'W',
+ buildHatchAdder(GT_MetaTileEntity_IntegratedOreFactory.class).atLeast(InputHatch, Muffler)
+ .casingIndex(CASING_INDEX2)
+ .dot(3)
+ .buildAndChain(GregTech_API.sBlockCasings4, 1))
+ .addElement(
+ 'O',
+ buildHatchAdder(GT_MetaTileEntity_IntegratedOreFactory.class).atLeast(OutputBus, OutputHatch)
+ .casingIndex(CASING_INDEX2)
+ .dot(4)
+ .buildAndChain(GregTech_API.sBlockCasings4, 1))
+ .build();
+
+ private static final HashSet<Integer> isCrushedOre = new HashSet<>();
+ private static final HashSet<Integer> isCrushedPureOre = new HashSet<>();
+ private static final HashSet<Integer> isPureDust = new HashSet<>();
+ private static final HashSet<Integer> isImpureDust = new HashSet<>();
+ private static final HashSet<Integer> isThermal = new HashSet<>();
+ private static final HashSet<Integer> isOre = new HashSet<>();
+ private static boolean isInit = false;
+ private ItemStack[] sMidProduct;
+ private int sMode = 0;
+ private boolean sVoidStone = false;
+ private int currentParallelism = 0;
+
+ private static void initHash() {
+ for (String name : OreDictionary.getOreNames()) {
+ if (name == null || name.isEmpty()) continue;
+ if (name.startsWith("crushedPurified")) {
+ for (ItemStack stack : OreDictionary.getOres(name)) {
+ isCrushedPureOre.add(GT_Utility.stackToInt(stack));
+ }
+ } else if (name.startsWith("crushedCentrifuged")) {
+ for (ItemStack stack : OreDictionary.getOres(name)) {
+ isThermal.add(GT_Utility.stackToInt(stack));
+ }
+ } else if (name.startsWith("crushed")) {
+ for (ItemStack stack : OreDictionary.getOres(name)) {
+ isCrushedOre.add(GT_Utility.stackToInt(stack));
+ }
+ } else if (name.startsWith("dustImpure")) {
+ for (ItemStack stack : OreDictionary.getOres(name)) {
+ isImpureDust.add(GT_Utility.stackToInt(stack));
+ }
+ } else if (name.startsWith("dustPure")) {
+ for (ItemStack stack : OreDictionary.getOres(name)) {
+ isPureDust.add(GT_Utility.stackToInt(stack));
+ }
+ } else if (name.startsWith("ore")) {
+ for (ItemStack stack : OreDictionary.getOres(name)) {
+ isOre.add(GT_Utility.stackToInt(stack));
+ }
+ } else if (name.startsWith("rawOre")) {
+ for (ItemStack stack : OreDictionary.getOres(name)) {
+ isOre.add(GT_Utility.stackToInt(stack));
+ }
+ }
+ }
+ }
+
+ public GT_MetaTileEntity_IntegratedOreFactory(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_IntegratedOreFactory(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_IntegratedOreFactory> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Ore Processor")
+ .addInfo("Controller Block for the Integrated Ore Factory")
+ .addInfo("It is OP. I mean ore processor.")
+ .addInfo("Do all ore processing in one step.")
+ .addInfo("Can process up to 1024 ores at a time.")
+ .addInfo("Every ore costs 30EU/t, 2L lubricant, 200L distilled water.")
+ .addInfo("Processing time is dependent on mode.")
+ .addInfo("Use a screwdriver to switch mode.")
+ .addInfo("Sneak click with screwdriver to void the stone dust.")
+ .addSeparator()
+ .beginStructureBlock(6, 12, 11, false)
+ .addController("The third layer")
+ .addStructureInfo("128 Advanced Iridium Plated Machine Casing")
+ .addStructureInfo("105 Clean Stainless Steel Machine Casing")
+ .addStructureInfo("48 Reinforced Glass")
+ .addStructureInfo("30 Tungstensteel Pipe Casing")
+ .addStructureInfo("16 Tungstensteel Frame Box")
+ .addStructureInfo("16 Steel Gear Box Casing")
+ .addEnergyHatch("Any bottom Casing", 1)
+ .addMaintenanceHatch("Any bottom Casing", 1)
+ .addInputBus("Input ore/crushed ore", 2)
+ .addInputHatch("Input lubricant/distilled water/washing chemicals", 3)
+ .addMufflerHatch("Output Pollution", 3)
+ .addOutputBus("Output products", 4)
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ protected IAlignmentLimits getInitialAlignmentLimits() {
+ return (d, r, f) -> !r.isUpsideDown() && !f.isVerticallyFliped();
+ }
+
+ @Override
+ public void construct(ItemStack itemStack, boolean b) {
+ buildPiece(STRUCTURE_PIECE_MAIN, itemStack, b, 8, 9, 1);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 8, 9, 1, elementBudget, env, false, true);
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ private static int getTime(int mode) {
+ return switch (mode) {
+ case 0 -> 30 * SECOND;
+ case 1 -> 15 * SECOND;
+ case 2 -> 10 * SECOND;
+ case 3 -> 20 * SECOND;
+ case 4 -> 17 * SECOND;
+ default ->
+ // go to hell
+ 1000000000;
+ };
+ }
+
+ @Override
+ @NotNull
+ public CheckRecipeResult checkProcessing() {
+ if (!isInit) {
+ initHash();
+ isInit = true;
+ }
+
+ List<ItemStack> tInput = getStoredInputs();
+ List<FluidStack> tInputFluid = getStoredFluids();
+ long availableEUt = GT_Utility.roundUpVoltage(getMaxInputVoltage());
+ if (availableEUt < RECIPE_EUT) {
+ return CheckRecipeResultRegistry.insufficientPower(RECIPE_EUT);
+ }
+ if (tInput.isEmpty() || tInputFluid.isEmpty()) {
+ return CheckRecipeResultRegistry.NO_RECIPE;
+ }
+
+ int maxParallel = MAX_PARA;
+ int originalMaxParallel = maxParallel;
+
+ GT_OverclockCalculator calculator = new GT_OverclockCalculator().setEUt(availableEUt)
+ .setRecipeEUt(RECIPE_EUT)
+ .setDuration(getTime(sMode))
+ .setParallel(originalMaxParallel);
+
+ double tickTimeAfterOC = calculator.calculateDurationUnderOneTick();
+
+ if (tickTimeAfterOC < 1) {
+ maxParallel = GT_Utility.safeInt((long) (maxParallel / tickTimeAfterOC), 0);
+ }
+
+ int maxParallelBeforeBatchMode = maxParallel;
+ if (isBatchModeEnabled()) {
+ maxParallel = GT_Utility.safeInt((long) maxParallel * getMaxBatchSize(), 0);
+ }
+
+ int currentParallel = (int) Math.min(maxParallel, availableEUt / RECIPE_EUT);
+ // Calculate parallel by fluids
+ int tLube = 0;
+ int tWater = 0;
+ for (FluidStack fluid : tInputFluid) {
+ if (fluid != null && fluid.equals(GT_ModHandler.getDistilledWater(1L))) {
+ tWater += fluid.amount;
+ } else if (fluid != null && fluid.equals(Materials.Lubricant.getFluid(1L))) {
+ tLube += fluid.amount;
+ }
+ }
+ currentParallel = Math.min(currentParallel, tLube / 2);
+ currentParallel = Math.min(currentParallel, tWater / 200);
+ if (currentParallel <= 0) {
+ return CheckRecipeResultRegistry.NO_RECIPE;
+ }
+
+ // Calculate parallel by items
+ int itemParallel = 0;
+ for (ItemStack ore : tInput) {
+ int tID = GT_Utility.stackToInt(ore);
+ if (tID == 0) continue;
+ if (isPureDust.contains(tID) || isImpureDust.contains(tID)
+ || isCrushedPureOre.contains(tID)
+ || isThermal.contains(tID)
+ || isCrushedOre.contains(tID)
+ || isOre.contains(tID)) {
+ if (itemParallel + ore.stackSize <= currentParallel) {
+ itemParallel += ore.stackSize;
+ } else {
+ itemParallel = currentParallel;
+ break;
+ }
+ }
+ }
+ currentParallel = itemParallel;
+ int currentParallelBeforeBatchMode = Math.min(currentParallel, maxParallelBeforeBatchMode);
+
+ calculator.setCurrentParallel(currentParallelBeforeBatchMode)
+ .calculate();
+
+ double batchMultiplierMax = 1;
+ // In case batch mode enabled
+ if (currentParallel > maxParallelBeforeBatchMode && calculator.getDuration() < getMaxBatchSize()) {
+ batchMultiplierMax = (double) getMaxBatchSize() / calculator.getDuration();
+ batchMultiplierMax = Math.min(batchMultiplierMax, (double) currentParallel / maxParallelBeforeBatchMode);
+ }
+
+ int finalParallel = (int) (batchMultiplierMax * maxParallelBeforeBatchMode);
+
+ // for scanner
+ setCurrentParallelism(finalParallel);
+
+ // Consume fluids
+ depleteInput(GT_ModHandler.getDistilledWater(finalParallel * 200L));
+ depleteInput(Materials.Lubricant.getFluid(finalParallel * 2L));
+
+ // Consume items and generate outputs
+ List<ItemStack> tOres = new ArrayList<>();
+ int remainingCost = finalParallel;
+ for (ItemStack ore : tInput) {
+ int tID = GT_Utility.stackToInt(ore);
+ if (tID == 0) continue;
+ if (isPureDust.contains(tID) || isImpureDust.contains(tID)
+ || isCrushedPureOre.contains(tID)
+ || isThermal.contains(tID)
+ || isCrushedOre.contains(tID)
+ || isOre.contains(tID)) {
+ if (remainingCost >= ore.stackSize) {
+ tOres.add(GT_Utility.copy(ore));
+ remainingCost -= ore.stackSize;
+ ore.stackSize = 0;
+ } else {
+ tOres.add(GT_Utility.copyAmountUnsafe(remainingCost, ore));
+ ore.stackSize -= remainingCost;
+ break;
+ }
+ }
+ }
+ sMidProduct = tOres.toArray(new ItemStack[0]);
+ switch (sMode) {
+ case 0 -> {
+ doMac(isOre);
+ doWash(isCrushedOre);
+ doThermal(isCrushedPureOre, isCrushedOre);
+ doMac(isThermal, isOre, isCrushedOre, isCrushedPureOre);
+ }
+ case 1 -> {
+ doMac(isOre);
+ doWash(isCrushedOre);
+ doMac(isOre, isCrushedOre, isCrushedPureOre);
+ doCentrifuge(isImpureDust, isPureDust);
+ }
+ case 2 -> {
+ doMac(isOre);
+ doMac(isThermal, isOre, isCrushedOre, isCrushedPureOre);
+ doCentrifuge(isImpureDust, isPureDust);
+ }
+ case 3 -> {
+ doMac(isOre);
+ doWash(isCrushedOre);
+ doSift(isCrushedPureOre);
+ }
+ case 4 -> {
+ doMac(isOre);
+ doChemWash(isCrushedOre, isCrushedPureOre);
+ doMac(isCrushedOre, isCrushedPureOre);
+ doCentrifuge(isImpureDust, isPureDust);
+ }
+ default -> {
+ return CheckRecipeResultRegistry.NO_RECIPE;
+ }
+ }
+
+ this.mEfficiency = 10000 - (getIdealStatus() - getRepairStatus()) * 1000;
+ this.mEfficiencyIncrease = 10000;
+ this.mOutputItems = sMidProduct;
+ this.mMaxProgresstime = (int) (calculator.getDuration() * batchMultiplierMax);
+ this.lEUt = calculator.getConsumption();
+ if (this.lEUt > 0) {
+ this.lEUt = -this.lEUt;
+ }
+ this.updateSlots();
+
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ }
+
+ @SafeVarargs
+ private boolean checkTypes(int aID, HashSet<Integer>... aTables) {
+ for (HashSet<Integer> set : aTables) {
+ if (set.contains(aID)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public final void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ if (aPlayer.isSneaking()) {
+ sVoidStone = !sVoidStone;
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ StatCollector.translateToLocalFormatted("GT5U.machines.oreprocessor.void", sVoidStone));
+ return;
+ }
+ sMode = (sMode + 1) % 5;
+ List<String> des = getDisplayMode(sMode);
+ GT_Utility.sendChatToPlayer(aPlayer, String.join("", des));
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ sMode = aNBT.getInteger("ssMode");
+ sVoidStone = aNBT.getBoolean("ssStone");
+ currentParallelism = aNBT.getInteger("currentParallelism");
+ super.loadNBTData(aNBT);
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ aNBT.setInteger("ssMode", sMode);
+ aNBT.setBoolean("ssStone", sVoidStone);
+ aNBT.setInteger("currentParallelism", currentParallelism);
+ super.saveNBTData(aNBT);
+ }
+
+ @SafeVarargs
+ private void doMac(HashSet<Integer>... aTables) {
+ List<ItemStack> tProduct = new ArrayList<>();
+ if (sMidProduct != null) {
+ for (ItemStack aStack : sMidProduct) {
+ int tID = GT_Utility.stackToInt(aStack);
+ if (checkTypes(tID, aTables)) {
+ GT_Recipe tRecipe = RecipeMaps.maceratorRecipes
+ .findRecipe(getBaseMetaTileEntity(), false, GT_Values.V[15], null, aStack);
+ if (tRecipe != null) {
+ tProduct.addAll(getOutputStack(tRecipe, aStack.stackSize));
+ } else {
+ tProduct.add(aStack);
+ }
+ } else {
+ tProduct.add(aStack);
+ }
+ }
+ }
+ doCompress(tProduct);
+ }
+
+ @SafeVarargs
+ private void doWash(HashSet<Integer>... aTables) {
+ List<ItemStack> tProduct = new ArrayList<>();
+ if (sMidProduct != null) {
+ for (ItemStack aStack : sMidProduct) {
+ int tID = GT_Utility.stackToInt(aStack);
+ if (checkTypes(tID, aTables)) {
+ GT_Recipe tRecipe = RecipeMaps.oreWasherRecipes.findRecipe(
+ getBaseMetaTileEntity(),
+ false,
+ GT_Values.V[15],
+ new FluidStack[] { GT_ModHandler.getDistilledWater(Integer.MAX_VALUE) },
+ aStack);
+ if (tRecipe != null) {
+ tProduct.addAll(getOutputStack(tRecipe, aStack.stackSize));
+ } else {
+ tProduct.add(aStack);
+ }
+ } else {
+ tProduct.add(aStack);
+ }
+ }
+ }
+ doCompress(tProduct);
+ }
+
+ @SafeVarargs
+ private void doThermal(HashSet<Integer>... aTables) {
+ List<ItemStack> tProduct = new ArrayList<>();
+ if (sMidProduct != null) {
+ for (ItemStack aStack : sMidProduct) {
+ int tID = GT_Utility.stackToInt(aStack);
+ if (checkTypes(tID, aTables)) {
+ GT_Recipe tRecipe = RecipeMaps.thermalCentrifugeRecipes
+ .findRecipe(getBaseMetaTileEntity(), false, GT_Values.V[15], null, aStack);
+ if (tRecipe != null) {
+ tProduct.addAll(getOutputStack(tRecipe, aStack.stackSize));
+ } else {
+ tProduct.add(aStack);
+ }
+ } else {
+ tProduct.add(aStack);
+ }
+ }
+ }
+ doCompress(tProduct);
+ }
+
+ @SafeVarargs
+ private void doCentrifuge(HashSet<Integer>... aTables) {
+ List<ItemStack> tProduct = new ArrayList<>();
+ if (sMidProduct != null) {
+ for (ItemStack aStack : sMidProduct) {
+ int tID = GT_Utility.stackToInt(aStack);
+ if (checkTypes(tID, aTables)) {
+ GT_Recipe tRecipe = RecipeMaps.centrifugeRecipes
+ .findRecipe(getBaseMetaTileEntity(), false, GT_Values.V[15], null, aStack);
+ if (tRecipe != null) {
+ tProduct.addAll(getOutputStack(tRecipe, aStack.stackSize));
+ } else {
+ tProduct.add(aStack);
+ }
+ } else {
+ tProduct.add(aStack);
+ }
+ }
+ }
+ doCompress(tProduct);
+ }
+
+ @SafeVarargs
+ private void doSift(HashSet<Integer>... aTables) {
+ List<ItemStack> tProduct = new ArrayList<>();
+ if (sMidProduct != null) {
+ for (ItemStack aStack : sMidProduct) {
+ int tID = GT_Utility.stackToInt(aStack);
+ if (checkTypes(tID, aTables)) {
+ GT_Recipe tRecipe = RecipeMaps.sifterRecipes
+ .findRecipe(getBaseMetaTileEntity(), false, GT_Values.V[15], null, aStack);
+ if (tRecipe != null) {
+ tProduct.addAll(getOutputStack(tRecipe, aStack.stackSize));
+ } else {
+ tProduct.add(aStack);
+ }
+ } else {
+ tProduct.add(aStack);
+ }
+ }
+ }
+ doCompress(tProduct);
+ }
+
+ @SafeVarargs
+ private void doChemWash(HashSet<Integer>... aTables) {
+ List<ItemStack> tProduct = new ArrayList<>();
+ if (sMidProduct != null) {
+ for (ItemStack aStack : sMidProduct) {
+ int tID = GT_Utility.stackToInt(aStack);
+ if (checkTypes(tID, aTables)) {
+ GT_Recipe tRecipe = RecipeMaps.chemicalBathRecipes.findRecipe(
+ getBaseMetaTileEntity(),
+ false,
+ GT_Values.V[15],
+ getStoredFluids().toArray(new FluidStack[0]),
+ aStack);
+ if (tRecipe != null && tRecipe.getRepresentativeFluidInput(0) != null) {
+ FluidStack tInputFluid = tRecipe.getRepresentativeFluidInput(0)
+ .copy();
+ int tStored = getFluidAmount(tInputFluid);
+ int tWashed = Math.min(tStored / tInputFluid.amount, aStack.stackSize);
+ depleteInput(new FluidStack(tInputFluid.getFluid(), tWashed * tInputFluid.amount));
+ tProduct.addAll(getOutputStack(tRecipe, tWashed));
+ if (tWashed < aStack.stackSize) {
+ tProduct.add(GT_Utility.copyAmountUnsafe(aStack.stackSize - tWashed, aStack));
+ }
+ } else {
+ tProduct.add(aStack);
+ }
+ } else {
+ tProduct.add(aStack);
+ }
+ }
+ }
+ doCompress(tProduct);
+ }
+
+ private int getFluidAmount(FluidStack aFluid) {
+ int tAmt = 0;
+ if (aFluid == null) return 0;
+ for (FluidStack fluid : getStoredFluids()) {
+ if (aFluid.isFluidEqual(fluid)) {
+ tAmt += fluid.amount;
+ }
+ }
+ return tAmt;
+ }
+
+ private List<ItemStack> getOutputStack(GT_Recipe aRecipe, int aTime) {
+ List<ItemStack> tOutput = new ArrayList<>();
+ for (int i = 0; i < aRecipe.mOutputs.length; i++) {
+ if (aRecipe.getOutput(i) == null) {
+ continue;
+ }
+ int tChance = aRecipe.getOutputChance(i);
+ if (tChance == 10000) {
+ tOutput.add(GT_Utility.copyAmountUnsafe(aTime * aRecipe.getOutput(i).stackSize, aRecipe.getOutput(i)));
+ } else {
+ // Use Normal Distribution
+ double u = aTime * (tChance / 10000D);
+ double e = aTime * (tChance / 10000D) * (1 - (tChance / 10000D));
+ Random random = new Random();
+ int tAmount = (int) Math.ceil(Math.sqrt(e) * random.nextGaussian() + u);
+ tOutput
+ .add(GT_Utility.copyAmountUnsafe(tAmount * aRecipe.getOutput(i).stackSize, aRecipe.getOutput(i)));
+ }
+ }
+ return tOutput.stream()
+ .filter(i -> (i != null && i.stackSize > 0))
+ .collect(Collectors.toList());
+ }
+
+ private void doCompress(List<ItemStack> aList) {
+ HashMap<Integer, Integer> rProduct = new HashMap<>();
+ for (ItemStack stack : aList) {
+ int tID = GT_Utility.stackToInt(stack);
+ if (sVoidStone) {
+ if (GT_Utility.areStacksEqual(Materials.Stone.getDust(1), stack)) {
+ continue;
+ }
+ }
+ if (tID != 0) {
+ if (rProduct.containsKey(tID)) {
+ rProduct.put(tID, rProduct.get(tID) + stack.stackSize);
+ } else {
+ rProduct.put(tID, stack.stackSize);
+ }
+ }
+ }
+ sMidProduct = new ItemStack[rProduct.size()];
+ int cnt = 0;
+ for (Integer id : rProduct.keySet()) {
+ ItemStack stack = GT_Utility.intToStack(id);
+ sMidProduct[cnt] = GT_Utility.copyAmountUnsafe(rProduct.get(id), stack);
+ cnt++;
+ }
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ return checkPiece(STRUCTURE_PIECE_MAIN, 8, 9, 1) && mMaintenanceHatches.size() <= 1
+ && !mMufflerHatches.isEmpty();
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public int getPollutionPerSecond(ItemStack aStack) {
+ return 200;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_IntegratedOreFactory(mName);
+ }
+
+ private void setCurrentParallelism(int parallelism) {
+ this.currentParallelism = parallelism;
+ }
+
+ private int getCurrentParallelism() {
+ return this.currentParallelism;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ List<String> informationData = new ArrayList<>(Arrays.asList(super.getInfoData()));
+ String parallelism = StatCollector.translateToLocal("GT5U.multiblock.parallelism") + ": "
+ + EnumChatFormatting.BLUE
+ + getCurrentParallelism()
+ + EnumChatFormatting.RESET;
+ informationData.add(parallelism);
+ informationData.add(StatCollector.translateToLocalFormatted("GT5U.machines.oreprocessor.void", sVoidStone));
+ informationData.addAll(getDisplayMode(sMode));
+ return informationData.toArray(new String[0]);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side == aFacing) {
+ if (aActive) return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX2),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_PROCESSING_ARRAY_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_PROCESSING_ARRAY_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX2), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_PROCESSING_ARRAY)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_PROCESSING_ARRAY_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX2) };
+ }
+
+ private static List<String> getDisplayMode(int mode) {
+ final EnumChatFormatting AQUA = EnumChatFormatting.AQUA;
+ final String CRUSH = StatCollector.translateToLocalFormatted("GT5U.machines.oreprocessor.Macerate");
+ final String WASH = StatCollector.translateToLocalFormatted("GT5U.machines.oreprocessor.Ore_Washer")
+ .replace(" ", " " + AQUA);
+ final String THERMAL = StatCollector.translateToLocalFormatted("GT5U.machines.oreprocessor.Thermal_Centrifuge")
+ .replace(" ", " " + AQUA);
+ final String CENTRIFUGE = StatCollector.translateToLocalFormatted("GT5U.machines.oreprocessor.Centrifuge");
+ final String SIFTER = StatCollector.translateToLocalFormatted("GT5U.machines.oreprocessor.Sifter");
+ final String CHEM_WASH = StatCollector.translateToLocalFormatted("GT5U.machines.oreprocessor.Chemical_Bathing")
+ .replace(" ", " " + AQUA);
+ final String ARROW = " " + AQUA + "-> ";
+
+ List<String> des = new ArrayList<>();
+ des.add(StatCollector.translateToLocalFormatted("GT5U.machines.oreprocessor1") + " ");
+
+ switch (mode) {
+ case 0 -> {
+ des.add(AQUA + CRUSH + ARROW);
+ des.add(AQUA + WASH + ARROW);
+ des.add(AQUA + THERMAL + ARROW);
+ des.add(AQUA + CRUSH + ' ');
+ }
+ case 1 -> {
+ des.add(AQUA + CRUSH + ARROW);
+ des.add(AQUA + WASH + ARROW);
+ des.add(AQUA + CRUSH + ARROW);
+ des.add(AQUA + CENTRIFUGE + ' ');
+ }
+ case 2 -> {
+ des.add(AQUA + CRUSH + ARROW);
+ des.add(AQUA + CRUSH + ARROW);
+ des.add(AQUA + CENTRIFUGE + ' ');
+ }
+ case 3 -> {
+ des.add(AQUA + CRUSH + ARROW);
+ des.add(AQUA + WASH + ARROW);
+ des.add(AQUA + SIFTER + ' ');
+ }
+ case 4 -> {
+ des.add(AQUA + CRUSH + ARROW);
+ des.add(AQUA + CHEM_WASH + ARROW);
+ des.add(AQUA + CRUSH + ARROW);
+ des.add(AQUA + CENTRIFUGE + ' ');
+ }
+ default -> des.add(StatCollector.translateToLocalFormatted("GT5U.machines.oreprocessor.WRONG_MODE"));
+ }
+
+ des.add(StatCollector.translateToLocalFormatted("GT5U.machines.oreprocessor2", getTime(mode) / 20));
+
+ return des;
+
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ super.getWailaBody(itemStack, currenttip, accessor, config);
+ NBTTagCompound tag = accessor.getNBTData();
+
+ currenttip.add(
+ StatCollector.translateToLocal("GT5U.multiblock.parallelism") + ": "
+ + EnumChatFormatting.BLUE
+ + tag.getInteger("currentParallelism")
+ + EnumChatFormatting.RESET);
+ currenttip.addAll(getDisplayMode(tag.getInteger("ssMode")));
+ currenttip
+ .add(StatCollector.translateToLocalFormatted("GT5U.machines.oreprocessor.void", tag.getBoolean("ssStone")));
+
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ tag.setInteger("ssMode", sMode);
+ tag.setBoolean("ssStone", sVoidStone);
+ tag.setInteger("currentParallelism", currentParallelism);
+ }
+
+ @Override
+ public boolean supportsBatchMode() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeBoiler.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeBoiler.java
new file mode 100644
index 0000000000..ae740447c8
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeBoiler.java
@@ -0,0 +1,504 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.lazy;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.onElementPass;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.Muffler;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.GT_Values.STEAM_PER_WATER;
+import static gregtech.api.enums.ItemList.Circuit_Integrated;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_BOILER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_BOILER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_BOILER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_BOILER_GLOW;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_Utility.formatNumbers;
+
+import java.util.ArrayList;
+
+import net.minecraft.block.Block;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidRegistry;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.GT_Mod;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.enums.Textures;
+import gregtech.api.enums.Textures.BlockIcons;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_EnhancedMultiBlockBase;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.recipe.maps.LargeBoilerFuelBackend;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Log;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_OreDictUnificator;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+
+public abstract class GT_MetaTileEntity_LargeBoiler
+ extends GT_MetaTileEntity_EnhancedMultiBlockBase<GT_MetaTileEntity_LargeBoiler> implements ISurvivalConstructable {
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final ClassValue<IStructureDefinition<GT_MetaTileEntity_LargeBoiler>> STRUCTURE_DEFINITION = new ClassValue<>() {
+
+ @Override
+ protected IStructureDefinition<GT_MetaTileEntity_LargeBoiler> computeValue(Class<?> type) {
+ return StructureDefinition.<GT_MetaTileEntity_LargeBoiler>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ transpose(
+ new String[][] { { "ccc", "ccc", "ccc" }, { "ccc", "cPc", "ccc" }, { "ccc", "cPc", "ccc" },
+ { "ccc", "cPc", "ccc" }, { "f~f", "fff", "fff" }, }))
+ .addElement('P', lazy(t -> ofBlock(t.getPipeBlock(), t.getPipeMeta())))
+ .addElement(
+ 'c',
+ lazy(
+ t -> buildHatchAdder(GT_MetaTileEntity_LargeBoiler.class).atLeast(OutputHatch)
+ .casingIndex(t.getCasingTextureIndex())
+ .dot(2)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_LargeBoiler::onCasingAdded,
+ ofBlock(t.getCasingBlock(), t.getCasingMeta())))))
+ .addElement(
+ 'f',
+ lazy(
+ t -> buildHatchAdder(GT_MetaTileEntity_LargeBoiler.class)
+ .atLeast(Maintenance, InputHatch, InputBus, Muffler)
+ .casingIndex(t.getFireboxTextureIndex())
+ .dot(1)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_LargeBoiler::onFireboxAdded,
+ ofBlock(t.getFireboxBlock(), t.getFireboxMeta())))))
+ .build();
+ }
+ };
+ private boolean firstRun = true;
+ private int mSuperEfficencyIncrease = 0;
+ private int integratedCircuitConfig = 0; // Steam output is reduced by 1000L per config
+ private int excessWater = 0; // Eliminate rounding errors for water
+ private int excessFuel = 0; // Eliminate rounding errors for fuels that burn half items
+ private int excessProjectedEU = 0; // Eliminate rounding errors from throttling the boiler
+ private int mCasingAmount;
+ private int mFireboxAmount;
+ protected int pollutionPerSecond = 1; // placeholder for the child classes
+
+ public GT_MetaTileEntity_LargeBoiler(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_LargeBoiler(String aName) {
+ super(aName);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+
+ tt.addMachineType("Boiler")
+ .addInfo("Controller block for the Large " + getCasingMaterial() + " Boiler");
+ // Tooltip differs between the boilers that output Superheated Steam (Titanium and Tungstensteel) and the ones
+ // that do not (Bronze and Steel)
+ if (isSuperheated()) {
+ tt.addInfo(
+ "Produces " + formatNumbers((getEUt() * 40) * ((runtimeBoost(20) / (20f)) / superToNormalSteam))
+ + "L of Superheated Steam with 1 Coal at "
+ + formatNumbers((getEUt() * 40L) / superToNormalSteam)
+ + "L/s") // ?
+ .addInfo("A programmed circuit in the main block throttles the boiler (-1000L/s per config)")
+ .addInfo("Only some solid fuels are allowed (check the NEI Large Boiler tab for details)")
+ .addInfo("If there are any disallowed fuels in the input bus, the boiler won't run!");
+ } else {
+ tt.addInfo(
+ "Produces " + formatNumbers((getEUt() * 40) * (runtimeBoost(20) / 20f))
+ + "L of Steam with 1 Coal at "
+ + formatNumbers(getEUt() * 40L)
+ + "L/s") // ?
+ .addInfo("A programmed circuit in the main block throttles the boiler (-1000L/s per config)")
+ .addInfo("Solid Fuels with a burn value that is too high or too low will not work");
+ }
+ tt.addInfo(
+ String.format(
+ "Diesel fuels have 1/4 efficiency - Takes %s seconds to heat up",
+ formatNumbers(500.0 / getEfficiencyIncrease()))) // ? check semifluid again
+ .addPollutionAmount(getPollutionPerSecond(null))
+ .addSeparator()
+ .beginStructureBlock(3, 5, 3, false)
+ .addController("Front bottom")
+ .addCasingInfoRange(getCasingMaterial() + " " + getCasingBlockType() + " Casing", 24, 31, false) // ?
+ .addOtherStructurePart(getCasingMaterial() + " Fire Boxes", "Bottom layer, 3 minimum")
+ .addOtherStructurePart(getCasingMaterial() + " Pipe Casing Blocks", "Inner 3 blocks")
+ .addMaintenanceHatch("Any firebox", 1)
+ .addMufflerHatch("Any firebox", 1)
+ .addInputBus("Solid fuel, Any firebox", 1)
+ .addInputHatch("Liquid fuel, Any firebox", 1)
+ .addStructureInfo("You can use either, or both")
+ .addInputHatch("Water, Any firebox", 1)
+ .addOutputHatch("Steam, any casing", 2)
+ .toolTipFinisher("Gregtech");
+
+ return tt;
+ }
+
+ public abstract String getCasingMaterial();
+
+ public abstract Block getCasingBlock();
+
+ public abstract String getCasingBlockType();
+
+ public abstract byte getCasingMeta();
+
+ public abstract byte getCasingTextureIndex();
+
+ public abstract Block getPipeBlock();
+
+ public abstract byte getPipeMeta();
+
+ public abstract Block getFireboxBlock();
+
+ public abstract byte getFireboxMeta();
+
+ public abstract byte getFireboxTextureIndex();
+
+ public abstract int getEUt();
+
+ public abstract int getEfficiencyIncrease();
+
+ public int getIntegratedCircuitConfig() {
+ return integratedCircuitConfig;
+ }
+
+ @Override
+ public int getPollutionPerSecond(ItemStack aStack) {
+ // allows for 0 pollution if circuit throttle is too high
+ return Math.max(
+ 0,
+ (int) (pollutionPerSecond
+ * (1 - GT_Mod.gregtechproxy.mPollutionReleasedByThrottle * getIntegratedCircuitConfig())));
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side == aFacing) {
+ if (aActive) return new ITexture[] { BlockIcons.getCasingTextureForId(getCasingTextureIndex()),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_BOILER_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_BOILER_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { BlockIcons.getCasingTextureForId(getCasingTextureIndex()), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_BOILER)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_BOILER_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(getCasingTextureIndex()) };
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ boolean isFuelValid() {
+ if (!isSuperheated()) return true;
+ for (ItemStack input : getStoredInputs()) {
+ if (!LargeBoilerFuelBackend.isAllowedSolidFuel(input)
+ && !Circuit_Integrated.isStackEqual(input, true, true)) {
+ // if any item is not in ALLOWED_SOLID_FUELS, operation cannot be allowed because it might still be
+ // consumed
+ this.mMaxProgresstime = 0;
+ this.mEUt = 0;
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ // Only for visual
+ return RecipeMaps.largeBoilerFakeFuels;
+ }
+
+ @Override
+ protected boolean filtersFluid() {
+ return false;
+ }
+
+ @Override
+ @NotNull
+ public CheckRecipeResult checkProcessing() {
+ if (!isFuelValid()) return CheckRecipeResultRegistry.NO_FUEL_FOUND;
+ // Do we have an integrated circuit with a valid configuration?
+ if (Circuit_Integrated.isStackEqual(mInventory[1], true, true)) {
+ int circuit_config = mInventory[1].getItemDamage();
+ if (circuit_config >= 1 && circuit_config <= 25) {
+ // If so, overwrite the current config
+ this.integratedCircuitConfig = circuit_config;
+ }
+ } else {
+ // If not, set the config to zero
+ this.integratedCircuitConfig = 0;
+ }
+
+ this.mSuperEfficencyIncrease = 0;
+ if (!isSuperheated()) {
+ for (GT_Recipe tRecipe : RecipeMaps.dieselFuels.getAllRecipes()) {
+ FluidStack tFluid = GT_Utility.getFluidForFilledItem(tRecipe.getRepresentativeInput(0), true);
+ if (tFluid != null && tRecipe.mSpecialValue > 1) {
+ tFluid.amount = 1000;
+ if (depleteInput(tFluid)) {
+ this.mMaxProgresstime = adjustBurnTimeForConfig(runtimeBoost(tRecipe.mSpecialValue / 2));
+ this.mEUt = adjustEUtForConfig(getEUt());
+ this.mEfficiencyIncrease = this.mMaxProgresstime * getEfficiencyIncrease() * 4;
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ }
+ }
+ }
+ for (GT_Recipe tRecipe : RecipeMaps.denseLiquidFuels.getAllRecipes()) {
+ FluidStack tFluid = GT_Utility.getFluidForFilledItem(tRecipe.getRepresentativeInput(0), true);
+ if (tFluid != null) {
+ tFluid.amount = 1000;
+ if (depleteInput(tFluid)) {
+ this.mMaxProgresstime = adjustBurnTimeForConfig(
+ Math.max(1, runtimeBoost(tRecipe.mSpecialValue * 2)));
+ this.mEUt = adjustEUtForConfig(getEUt());
+ this.mEfficiencyIncrease = this.mMaxProgresstime * getEfficiencyIncrease();
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ }
+ }
+ }
+ }
+
+ ArrayList<ItemStack> tInputList = getStoredInputs();
+ if (!tInputList.isEmpty()) {
+ if (isSuperheated()) {
+ for (ItemStack tInput : tInputList) {
+ if (tInput != GT_OreDictUnificator.get(OrePrefixes.bucket, Materials.Lava, 1)) {
+ if (GT_Utility.getFluidForFilledItem(tInput, true) == null
+ && (this.mMaxProgresstime = GT_ModHandler.getFuelValue(tInput) / 80) > 0) {
+ this.excessFuel += GT_ModHandler.getFuelValue(tInput) % 80;
+ this.mMaxProgresstime += this.excessFuel / 80;
+ this.excessFuel %= 80;
+ this.mMaxProgresstime = adjustBurnTimeForConfig(runtimeBoost(this.mMaxProgresstime));
+ this.mEUt = adjustEUtForConfig(getEUt());
+ this.mEfficiencyIncrease = this.mMaxProgresstime * getEfficiencyIncrease();
+ this.mOutputItems = new ItemStack[] { GT_Utility.getContainerItem(tInput, true) };
+ tInput.stackSize -= 1;
+ updateSlots();
+ if (this.mEfficiencyIncrease > 5000) {
+ this.mEfficiencyIncrease = 0;
+ this.mSuperEfficencyIncrease = 20;
+ }
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ }
+ }
+ }
+ } else {
+ for (ItemStack tInput : tInputList) {
+ if (tInput != GT_OreDictUnificator.get(OrePrefixes.bucket, Materials.Lava, 1)) {
+ // Solid fuels with burn values below getEUt are ignored (mostly items like sticks), and also
+ // those with very high fuel values that would cause an overflow error.
+ if (GT_Utility.getFluidForFilledItem(tInput, true) == null
+ && (this.mMaxProgresstime = GT_ModHandler.getFuelValue(tInput) / 80) > 0
+ && (GT_ModHandler.getFuelValue(tInput) * 2 / this.getEUt()) > 1
+ && GT_ModHandler.getFuelValue(tInput) < 100000000) {
+ this.excessFuel += GT_ModHandler.getFuelValue(tInput) % 80;
+ this.mMaxProgresstime += this.excessFuel / 80;
+ this.excessFuel %= 80;
+ this.mMaxProgresstime = adjustBurnTimeForConfig(runtimeBoost(this.mMaxProgresstime));
+ this.mEUt = adjustEUtForConfig(getEUt());
+ this.mEfficiencyIncrease = this.mMaxProgresstime * getEfficiencyIncrease();
+ this.mOutputItems = new ItemStack[] { GT_Utility.getContainerItem(tInput, true) };
+ tInput.stackSize -= 1;
+ updateSlots();
+ if (this.mEfficiencyIncrease > 5000) {
+ this.mEfficiencyIncrease = 0;
+ this.mSuperEfficencyIncrease = 20;
+ }
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ }
+ }
+ }
+ }
+ }
+ this.mMaxProgresstime = 0;
+ this.mEUt = 0;
+ return CheckRecipeResultRegistry.NO_FUEL_FOUND;
+ }
+
+ abstract int runtimeBoost(int mTime);
+
+ abstract boolean isSuperheated();
+
+ private final int superToNormalSteam = 3;
+
+ @Override
+ public boolean onRunningTick(ItemStack aStack) {
+ if (this.mEUt > 0) {
+ if (this.mSuperEfficencyIncrease > 0) mEfficiency = Math.max(
+ 0,
+ Math.min(
+ mEfficiency + mSuperEfficencyIncrease,
+ getMaxEfficiency(mInventory[1]) - ((getIdealStatus() - getRepairStatus()) * 1000)));
+ int tGeneratedEU = (int) (this.mEUt * 2L * this.mEfficiency / 10000L);
+ if (tGeneratedEU > 0) {
+ long amount = (tGeneratedEU + STEAM_PER_WATER) / STEAM_PER_WATER;
+ excessWater += amount * STEAM_PER_WATER - tGeneratedEU;
+ amount -= excessWater / STEAM_PER_WATER;
+ excessWater %= STEAM_PER_WATER;
+ startRecipeProcessing();
+ if (isSuperheated()) {
+ // Consumes only one third of the water if producing Superheated Steam, to maintain water in the
+ // chain.
+ if (depleteInput(Materials.Water.getFluid(amount / superToNormalSteam))
+ || depleteInput(GT_ModHandler.getDistilledWater(amount / superToNormalSteam))) {
+ // Outputs Superheated Steam instead of Steam, at one third of the amount (equivalent in power
+ // output to the normal Steam amount).
+ addOutput(
+ FluidRegistry.getFluidStack("ic2superheatedsteam", tGeneratedEU / superToNormalSteam));
+ } else {
+ GT_Log.exp.println("Boiler " + this.mName + " had no Water!");
+ explodeMultiblock();
+ }
+ } else {
+ if (depleteInput(Materials.Water.getFluid(amount))
+ || depleteInput(GT_ModHandler.getDistilledWater(amount))) {
+ addOutput(GT_ModHandler.getSteam(tGeneratedEU));
+ } else {
+ GT_Log.exp.println("Boiler " + this.mName + " had no Water!");
+ explodeMultiblock();
+ }
+ }
+ endRecipeProcessing();
+ }
+ return true;
+ }
+ return true;
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setInteger("excessFuel", excessFuel);
+ aNBT.setInteger("excessWater", excessWater);
+ aNBT.setInteger("excessProjectedEU", excessProjectedEU);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ excessFuel = aNBT.getInteger("excessFuel");
+ excessWater = aNBT.getInteger("excessWater");
+ excessProjectedEU = aNBT.getInteger("excessProjectedEU");
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (mProgresstime > 0 && firstRun) {
+ firstRun = false;
+ GT_Mod.achievements.issueAchievement(
+ aBaseMetaTileEntity.getWorld()
+ .getPlayerEntityByName(aBaseMetaTileEntity.getOwnerName()),
+ "extremepressure");
+ }
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_LargeBoiler> getStructureDefinition() {
+ return STRUCTURE_DEFINITION.get(getClass());
+ }
+
+ private void onCasingAdded() {
+ mCasingAmount++;
+ }
+
+ private void onFireboxAdded() {
+ mFireboxAmount++;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ mCasingAmount = 0;
+ mFireboxAmount = 0;
+ return checkPiece(STRUCTURE_PIECE_MAIN, 1, 4, 0) && mCasingAmount >= 24
+ && mFireboxAmount >= 3
+ && mMaintenanceHatches.size() == 1
+ && !mMufflerHatches.isEmpty();
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ private int adjustEUtForConfig(int rawEUt) {
+ int adjustedSteamOutput = rawEUt - (isSuperheated() ? 75 : 25) * integratedCircuitConfig;
+ return Math.max(adjustedSteamOutput, 25);
+ }
+
+ private int adjustBurnTimeForConfig(int rawBurnTime) {
+ if (mEfficiency < getMaxEfficiency(mInventory[1]) - ((getIdealStatus() - getRepairStatus()) * 1000)) {
+ return rawBurnTime;
+ }
+ int adjustedEUt = Math.max(25, getEUt() - (isSuperheated() ? 75 : 25) * integratedCircuitConfig);
+ int adjustedBurnTime = rawBurnTime * getEUt() / adjustedEUt;
+ this.excessProjectedEU += getEUt() * rawBurnTime - adjustedEUt * adjustedBurnTime;
+ adjustedBurnTime += this.excessProjectedEU / adjustedEUt;
+ this.excessProjectedEU %= adjustedEUt;
+ return adjustedBurnTime;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 1, 4, 0);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 1, 4, 0, elementBudget, env, false, true);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeBoiler_Bronze.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeBoiler_Bronze.java
new file mode 100644
index 0000000000..2effcf7966
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeBoiler_Bronze.java
@@ -0,0 +1,96 @@
+package gregtech.common.tileentities.machines.multi;
+
+import net.minecraft.block.Block;
+
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+
+public class GT_MetaTileEntity_LargeBoiler_Bronze extends GT_MetaTileEntity_LargeBoiler {
+
+ public GT_MetaTileEntity_LargeBoiler_Bronze(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ pollutionPerSecond = GT_Mod.gregtechproxy.mPollutionLargeBronzeBoilerPerSecond;
+ }
+
+ public GT_MetaTileEntity_LargeBoiler_Bronze(String aName) {
+ super(aName);
+ pollutionPerSecond = GT_Mod.gregtechproxy.mPollutionLargeBronzeBoilerPerSecond;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_LargeBoiler_Bronze(this.mName);
+ }
+
+ @Override
+ public String getCasingMaterial() {
+ return "Bronze";
+ }
+
+ @Override
+ public String getCasingBlockType() {
+ return "Plated Bricks";
+ }
+
+ @Override
+ public Block getCasingBlock() {
+ return GregTech_API.sBlockCasings1;
+ }
+
+ @Override
+ public byte getCasingMeta() {
+ return 10;
+ }
+
+ @Override
+ public byte getCasingTextureIndex() {
+ return 10;
+ }
+
+ @Override
+ public Block getPipeBlock() {
+ return GregTech_API.sBlockCasings2;
+ }
+
+ @Override
+ public byte getPipeMeta() {
+ return 12;
+ }
+
+ @Override
+ public Block getFireboxBlock() {
+ return GregTech_API.sBlockCasings3;
+ }
+
+ @Override
+ public byte getFireboxMeta() {
+ return 13;
+ }
+
+ @Override
+ public byte getFireboxTextureIndex() {
+ return 45;
+ }
+
+ @Override
+ public int getEUt() {
+ return 400;
+ }
+
+ @Override
+ public int getEfficiencyIncrease() {
+ return 16;
+ }
+
+ @Override
+ int runtimeBoost(int mTime) {
+ return mTime * 2;
+ }
+
+ @Override
+ boolean isSuperheated() {
+ return false;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeBoiler_Steel.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeBoiler_Steel.java
new file mode 100644
index 0000000000..c7bd766945
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeBoiler_Steel.java
@@ -0,0 +1,96 @@
+package gregtech.common.tileentities.machines.multi;
+
+import net.minecraft.block.Block;
+
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+
+public class GT_MetaTileEntity_LargeBoiler_Steel extends GT_MetaTileEntity_LargeBoiler {
+
+ public GT_MetaTileEntity_LargeBoiler_Steel(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ pollutionPerSecond = GT_Mod.gregtechproxy.mPollutionLargeSteelBoilerPerSecond;
+ }
+
+ public GT_MetaTileEntity_LargeBoiler_Steel(String aName) {
+ super(aName);
+ pollutionPerSecond = GT_Mod.gregtechproxy.mPollutionLargeSteelBoilerPerSecond;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_LargeBoiler_Steel(this.mName);
+ }
+
+ @Override
+ public String getCasingMaterial() {
+ return "Steel";
+ }
+
+ @Override
+ public String getCasingBlockType() {
+ return "Machine Casings";
+ }
+
+ @Override
+ public Block getCasingBlock() {
+ return GregTech_API.sBlockCasings2;
+ }
+
+ @Override
+ public byte getCasingMeta() {
+ return 0;
+ }
+
+ @Override
+ public byte getCasingTextureIndex() {
+ return 16;
+ }
+
+ @Override
+ public Block getPipeBlock() {
+ return GregTech_API.sBlockCasings2;
+ }
+
+ @Override
+ public byte getPipeMeta() {
+ return 13;
+ }
+
+ @Override
+ public Block getFireboxBlock() {
+ return GregTech_API.sBlockCasings3;
+ }
+
+ @Override
+ public byte getFireboxMeta() {
+ return 14;
+ }
+
+ @Override
+ public byte getFireboxTextureIndex() {
+ return 46;
+ }
+
+ @Override
+ public int getEUt() {
+ return 1000;
+ }
+
+ @Override
+ public int getEfficiencyIncrease() {
+ return 12;
+ }
+
+ @Override
+ int runtimeBoost(int mTime) {
+ return mTime;
+ }
+
+ @Override
+ boolean isSuperheated() {
+ return false;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeBoiler_Titanium.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeBoiler_Titanium.java
new file mode 100644
index 0000000000..61f2756622
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeBoiler_Titanium.java
@@ -0,0 +1,96 @@
+package gregtech.common.tileentities.machines.multi;
+
+import net.minecraft.block.Block;
+
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+
+public class GT_MetaTileEntity_LargeBoiler_Titanium extends GT_MetaTileEntity_LargeBoiler {
+
+ public GT_MetaTileEntity_LargeBoiler_Titanium(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ pollutionPerSecond = GT_Mod.gregtechproxy.mPollutionLargeTitaniumBoilerPerSecond;
+ }
+
+ public GT_MetaTileEntity_LargeBoiler_Titanium(String aName) {
+ super(aName);
+ pollutionPerSecond = GT_Mod.gregtechproxy.mPollutionLargeTitaniumBoilerPerSecond;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_LargeBoiler_Titanium(this.mName);
+ }
+
+ @Override
+ public String getCasingMaterial() {
+ return "Titanium";
+ }
+
+ @Override
+ public String getCasingBlockType() {
+ return "Machine Casings";
+ }
+
+ @Override
+ public Block getCasingBlock() {
+ return GregTech_API.sBlockCasings4;
+ }
+
+ @Override
+ public byte getCasingMeta() {
+ return 2;
+ }
+
+ @Override
+ public byte getCasingTextureIndex() {
+ return 50;
+ }
+
+ @Override
+ public Block getPipeBlock() {
+ return GregTech_API.sBlockCasings2;
+ }
+
+ @Override
+ public byte getPipeMeta() {
+ return 14;
+ }
+
+ @Override
+ public Block getFireboxBlock() {
+ return GregTech_API.sBlockCasings4;
+ }
+
+ @Override
+ public byte getFireboxMeta() {
+ return 3;
+ }
+
+ @Override
+ public byte getFireboxTextureIndex() {
+ return 51;
+ }
+
+ @Override
+ public int getEUt() {
+ return 4000;
+ }
+
+ @Override
+ public int getEfficiencyIncrease() {
+ return 8;
+ }
+
+ @Override
+ int runtimeBoost(int mTime) {
+ return mTime * 130 / 400;
+ }
+
+ @Override
+ boolean isSuperheated() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeBoiler_TungstenSteel.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeBoiler_TungstenSteel.java
new file mode 100644
index 0000000000..f00ea287b2
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeBoiler_TungstenSteel.java
@@ -0,0 +1,96 @@
+package gregtech.common.tileentities.machines.multi;
+
+import net.minecraft.block.Block;
+
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+
+public class GT_MetaTileEntity_LargeBoiler_TungstenSteel extends GT_MetaTileEntity_LargeBoiler {
+
+ public GT_MetaTileEntity_LargeBoiler_TungstenSteel(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ pollutionPerSecond = GT_Mod.gregtechproxy.mPollutionLargeTungstenSteelBoilerPerSecond;
+ }
+
+ public GT_MetaTileEntity_LargeBoiler_TungstenSteel(String aName) {
+ super(aName);
+ pollutionPerSecond = GT_Mod.gregtechproxy.mPollutionLargeTungstenSteelBoilerPerSecond;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_LargeBoiler_TungstenSteel(this.mName);
+ }
+
+ @Override
+ public String getCasingMaterial() {
+ return "TungstenSteel";
+ }
+
+ @Override
+ public String getCasingBlockType() {
+ return "Machine Casings";
+ }
+
+ @Override
+ public Block getCasingBlock() {
+ return GregTech_API.sBlockCasings4;
+ }
+
+ @Override
+ public byte getCasingMeta() {
+ return 0;
+ }
+
+ @Override
+ public byte getCasingTextureIndex() {
+ return 48;
+ }
+
+ @Override
+ public Block getPipeBlock() {
+ return GregTech_API.sBlockCasings2;
+ }
+
+ @Override
+ public byte getPipeMeta() {
+ return 15;
+ }
+
+ @Override
+ public Block getFireboxBlock() {
+ return GregTech_API.sBlockCasings3;
+ }
+
+ @Override
+ public byte getFireboxMeta() {
+ return 15;
+ }
+
+ @Override
+ public byte getFireboxTextureIndex() {
+ return 47;
+ }
+
+ @Override
+ public int getEUt() {
+ return 16000;
+ }
+
+ @Override
+ public int getEfficiencyIncrease() {
+ return 4;
+ }
+
+ @Override
+ int runtimeBoost(int mTime) {
+ return mTime * 120 / 750;
+ }
+
+ @Override
+ boolean isSuperheated() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeChemicalReactor.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeChemicalReactor.java
new file mode 100644
index 0000000000..8e4b5ec650
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeChemicalReactor.java
@@ -0,0 +1,298 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.onElementPass;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.casingTexturePages;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.IChatComponent;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizon.structurelib.StructureLibAPI;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.AutoPlaceEnvironment;
+import com.gtnewhorizon.structurelib.structure.IItemSource;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.IStructureElement;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+import com.gtnewhorizon.structurelib.structure.StructureUtility;
+import com.gtnewhorizon.structurelib.util.ItemStackPredicate;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.HeatingCoilLevel;
+import gregtech.api.interfaces.IHeatingCoil;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_EnhancedMultiBlockBase;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+
+public class GT_MetaTileEntity_LargeChemicalReactor extends
+ GT_MetaTileEntity_EnhancedMultiBlockBase<GT_MetaTileEntity_LargeChemicalReactor> implements ISurvivalConstructable {
+
+ private static final int CASING_INDEX = 176;
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final IStructureDefinition<GT_MetaTileEntity_LargeChemicalReactor> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_LargeChemicalReactor>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ transpose(new String[][] { { "ccc", "cxc", "ccc" }, { "c~c", "xPx", "cxc" }, { "ccc", "cxc", "ccc" }, }))
+ .addElement('P', ofBlock(GregTech_API.sBlockCasings8, 1))
+ .addElement(
+ 'c',
+ buildHatchAdder(GT_MetaTileEntity_LargeChemicalReactor.class)
+ .atLeast(InputHatch, OutputHatch, InputBus, OutputBus, Maintenance, Energy)
+ .casingIndex(CASING_INDEX)
+ .dot(1)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_LargeChemicalReactor::onCasingAdded,
+ ofBlock(GregTech_API.sBlockCasings8, 0))))
+ .addElement(
+ 'x',
+ buildHatchAdder(GT_MetaTileEntity_LargeChemicalReactor.class)
+ .atLeast(InputHatch, OutputHatch, InputBus, OutputBus, Maintenance, Energy)
+ .casingIndex(CASING_INDEX)
+ .dot(1)
+ .buildAndChain(
+ CoilStructureElement.INSTANCE,
+ onElementPass(
+ GT_MetaTileEntity_LargeChemicalReactor::onCasingAdded,
+ ofBlock(GregTech_API.sBlockCasings8, 0))))
+ .build();
+
+ private int mCasingAmount;
+ private int mCoilAmount;
+
+ public GT_MetaTileEntity_LargeChemicalReactor(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_LargeChemicalReactor(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_LargeChemicalReactor(this.mName);
+ }
+
+ @Override
+ public GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Chemical Reactor")
+ .addInfo("Controller block for the Large Chemical Reactor")
+ .addInfo("Does not lose efficiency when overclocked")
+ .addInfo("Accepts fluids instead of fluid cells")
+ .addSeparator()
+ .beginStructureBlock(3, 3, 3, false)
+ .addController("Front center")
+ .addCasingInfoRange("Chemically Inert Machine Casing", 8, 22, false)
+ .addOtherStructurePart("PTFE Pipe Machine Casing", "Center")
+ .addOtherStructurePart("Heating Coil", "Adjacent to the PTFE Pipe Machine Casing", 1)
+ .addEnergyHatch("Any casing", 1, 2)
+ .addMaintenanceHatch("Any casing", 1, 2)
+ .addInputBus("Any casing", 1, 2)
+ .addInputHatch("Any casing", 1, 2)
+ .addOutputBus("Any casing", 1, 2)
+ .addOutputHatch("Any casing", 1, 2)
+ .addStructureInfo("You can have multiple hatches/busses")
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side == aFacing) {
+ if (aActive) return new ITexture[] { casingTexturePages[1][48], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { casingTexturePages[1][48], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { casingTexturePages[1][48] };
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public boolean supportsSingleRecipeLocking() {
+ return true;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.multiblockChemicalReactorRecipes;
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic().enablePerfectOverclock();
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_LargeChemicalReactor> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ private void onCasingAdded() {
+ mCasingAmount++;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ mCasingAmount = 0;
+ mCoilAmount = 0;
+ return checkPiece(STRUCTURE_PIECE_MAIN, 1, 1, 0) && mCasingAmount >= 8
+ && mCoilAmount == 1
+ && !mEnergyHatches.isEmpty()
+ && mMaintenanceHatches.size() == 1;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ mCoilAmount = 0;
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 1, 1, 0);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ mCoilAmount = 0;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 1, 1, 0, elementBudget, env, false, true);
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ private enum CoilStructureElement implements IStructureElement<GT_MetaTileEntity_LargeChemicalReactor> {
+
+ INSTANCE;
+
+ @Override
+ public boolean check(GT_MetaTileEntity_LargeChemicalReactor t, World world, int x, int y, int z) {
+ Block block = world.getBlock(x, y, z);
+ if (block instanceof IHeatingCoil
+ && ((IHeatingCoil) block).getCoilHeat(world.getBlockMetadata(x, y, z)) != HeatingCoilLevel.None) {
+ return t.mCoilAmount++ == 0;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean spawnHint(GT_MetaTileEntity_LargeChemicalReactor t, World world, int x, int y, int z,
+ ItemStack trigger) {
+ StructureLibAPI.hintParticle(world, x, y, z, GregTech_API.sBlockCasings5, 0);
+ return true;
+ }
+
+ @Override
+ public boolean placeBlock(GT_MetaTileEntity_LargeChemicalReactor t, World world, int x, int y, int z,
+ ItemStack trigger) {
+ if (t.mCoilAmount > 0) return false;
+ boolean b = world.setBlock(x, y, z, GregTech_API.sBlockCasings5, 0, 3);
+ if (b) t.mCoilAmount++;
+ return b;
+ }
+
+ @Override
+ public PlaceResult survivalPlaceBlock(GT_MetaTileEntity_LargeChemicalReactor t, World world, int x, int y,
+ int z, ItemStack trigger, IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
+ return survivalPlaceBlock(t, world, x, y, z, trigger, AutoPlaceEnvironment.fromLegacy(s, actor, chatter));
+ }
+
+ @Override
+ public BlocksToPlace getBlocksToPlace(
+ GT_MetaTileEntity_LargeChemicalReactor gt_metaTileEntity_largeChemicalReactor, World world, int x, int y,
+ int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ return BlocksToPlace.create(
+ IntStream.range(0, 8)
+ .mapToObj(i -> new ItemStack(GregTech_API.sBlockCasings5, 1, i))
+ .collect(Collectors.toList()));
+ }
+
+ @Override
+ public PlaceResult survivalPlaceBlock(GT_MetaTileEntity_LargeChemicalReactor t, World world, int x, int y,
+ int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ if (t.mCoilAmount > 0) return PlaceResult.SKIP;
+ if (check(t, world, x, y, z)) return PlaceResult.SKIP;
+ if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, env.getActor())) return PlaceResult.REJECT;
+ ItemStack result = env.getSource()
+ .takeOne(ItemStackPredicate.from(GregTech_API.sBlockCasings5), true);
+ if (result == null) return PlaceResult.REJECT;
+ PlaceResult ret = StructureUtility.survivalPlaceBlock(
+ result,
+ ItemStackPredicate.NBTMode.EXACT,
+ null,
+ true,
+ world,
+ x,
+ y,
+ z,
+ env.getSource(),
+ env.getActor(),
+ env.getChatter());
+ if (ret == PlaceResult.ACCEPT) t.mCoilAmount++;
+ return ret;
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeFluidExtractor.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeFluidExtractor.java
new file mode 100644
index 0000000000..c1a5bc3458
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeFluidExtractor.java
@@ -0,0 +1,419 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.github.bartimaeusnek.bartworks.util.BW_Util.ofGlassTiered;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.onElementPass;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.withChannel;
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.GT_Values.VN;
+import static gregtech.api.enums.Textures.BlockIcons.getCasingTextureForId;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_StructureUtility.ofCoil;
+import static gregtech.api.util.GT_StructureUtility.ofFrame;
+import static gregtech.api.util.GT_StructureUtility.ofSolenoidCoil;
+import static net.minecraft.util.EnumChatFormatting.RESET;
+import static net.minecraft.util.EnumChatFormatting.YELLOW;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import javax.annotation.Nullable;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.common.widget.DynamicPositionedColumn;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.HeatingCoilLevel;
+import gregtech.api.enums.Materials;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gtPlusPlus.xmod.gregtech.common.blocks.textures.TexturesGtBlock;
+
+public class GT_MetaTileEntity_LargeFluidExtractor
+ extends GT_MetaTileEntity_ExtendedPowerMultiBlockBase<GT_MetaTileEntity_LargeFluidExtractor>
+ implements ISurvivalConstructable {
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final int CASING_INDEX = 48; // Robust Tungstensteel Machine Casing
+ private static final int BASE_CASING_COUNT = 24 + 24 + 9;
+ private static final int MAX_HATCHES_ALLOWED = 16;
+
+ private static final double BASE_SPEED_BONUS = 1.5;
+ private static final double BASE_EU_MULTIPLIER = 0.8;
+
+ private static final double SPEED_PER_COIL = 0.1;
+ private static final int PARALLELS_PER_SOLENOID = 8;
+ private static final double HEATING_COIL_EU_MULTIPLIER = 0.9;
+
+ // spotless:off
+ private static final IStructureDefinition<GT_MetaTileEntity_LargeFluidExtractor> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_LargeFluidExtractor>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ transpose(
+ new String[][] {
+ {" ", " ccc ", " ccc ", " ccc ", " "},
+ {" ", " f f ", " s ", " f f ", " "},
+ {" ", " f f ", " s ", " f f ", " "},
+ {" ", " f f ", " s ", " f f ", " "},
+ {"ccccc", "ccccc", "ccscc", "ccccc", "ccccc"},
+ {"fgggf", "ghhhg", "ghshg", "ghhhg", "fgggf"},
+ {"fgggf", "ghhhg", "ghshg", "ghhhg", "fgggf"},
+ {"fgggf", "ghhhg", "ghshg", "ghhhg", "fgggf"},
+ {"cc~cc", "ccccc", "ccccc", "ccccc", "ccccc"},
+ }))
+ .addElement('c',
+ buildHatchAdder(GT_MetaTileEntity_LargeFluidExtractor.class)
+ .atLeast(InputBus, OutputBus, OutputHatch, Energy, Maintenance)
+ .casingIndex(CASING_INDEX) // Robust Tungstensteel Machine Casing
+ .dot(1)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_LargeFluidExtractor::onCasingAdded,
+ ofBlock(GregTech_API.sBlockCasings4, 0))) // Robust Tungstensteel Machine Casing
+ )
+ .addElement(
+ 'g',
+ withChannel(
+ "glass",
+ ofGlassTiered(
+ (byte) 1, (byte) 127, (byte) 0,
+ GT_MetaTileEntity_LargeFluidExtractor::setGlassTier,
+ GT_MetaTileEntity_LargeFluidExtractor::getGlassTier,
+ 2))
+ )
+ .addElement(
+ 'h',
+ withChannel(
+ "coil",
+ ofCoil(
+ GT_MetaTileEntity_LargeFluidExtractor::setCoilLevel,
+ GT_MetaTileEntity_LargeFluidExtractor::getCoilLevel))
+ )
+ .addElement(
+ 's',
+ withChannel(
+ "solenoid",
+ ofSolenoidCoil(
+ GT_MetaTileEntity_LargeFluidExtractor::setSolenoidLevel,
+ GT_MetaTileEntity_LargeFluidExtractor::getSolenoidLevel))
+ )
+ .addElement(
+ 'f',
+ ofFrame(Materials.BlackSteel)
+ )
+ .build();
+ // spotless:on
+
+ private byte mGlassTier = 0;
+ @Nullable
+ private HeatingCoilLevel mCoilLevel = null;
+ @Nullable
+ private Byte mSolenoidLevel = null;
+ private int mCasingAmount;
+ private boolean mStructureBadGlassTier = false, mStructureBadCasingCount = false;
+
+ public GT_MetaTileEntity_LargeFluidExtractor(final int aID, final String aName, final String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_LargeFluidExtractor(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_LargeFluidExtractor> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public void clearHatches() {
+ super.clearHatches();
+
+ mCasingAmount = 0;
+ mStructureBadGlassTier = false;
+ mStructureBadCasingCount = false;
+ mGlassTier = 0;
+ mCoilLevel = null;
+ mSolenoidLevel = null;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ if (!checkPiece(STRUCTURE_PIECE_MAIN, 2, 8, 0)) {
+ return false;
+ }
+
+ if (mCasingAmount < (BASE_CASING_COUNT - MAX_HATCHES_ALLOWED)) {
+ mStructureBadCasingCount = true;
+ }
+
+ for (var energyHatch : mEnergyHatches) {
+ if (energyHatch.getBaseMetaTileEntity() == null) {
+ continue;
+ }
+
+ if (mGlassTier < 10 && energyHatch.getTierForStructure() > mGlassTier) {
+ mStructureBadGlassTier = true;
+ }
+ }
+
+ return !mStructureBadGlassTier && !mStructureBadCasingCount;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 2, 8, 0);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 2, 8, 0, elementBudget, env, false, true);
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic();
+ }
+
+ @Override
+ protected void setProcessingLogicPower(ProcessingLogic logic) {
+ super.setProcessingLogicPower(logic);
+ logic.setAvailableAmperage(mEnergyHatches.size());
+ logic.setEuModifier((float) (getEUMultiplier()));
+ logic.setMaxParallel(getParallels());
+ logic.setSpeedBonus(1.0f / (float) (getSpeedBonus()));
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_LargeFluidExtractor(this.mName);
+ }
+
+ private void onCasingAdded() {
+ mCasingAmount++;
+ }
+
+ private byte getGlassTier() {
+ return mGlassTier;
+ }
+
+ private void setGlassTier(byte tier) {
+ mGlassTier = tier;
+ }
+
+ private HeatingCoilLevel getCoilLevel() {
+ return mCoilLevel;
+ }
+
+ private void setCoilLevel(HeatingCoilLevel level) {
+ mCoilLevel = level;
+ }
+
+ private Byte getSolenoidLevel() {
+ return mSolenoidLevel;
+ }
+
+ private void setSolenoidLevel(byte level) {
+ mSolenoidLevel = level;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, ForgeDirection facing,
+ int colorIndex, boolean active, boolean redstoneLevel) {
+ if (side == facing) {
+ if (active) {
+ return new ITexture[] { getCasingTextureForId(CASING_INDEX), TextureFactory.builder()
+ .addIcon(TexturesGtBlock.Overlay_Machine_Controller_Advanced_Active)
+ .extFacing()
+ .build() };
+ } else {
+ return new ITexture[] { getCasingTextureForId(CASING_INDEX), TextureFactory.builder()
+ .addIcon(TexturesGtBlock.Overlay_Machine_Controller_Advanced)
+ .extFacing()
+ .build() };
+ }
+ }
+ return new ITexture[] { getCasingTextureForId(CASING_INDEX) };
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+
+ // spotless:off
+ tt.addMachineType("Fluid Extractor")
+ .addInfo("Controller block for the Large Fluid Extractor")
+ .addInfo(String.format(
+ "%d%% faster than single block machines of the same voltage",
+ (int) Math.round((BASE_SPEED_BONUS - 1) * 100)
+ ))
+ .addInfo(String.format(
+ "Only uses %d%% of the EU/t normally required",
+ (int) Math.round(BASE_EU_MULTIPLIER * 100)
+ ))
+ .addInfo(String.format(
+ "Every coil tier gives a +%d%% speed bonus and a %d%% EU/t discount (multiplicative)",
+ (int) Math.round(SPEED_PER_COIL * 100),
+ (int) Math.round((1 - HEATING_COIL_EU_MULTIPLIER) * 100)
+ ))
+ .addInfo(String.format(
+ "Every solenoid tier gives +%d parallels",
+ (int) PARALLELS_PER_SOLENOID
+ ))
+ .addInfo(String.format(
+ "The EU multiplier is %s%.2f * (%.2f ^ Heating Coil Tier)%s, prior to overclocks",
+ EnumChatFormatting.ITALIC,
+ BASE_EU_MULTIPLIER,
+ HEATING_COIL_EU_MULTIPLIER,
+ EnumChatFormatting.GRAY
+ ))
+ .addInfo("The energy hatch tier is limited by the glass tier. UEV glass unlocks all tiers.")
+ .addSeparator()
+ .beginStructureBlock(5, 9, 5, false)
+ .addController("Front Center (Bottom Layer)")
+ .addCasingInfoMin("Robust Tungstensteel Machine Casing", BASE_CASING_COUNT - MAX_HATCHES_ALLOWED, false)
+ .addCasingInfoExactly("Borosilicate Glass (any)", 9 * 4, true)
+ .addCasingInfoExactly("Solenoid Superconducting Coil (any)", 7, true)
+ .addCasingInfoExactly("Heating Coils (any)", 8 * 3, true)
+ .addCasingInfoExactly("Black Steel Frame Box", 3 * 8, false)
+ .addInputBus("Any Robust Tungstensteel Machine Casing", 1)
+ .addOutputBus("Any Robust Tungstensteel Machine Casing", 1)
+ .addOutputHatch("Any Robust Tungstensteel Machine Casing", 1)
+ .addEnergyHatch("Any Robust Tungstensteel Machine Casing", 1)
+ .addMaintenanceHatch("Any Robust Tungstensteel Machine Casing", 1)
+ .toolTipFinisher("GregTech");
+ // spotless:on
+
+ return tt;
+ }
+
+ @Override
+ protected void drawTexts(DynamicPositionedColumn screenElements, SlotWidget inventorySlot) {
+ super.drawTexts(screenElements, inventorySlot);
+
+ screenElements.widgets(TextWidget.dynamicString(() -> {
+ if (mStructureBadCasingCount) {
+ return String.format(
+ "%sNot enough casings: need %d, but\nhave %d.%s",
+ EnumChatFormatting.DARK_RED,
+ BASE_CASING_COUNT - MAX_HATCHES_ALLOWED,
+ mCasingAmount,
+ RESET);
+ }
+
+ if (mStructureBadGlassTier) {
+ int hatchTier = 0;
+
+ for (var hatch : mEnergyHatches) {
+ if (hatch.mTier > hatchTier) hatchTier = hatch.mTier;
+ }
+
+ return String.format(
+ "%sEnergy hatch tier (%s) is too high\nfor the glass tier (%s).%s",
+ EnumChatFormatting.DARK_RED,
+ VN[hatchTier],
+ VN[mGlassTier],
+ RESET);
+ }
+
+ return "";
+ })
+ .setTextAlignment(Alignment.CenterLeft));
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10_000;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.fluidExtractionRecipes;
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsBatchMode() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsSingleRecipeLocking() {
+ return true;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ var data = new ArrayList<String>();
+
+ data.addAll(Arrays.asList(super.getInfoData()));
+
+ data.add(String.format("Max Parallels: %s%d%s", YELLOW, getParallels(), RESET));
+ data.add(String.format("Heating Coil Speed Bonus: +%s%.0f%s %%", YELLOW, getCoilSpeedBonus() * 100, RESET));
+ data.add(String.format("Total Speed Multiplier: %s%.0f%s %%", YELLOW, getSpeedBonus() * 100, RESET));
+ data.add(String.format("Total EU/t Multiplier: %s%.0f%s %%", YELLOW, getEUMultiplier() * 100, RESET));
+
+ return data.toArray(new String[data.size()]);
+ }
+
+ public int getParallels() {
+ return Math.max(1, mSolenoidLevel == null ? 0 : (PARALLELS_PER_SOLENOID * mSolenoidLevel));
+ }
+
+ public float getCoilSpeedBonus() {
+ return (float) ((mCoilLevel == null ? 0 : SPEED_PER_COIL * mCoilLevel.getTier()));
+ }
+
+ public float getSpeedBonus() {
+ return (float) (BASE_SPEED_BONUS + getCoilSpeedBonus());
+ }
+
+ public float getEUMultiplier() {
+ double heatingBonus = (mCoilLevel == null ? 0 : Math.pow(HEATING_COIL_EU_MULTIPLIER, mCoilLevel.getTier()));
+
+ return (float) (BASE_EU_MULTIPLIER * heatingBonus);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine.java
new file mode 100644
index 0000000000..54ad5939f8
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine.java
@@ -0,0 +1,492 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.lazy;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.enums.GT_HatchElement.Dynamo;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.Muffler;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.Textures.BlockIcons.TURBINE_NEW;
+import static gregtech.api.enums.Textures.BlockIcons.TURBINE_NEW_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.TURBINE_NEW_EMPTY;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_Utility.filterValidMTEs;
+
+import java.util.ArrayList;
+
+import net.minecraft.block.Block;
+import net.minecraft.client.renderer.RenderBlocks;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.IBlockAccess;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.gtnewhorizon.structurelib.alignment.IAlignmentLimits;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
+import com.gtnewhorizon.structurelib.alignment.enumerable.Flip;
+import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.enums.Dyes;
+import gregtech.api.interfaces.IIconContainer;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.items.GT_MetaGenerated_Tool;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_EnhancedMultiBlockBase;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Dynamo;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Muffler;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.LightingHelper;
+import gregtech.api.util.shutdown.ShutDownReasonRegistry;
+import gregtech.common.items.GT_MetaGenerated_Tool_01;
+import gregtech.common.render.GT_RenderUtil;
+
+public abstract class GT_MetaTileEntity_LargeTurbine
+ extends GT_MetaTileEntity_EnhancedMultiBlockBase<GT_MetaTileEntity_LargeTurbine> implements ISurvivalConstructable {
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final ClassValue<IStructureDefinition<GT_MetaTileEntity_LargeTurbine>> STRUCTURE_DEFINITION = new ClassValue<>() {
+
+ @Override
+ protected IStructureDefinition<GT_MetaTileEntity_LargeTurbine> computeValue(Class<?> type) {
+ return StructureDefinition.<GT_MetaTileEntity_LargeTurbine>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ transpose(
+ new String[][] { { " ", " ", " ", " ", " ", },
+ { " --- ", " ccc ", " hhh ", " hhh ", " hhh ", },
+ { " --- ", " c~c ", " h-h ", " h-h ", " hdh ", },
+ { " --- ", " ccc ", " hhh ", " hhh ", " hhh ", },
+ { " ", " ", " ", " ", " ", }, }))
+ .addElement('c', lazy(t -> ofBlock(t.getCasingBlock(), t.getCasingMeta())))
+ .addElement('d', lazy(t -> Dynamo.newAny(t.getCasingTextureIndex(), 1)))
+ .addElement(
+ 'h',
+ lazy(
+ t -> buildHatchAdder(GT_MetaTileEntity_LargeTurbine.class)
+ .atLeast(Maintenance, InputHatch, OutputHatch, OutputBus, InputBus, Muffler)
+ .casingIndex(t.getCasingTextureIndex())
+ .dot(2)
+ .buildAndChain(t.getCasingBlock(), t.getCasingMeta())))
+ .build();
+ }
+ };
+
+ protected int baseEff = 0;
+ protected int optFlow = 0;
+ protected double realOptFlow = 0;
+ protected int storedFluid = 0;
+ protected int counter = 0;
+ protected boolean looseFit = false;
+ protected int overflowMultiplier = 0;
+ protected final float[] flowMultipliers = new float[] { 1, 1, 1 };
+
+ // client side stuff
+ protected boolean mHasTurbine;
+ // mMachine got overwritten by StructureLib extended facing query response
+ // so we use a separate field for this
+ protected boolean mFormed;
+
+ public GT_MetaTileEntity_LargeTurbine(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_LargeTurbine(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return getMaxEfficiency(aStack) > 0;
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_LargeTurbine> getStructureDefinition() {
+ return STRUCTURE_DEFINITION.get(getClass());
+ }
+
+ @Override
+ protected IAlignmentLimits getInitialAlignmentLimits() {
+ return (d, r, f) -> r.isNotRotated() && f.isNotFlipped();
+ }
+
+ @Override
+ protected ExtendedFacing getCorrectedAlignment(ExtendedFacing aOldFacing) {
+ return aOldFacing.with(Flip.NONE)
+ .with(Rotation.NORMAL);
+ }
+
+ @Override
+ public boolean isFlipChangeAllowed() {
+ return false;
+ }
+
+ @Override
+ public boolean isRotationChangeAllowed() {
+ return false;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ return checkPiece(STRUCTURE_PIECE_MAIN, 2, 2, 1) && mMaintenanceHatches.size() == 1
+ && mMufflerHatches.isEmpty() == (getPollutionPerTick(null) == 0);
+ }
+
+ public abstract Block getCasingBlock();
+
+ public abstract byte getCasingMeta();
+
+ public abstract int getCasingTextureIndex();
+
+ public boolean isNewStyleRendering() {
+ return false;
+ }
+
+ public IIconContainer[] getTurbineTextureActive() {
+ return TURBINE_NEW_ACTIVE;
+ }
+
+ public IIconContainer[] getTurbineTextureFull() {
+ return TURBINE_NEW;
+ }
+
+ public IIconContainer[] getTurbineTextureEmpty() {
+ return TURBINE_NEW_EMPTY;
+ }
+
+ @Override
+ public boolean renderInWorld(IBlockAccess aWorld, int aX, int aY, int aZ, Block aBlock, RenderBlocks aRenderer) {
+ if (!isNewStyleRendering() || !mFormed) return false;
+ int[] tABCCoord = new int[] { -1, -1, 0 };
+ int[] tXYZOffset = new int[3];
+ final ForgeDirection tFacing = getBaseMetaTileEntity().getFrontFacing();
+ final ExtendedFacing tExtendedFacing = getExtendedFacing();
+ final ForgeDirection tDirection = tExtendedFacing.getDirection();
+ final LightingHelper tLighting = new LightingHelper(aRenderer);
+
+ // for some reason +x and -z need this field set to true, but not any other sides
+ if (tFacing == ForgeDirection.NORTH || tFacing == ForgeDirection.EAST) aRenderer.field_152631_f = true;
+ final Block tBlock = getCasingBlock();
+
+ IIconContainer[] tTextures;
+ if (getBaseMetaTileEntity().isActive()) tTextures = getTurbineTextureActive();
+ else if (hasTurbine()) tTextures = getTurbineTextureFull();
+ else tTextures = getTurbineTextureEmpty();
+
+ assert tTextures != null && tTextures.length == tABCCoord.length;
+
+ for (int i = 0; i < 9; i++) {
+ if (i != 4) { // do not draw ourselves again.
+ tExtendedFacing.getWorldOffset(tABCCoord, tXYZOffset);
+ // since structure check passed, we can assume it is turbine casing
+ int tX = tXYZOffset[0] + aX;
+ int tY = tXYZOffset[1] + aY;
+ int tZ = tXYZOffset[2] + aZ;
+ // we skip the occlusion test, as we always require a working turbine to have a block of air before it
+ // so the front face cannot be occluded whatsoever in the most cases.
+ Tessellator.instance.setBrightness(
+ tBlock.getMixedBrightnessForBlock(
+ aWorld,
+ aX + tDirection.offsetX,
+ tY + tDirection.offsetY,
+ aZ + tDirection.offsetZ));
+ tLighting.setupLighting(tBlock, tX, tY, tZ, tFacing)
+ .setupColor(tFacing, Dyes._NULL.mRGBa);
+ GT_RenderUtil.renderBlockIcon(
+ aRenderer,
+ tBlock,
+ tX + tDirection.offsetX * 0.01,
+ tY + tDirection.offsetY * 0.01,
+ tZ + tDirection.offsetZ * 0.01,
+ tTextures[i].getIcon(),
+ tFacing);
+ }
+ if (++tABCCoord[0] == 2) {
+ tABCCoord[0] = -1;
+ tABCCoord[1]++;
+ }
+ }
+
+ aRenderer.field_152631_f = false;
+ return false;
+ }
+
+ @Override
+ public void onValueUpdate(byte aValue) {
+ mHasTurbine = (aValue & 0x1) != 0;
+ mFormed = (aValue & 0x2) != 0;
+ super.onValueUpdate(aValue);
+ }
+
+ @Override
+ public byte getUpdateData() {
+ return (byte) ((hasTurbine() ? 1 : 0) | (mMachine ? 2 : 0));
+ }
+
+ @Override
+ public boolean addToMachineList(IGregTechTileEntity tTileEntity, int aBaseCasingIndex) {
+ return addMaintenanceToMachineList(tTileEntity, getCasingTextureIndex())
+ || addInputToMachineList(tTileEntity, getCasingTextureIndex())
+ || addOutputToMachineList(tTileEntity, getCasingTextureIndex())
+ || addMufflerToMachineList(tTileEntity, getCasingTextureIndex());
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ }
+
+ @Override
+ @NotNull
+ public CheckRecipeResult checkProcessing() {
+ ItemStack controllerSlot = getControllerSlot();
+ if ((counter & 7) == 0
+ && (controllerSlot == null || !(controllerSlot.getItem() instanceof GT_MetaGenerated_Tool)
+ || controllerSlot.getItemDamage() < 170
+ || controllerSlot.getItemDamage() > 179)) {
+ stopMachine(ShutDownReasonRegistry.NO_TURBINE);
+ return CheckRecipeResultRegistry.NO_TURBINE_FOUND;
+ }
+ ArrayList<FluidStack> tFluids = getStoredFluids();
+ if (!tFluids.isEmpty()) {
+
+ if (baseEff == 0 || optFlow == 0
+ || counter >= 512
+ || this.getBaseMetaTileEntity()
+ .hasWorkJustBeenEnabled()
+ || this.getBaseMetaTileEntity()
+ .hasInventoryBeenModified()) {
+ counter = 0;
+ baseEff = GT_Utility.safeInt(
+ (long) ((5F
+ + ((GT_MetaGenerated_Tool) controllerSlot.getItem()).getToolCombatDamage(controllerSlot))
+ * 1000F));
+ optFlow = GT_Utility.safeInt(
+ (long) Math.max(
+ Float.MIN_NORMAL,
+ ((GT_MetaGenerated_Tool) controllerSlot.getItem()).getToolStats(controllerSlot)
+ .getSpeedMultiplier() * GT_MetaGenerated_Tool.getPrimaryMaterial(controllerSlot).mToolSpeed
+ * 50));
+
+ overflowMultiplier = getOverflowMultiplier(controllerSlot);
+
+ flowMultipliers[0] = GT_MetaGenerated_Tool.getPrimaryMaterial(controllerSlot).mSteamMultiplier;
+ flowMultipliers[1] = GT_MetaGenerated_Tool.getPrimaryMaterial(controllerSlot).mGasMultiplier;
+ flowMultipliers[2] = GT_MetaGenerated_Tool.getPrimaryMaterial(controllerSlot).mPlasmaMultiplier;
+
+ if (optFlow <= 0 || baseEff <= 0) {
+ stopMachine(ShutDownReasonRegistry.NONE); // in case the turbine got removed
+ return CheckRecipeResultRegistry.NO_FUEL_FOUND;
+ }
+ } else {
+ counter++;
+ }
+ }
+
+ int newPower = fluidIntoPower(tFluids, optFlow, baseEff, overflowMultiplier, flowMultipliers); // How much the
+ // turbine should
+ // be producing
+ // with this flow
+ int difference = newPower - this.mEUt; // difference between current output and new output
+
+ // Magic numbers: can always change by at least 10 eu/t, but otherwise by at most 1 percent of the difference in
+ // power level (per tick)
+ // This is how much the turbine can actually change during this tick
+ int maxChangeAllowed = Math.max(10, GT_Utility.safeInt((long) Math.abs(difference) / 100));
+
+ if (Math.abs(difference) > maxChangeAllowed) { // If this difference is too big, use the maximum allowed change
+ int change = maxChangeAllowed * (difference > 0 ? 1 : -1); // Make the change positive or negative.
+ this.mEUt += change; // Apply the change
+ } else this.mEUt = newPower;
+
+ if (this.mEUt <= 0) {
+ // stopMachine();
+ this.mEUt = 0;
+ this.mEfficiency = 0;
+ return CheckRecipeResultRegistry.NO_FUEL_FOUND;
+ } else {
+ this.mMaxProgresstime = 1;
+ this.mEfficiencyIncrease = 10;
+ // Overvoltage is handled inside the MultiBlockBase when pushing out to dynamos. no need to do it here.
+ return CheckRecipeResultRegistry.GENERATING;
+ }
+ }
+
+ abstract int fluidIntoPower(ArrayList<FluidStack> aFluids, int aOptFlow, int aBaseEff, int overflowMultiplier,
+ float[] flowMultipliers);
+
+ abstract float getOverflowEfficiency(int totalFlow, int actualOptimalFlow, int overflowMultiplier);
+
+ // Gets the maximum output that the turbine currently can handle. Going above this will cause the turbine to explode
+ public long getMaximumOutput() {
+ long aTotal = 0;
+ for (GT_MetaTileEntity_Hatch_Dynamo aDynamo : filterValidMTEs(mDynamoHatches)) {
+ long aVoltage = aDynamo.maxEUOutput();
+ aTotal = aDynamo.maxAmperesOut() * aVoltage;
+ }
+ return aTotal;
+ }
+
+ public int getOverflowMultiplier(ItemStack aStack) {
+ int aOverflowMultiplier = 0;
+ int toolQualityLevel = GT_MetaGenerated_Tool.getPrimaryMaterial(aStack).mToolQuality;
+ if (toolQualityLevel >= 6) {
+ aOverflowMultiplier = 3;
+ } else if (toolQualityLevel >= 3) {
+ aOverflowMultiplier = 2;
+ } else {
+ aOverflowMultiplier = 1;
+ }
+ return aOverflowMultiplier;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 1;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ if (GT_Utility.isStackInvalid(aStack)) {
+ return 0;
+ }
+ if (aStack.getItem() instanceof GT_MetaGenerated_Tool_01) {
+ return 10000;
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ int mPollutionReduction = 0;
+ for (GT_MetaTileEntity_Hatch_Muffler tHatch : filterValidMTEs(mMufflerHatches)) {
+ mPollutionReduction = Math.max(tHatch.calculatePollutionReduction(100), mPollutionReduction);
+ }
+
+ String tRunning = mMaxProgresstime > 0
+ ? EnumChatFormatting.GREEN + StatCollector.translateToLocal("GT5U.turbine.running.true")
+ + EnumChatFormatting.RESET
+ : EnumChatFormatting.RED + StatCollector.translateToLocal("GT5U.turbine.running.false")
+ + EnumChatFormatting.RESET;
+ String tMaintainance = getIdealStatus() == getRepairStatus()
+ ? EnumChatFormatting.GREEN + StatCollector.translateToLocal("GT5U.turbine.maintenance.false")
+ + EnumChatFormatting.RESET
+ : EnumChatFormatting.RED + StatCollector.translateToLocal("GT5U.turbine.maintenance.true")
+ + EnumChatFormatting.RESET;
+ int tDura = 0;
+
+ if (mInventory[1] != null && mInventory[1].getItem() instanceof GT_MetaGenerated_Tool_01) {
+ tDura = GT_Utility.safeInt(
+ (long) (100.0f / GT_MetaGenerated_Tool.getToolMaxDamage(mInventory[1])
+ * (GT_MetaGenerated_Tool.getToolDamage(mInventory[1])) + 1));
+ }
+
+ long storedEnergy = 0;
+ long maxEnergy = 0;
+ for (GT_MetaTileEntity_Hatch_Dynamo tHatch : filterValidMTEs(mDynamoHatches)) {
+ storedEnergy += tHatch.getBaseMetaTileEntity()
+ .getStoredEU();
+ maxEnergy += tHatch.getBaseMetaTileEntity()
+ .getEUCapacity();
+ }
+ String[] ret = new String[] {
+ // 8 Lines available for information panels
+ tRunning + ": "
+ + EnumChatFormatting.RED
+ + GT_Utility.formatNumbers(((long) mEUt * mEfficiency) / 10000)
+ + EnumChatFormatting.RESET
+ + " EU/t", /* 1 */
+ tMaintainance, /* 2 */
+ StatCollector.translateToLocal("GT5U.turbine.efficiency") + ": "
+ + EnumChatFormatting.YELLOW
+ + (mEfficiency / 100F)
+ + EnumChatFormatting.RESET
+ + "%", /* 2 */
+ StatCollector.translateToLocal("GT5U.multiblock.energy") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(storedEnergy)
+ + EnumChatFormatting.RESET
+ + " EU / "
+ + /* 3 */ EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(maxEnergy)
+ + EnumChatFormatting.RESET
+ + " EU",
+ StatCollector.translateToLocal("GT5U.turbine.flow") + ": "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(GT_Utility.safeInt((long) realOptFlow))
+ + EnumChatFormatting.RESET
+ + " L/t"
+ + /* 4 */ EnumChatFormatting.YELLOW
+ + " ("
+ + (looseFit ? StatCollector.translateToLocal("GT5U.turbine.loose")
+ : StatCollector.translateToLocal("GT5U.turbine.tight"))
+ + ")", /* 5 */
+ StatCollector.translateToLocal("GT5U.turbine.fuel") + ": "
+ + EnumChatFormatting.GOLD
+ + GT_Utility.formatNumbers(storedFluid)
+ + EnumChatFormatting.RESET
+ + "L", /* 6 */
+ StatCollector.translateToLocal(
+ "GT5U.turbine.dmg") + ": " + EnumChatFormatting.RED + tDura + EnumChatFormatting.RESET + "%", /* 7 */
+ StatCollector.translateToLocal("GT5U.multiblock.pollution") + ": "
+ + EnumChatFormatting.GREEN
+ + mPollutionReduction
+ + EnumChatFormatting.RESET
+ + " %" /* 8 */
+ };
+ if (!this.getClass()
+ .getName()
+ .contains("Steam"))
+ ret[4] = StatCollector.translateToLocal("GT5U.turbine.flow") + ": "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.safeInt((long) realOptFlow)
+ + EnumChatFormatting.RESET
+ + " L/t";
+ return ret;
+ }
+
+ public boolean hasTurbine() {
+ return getBaseMetaTileEntity() != null && getBaseMetaTileEntity().isClientSide() ? mHasTurbine
+ : this.getMaxEfficiency(mInventory[1]) > 0;
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return true;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 2, 2, 1);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 2, 2, 1, elementBudget, env, false, true);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine_Gas.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine_Gas.java
new file mode 100644
index 0000000000..71dc1c4d15
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine_Gas.java
@@ -0,0 +1,220 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static gregtech.api.enums.Textures.BlockIcons.LARGETURBINE_NEW5;
+import static gregtech.api.enums.Textures.BlockIcons.LARGETURBINE_NEW_ACTIVE5;
+import static gregtech.api.enums.Textures.BlockIcons.LARGETURBINE_NEW_EMPTY5;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASINGS;
+import static gregtech.api.enums.Textures.BlockIcons.casingTexturePages;
+
+import java.util.ArrayList;
+
+import net.minecraft.block.Block;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_LargeTurbine_Gas extends GT_MetaTileEntity_LargeTurbine {
+
+ public GT_MetaTileEntity_LargeTurbine_Gas(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_LargeTurbine_Gas(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ return new ITexture[] { MACHINE_CASINGS[1][colorIndex + 1],
+ aFacing == side ? (aActive ? TextureFactory.builder()
+ .addIcon(LARGETURBINE_NEW_ACTIVE5)
+ .build()
+ : hasTurbine() ? TextureFactory.builder()
+ .addIcon(LARGETURBINE_NEW5)
+ .build()
+ : TextureFactory.builder()
+ .addIcon(LARGETURBINE_NEW_EMPTY5)
+ .build())
+ : casingTexturePages[0][58] };
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Gas Turbine")
+ .addInfo("Controller block for the Large Gas Turbine")
+ .addInfo("Needs a Turbine, place inside controller")
+ // .addInfo("The excess fuel that gets consumed will be voided!")
+ .addPollutionAmount(getPollutionPerSecond(null))
+ .addSeparator()
+ .beginStructureBlock(3, 3, 4, true)
+ .addController("Front center")
+ .addCasingInfoRange("Stainless Steel Turbine Casing", 8, 30, false)
+ .addDynamoHatch("Back center", 1)
+ .addMaintenanceHatch("Side centered", 2)
+ .addMufflerHatch("Side centered", 2)
+ .addInputHatch("Gas Fuel, Side centered", 2)
+ .addOtherStructurePart("Air", "3x3 area in front of controller")
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ public int getFuelValue(FluidStack aLiquid) {
+ if (aLiquid == null) return 0;
+ GT_Recipe tFuel = RecipeMaps.gasTurbineFuels.getBackend()
+ .findFuel(aLiquid);
+ if (tFuel != null) return tFuel.mSpecialValue;
+ return 0;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_LargeTurbine_Gas(mName);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.gasTurbineFuels;
+ }
+
+ @Override
+ public int getRecipeCatalystPriority() {
+ return -1;
+ }
+
+ @Override
+ protected boolean filtersFluid() {
+ return false;
+ }
+
+ @Override
+ public Block getCasingBlock() {
+ return GregTech_API.sBlockCasings4;
+ }
+
+ @Override
+ public byte getCasingMeta() {
+ return 10;
+ }
+
+ @Override
+ public int getCasingTextureIndex() {
+ return 58;
+ }
+
+ @Override
+ public boolean isNewStyleRendering() {
+ return true;
+ }
+
+ @Override
+ public int getPollutionPerSecond(ItemStack aStack) {
+ return GT_Mod.gregtechproxy.mPollutionLargeGasTurbinePerSecond;
+ }
+
+ @Override
+ int fluidIntoPower(ArrayList<FluidStack> aFluids, int aOptFlow, int aBaseEff, int overflowMultiplier,
+ float[] flowMultipliers) {
+ if (aFluids.size() >= 1) {
+ int tEU = 0;
+ int actualOptimalFlow = 0;
+
+ FluidStack firstFuelType = new FluidStack(aFluids.get(0), 0); // Identify a SINGLE type of fluid to process.
+ // Doesn't matter which one. Ignore the rest!
+ int fuelValue = getFuelValue(firstFuelType);
+
+ if (aOptFlow < fuelValue) {
+ // turbine too weak and/or fuel too powerful
+ // at least consume 1L
+ this.realOptFlow = 1;
+ // wastes the extra fuel and generate aOptFlow directly
+ depleteInput(new FluidStack(firstFuelType, 1));
+ this.storedFluid += 1;
+ return GT_Utility.safeInt((long) aOptFlow * (long) aBaseEff / 10000L);
+ }
+
+ actualOptimalFlow = GT_Utility.safeInt((long) (aOptFlow * flowMultipliers[1] / fuelValue));
+ this.realOptFlow = actualOptimalFlow;
+
+ // Allowed to use up to 450% optimal flow rate, depending on the value of overflowMultiplier.
+ // This value is chosen because the highest EU/t possible depends on the overflowMultiplier, and the formula
+ // used
+ // makes it so the flow rate for that max, per value of overflowMultiplier, is (percentage of optimal flow
+ // rate):
+ // - 150% if it is 1
+ // - 300% if it is 2
+ // - 450% if it is 3
+ // Variable required outside of loop for multi-hatch scenarios.
+ int remainingFlow = GT_Utility.safeInt((long) (actualOptimalFlow * (1.5f * overflowMultiplier)));
+ int flow = 0;
+ int totalFlow = 0;
+
+ storedFluid = 0;
+ for (FluidStack aFluid : aFluids) {
+ if (aFluid.isFluidEqual(firstFuelType)) {
+ flow = Math.min(aFluid.amount, remainingFlow); // try to use up to the max flow defined just above
+ depleteInput(new FluidStack(aFluid, flow)); // deplete that amount
+ this.storedFluid += aFluid.amount;
+ remainingFlow -= flow; // track amount we're allowed to continue depleting from hatches
+ totalFlow += flow; // track total input used
+ }
+ }
+ if (totalFlow <= 0) return 0;
+ tEU = GT_Utility.safeInt((long) totalFlow * fuelValue);
+
+ if (totalFlow != actualOptimalFlow) {
+ float efficiency = getOverflowEfficiency(totalFlow, actualOptimalFlow, overflowMultiplier);
+ tEU *= efficiency;
+ }
+ tEU = GT_Utility.safeInt((long) tEU * (long) aBaseEff / 10000L);
+
+ // EU/t output cap to properly tier the LGT against the Advanced LGT, will be implemented in a future dev
+ // update
+ /*
+ * if (tEU > 8192) { tEU = 8192; }
+ */
+
+ // If next output is above the maximum the dynamo can handle, set it to the maximum instead of exploding the
+ // turbine
+ // Raising the maximum allowed flow rate to account for the efficiency changes beyond the optimal flow
+ // When the max fuel consumption rate was increased, turbines could explode on world load
+ if (tEU > getMaximumOutput()) {
+ tEU = GT_Utility.safeInt(getMaximumOutput());
+ }
+ return tEU;
+ }
+ return 0;
+ }
+
+ @Override
+ float getOverflowEfficiency(int totalFlow, int actualOptimalFlow, int overflowMultiplier) {
+ // overflowMultiplier changes how quickly the turbine loses efficiency after flow goes beyond the optimal value
+ // At the default value of 1, any flow will generate less EU/t than optimal flow, regardless of the amount of
+ // fuel used
+ // The bigger this number is, the slower efficiency loss happens as flow moves beyond the optimal value
+ // Gases are the second most efficient in this regard, with plasma being the most efficient
+ float efficiency = 0;
+
+ if (totalFlow > actualOptimalFlow) {
+ efficiency = 1.0f - Math.abs((totalFlow - actualOptimalFlow))
+ / ((float) actualOptimalFlow * ((overflowMultiplier * 3) - 1));
+ } else {
+ efficiency = 1.0f - Math.abs((totalFlow - actualOptimalFlow) / (float) actualOptimalFlow);
+ }
+
+ return efficiency;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine_GasAdvanced.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine_GasAdvanced.java
new file mode 100644
index 0000000000..4926655476
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine_GasAdvanced.java
@@ -0,0 +1,219 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static gregtech.api.enums.Textures.BlockIcons.LARGETURBINE_NEW5;
+import static gregtech.api.enums.Textures.BlockIcons.LARGETURBINE_NEW_ACTIVE5;
+import static gregtech.api.enums.Textures.BlockIcons.LARGETURBINE_NEW_EMPTY5;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASINGS;
+import static gregtech.api.enums.Textures.BlockIcons.casingTexturePages;
+
+import java.util.ArrayList;
+
+import net.minecraft.block.Block;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_LargeTurbine_GasAdvanced extends GT_MetaTileEntity_LargeTurbine {
+
+ public GT_MetaTileEntity_LargeTurbine_GasAdvanced(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_LargeTurbine_GasAdvanced(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ return new ITexture[] { MACHINE_CASINGS[1][colorIndex + 1],
+ aFacing == side ? (aActive ? TextureFactory.builder()
+ .addIcon(LARGETURBINE_NEW_ACTIVE5)
+ .build()
+ : hasTurbine() ? TextureFactory.builder()
+ .addIcon(LARGETURBINE_NEW5)
+ .build()
+ : TextureFactory.builder()
+ .addIcon(LARGETURBINE_NEW_EMPTY5)
+ .build())
+ : casingTexturePages[1][57] };
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Gas Turbine")
+ .addInfo("Warning: This is an experimental multiblock, subject to changes ")
+ .addInfo("Controller block for the Large Advanced Gas Turbine")
+ .addInfo("Needs a Turbine, place inside controller")
+ .addInfo("Only accepts gases above 800k EU/bucket")
+ .addInfo("Has no maximum EU/t output, only depends on the Dynamo Hatch")
+ .addPollutionAmount(getPollutionPerSecond(null))
+ .addSeparator()
+ .beginStructureBlock(3, 3, 4, true)
+ .addController("Front center")
+ .addCasingInfoRange("Advanced Gas Turbine Casing", 8, 30, false)
+ .addDynamoHatch("Back center", 1)
+ .addMaintenanceHatch("Side centered", 2)
+ .addMufflerHatch("Side centered", 2)
+ .addInputHatch("Gas Fuel, Side centered", 2)
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ public int getFuelValue(FluidStack aLiquid) {
+ if (aLiquid == null) return 0;
+ GT_Recipe tFuel = RecipeMaps.gasTurbineFuels.getBackend()
+ .findFuel(aLiquid);
+ if (tFuel != null) return tFuel.mSpecialValue;
+ return 0;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_LargeTurbine_GasAdvanced(mName);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.gasTurbineFuels;
+ }
+
+ @Override
+ public int getRecipeCatalystPriority() {
+ return -2;
+ }
+
+ @Override
+ protected boolean filtersFluid() {
+ return false;
+ }
+
+ @Override
+ public Block getCasingBlock() {
+ return GregTech_API.sBlockCasings8;
+ }
+
+ @Override
+ public byte getCasingMeta() {
+ return 9;
+ }
+
+ @Override
+ public int getCasingTextureIndex() {
+ return 185;
+ }
+
+ @Override
+ public boolean isNewStyleRendering() {
+ return true;
+ }
+
+ @Override
+ public int getPollutionPerSecond(ItemStack aStack) {
+ return GT_Mod.gregtechproxy.mPollutionLargeGasTurbinePerSecond;
+ }
+
+ @Override
+ int fluidIntoPower(ArrayList<FluidStack> aFluids, int aOptFlow, int aBaseEff, int overflowMultiplier,
+ float[] flowMultipliers) {
+ if (aFluids.size() >= 1) {
+ int tEU = 0;
+ int actualOptimalFlow = 0;
+
+ FluidStack firstFuelType = new FluidStack(aFluids.get(0), 0); // Identify a SINGLE type of fluid to process.
+ // Doesn't matter which one. Ignore the rest!
+ int fuelValue = getFuelValue(firstFuelType);
+
+ if (fuelValue < 100) {
+ return 0;
+ }
+
+ if (aOptFlow < fuelValue) {
+ // turbine too weak and/or fuel too powerful
+ // at least consume 1L
+ this.realOptFlow = 1;
+ // wastes the extra fuel and generate aOptFlow directly
+ depleteInput(new FluidStack(firstFuelType, 1));
+ this.storedFluid += 1;
+ return GT_Utility.safeInt((long) aOptFlow * (long) aBaseEff / 10000L);
+ }
+
+ actualOptimalFlow = GT_Utility.safeInt((long) (aOptFlow * flowMultipliers[1] / fuelValue));
+ this.realOptFlow = actualOptimalFlow;
+
+ // Allowed to use up to 450% optimal flow rate, depending on the value of overflowMultiplier.
+ // This value is chosen because the highest EU/t possible depends on the overflowMultiplier, and the formula
+ // used
+ // makes it so the flow rate for that max, per value of overflowMultiplier, is (percentage of optimal flow
+ // rate):
+ // - 150% if it is 1
+ // - 300% if it is 2
+ // - 450% if it is 3
+ // Variable required outside of loop for multi-hatch scenarios.
+ int remainingFlow = GT_Utility.safeInt((long) (actualOptimalFlow * (1.5f * overflowMultiplier)));
+ int flow = 0;
+ int totalFlow = 0;
+
+ storedFluid = 0;
+ for (FluidStack aFluid : aFluids) {
+ if (aFluid.isFluidEqual(firstFuelType)) {
+ flow = Math.min(aFluid.amount, remainingFlow); // try to use up to the max flow defined just above
+ depleteInput(new FluidStack(aFluid, flow)); // deplete that amount
+ this.storedFluid += aFluid.amount;
+ remainingFlow -= flow; // track amount we're allowed to continue depleting from hatches
+ totalFlow += flow; // track total input used
+ }
+ }
+ if (totalFlow <= 0) return 0;
+ tEU = GT_Utility.safeInt((long) totalFlow * fuelValue);
+
+ if (totalFlow != actualOptimalFlow) {
+ float efficiency = getOverflowEfficiency(totalFlow, actualOptimalFlow, overflowMultiplier);
+ tEU *= efficiency;
+ }
+ tEU = GT_Utility.safeInt((long) tEU * (long) aBaseEff / 10000L);
+
+ // If next output is above the maximum the dynamo can handle, set it to the maximum instead of exploding the
+ // turbine
+ // Raising the maximum allowed flow rate to account for the efficiency changes beyond the optimal flow
+ // When the max fuel consumption rate was increased, turbines could explode on world load
+ if (tEU > getMaximumOutput()) {
+ tEU = GT_Utility.safeInt(getMaximumOutput());
+ }
+ return tEU;
+ }
+ return 0;
+ }
+
+ @Override
+ float getOverflowEfficiency(int totalFlow, int actualOptimalFlow, int overflowMultiplier) {
+ // overflowMultiplier changes how quickly the turbine loses efficiency after flow goes beyond the optimal value
+ // At the default value of 1, any flow will generate less EU/t than optimal flow, regardless of the amount of
+ // fuel used
+ // The bigger this number is, the slower efficiency loss happens as flow moves beyond the optimal value
+ // Gases are the second most efficient in this regard, with plasma being the most efficient
+ float efficiency = 0;
+
+ if (totalFlow > actualOptimalFlow) {
+ efficiency = 1.0f - Math.abs((totalFlow - actualOptimalFlow))
+ / ((float) actualOptimalFlow * ((overflowMultiplier * 3) - 1));
+ } else {
+ efficiency = 1.0f - Math.abs((totalFlow - actualOptimalFlow) / (float) actualOptimalFlow);
+ }
+
+ return efficiency;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine_HPSteam.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine_HPSteam.java
new file mode 100644
index 0000000000..d61e75eac3
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine_HPSteam.java
@@ -0,0 +1,232 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static gregtech.api.enums.Textures.BlockIcons.LARGETURBINE_NEW5;
+import static gregtech.api.enums.Textures.BlockIcons.LARGETURBINE_NEW_ACTIVE5;
+import static gregtech.api.enums.Textures.BlockIcons.LARGETURBINE_NEW_EMPTY5;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASINGS;
+import static gregtech.api.enums.Textures.BlockIcons.casingTexturePages;
+import static gregtech.api.objects.XSTR.XSTR_INSTANCE;
+import static gregtech.common.tileentities.machines.multi.GT_MetaTileEntity_LargeTurbine_Steam.calculateLooseFlow;
+
+import java.util.ArrayList;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_LargeTurbine_HPSteam extends GT_MetaTileEntity_LargeTurbine {
+
+ public boolean achievement = false;
+ private boolean looseFit = false;
+
+ public GT_MetaTileEntity_LargeTurbine_HPSteam(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_LargeTurbine_HPSteam(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ return new ITexture[] { MACHINE_CASINGS[1][colorIndex + 1],
+ aFacing == side ? (aActive ? TextureFactory.builder()
+ .addIcon(LARGETURBINE_NEW_ACTIVE5)
+ .build()
+ : hasTurbine() ? TextureFactory.builder()
+ .addIcon(LARGETURBINE_NEW5)
+ .build()
+ : TextureFactory.builder()
+ .addIcon(LARGETURBINE_NEW_EMPTY5)
+ .build())
+ : casingTexturePages[0][59] };
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Steam Turbine")
+ .addInfo("Controller block for the Large High Pressure Steam Turbine")
+ .addInfo("Needs a Turbine, place inside controller")
+ .addInfo("Outputs Steam as well as producing power")
+ .addInfo("Power output depends on turbine and fitting")
+ .addInfo("Use screwdriver to adjust fitting of turbine")
+ .addSeparator()
+ .beginStructureBlock(3, 3, 4, true)
+ .addController("Front center")
+ .addCasingInfoRange("Titanium Turbine Casing", 8, 31, false)
+ .addDynamoHatch("Back center", 1)
+ .addMaintenanceHatch("Side centered", 2)
+ .addInputHatch("Superheated Steam, Side centered", 2)
+ .addOutputHatch("Steam, Side centered", 2)
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_LargeTurbine_HPSteam(mName);
+ }
+
+ @Override
+ public Block getCasingBlock() {
+ return GregTech_API.sBlockCasings4;
+ }
+
+ @Override
+ public byte getCasingMeta() {
+ return 11;
+ }
+
+ @Override
+ public int getCasingTextureIndex() {
+ return 59;
+ }
+
+ @Override
+ public boolean isNewStyleRendering() {
+ return true;
+ }
+
+ @Override
+ int fluidIntoPower(ArrayList<FluidStack> aFluids, int aOptFlow, int aBaseEff, int overflowEfficiency,
+ float[] flowMultipliers) {
+ if (looseFit) {
+ float[] calculatedFlow = calculateLooseFlow(aOptFlow, aBaseEff);
+ aOptFlow = GT_Utility.safeInt((long) calculatedFlow[0]);
+ aBaseEff = GT_Utility.safeInt((long) calculatedFlow[1]);
+ }
+ int tEU = 0;
+ int totalFlow = 0; // Byproducts are based on actual flow
+ int flow = 0;
+
+ // Allowed to use up to 300% optimal flow rate, depending on the value of overflowMultiplier.
+ // This value is chosen because the highest EU/t possible depends on the overflowMultiplier, and the formula
+ // used
+ // makes it so the flow rate for that max, per value of overflowMultiplier, is (percentage of optimal flow
+ // rate):
+ // - 200% if it is 1
+ // - 250% if it is 2
+ // - 300% if it is 3
+ // Variable required outside of loop for multi-hatch scenarios.
+ this.realOptFlow = aOptFlow * flowMultipliers[0];
+ int remainingFlow = GT_Utility.safeInt((long) (realOptFlow * (0.5f * overflowMultiplier + 1.5)));
+
+ storedFluid = 0;
+ for (int i = 0; i < aFluids.size() && remainingFlow > 0; i++) {
+ final FluidStack aFluidStack = aFluids.get(i);
+ if (GT_ModHandler.isSuperHeatedSteam(aFluidStack)) {
+ flow = Math.min(aFluidStack.amount, remainingFlow); // try to use up to the max flow defined just above
+ depleteInput(new FluidStack(aFluidStack, flow)); // deplete that amount
+ this.storedFluid += aFluidStack.amount;
+ remainingFlow -= flow; // track amount we're allowed to continue depleting from hatches
+ totalFlow += flow; // track total input used
+ if (!achievement) {
+ try {
+ GT_Mod.achievements.issueAchievement(
+ this.getBaseMetaTileEntity()
+ .getWorld()
+ .getPlayerEntityByName(
+ this.getBaseMetaTileEntity()
+ .getOwnerName()),
+ "efficientsteam");
+ } catch (Exception ignored) {}
+ achievement = true;
+ }
+ } else if (GT_ModHandler.isAnySteam(aFluidStack)) {
+ depleteInput(new FluidStack(aFluidStack, aFluidStack.amount));
+ }
+ }
+ if (totalFlow <= 0) return 0;
+ tEU = totalFlow;
+ addOutput(GT_ModHandler.getSteam(totalFlow));
+ if (totalFlow == (GT_Utility.safeInt((long) realOptFlow))) {
+ tEU = GT_Utility.safeInt((long) tEU * (long) aBaseEff / 10000L);
+ } else {
+ float efficiency = getOverflowEfficiency(
+ totalFlow,
+ (GT_Utility.safeInt((long) realOptFlow)),
+ overflowMultiplier);
+ tEU *= efficiency;
+ tEU = Math.max(1, GT_Utility.safeInt((long) tEU * (long) aBaseEff / 10000L));
+ }
+
+ // If next output is above the maximum the dynamo can handle, set it to the maximum instead of exploding the
+ // turbine
+ // Raising the maximum allowed flow rate to account for the efficiency changes beyond the optimal flow rate can
+ // explode turbines on world load
+ if (tEU > getMaximumOutput()) {
+ tEU = GT_Utility.safeInt(getMaximumOutput());
+ }
+
+ return tEU;
+ }
+
+ @Override
+ float getOverflowEfficiency(int totalFlow, int actualOptimalFlow, int overflowMultiplier) {
+ // overflowMultiplier changes how quickly the turbine loses efficiency after flow goes beyond the optimal value
+ // At the default value of 1, any flow will generate less EU/t than optimal flow, regardless of the amount of
+ // fuel used
+ // The bigger this number is, the slower efficiency loss happens as flow moves beyond the optimal value
+ // Superheated steam is the second least efficient out of all turbine fuels in this regard, with steam being the
+ // least efficient
+ float efficiency = 0;
+
+ if (totalFlow > actualOptimalFlow) {
+ efficiency = 1.0f
+ - Math.abs((totalFlow - actualOptimalFlow)) / ((float) actualOptimalFlow * (overflowMultiplier + 2));
+ } else {
+ efficiency = 1.0f - Math.abs((totalFlow - actualOptimalFlow) / (float) actualOptimalFlow);
+ }
+
+ return efficiency;
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ if (side == getBaseMetaTileEntity().getFrontFacing()) {
+ looseFit ^= true;
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ looseFit ? GT_Utility.trans("500", "Fitting: Loose - More Flow")
+ : GT_Utility.trans("501", "Fitting: Tight - More Efficiency"));
+ }
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return (looseFit && XSTR_INSTANCE.nextInt(4) == 0) ? 0 : 1;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ super.looseFit = looseFit;
+ return super.getInfoData();
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setBoolean("turbineFitting", looseFit);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ looseFit = aNBT.getBoolean("turbineFitting");
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine_Plasma.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine_Plasma.java
new file mode 100644
index 0000000000..2ecc79cd3b
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine_Plasma.java
@@ -0,0 +1,391 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static gregtech.api.enums.Textures.BlockIcons.LARGETURBINE_NEW5;
+import static gregtech.api.enums.Textures.BlockIcons.LARGETURBINE_NEW_ACTIVE5;
+import static gregtech.api.enums.Textures.BlockIcons.LARGETURBINE_NEW_EMPTY5;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASINGS;
+import static gregtech.api.enums.Textures.BlockIcons.casingTexturePages;
+import static gregtech.api.util.GT_Utility.filterValidMTEs;
+
+import java.util.ArrayList;
+
+import net.minecraft.block.Block;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidRegistry;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.jetbrains.annotations.NotNull;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.items.GT_MetaGenerated_Tool;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Dynamo;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Muffler;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.shutdown.ShutDownReasonRegistry;
+import gregtech.common.items.GT_MetaGenerated_Tool_01;
+
+public class GT_MetaTileEntity_LargeTurbine_Plasma extends GT_MetaTileEntity_LargeTurbine {
+
+ public GT_MetaTileEntity_LargeTurbine_Plasma(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_LargeTurbine_Plasma(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ return new ITexture[] { MACHINE_CASINGS[1][colorIndex + 1],
+ aFacing == side ? (aActive ? TextureFactory.builder()
+ .addIcon(LARGETURBINE_NEW_ACTIVE5)
+ .build()
+ : hasTurbine() ? TextureFactory.builder()
+ .addIcon(LARGETURBINE_NEW5)
+ .build()
+ : TextureFactory.builder()
+ .addIcon(LARGETURBINE_NEW_EMPTY5)
+ .build())
+ : casingTexturePages[0][60] };
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Plasma Turbine")
+ .addInfo("Controller block for the Large Plasma Generator")
+ .addInfo("Needs a Turbine, place inside controller")
+ .addInfo("Use your Fusion Reactor to produce the Plasma")
+ .addSeparator()
+ .beginStructureBlock(3, 3, 4, true)
+ .addController("Front center")
+ .addCasingInfoRange("Tungstensteel Turbine Casing", 8, 31, false)
+ .addDynamoHatch("Back center", 1)
+ .addMaintenanceHatch("Side centered", 2)
+ .addInputHatch("Plasma Fluid, Side centered", 2)
+ .addOutputHatch("Molten Fluid, optional, Side centered", 2)
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ public int getFuelValue(FluidStack aLiquid) {
+ if (aLiquid == null) return 0;
+ GT_Recipe tFuel = RecipeMaps.plasmaFuels.getBackend()
+ .findFuel(aLiquid);
+ if (tFuel != null) return tFuel.mSpecialValue;
+ return 0;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_LargeTurbine_Plasma(mName);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.plasmaFuels;
+ }
+
+ @Override
+ public int getRecipeCatalystPriority() {
+ return -1;
+ }
+
+ @Override
+ protected boolean filtersFluid() {
+ return false;
+ }
+
+ @Override
+ public Block getCasingBlock() {
+ return GregTech_API.sBlockCasings4;
+ }
+
+ @Override
+ public byte getCasingMeta() {
+ return 12;
+ }
+
+ @Override
+ public int getCasingTextureIndex() {
+ return 60;
+ }
+
+ @Override
+ public boolean isNewStyleRendering() {
+ return true;
+ }
+
+ @Override
+ int fluidIntoPower(ArrayList<FluidStack> aFluids, int aOptFlow, int aBaseEff, int overflowMultiplier,
+ float[] flowMultipliers) {
+ if (aFluids.size() >= 1) {
+ aOptFlow *= 800; // CHANGED THINGS HERE, check recipe runs once per 20 ticks
+ int tEU = 0;
+
+ int actualOptimalFlow = 0;
+
+ FluidStack firstFuelType = new FluidStack(aFluids.get(0), 0); // Identify a SINGLE type of fluid to process.
+ // Doesn't matter which one. Ignore the rest!
+ int fuelValue = getFuelValue(firstFuelType);
+ actualOptimalFlow = GT_Utility
+ .safeInt((long) Math.ceil((double) aOptFlow * flowMultipliers[2] / (double) fuelValue));
+ this.realOptFlow = actualOptimalFlow; // For scanner info
+
+ // Allowed to use up to 550% optimal flow rate, depending on the value of overflowMultiplier.
+ // This value is chosen because the highest EU/t possible depends on the overflowMultiplier, and the formula
+ // used
+ // makes it so the flow rate for that max, per value of overflowMultiplier, is (percentage of optimal flow
+ // rate):
+ // - 250% if it is 1
+ // - 400% if it is 2
+ // - 550% if it is 3
+ // Variable required outside of loop for multi-hatch scenarios.
+ int remainingFlow = GT_Utility.safeInt((long) (actualOptimalFlow * (1.5f * overflowMultiplier + 1)));
+ int flow = 0;
+ int totalFlow = 0;
+
+ storedFluid = 0;
+ for (FluidStack aFluid : aFluids) {
+ if (aFluid.isFluidEqual(firstFuelType)) {
+ flow = Math.min(aFluid.amount, remainingFlow); // try to use up to the max flow defined just above
+ depleteInput(new FluidStack(aFluid, flow)); // deplete that amount
+ this.storedFluid += aFluid.amount;
+ remainingFlow -= flow; // track amount we're allowed to continue depleting from hatches
+ totalFlow += flow; // track total input used
+ }
+ }
+ String fn = FluidRegistry.getFluidName(firstFuelType);
+ String[] nameSegments = fn.split("\\.", 2);
+ if (nameSegments.length == 2) {
+ String outputName = nameSegments[1];
+ FluidStack output = FluidRegistry.getFluidStack(outputName, totalFlow);
+ if (output == null) {
+ output = FluidRegistry.getFluidStack("molten." + outputName, totalFlow);
+ }
+ if (output != null) {
+ addOutput(output);
+ }
+ }
+ if (totalFlow <= 0) return 0;
+ tEU = GT_Utility.safeInt((long) ((fuelValue / 20D) * (double) totalFlow));
+
+ // GT_FML_LOGGER.info(totalFlow+" : "+fuelValue+" : "+aOptFlow+" : "+actualOptimalFlow+" : "+tEU);
+
+ if (totalFlow != actualOptimalFlow) {
+ float efficiency = getOverflowEfficiency(totalFlow, actualOptimalFlow, overflowMultiplier);
+ tEU = (int) (tEU * efficiency);
+ }
+ tEU = GT_Utility.safeInt((long) (aBaseEff / 10000D * tEU));
+
+ // If next output is above the maximum the dynamo can handle, set it to the maximum instead of exploding the
+ // turbine
+ // Raising the maximum allowed flow rate to account for the efficiency changes beyond the optimal flow rate
+ // can explode turbines on world load
+ if (tEU > getMaximumOutput()) {
+ tEU = GT_Utility.safeInt(getMaximumOutput());
+ }
+
+ return tEU;
+ }
+ return 0;
+ }
+
+ @Override
+ float getOverflowEfficiency(int totalFlow, int actualOptimalFlow, int overflowMultiplier) {
+ // overflowMultiplier changes how quickly the turbine loses efficiency after flow goes beyond the optimal value
+ // At the default value of 1, any flow will generate less EU/t than optimal flow, regardless of the amount of
+ // fuel used
+ // The bigger this number is, the slower efficiency loss happens as flow moves beyond the optimal value
+ // Plasmas are the most efficient out of all turbine fuels in this regard
+ float efficiency = 0;
+
+ if (totalFlow > actualOptimalFlow) {
+ efficiency = 1.0f - Math.abs((totalFlow - actualOptimalFlow))
+ / ((float) actualOptimalFlow * ((overflowMultiplier * 3) + 1));
+ } else {
+ efficiency = 1.0f - Math.abs((totalFlow - actualOptimalFlow) / (float) actualOptimalFlow);
+ }
+
+ return efficiency;
+ }
+
+ @Override
+ @NotNull
+ public CheckRecipeResult checkProcessing() {
+ ItemStack controllerSlot = getControllerSlot();
+ if ((counter & 7) == 0
+ && (controllerSlot == null || !(controllerSlot.getItem() instanceof GT_MetaGenerated_Tool)
+ || controllerSlot.getItemDamage() < 170
+ || controllerSlot.getItemDamage() > 179)) {
+ stopMachine(ShutDownReasonRegistry.NO_TURBINE);
+ return CheckRecipeResultRegistry.NO_TURBINE_FOUND;
+ }
+ ArrayList<FluidStack> tFluids = getStoredFluids();
+ if (!tFluids.isEmpty()) {
+ if (baseEff == 0 || optFlow == 0
+ || counter >= 512
+ || this.getBaseMetaTileEntity()
+ .hasWorkJustBeenEnabled()
+ || this.getBaseMetaTileEntity()
+ .hasInventoryBeenModified()) {
+ counter = 0;
+ baseEff = GT_Utility.safeInt(
+ (long) ((5F
+ + ((GT_MetaGenerated_Tool) controllerSlot.getItem()).getToolCombatDamage(controllerSlot))
+ * 1000F));
+ optFlow = GT_Utility.safeInt(
+ (long) Math.max(
+ Float.MIN_NORMAL,
+ ((GT_MetaGenerated_Tool) controllerSlot.getItem()).getToolStats(controllerSlot)
+ .getSpeedMultiplier() * GT_MetaGenerated_Tool.getPrimaryMaterial(controllerSlot).mToolSpeed
+ * 50));
+ overflowMultiplier = getOverflowMultiplier(controllerSlot);
+
+ flowMultipliers[0] = GT_MetaGenerated_Tool.getPrimaryMaterial(controllerSlot).mSteamMultiplier;
+ flowMultipliers[1] = GT_MetaGenerated_Tool.getPrimaryMaterial(controllerSlot).mGasMultiplier;
+ flowMultipliers[2] = GT_MetaGenerated_Tool.getPrimaryMaterial(controllerSlot).mPlasmaMultiplier;
+
+ if (optFlow <= 0 || baseEff <= 0) {
+ stopMachine(ShutDownReasonRegistry.NONE); // in case the turbine got removed
+ return CheckRecipeResultRegistry.NO_FUEL_FOUND;
+ }
+ } else {
+ counter++;
+ }
+ }
+
+ int newPower = fluidIntoPower(tFluids, optFlow, baseEff, overflowMultiplier, flowMultipliers); // How much the
+ // turbine should
+ // be producing
+ // with this flow
+
+ int difference = newPower - this.mEUt; // difference between current output and new output
+
+ // Magic numbers: can always change by at least 10 eu/t, but otherwise by at most 1 percent of the difference in
+ // power level (per tick)
+ // This is how much the turbine can actually change during this tick
+ int maxChangeAllowed = Math.max(200, GT_Utility.safeInt((long) Math.abs(difference) / 5));
+
+ if (Math.abs(difference) > maxChangeAllowed) { // If this difference is too big, use the maximum allowed change
+ int change = maxChangeAllowed * (difference > 0 ? 1 : -1); // Make the change positive or negative.
+ this.mEUt += change; // Apply the change
+ } else this.mEUt = newPower;
+
+ if (this.mEUt <= 0) {
+ // stopMachine();
+ this.mEUt = 0;
+ this.mEfficiency = 0;
+ return CheckRecipeResultRegistry.NO_FUEL_FOUND;
+ } else {
+ this.mMaxProgresstime = 20;
+ this.mEfficiencyIncrease = 200;
+ // Overvoltage is handled inside the MultiBlockBase when pushing out to dynamos. no need to do it here.
+
+ return CheckRecipeResultRegistry.GENERATING;
+ }
+ }
+
+ @Override
+ public String[] getInfoData() {
+ int mPollutionReduction = 0;
+ for (GT_MetaTileEntity_Hatch_Muffler tHatch : filterValidMTEs(mMufflerHatches)) {
+ mPollutionReduction = Math.max(tHatch.calculatePollutionReduction(100), mPollutionReduction);
+ }
+
+ String tRunning = mMaxProgresstime > 0
+ ? EnumChatFormatting.GREEN + StatCollector.translateToLocal("GT5U.turbine.running.true")
+ + EnumChatFormatting.RESET
+ : EnumChatFormatting.RED + StatCollector.translateToLocal("GT5U.turbine.running.false")
+ + EnumChatFormatting.RESET;
+ String tMaintainance = getIdealStatus() == getRepairStatus()
+ ? EnumChatFormatting.GREEN + StatCollector.translateToLocal("GT5U.turbine.maintenance.false")
+ + EnumChatFormatting.RESET
+ : EnumChatFormatting.RED + StatCollector.translateToLocal("GT5U.turbine.maintenance.true")
+ + EnumChatFormatting.RESET;
+ int tDura = 0;
+
+ if (mInventory[1] != null && mInventory[1].getItem() instanceof GT_MetaGenerated_Tool_01) {
+ tDura = GT_Utility.safeInt(
+ (long) (100.0f / GT_MetaGenerated_Tool.getToolMaxDamage(mInventory[1])
+ * (GT_MetaGenerated_Tool.getToolDamage(mInventory[1])) + 1));
+ }
+
+ long storedEnergy = 0;
+ long maxEnergy = 0;
+ for (GT_MetaTileEntity_Hatch_Dynamo tHatch : filterValidMTEs(mDynamoHatches)) {
+ storedEnergy += tHatch.getBaseMetaTileEntity()
+ .getStoredEU();
+ maxEnergy += tHatch.getBaseMetaTileEntity()
+ .getEUCapacity();
+ }
+ String[] ret = new String[] {
+ // 8 Lines available for information panels
+ tRunning + ": "
+ + EnumChatFormatting.RED
+ + GT_Utility.formatNumbers(((long) mEUt * mEfficiency) / 10000)
+ + EnumChatFormatting.RESET
+ + " EU/t", /* 1 */
+ tMaintainance, /* 2 */
+ StatCollector.translateToLocal("GT5U.turbine.efficiency") + ": "
+ + EnumChatFormatting.YELLOW
+ + (mEfficiency / 100F)
+ + EnumChatFormatting.RESET
+ + "%", /* 2 */
+ StatCollector.translateToLocal("GT5U.multiblock.energy") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(storedEnergy)
+ + EnumChatFormatting.RESET
+ + " EU / "
+ + /* 3 */ EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(maxEnergy)
+ + EnumChatFormatting.RESET
+ + " EU",
+ StatCollector.translateToLocal("GT5U.turbine.flow") + ": "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(GT_Utility.safeInt((long) realOptFlow))
+ + EnumChatFormatting.RESET
+ + " L/s"
+ + /* 4 */ EnumChatFormatting.YELLOW
+ + " ("
+ + (looseFit ? StatCollector.translateToLocal("GT5U.turbine.loose")
+ : StatCollector.translateToLocal("GT5U.turbine.tight"))
+ + ")", /* 5 */
+ StatCollector.translateToLocal("GT5U.turbine.fuel") + ": "
+ + EnumChatFormatting.GOLD
+ + GT_Utility.formatNumbers(storedFluid)
+ + EnumChatFormatting.RESET
+ + "L", /* 6 */
+ StatCollector.translateToLocal(
+ "GT5U.turbine.dmg") + ": " + EnumChatFormatting.RED + tDura + EnumChatFormatting.RESET + "%", /* 7 */
+ StatCollector.translateToLocal("GT5U.multiblock.pollution") + ": "
+ + EnumChatFormatting.GREEN
+ + mPollutionReduction
+ + EnumChatFormatting.RESET
+ + " %" /* 8 */
+ };
+ if (!this.getClass()
+ .getName()
+ .contains("Steam"))
+ ret[4] = StatCollector.translateToLocal("GT5U.turbine.flow") + ": "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.safeInt((long) realOptFlow)
+ + EnumChatFormatting.RESET
+ + " L/s";
+ return ret;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine_Steam.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine_Steam.java
new file mode 100644
index 0000000000..849d0af462
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeTurbine_Steam.java
@@ -0,0 +1,273 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static gregtech.api.enums.GT_Values.STEAM_PER_WATER;
+import static gregtech.api.enums.Textures.BlockIcons.LARGETURBINE_NEW5;
+import static gregtech.api.enums.Textures.BlockIcons.LARGETURBINE_NEW_ACTIVE5;
+import static gregtech.api.enums.Textures.BlockIcons.LARGETURBINE_NEW_EMPTY5;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASINGS;
+import static gregtech.api.enums.Textures.BlockIcons.casingTexturePages;
+import static gregtech.api.objects.XSTR.XSTR_INSTANCE;
+
+import java.util.ArrayList;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_LargeTurbine_Steam extends GT_MetaTileEntity_LargeTurbine {
+
+ private int excessWater;
+ private boolean achievement = false;
+ private boolean looseFit = false;
+
+ public GT_MetaTileEntity_LargeTurbine_Steam(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_LargeTurbine_Steam(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ return new ITexture[] { MACHINE_CASINGS[1][colorIndex + 1],
+ aFacing == side ? (aActive ? TextureFactory.builder()
+ .addIcon(LARGETURBINE_NEW_ACTIVE5)
+ .build()
+ : hasTurbine() ? TextureFactory.builder()
+ .addIcon(LARGETURBINE_NEW5)
+ .build()
+ : TextureFactory.builder()
+ .addIcon(LARGETURBINE_NEW_EMPTY5)
+ .build())
+ : casingTexturePages[0][57] };
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Steam Turbine")
+ .addInfo("Controller block for the Large Steam Turbine")
+ .addInfo("Needs a Turbine, place inside controller")
+ .addInfo("Outputs Distilled Water as well as producing power")
+ .addInfo("Power output depends on turbine and fitting")
+ .addInfo("Use screwdriver to adjust fitting of turbine")
+ .addSeparator()
+ .beginStructureBlock(3, 3, 4, true)
+ .addController("Front center")
+ .addCasingInfoRange("Turbine Casing", 8, 31, false)
+ .addDynamoHatch("Back center", 1)
+ .addMaintenanceHatch("Side centered", 2)
+ .addInputHatch("Steam, Side centered", 2)
+ .addOutputHatch("Distilled Water, Side centered", 2)
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_LargeTurbine_Steam(mName);
+ }
+
+ @Override
+ public Block getCasingBlock() {
+ return GregTech_API.sBlockCasings4;
+ }
+
+ @Override
+ public byte getCasingMeta() {
+ return 9;
+ }
+
+ @Override
+ public int getCasingTextureIndex() {
+ return 16;
+ }
+
+ @Override
+ public boolean isNewStyleRendering() {
+ return true;
+ }
+
+ private int condenseSteam(int steam) {
+ excessWater += steam;
+ int water = excessWater / STEAM_PER_WATER;
+ excessWater %= STEAM_PER_WATER;
+ return water;
+ }
+
+ @Override
+ int fluidIntoPower(ArrayList<FluidStack> aFluids, int aOptFlow, int aBaseEff, int overflowEfficiency,
+ float[] flowMultipliers) {
+ if (looseFit) {
+ float[] calculatedFlow = calculateLooseFlow(aOptFlow, aBaseEff);
+ aOptFlow = GT_Utility.safeInt((long) calculatedFlow[0]);
+ aBaseEff = GT_Utility.safeInt((long) calculatedFlow[1]);
+ }
+ int tEU = 0;
+ int totalFlow = 0; // Byproducts are based on actual flow
+ int flow = 0;
+
+ // Allowed to use up to 250% optimal flow rate, depending on the value of overflowMultiplier.
+ // This value is chosen because the highest EU/t possible depends on the overflowMultiplier, and the formula
+ // used
+ // makes it so the flow rate for that max, per value of overflowMultiplier, is (percentage of optimal flow
+ // rate):
+ // - 150% if it is 1
+ // - 200% if it is 2
+ // - 250% if it is 3
+ // Variable required outside of loop for multi-hatch scenarios.
+ this.realOptFlow = aOptFlow * flowMultipliers[0];
+ int remainingFlow = GT_Utility.safeInt((long) (realOptFlow * (0.5f * overflowMultiplier + 1)));
+
+ storedFluid = 0;
+ for (int i = 0; i < aFluids.size() && remainingFlow > 0; i++) { // loop through each hatch; extract inputs and
+ // track totals.
+ final FluidStack aFluidStack = aFluids.get(i);
+ if (GT_ModHandler.isAnySteam(aFluidStack)) {
+ flow = Math.min(aFluidStack.amount, remainingFlow); // try to use up to the max flow defined just above
+ depleteInput(new FluidStack(aFluidStack, flow)); // deplete that amount
+ this.storedFluid += aFluidStack.amount;
+ remainingFlow -= flow; // track amount we're allowed to continue depleting from hatches
+ totalFlow += flow; // track total input used
+ if (!achievement) {
+ GT_Mod.achievements.issueAchievement(
+ this.getBaseMetaTileEntity()
+ .getWorld()
+ .getPlayerEntityByName(
+ this.getBaseMetaTileEntity()
+ .getOwnerName()),
+ "muchsteam");
+ achievement = true;
+ }
+ } else if (GT_ModHandler.isSuperHeatedSteam(aFluidStack)) {
+ depleteInput(new FluidStack(aFluidStack, aFluidStack.amount));
+ }
+ }
+ if (totalFlow <= 0) return 0;
+ tEU = totalFlow;
+ int waterToOutput = condenseSteam(totalFlow);
+ addOutput(GT_ModHandler.getDistilledWater(waterToOutput));
+ if (totalFlow == (GT_Utility.safeInt((long) realOptFlow))) {
+ tEU = GT_Utility.safeInt((long) tEU * (long) aBaseEff / 20000L);
+ } else {
+ float efficiency = getOverflowEfficiency(
+ totalFlow,
+ (GT_Utility.safeInt((long) realOptFlow)),
+ overflowMultiplier);
+ tEU *= efficiency;
+ tEU = Math.max(1, GT_Utility.safeInt((long) tEU * (long) aBaseEff / 20000L));
+ }
+
+ // If next output is above the maximum the dynamo can handle, set it to the maximum instead of exploding the
+ // turbine
+ // Raising the maximum allowed flow rate to account for the efficiency changes beyond the optimal flow rate can
+ // explode turbines on world load
+ if (tEU > getMaximumOutput()) {
+ tEU = GT_Utility.safeInt(getMaximumOutput());
+ }
+
+ return tEU;
+ }
+
+ @Override
+ float getOverflowEfficiency(int totalFlow, int actualOptimalFlow, int overflowMultiplier) {
+ // overflowMultiplier changes how quickly the turbine loses efficiency after flow goes beyond the optimal value
+ // At the default value of 1, any flow will generate less EU/t than optimal flow, regardless of the amount of
+ // fuel used
+ // The bigger this number is, the slower efficiency loss happens as flow moves beyond the optimal value
+ // Steam is the least efficient out of all turbine fuels in this regard
+ float efficiency = 0;
+
+ if (totalFlow > actualOptimalFlow) {
+ efficiency = 1.0f
+ - Math.abs((totalFlow - actualOptimalFlow)) / ((float) actualOptimalFlow * (overflowMultiplier + 1));
+ } else {
+ efficiency = 1.0f - Math.abs((totalFlow - actualOptimalFlow) / (float) actualOptimalFlow);
+ }
+
+ return efficiency;
+ }
+
+ public static float[] calculateLooseFlow(float aOptFlow, float aBaseEff) {
+ aOptFlow *= 4f;
+ if (aBaseEff >= 26000f) {
+ aOptFlow = aOptFlow * (float) Math.pow(1.1f, ((aBaseEff - 8000f) / 10000f) * 20f);
+ aBaseEff = aBaseEff * 0.6f;
+ } else if (aBaseEff > 22000f) {
+ aOptFlow = aOptFlow * (float) Math.pow(1.1f, ((aBaseEff - 7000f) / 10000f) * 20f);
+ aBaseEff = aBaseEff * 0.65f;
+ } else if (aBaseEff > 18000f) {
+ aOptFlow = aOptFlow * (float) Math.pow(1.1f, ((aBaseEff - 6000f) / 10000f) * 20f);
+ aBaseEff = aBaseEff * 0.70f;
+ } else if (aBaseEff > 14000f) {
+ aOptFlow = aOptFlow * (float) Math.pow(1.1f, ((aBaseEff - 5000f) / 10000f) * 20f);
+ aBaseEff = aBaseEff * 0.75f;
+ } else if (aBaseEff > 10000f) {
+ aOptFlow = aOptFlow * (float) Math.pow(1.1f, ((aBaseEff - 4000f) / 10000f) * 20f);
+ aBaseEff = aBaseEff * 0.8f;
+ } else if (aBaseEff > 6000f) {
+ aOptFlow = aOptFlow * (float) Math.pow(1.1f, ((aBaseEff - 3000f) / 10000f) * 20f);
+ aBaseEff = aBaseEff * 0.85f;
+ } else {
+ aBaseEff = aBaseEff * 0.9f;
+ }
+
+ if (aBaseEff % 100 != 0) {
+ aBaseEff -= aBaseEff % 100;
+ }
+
+ float[] looseFlow = new float[2];
+ looseFlow[0] = aOptFlow;
+ looseFlow[1] = aBaseEff;
+ return looseFlow;
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ if (side == getBaseMetaTileEntity().getFrontFacing()) {
+ looseFit ^= true;
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ looseFit ? GT_Utility.trans("500", "Fitting: Loose - More Flow")
+ : GT_Utility.trans("501", "Fitting: Tight - More Efficiency"));
+ }
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return (looseFit && XSTR_INSTANCE.nextInt(4) == 0) ? 0 : 1;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ super.looseFit = looseFit;
+ return super.getInfoData();
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setBoolean("turbineFitting", looseFit);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ looseFit = aNBT.getBoolean("turbineFitting");
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_MultiAutoclave.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_MultiAutoclave.java
new file mode 100644
index 0000000000..fdb34b6d25
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_MultiAutoclave.java
@@ -0,0 +1,425 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlocksTiered;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.onElementPass;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.withChannel;
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.Muffler;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.GT_Values.AuthorVolence;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_AUTOCLAVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_AUTOCLAVE_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_AUTOCLAVE_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_AUTOCLAVE_GLOW;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_StructureUtility.ofCoil;
+import static gregtech.api.util.GT_StructureUtility.ofFrame;
+
+import java.text.DecimalFormat;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import org.apache.commons.lang3.tuple.Pair;
+
+import com.google.common.collect.ImmutableList;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.HeatingCoilLevel;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase;
+import gregtech.api.multitileentity.multiblock.casing.Glasses;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.blocks.GT_Block_Casings10;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public class GT_MetaTileEntity_MultiAutoclave extends
+ GT_MetaTileEntity_ExtendedPowerMultiBlockBase<GT_MetaTileEntity_MultiAutoclave> implements ISurvivalConstructable {
+
+ public GT_MetaTileEntity_MultiAutoclave(final int aID, final String aName, final String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_MultiAutoclave(String aName) {
+ super(aName);
+ }
+
+ private HeatingCoilLevel heatLevel;
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+
+ protected int itemPipeTier = 0;
+ protected int fluidPipeTier = 0;
+
+ private static Integer getItemPipeTierFromMeta(Block block, Integer metaID) {
+ if (block != GregTech_API.sBlockCasings11) return -1;
+ if (metaID < 0 || metaID > 7) return -1;
+ return metaID + 1;
+ }
+
+ private void setItemPipeTier(int tier) {
+ itemPipeTier = tier;
+ }
+
+ private int getItemPipeTier() {
+ return itemPipeTier;
+ }
+
+ private static Integer getFluidTierFromMeta(Block block, Integer metaID) {
+ if (block != GregTech_API.sBlockCasings2) return -1;
+ if (metaID < 12 || metaID > 15) return -1;
+ return metaID - 11;
+ }
+
+ private void setFluidPipeTier(int tier) {
+ fluidPipeTier = tier;
+ }
+
+ private int getFluidPipeTier() {
+ return fluidPipeTier;
+ }
+
+ public HeatingCoilLevel getCoilLevel() {
+ return this.heatLevel;
+ }
+
+ public void setCoilLevel(HeatingCoilLevel aCoilLevel) {
+ this.heatLevel = aCoilLevel;
+ }
+
+ public Integer getCoilTier() {
+ return (int) this.getCoilLevel()
+ .getTier() + 1;
+ }
+
+ private static final IStructureDefinition<GT_MetaTileEntity_MultiAutoclave> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_MultiAutoclave>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ transpose(
+ new String[][] {
+ { " AAA ", " AFA ", " AFA ", " AFA ", " AFA ", " AFA ", " AFA ", " AFA ",
+ " AAA " },
+ { " ABBBA ", " A A ", " A A ", " A A ", " A A ", " A A ", " A A ", " A A ",
+ " ABBBA ", },
+ { "ABBBBBA", "A C C A", "A C C A", "A C C A", "A C C A", "A C C A", "A C C A", "A C C A",
+ "ABBBBBA", },
+ { "ABBBBBA", "ACDEDCA", "ACDEDCA", "ACDEDCA", "ACDEDCA", "ACDEDCA", "ACDEDCA", "ACDEDCA",
+ "ABBBBBA", },
+ { "ABBBBBA", "A C C A", "A C C A", "A C C A", "A C C A", "A C C A", "A C C A", "A C C A",
+ "ABBBBBA", },
+ { "AABBBAA", " A A ", " A A ", " A A ", " A A ", " A A ", " A A ", " A A ",
+ "AABBBAA", },
+ { "A A~A A", " AAA ", " AAA ", " AAA ", " AAA ", " AAA ", " AAA ", " AAA ",
+ "A AAA A" } }))
+ .addElement(
+ 'A',
+ buildHatchAdder(GT_MetaTileEntity_MultiAutoclave.class)
+ .atLeast(InputBus, OutputBus, InputHatch, OutputHatch, Maintenance, Muffler, Energy)
+ .casingIndex(((GT_Block_Casings10) GregTech_API.sBlockCasings10).getTextureIndex(3))
+ .dot(1)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_MultiAutoclave::onCasingAdded,
+ ofBlock(GregTech_API.sBlockCasings10, 3))))
+ .addElement('B', Glasses.chainAllGlasses()) // Steel Casings
+ .addElement('C', ofFrame(Materials.Polytetrafluoroethylene)) // PTFE Frame
+ .addElement(
+ 'D',
+ ofBlocksTiered(
+ GT_MetaTileEntity_MultiAutoclave::getFluidTierFromMeta,
+ ImmutableList.of(
+ Pair.of(GregTech_API.sBlockCasings2, 12),
+ Pair.of(GregTech_API.sBlockCasings2, 13),
+ Pair.of(GregTech_API.sBlockCasings2, 14),
+ Pair.of(GregTech_API.sBlockCasings2, 15)),
+ -2,
+ GT_MetaTileEntity_MultiAutoclave::setFluidPipeTier,
+ GT_MetaTileEntity_MultiAutoclave::getFluidPipeTier))
+ .addElement(
+ 'E',
+ ofBlocksTiered(
+ GT_MetaTileEntity_MultiAutoclave::getItemPipeTierFromMeta,
+ ImmutableList.of(
+ Pair.of(GregTech_API.sBlockCasings11, 0),
+ Pair.of(GregTech_API.sBlockCasings11, 1),
+ Pair.of(GregTech_API.sBlockCasings11, 2),
+ Pair.of(GregTech_API.sBlockCasings11, 3),
+ Pair.of(GregTech_API.sBlockCasings11, 4),
+ Pair.of(GregTech_API.sBlockCasings11, 5),
+ Pair.of(GregTech_API.sBlockCasings11, 6),
+ Pair.of(GregTech_API.sBlockCasings11, 7)),
+ -2,
+ GT_MetaTileEntity_MultiAutoclave::setItemPipeTier,
+ GT_MetaTileEntity_MultiAutoclave::getItemPipeTier))
+ .addElement(
+ 'F',
+ withChannel(
+ "coil",
+ ofCoil(GT_MetaTileEntity_MultiAutoclave::setCoilLevel, GT_MetaTileEntity_MultiAutoclave::getCoilLevel)))
+ .build();
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_MultiAutoclave> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Autoclave")
+ .addInfo("Controller Block for the Industrial Autoclave.")
+ .addInfo("Gains 12 parallels per item pipe casing tier.")
+ .addInfo("Each pipe casing (bronze, steel, titanium, tungstensteel)")
+ .addInfo("decreases the EU usageby 1/pipe tier.")
+ .addInfo("Heating Coils increase speed by 1/((tier + 1) / 2).")
+ .addInfo("Needs a minimum of 128 Pressure Containment Casings.")
+ .addInfo(AuthorVolence)
+ .addSeparator()
+ .beginStructureBlock(7, 5, 5, true)
+ .addController("Front Center")
+ .addCasingInfoMin("Pressure Containment Casings", 128, false)
+ .addCasingInfoExactly("Item Pipe Casings", 7, true)
+ .addCasingInfoExactly("Pipe Casings", 14, true)
+ .addCasingInfoExactly("Heating Coils", 7, true)
+ .addCasingInfoExactly("PTFE Frame", 42, false)
+ .addInputBus("Any of the Pressure Containment Casings", 1)
+ .addOutputBus("Any of the Pressure Containment Casings", 1)
+ .addEnergyHatch("Any of the Pressure Containment Casings", 1)
+ .addMaintenanceHatch("Any of the Pressure Containment Casings", 1)
+ .addMufflerHatch("Any of the Pressure Containment Casings", 1)
+ .toolTipFinisher("GregTech");
+ return tt;
+ }
+
+ private int mCasingAmount;
+
+ private void onCasingAdded() {
+ mCasingAmount++;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ fluidPipeTier = -2;
+ itemPipeTier = -2;
+ mCasingAmount = 0;
+ mEnergyHatches.clear();
+ setCoilLevel(HeatingCoilLevel.None);
+ if (!checkPiece(STRUCTURE_PIECE_MAIN, 3, 6, 0)) return false;
+ return this.mMaintenanceHatches.size() == 1 && fluidPipeTier >= 0
+ && mCasingAmount >= 128
+ && itemPipeTier >= 0
+ && mEnergyHatches.size() >= 1
+ && mMufflerHatches.size() == 1;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ ITexture[] rTexture;
+ if (side == aFacing) {
+ if (aActive) {
+ rTexture = new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings10, 3)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_AUTOCLAVE_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_AUTOCLAVE_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ } else {
+ rTexture = new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings10, 3)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_AUTOCLAVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_AUTOCLAVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ } else {
+ rTexture = new ITexture[] { Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings10, 3)) };
+ }
+ return rTexture;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 3, 6, 0);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ int build = survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 3, 6, 0, elementBudget, env, false, true);
+ return build;
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_MultiAutoclave(this.mName);
+ }
+
+ public float euModifier(int fluidPipeTier) {
+ return (float) (12 - fluidPipeTier) / 12;
+ }
+
+ public float speedBoost(int coilTier) {
+ return (float) 1 / (1 + 0.25f * coilTier);
+ }
+
+ public int getMaxParallelRecipes() {
+ return itemPipeTier * 12;
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic() {
+
+ @Override
+ @Nonnull
+ public CheckRecipeResult process() {
+ euModifier = euModifier(fluidPipeTier);
+ speedBoost = speedBoost(getCoilTier());
+ return super.process();
+ }
+ }.setMaxParallelSupplier(this::getMaxParallelRecipes);
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ tag.setInteger("fluidPipeTier", getFluidPipeTier());
+ tag.setInteger("itemPipeTier", getItemPipeTier());
+ tag.setInteger("coilTier", getCoilTier());
+ tag.setFloat("getMaxParallelRecipes", getMaxParallelRecipes());
+ }
+
+ private static final DecimalFormat dfTwo = new DecimalFormat("0.00");
+ private static final DecimalFormat dfNone = new DecimalFormat("#");
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ super.getWailaBody(itemStack, currenttip, accessor, config);
+ NBTTagCompound tag = accessor.getNBTData();
+ currenttip.add(
+ StatCollector.translateToLocal("GT5U.multiblock.fluidPipeTier") + ": "
+ + EnumChatFormatting.WHITE
+ + Math.max(0, tag.getInteger("fluidPipeTier")));
+ currenttip.add(
+ StatCollector.translateToLocal("GT5U.multiblock.euModifier") + ": "
+ + EnumChatFormatting.WHITE
+ + dfTwo.format(Math.max(0, euModifier(tag.getInteger("fluidPipeTier")) * 100))
+ + "%");
+ currenttip.add(
+ StatCollector.translateToLocal("GT5U.multiblock.itemPipeTier") + ": "
+ + EnumChatFormatting.WHITE
+ + Math.max(0, tag.getInteger("itemPipeTier")));
+ currenttip.add(
+ StatCollector.translateToLocal("GT5U.multiblock.parallelism") + ": "
+ + EnumChatFormatting.WHITE
+ + dfNone.format(Math.max(0, tag.getFloat("getMaxParallelRecipes"))));
+ currenttip.add(
+ StatCollector.translateToLocal("GT5U.multiblock.coilLevel") + ": "
+ + EnumChatFormatting.WHITE
+ + Math.max(0, tag.getInteger("coilTier")));
+ currenttip.add(
+ StatCollector.translateToLocal("GT5U.multiblock.speed") + ": "
+ + EnumChatFormatting.WHITE
+ + dfNone.format(Math.max(0, 100 / speedBoost(tag.getInteger("coilTier"))))
+ + "%");
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.autoclaveRecipes;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsBatchMode() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsInputSeparation() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsSingleRecipeLocking() {
+ return true;
+ }
+
+ @Override
+ protected void setProcessingLogicPower(ProcessingLogic logic) {
+ logic.setAvailableVoltage(GT_Utility.roundUpVoltage(this.getMaxInputVoltage()));
+ logic.setAvailableAmperage(1L);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_MultiCanner.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_MultiCanner.java
new file mode 100644
index 0000000000..a622f8db2c
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_MultiCanner.java
@@ -0,0 +1,314 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.*;
+import static gregtech.api.enums.GT_HatchElement.*;
+import static gregtech.api.enums.GT_Values.AuthorFourIsTheNumber;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_CANNER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_CANNER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_CANNER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_CANNER_GLOW;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Textures;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.blocks.GT_Block_Casings2;
+import gtPlusPlus.core.util.minecraft.PlayerUtils;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public class GT_MetaTileEntity_MultiCanner extends
+ GT_MetaTileEntity_ExtendedPowerMultiBlockBase<GT_MetaTileEntity_MultiCanner> implements ISurvivalConstructable {
+
+ private static final int MACHINEMODE_CANNER = 0;
+ private static final int MACHINEMODE_FLUIDCANNER = 1;
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final IStructureDefinition<GT_MetaTileEntity_MultiCanner> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_MultiCanner>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ (transpose(
+ new String[][] { { " AAA ", " AAAAA ", "AAAAAAA", "AAAAAAA", "AAAAAAA", " AAAAA ", " AAA " },
+ { " ", " B B ", " BAAAB ", " A A ", " BAAAB ", " B B ", " " },
+ { " ", " B B ", " BA~AB ", " A A ", " BAAAB ", " B B ", " " },
+ { " ", " B B ", " BAAAB ", " A A ", " BAAAB ", " B B ", " " },
+ { " AAA ", " AAAAA ", "AAAAAAA", "AAAAAAA", "AAAAAAA", " AAAAA ", " AAA " } })))
+ .addElement(
+ 'A',
+ buildHatchAdder(GT_MetaTileEntity_MultiCanner.class)
+ .atLeast(InputBus, OutputBus, Maintenance, Energy, InputHatch, OutputHatch)
+ .casingIndex(((GT_Block_Casings2) GregTech_API.sBlockCasings2).getTextureIndex(0))
+ .dot(1)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_MultiCanner::onCasingAdded,
+ ofBlock(GregTech_API.sBlockCasings2, 0))))
+ .addElement('B', ofBlock(GregTech_API.sBlockCasings2, 13))
+ .build();
+
+ public GT_MetaTileEntity_MultiCanner(final int aID, final String aName, final String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_MultiCanner(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_MultiCanner> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_MultiCanner(this.mName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ ITexture[] rTexture;
+ if (side == aFacing) {
+ if (aActive) {
+ rTexture = new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings2, 0)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_CANNER_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_CANNER_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ } else {
+ rTexture = new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings2, 0)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_CANNER)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_CANNER_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ } else {
+ rTexture = new ITexture[] { Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings2, 0)) };
+ }
+ return rTexture;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Canner/Fluid Canner")
+ .addInfo("Controller Block for the TurboCan Pro")
+ .addInfo("Use screwdriver to switch mode")
+ .addInfo("100% faster than single block machines of the same voltage")
+ .addInfo("Gains 8 parallels per voltage tier")
+ .addInfo(EnumChatFormatting.BLUE + "It's uncanny!")
+ .addInfo(AuthorFourIsTheNumber)
+ .addSeparator()
+ .beginStructureBlock(7, 5, 7, true)
+ .addController("Front Center")
+ .addCasingInfoMin("Solid Steel Machine Casing", 85, false)
+ .addCasingInfoExactly("Steel Pipe Casing", 24, false)
+ .addInputBus("Any Solid Steel Casing", 1)
+ .addOutputBus("Any Solid Steel Casing", 1)
+ .addInputHatch("Any Solid Steel Casing", 1)
+ .addOutputHatch("Any Solid Steel Casing", 1)
+ .addEnergyHatch("Any Solid Steel Casing", 1)
+ .addMaintenanceHatch("Any Solid Steel Casing", 1)
+ .toolTipFinisher("GregTech");
+ return tt;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 3, 2, 2);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 3, 2, 2, elementBudget, env, false, true);
+ }
+
+ private int mCasingAmount;
+
+ private void onCasingAdded() {
+ mCasingAmount++;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ mCasingAmount = 0;
+ mEnergyHatches.clear();
+
+ if (!checkPiece(STRUCTURE_PIECE_MAIN, 3, 2, 2)) return false;
+ if (mCasingAmount < 85) return false;
+
+ // All checks passed!
+ return true;
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic().setSpeedBonus(1F / 2F)
+ .setMaxParallelSupplier(this::getMaxParallelRecipes);
+ }
+
+ public int getMaxParallelRecipes() {
+ return (8 * GT_Utility.getTier(this.getMaxInputVoltage()));
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return (machineMode == MACHINEMODE_FLUIDCANNER) ? RecipeMaps.fluidCannerRecipes : RecipeMaps.cannerRecipes;
+ }
+
+ @Nonnull
+ @Override
+ public Collection<RecipeMap<?>> getAvailableRecipeMaps() {
+ return Arrays.asList(RecipeMaps.fluidCannerRecipes, RecipeMaps.cannerRecipes);
+ }
+
+ @Override
+ public int getRecipeCatalystPriority() {
+ return -10;
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ if (aNBT.hasKey("fluidMode")) {
+ machineMode = aNBT.getBoolean("fluidMode") ? MACHINEMODE_FLUIDCANNER : MACHINEMODE_CANNER;
+ }
+ super.loadNBTData(aNBT);
+ }
+
+ @Override
+ public boolean supportsMachineModeSwitch() {
+ return true;
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ setMachineMode(nextMachineMode());
+ PlayerUtils.messagePlayer(
+ aPlayer,
+ String.format(StatCollector.translateToLocal("GT5U.MULTI_MACHINE_CHANGE"), getMachineModeName()));
+ }
+
+ @Override
+ public void setMachineModeIcons() {
+ machineModeIcons.add(GT_UITextures.OVERLAY_BUTTON_MACHINEMODE_PACKAGER);
+ machineModeIcons.add(GT_UITextures.OVERLAY_BUTTON_MACHINEMODE_LPF_FLUID);
+ }
+
+ @Override
+ public String getMachineModeName() {
+ return StatCollector.translateToLocal("GT5U.MULTI_CANNER.mode." + machineMode);
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ tag.setInteger("mode", machineMode);
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ super.getWailaBody(itemStack, currentTip, accessor, config);
+ final NBTTagCompound tag = accessor.getNBTData();
+ currentTip.add(
+ StatCollector.translateToLocal("GT5U.machines.oreprocessor1") + " "
+ + EnumChatFormatting.WHITE
+ + StatCollector.translateToLocal("GT5U.MULTI_CANNER.mode." + tag.getInteger("mode"))
+ + EnumChatFormatting.RESET);
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsBatchMode() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsInputSeparation() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsSingleRecipeLocking() {
+ return true;
+ }
+
+ @Override
+ protected void setProcessingLogicPower(ProcessingLogic logic) {
+ logic.setAvailableVoltage(GT_Utility.roundUpVoltage(this.getMaxInputVoltage()));
+ logic.setAvailableAmperage(1L);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_MultiFurnace.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_MultiFurnace.java
new file mode 100644
index 0000000000..5d611b270d
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_MultiFurnace.java
@@ -0,0 +1,377 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.Muffler;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_Values.VN;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_SMELTER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_SMELTER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_SMELTER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_SMELTER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.casingTexturePages;
+import static gregtech.api.util.GT_Utility.filterValidMTEs;
+
+import java.util.ArrayList;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.HeatingCoilLevel;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Energy;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Muffler;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_OverclockCalculator;
+import gregtech.api.util.GT_StructureUtility;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_MultiFurnace
+ extends GT_MetaTileEntity_AbstractMultiFurnace<GT_MetaTileEntity_MultiFurnace> implements ISurvivalConstructable {
+
+ private int mLevel = 0;
+ private int mCostDiscount = 1;
+
+ private static final long RECIPE_EUT = 4;
+ private static final int RECIPE_DURATION = 512;
+ private static final int CASING_INDEX = 11;
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final IStructureDefinition<GT_MetaTileEntity_MultiFurnace> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_MultiFurnace>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ transpose(new String[][] { { "ccc", "cmc", "ccc" }, { "CCC", "C-C", "CCC" }, { "b~b", "bbb", "bbb" } }))
+ .addElement('c', ofBlock(GregTech_API.sBlockCasings1, CASING_INDEX))
+ .addElement('m', Muffler.newAny(CASING_INDEX, 2))
+ .addElement(
+ 'C',
+ GT_StructureUtility
+ .ofCoil(GT_MetaTileEntity_MultiFurnace::setCoilLevel, GT_MetaTileEntity_MultiFurnace::getCoilLevel))
+ .addElement(
+ 'b',
+ ofChain(
+ GT_StructureUtility.<GT_MetaTileEntity_MultiFurnace>buildHatchAdder()
+ .atLeast(Maintenance, InputBus, OutputBus, Energy)
+ .casingIndex(CASING_INDEX)
+ .dot(1)
+ .build(),
+ ofBlock(GregTech_API.sBlockCasings1, CASING_INDEX)))
+ .build();
+
+ public GT_MetaTileEntity_MultiFurnace(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_MultiFurnace(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_MultiFurnace(this.mName);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Furnace")
+ .addInfo("Controller Block for the Multi Smelter")
+ .addInfo("Smelts up to 8-8192 items at once")
+ .addInfo("Items smelted increases with coil tier")
+ .addPollutionAmount(getPollutionPerSecond(null))
+ .addSeparator()
+ .beginStructureBlock(3, 3, 3, true)
+ .addController("Front bottom")
+ .addCasingInfoRange("Heat Proof Machine Casing", 8, 14, false)
+ .addOtherStructurePart("Heating Coils", "Middle layer")
+ .addEnergyHatch("Any bottom casing", 1)
+ .addMaintenanceHatch("Any bottom casing", 1)
+ .addMufflerHatch("Top Middle", 2)
+ .addInputBus("Any bottom casing", 1)
+ .addOutputBus("Any bottom casing", 1)
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ if (sideDirection != facingDirection) return new ITexture[] { casingTexturePages[0][CASING_INDEX] };
+ if (active) return new ITexture[] { casingTexturePages[0][CASING_INDEX], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_SMELTER_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_SMELTER_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { casingTexturePages[0][CASING_INDEX], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_SMELTER)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_SMELTER_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.furnaceRecipes;
+ }
+
+ @Override
+ public int getPollutionPerSecond(ItemStack aStack) {
+ return GT_Mod.gregtechproxy.mPollutionMultiSmelterPerSecond;
+ }
+
+ @Override
+ @NotNull
+ public CheckRecipeResult checkProcessing() {
+ ArrayList<ItemStack> tInputList = getAllStoredInputs();
+ if (tInputList.isEmpty()) return CheckRecipeResultRegistry.NO_RECIPE;
+
+ long inputVoltage = getMaxInputVoltage();
+
+ int fakeOriginalMaxParallel = 1;
+ GT_OverclockCalculator calculator = new GT_OverclockCalculator().setEUt(inputVoltage)
+ .setRecipeEUt(RECIPE_EUT)
+ .setDuration(RECIPE_DURATION)
+ .setParallel(fakeOriginalMaxParallel);
+
+ int maxParallel = this.mLevel;
+ int originalMaxParallel = maxParallel;
+ double tickTimeAfterOC = calculator.calculateDurationUnderOneTick();
+ if (tickTimeAfterOC < 1) {
+ maxParallel = GT_Utility.safeInt((long) (maxParallel / tickTimeAfterOC), 0);
+ }
+
+ int maxParallelBeforeBatchMode = maxParallel;
+ if (isBatchModeEnabled()) {
+ maxParallel = GT_Utility.safeInt((long) maxParallel * getMaxBatchSize(), 0);
+ }
+
+ // Calculate parallel
+ int currentParallel = 0;
+ for (ItemStack item : tInputList) {
+ ItemStack smeltedOutput = GT_ModHandler.getSmeltingOutput(item, false, null);
+ if (smeltedOutput != null) {
+ if (item.stackSize <= (maxParallel - currentParallel)) {
+ currentParallel += item.stackSize;
+ } else {
+ currentParallel = maxParallel;
+ break;
+ }
+ }
+ }
+ if (currentParallel <= 0) {
+ return CheckRecipeResultRegistry.NO_RECIPE;
+ }
+ int currentParallelBeforeBatchMode = Math.min(currentParallel, maxParallelBeforeBatchMode);
+ int fakeCurrentParallel = (int) Math.ceil((double) currentParallelBeforeBatchMode / originalMaxParallel);
+
+ calculator.setCurrentParallel(fakeCurrentParallel)
+ .calculate();
+
+ double batchMultiplierMax = 1;
+ // In case batch mode enabled
+ if (currentParallel > maxParallelBeforeBatchMode && calculator.getDuration() < getMaxBatchSize()) {
+ batchMultiplierMax = (double) getMaxBatchSize() / calculator.getDuration();
+ batchMultiplierMax = Math.min(batchMultiplierMax, (double) currentParallel / maxParallelBeforeBatchMode);
+ }
+ int finalParallel = (int) (batchMultiplierMax * maxParallelBeforeBatchMode);
+
+ // Consume inputs and generate outputs
+ ArrayList<ItemStack> smeltedOutputs = new ArrayList<>();
+ int remainingCost = finalParallel;
+ for (ItemStack item : tInputList) {
+ ItemStack smeltedOutput = GT_ModHandler.getSmeltingOutput(item, false, null);
+ if (smeltedOutput != null) {
+ if (remainingCost >= item.stackSize) {
+ remainingCost -= item.stackSize;
+ smeltedOutput.stackSize *= item.stackSize;
+ item.stackSize = 0;
+ smeltedOutputs.add(smeltedOutput);
+ } else {
+ smeltedOutput.stackSize *= remainingCost;
+ item.stackSize -= remainingCost;
+ smeltedOutputs.add(smeltedOutput);
+ break;
+ }
+ }
+ }
+ this.mOutputItems = smeltedOutputs.toArray(new ItemStack[0]);
+
+ this.mEfficiency = 10000 - (getIdealStatus() - getRepairStatus()) * 1000;
+ this.mEfficiencyIncrease = 10000;
+ this.mMaxProgresstime = (int) (calculator.getDuration() * batchMultiplierMax);
+ this.lEUt = calculator.getConsumption();
+
+ if (this.lEUt > 0) this.lEUt = -this.lEUt;
+
+ updateSlots();
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_MultiFurnace> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ this.mLevel = 0;
+ this.mCostDiscount = 1;
+
+ replaceDeprecatedCoils(aBaseMetaTileEntity);
+
+ setCoilLevel(HeatingCoilLevel.None);
+
+ if (!checkPiece(STRUCTURE_PIECE_MAIN, 1, 2, 0)) return false;
+
+ if (getCoilLevel() == HeatingCoilLevel.None) return false;
+
+ if (mMaintenanceHatches.size() != 1) return false;
+
+ if (getCoilLevel().getHeat() < 9000) {
+ this.mLevel = 8 * getCoilLevel().getLevel();
+ } else {
+ this.mLevel = 1 << (getCoilLevel().getTier());
+ }
+ this.mCostDiscount = getCoilLevel().getCostDiscount();
+ return true;
+ }
+
+ private void replaceDeprecatedCoils(IGregTechTileEntity aBaseMetaTileEntity) {
+ final int xDir = aBaseMetaTileEntity.getBackFacing().offsetX;
+ final int zDir = aBaseMetaTileEntity.getBackFacing().offsetZ;
+ final int tX = aBaseMetaTileEntity.getXCoord() + xDir;
+ final int tY = aBaseMetaTileEntity.getYCoord();
+ final int tZ = aBaseMetaTileEntity.getZCoord() + zDir;
+ int tUsedMeta;
+ for (int xPos = tX - 1; xPos <= tX + 1; xPos++) for (int zPos = tZ - 1; zPos <= tZ + 1; zPos++) {
+ if ((xPos == tX) && (zPos == tZ)) continue;
+ tUsedMeta = aBaseMetaTileEntity.getMetaID(xPos, tY + 1, zPos);
+ if (tUsedMeta >= 12 && tUsedMeta <= 14
+ && aBaseMetaTileEntity.getBlock(xPos, tY + 1, zPos) == GregTech_API.sBlockCasings1)
+ aBaseMetaTileEntity.getWorld()
+ .setBlock(xPos, tY + 1, zPos, GregTech_API.sBlockCasings5, tUsedMeta - 12, 3);
+ }
+ }
+
+ @Override
+ public String[] getInfoData() {
+ int mPollutionReduction = 0;
+ for (final GT_MetaTileEntity_Hatch_Muffler tHatch : filterValidMTEs(mMufflerHatches))
+ mPollutionReduction = Math.max(tHatch.calculatePollutionReduction(100), mPollutionReduction);
+
+ long storedEnergy = 0;
+ long maxEnergy = 0;
+ for (final GT_MetaTileEntity_Hatch_Energy tHatch : filterValidMTEs(mEnergyHatches)) {
+ storedEnergy += tHatch.getBaseMetaTileEntity()
+ .getStoredEU();
+ maxEnergy += tHatch.getBaseMetaTileEntity()
+ .getEUCapacity();
+ }
+
+ return new String[] {
+ StatCollector.translateToLocal("GT5U.multiblock.Progress") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(mProgresstime / 20)
+ + EnumChatFormatting.RESET
+ + " s / "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(mMaxProgresstime / 20)
+ + EnumChatFormatting.RESET
+ + " s",
+ StatCollector.translateToLocal("GT5U.multiblock.energy") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(storedEnergy)
+ + EnumChatFormatting.RESET
+ + " EU / "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(maxEnergy)
+ + EnumChatFormatting.RESET
+ + " EU",
+ StatCollector.translateToLocal("GT5U.multiblock.usage") + ": "
+ + EnumChatFormatting.RED
+ + GT_Utility.formatNumbers(-lEUt)
+ + EnumChatFormatting.RESET
+ + " EU/t",
+ StatCollector.translateToLocal("GT5U.multiblock.mei") + ": "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(getMaxInputVoltage())
+ + EnumChatFormatting.RESET
+ + " EU/t(*2A) "
+ + StatCollector.translateToLocal("GT5U.machines.tier")
+ + ": "
+ + EnumChatFormatting.YELLOW
+ + VN[GT_Utility.getTier(getMaxInputVoltage())]
+ + EnumChatFormatting.RESET,
+ StatCollector.translateToLocal("GT5U.multiblock.problems") + ": "
+ + EnumChatFormatting.RED
+ + (getIdealStatus() - getRepairStatus())
+ + EnumChatFormatting.RESET
+ + " "
+ + StatCollector.translateToLocal("GT5U.multiblock.efficiency")
+ + ": "
+ + EnumChatFormatting.YELLOW
+ + mEfficiency / 100.0F
+ + EnumChatFormatting.RESET
+ + " %",
+ StatCollector.translateToLocal("GT5U.MS.multismelting") + ": "
+ + EnumChatFormatting.GREEN
+ + mLevel
+ + EnumChatFormatting.RESET
+ + " Discount: (EU/t) / "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(mCostDiscount)
+ + EnumChatFormatting.RESET,
+ StatCollector.translateToLocal("GT5U.multiblock.pollution") + ": "
+ + EnumChatFormatting.GREEN
+ + mPollutionReduction
+ + EnumChatFormatting.RESET
+ + " %" };
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 1, 2, 0);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 1, 2, 0, elementBudget, env, false, true);
+ }
+
+ @Override
+ public boolean supportsBatchMode() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_MultiLathe.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_MultiLathe.java
new file mode 100644
index 0000000000..dc15d89c70
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_MultiLathe.java
@@ -0,0 +1,377 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlocksTiered;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.onElementPass;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.Muffler;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_Values.AuthorVolence;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_LATHE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_LATHE_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_LATHE_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_LATHE_GLOW;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+
+import java.text.DecimalFormat;
+import java.util.List;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import org.apache.commons.lang3.tuple.Pair;
+
+import com.google.common.collect.ImmutableList;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.GregTechTileClientEvents;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_EnhancedMultiBlockBase;
+import gregtech.api.multitileentity.multiblock.casing.Glasses;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.blocks.GT_Block_Casings2;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public class GT_MetaTileEntity_MultiLathe extends GT_MetaTileEntity_EnhancedMultiBlockBase<GT_MetaTileEntity_MultiLathe>
+ implements ISurvivalConstructable {
+
+ public GT_MetaTileEntity_MultiLathe(final int aID, final String aName, final String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_MultiLathe(String aName) {
+ super(aName);
+ }
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final String STRUCTURE_PIECE_BODY = "body";
+ private static final String STRUCTURE_PIECE_BODY_ALT = "body_alt";
+
+ protected int pipeTier = 0;
+
+ public enum PipeTiers {
+
+ Platinum(4, 1F),
+ Osmium(8, 1.25F),
+ Quantium(12, 1.5F),
+ FluxedElectrum(16, 2F),
+ BlackPlutonium(32, 2.5F);
+
+ final int maxParallel;
+ final float speedBoost;
+
+ PipeTiers(int maxParallel, float speedBoost) {
+ this.maxParallel = maxParallel;
+ this.speedBoost = speedBoost;
+ }
+ }
+
+ private PipeTiers getPipeData() {
+ pipeTier = getPipeTier();
+ return switch (pipeTier) {
+ case 2 -> PipeTiers.Osmium;
+ case 3 -> PipeTiers.Quantium;
+ case 4 -> PipeTiers.FluxedElectrum;
+ case 5 -> PipeTiers.BlackPlutonium;
+ default -> PipeTiers.Platinum;
+ };
+ }
+
+ // get tier from block meta
+ private static Integer getTierFromMeta(Block block, Integer metaID) {
+ if (block != GregTech_API.sBlockCasings11) return -1;
+ if (metaID < 3 || metaID > 7) return -1;
+ return (metaID - 2);
+ }
+
+ private void setPipeTier(int tier) {
+ pipeTier = tier;
+ }
+
+ private int getPipeTier() {
+ return pipeTier;
+ }
+
+ private static final IStructureDefinition<GT_MetaTileEntity_MultiLathe> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_MultiLathe>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ transpose(new String[][] { { " " }, { " " }, { " " }, { " " }, { "AAA~AAA" } }))
+ .addShape(
+ STRUCTURE_PIECE_BODY,
+ (transpose(
+ new String[][] { { " ", "AAAAAAA", " ", " " },
+ { "DBCCCCD", "DBCCCCD", "DBCCCCD", " " }, { "DBCCCCD", "DBFFFFD", "DBCCCCD", " " },
+ { "DBCCCCD", "DBCCCCD", "DBCCCCD", " " }, { "AAAAAAA", "AAAAAAA", "AAAAAAA", "AAAAAAA" } })))
+ .addShape(
+ STRUCTURE_PIECE_BODY_ALT,
+ (transpose(
+ new String[][] { { " ", "AAAAAAA", " ", " " },
+ { "DCCCCBD", "DCCCCBD", "DCCCCBD", " " }, { "DCCCCBD", "DFFFFBD", "DCCCCBD", " " },
+ { "DCCCCBD", "DCCCCBD", "DCCCCBD", " " }, { "AAAAAAA", "AAAAAAA", "AAAAAAA", "AAAAAAA" } })))
+ .addElement(
+ 'A',
+ buildHatchAdder(GT_MetaTileEntity_MultiLathe.class).atLeast(Maintenance, Muffler, Energy)
+ .casingIndex(((GT_Block_Casings2) GregTech_API.sBlockCasings2).getTextureIndex(0))
+ .dot(1)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_MultiLathe::onCasingAdded,
+ ofBlock(GregTech_API.sBlockCasings2, 0))))
+ .addElement('B', ofBlock(GregTech_API.sBlockCasings3, 10)) // Steel Casings
+ .addElement('C', Glasses.chainAllGlasses()) // Glass
+ .addElement(
+ 'D',
+ buildHatchAdder(GT_MetaTileEntity_MultiLathe.class)
+ .atLeast(InputBus, OutputBus, Maintenance, Muffler, Energy)
+ .casingIndex(((GT_Block_Casings2) GregTech_API.sBlockCasings2).getTextureIndex(0))
+ .dot(1)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_MultiLathe::onCasingAdded,
+ ofBlock(GregTech_API.sBlockCasings2, 0))))
+ .addElement(
+ 'F',
+ ofBlocksTiered(
+ GT_MetaTileEntity_MultiLathe::getTierFromMeta,
+ ImmutableList.of(
+ Pair.of(GregTech_API.sBlockCasings11, 3),
+ Pair.of(GregTech_API.sBlockCasings11, 4),
+ Pair.of(GregTech_API.sBlockCasings11, 5),
+ Pair.of(GregTech_API.sBlockCasings11, 6),
+ Pair.of(GregTech_API.sBlockCasings11, 7)),
+ -2,
+ GT_MetaTileEntity_MultiLathe::setPipeTier,
+ GT_MetaTileEntity_MultiLathe::getPipeTier))
+ .build();
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_MultiLathe> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_MultiLathe(this.mName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ ITexture[] rTexture;
+ if (side == aFacing) {
+ if (aActive) {
+ rTexture = new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings2, 0)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_LATHE_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_LATHE_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ } else {
+ rTexture = new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings2, 0)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_LATHE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_LATHE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ } else {
+ rTexture = new ITexture[] { Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings2, 0)) };
+ }
+ return rTexture;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Lathe")
+ .addInfo("Controller Block for the Industrial Precision Lathe")
+ .addInfo("Gains 2 parallels per voltage tier,")
+ .addInfo("and 4 parallels per pipe casing tier (16 for Black Plutonium)")
+ .addInfo("Better pipe casings increase speed")
+ .addInfo("Only uses 80% of the recipe's required energy")
+ .addInfo(AuthorVolence)
+ .addSeparator()
+ .beginStructureBlock(7, 5, 5, true)
+ .addController("Front Center")
+ .addCasingInfoMin("Solid Steel Machine Casing", 36, false)
+ .addCasingInfoExactly("Steel Pipe Casing", 8, false)
+ .addInputBus("Any of the 9 Solid Steel Casing at Each End", 1)
+ .addOutputBus("Any of the 9 Solid Steel Casing at Each End", 1)
+ .addEnergyHatch("Any Solid Steel Casing", 1)
+ .addMaintenanceHatch("Any Solid Steel Casing", 1)
+ .addMufflerHatch("Any Solid Steel Casing", 1)
+ .addOtherStructurePart("4 Item Pipe Casings", "Center of the glass", 4)
+ .toolTipFinisher("GregTech");
+ return tt;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 3, 4, 0);
+ buildPiece(STRUCTURE_PIECE_BODY, stackSize, hintsOnly, 3, 4, -1);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ int build = survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 3, 4, 0, elementBudget, env, false, true);
+ if (build >= 0) return build;
+ build = survivialBuildPiece(STRUCTURE_PIECE_BODY, stackSize, 3, 4, -1, elementBudget, env, false, true);
+ if (build >= 0) return build;
+ build = survivialBuildPiece(STRUCTURE_PIECE_BODY_ALT, stackSize, 3, 4, -1, elementBudget, env, false, true);
+ return build;
+ }
+
+ private int mCasingAmount;
+
+ private void onCasingAdded() {
+ mCasingAmount++;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ pipeTier = -2;
+ mEnergyHatches.clear();
+ if (!checkPiece(STRUCTURE_PIECE_MAIN, 3, 4, 0)) return false;
+ getBaseMetaTileEntity().sendBlockEvent(GregTechTileClientEvents.CHANGE_CUSTOM_DATA, getUpdateData());
+ if (!checkPiece(STRUCTURE_PIECE_BODY, 3, 4, -1) && !checkPiece(STRUCTURE_PIECE_BODY_ALT, 3, 4, -1))
+ return false;
+ return this.mMaintenanceHatches.size() == 1 && pipeTier >= -1
+ && mEnergyHatches.size() >= 1
+ && mInputBusses.size() >= 1
+ && mMufflerHatches.size() == 1;
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic()
+ .setSpeedBonus(1F / (getPipeData().speedBoost + GT_Utility.getTier(this.getMaxInputVoltage()) / 4F))
+ .setEuModifier(0.8F)
+ .setMaxParallelSupplier(this::getMaxParallelRecipes);
+ }
+
+ public int getMaxParallelRecipes() {
+ return getPipeData().maxParallel + (GT_Utility.getTier(this.getMaxInputVoltage()) * 2);
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ tag.setInteger("itemPipeTier", Math.max(0, pipeTier));
+ tag.setFloat(
+ "speedBonus",
+ Math.max(0, 100 / (1F / (getPipeData().speedBoost + GT_Utility.getTier(this.getMaxInputVoltage()) / 4F))));
+ tag.setFloat("getMaxParallelRecipes", Math.max(0, getMaxParallelRecipes()));
+ }
+
+ private static final DecimalFormat dfNone = new DecimalFormat("#");
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ super.getWailaBody(itemStack, currenttip, accessor, config);
+ NBTTagCompound tag = accessor.getNBTData();
+ currenttip.add(
+ StatCollector.translateToLocal("GT5U.multiblock.itemPipeTier") + ": "
+ + EnumChatFormatting.WHITE
+ + Math.max(0, tag.getInteger("itemPipeTier")));
+ currenttip.add(
+ StatCollector.translateToLocal("GT5U.multiblock.parallelism") + ": "
+ + EnumChatFormatting.WHITE
+ + dfNone.format(Math.max(0, tag.getFloat("getMaxParallelRecipes"))));
+ currenttip.add(
+ StatCollector.translateToLocal("GT5U.multiblock.speed") + ": "
+ + EnumChatFormatting.WHITE
+ + dfNone.format(Math.max(0, tag.getFloat("speedBonus")))
+ + "%");
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.latheRecipes;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsBatchMode() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsInputSeparation() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsSingleRecipeLocking() {
+ return true;
+ }
+
+ @Override
+ protected void setProcessingLogicPower(ProcessingLogic logic) {
+ logic.setAvailableVoltage(GT_Utility.roundUpVoltage(this.getMaxInputVoltage()));
+ logic.setAvailableAmperage(1L);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_NanoForge.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_NanoForge.java
new file mode 100644
index 0000000000..adceabd70c
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_NanoForge.java
@@ -0,0 +1,467 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.ExoticEnergy;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_Values.AuthorBlueWeabo;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ASSEMBLY_LINE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ASSEMBLY_LINE_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ASSEMBLY_LINE_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ASSEMBLY_LINE_GLOW;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_StructureUtility.ofFrame;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+import com.gtnewhorizons.modularui.common.widget.DynamicPositionedColumn;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.MaterialsUEVplus;
+import gregtech.api.enums.Textures.BlockIcons;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_OverclockCalculator;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.blocks.GT_Block_Casings8;
+
+public class GT_MetaTileEntity_NanoForge extends
+ GT_MetaTileEntity_ExtendedPowerMultiBlockBase<GT_MetaTileEntity_NanoForge> implements ISurvivalConstructable {
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final String STRUCTURE_PIECE_TIER2 = "tier2";
+ private static final String STRUCTURE_PIECE_TIER3 = "tier3";
+ private static final IStructureDefinition<GT_MetaTileEntity_NanoForge> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_NanoForge>builder()
+ // spotless:off
+ .addShape(STRUCTURE_PIECE_MAIN, transpose(new String[][] {
+ {" "," "," F "," C "," C "," C "," C "," F "," "," "},
+ {" "," "," F "," C "," C "," C "," C "," F "," "," "},
+ {" "," "," F "," C "," C "," C "," C "," F "," "," "},
+ {" "," "," F "," C "," C "," C "," C "," F "," "," "},
+ {" "," "," F "," C "," C "," C "," C "," F "," "," "},
+ {" "," "," FCF "," C C "," C C "," C C "," C C "," FCF "," "," "},
+ {" "," "," FCF "," C C "," C C "," C C "," C C "," FCF "," "," "},
+ {" "," "," FCF "," C C "," C C "," C C "," C C "," FCF "," "," "},
+ {" "," "," FCF "," C C "," C C "," C C "," C C "," FCF "," "," "},
+ {" "," "," FCF "," C C "," C C "," C C "," C C "," FCF "," "," "},
+ {" "," "," FCF "," C C "," C C "," C C "," C C "," FCF "," "," "},
+ {" "," "," FCF "," C C "," C C "," C C "," C C "," FCF "," "," "},
+ {" "," "," FCF "," C C "," C C "," C C "," C C "," FCF "," "," "},
+ {" "," "," FCF "," C C "," C C "," C C "," C C "," FCF "," "," "},
+ {" "," FCF "," FC CF "," C C "," C C "," C C "," C C "," FC CF "," FCF "," "},
+ {" "," FCF "," FC CF "," C C "," C C "," C C "," C C "," FC CF "," FCF "," "},
+ {" "," FCF "," FC CF "," C C "," C C "," C C "," C C "," FC CF "," FCF "," "},
+ {" "," FCF "," FC CF "," C C "," C C "," C C "," C C "," FC CF "," FCF "," "},
+ {" C "," FCF "," CC CC "," C C "," C C "," C C "," C C "," CC CC "," FCF "," C "},
+ {" C "," FCF "," CC CC "," C C "," C C "," C C "," C C "," CC CC "," FCF "," C "},
+ {" C "," FCF "," CC CC "," C C "," C C "," C C "," C C "," CC CC "," FCF "," C "},
+ {" C "," FCF "," CC CC "," CC CC "," CC CC "," CC CC "," CC CC "," CC CC "," FCF "," C "},
+ {" C "," FCF "," CC CC "," CC CC "," CC CC "," CC CC "," CC CC "," CC CC "," FCF "," C "},
+ {" C "," FCF "," CC CC "," C C "," C C "," C C "," C C "," CC CC "," FCF "," C "},
+ {" C "," FCF "," CC CC "," C C "," C C "," C C "," C C "," CC CC "," FCF "," C "},
+ {" C "," FCF "," CC CC "," C C "," C C "," C C "," C C "," CC CC "," FCF "," C "},
+ {" "," FCF "," FC CF "," C C "," C C "," C C "," C C "," FC CF "," FCF "," "},
+ {" "," FCF "," FC CF "," C C "," C C "," C C "," C C "," FC CF "," FCF "," "},
+ {" "," FCF "," FC CF "," C C "," C C "," C C "," C C "," FC CF "," FCF "," "},
+ {" "," FCF "," FC CF "," C C "," C C "," C C "," C C "," FC CF "," FCF "," "},
+ {" "," "," FCF "," C C "," C C "," C C "," C C "," FCF "," "," "},
+ {" "," "," FCF "," C C "," C C "," C C "," C C "," FCF "," "," "},
+ {" "," "," FCF "," C C "," C C "," C C "," C C "," FCF "," "," "},
+ {" "," "," FCF "," C C "," C C "," C C "," C C "," FCF "," "," "},
+ {" "," "," FCF "," C C "," C C "," C C "," C C "," FCF "," "," "},
+ {" "," "," FCF "," C C "," C C "," C C "," C C "," FCF "," "," "},
+ {" "," "," FCF "," C C "," C C "," C C "," C C "," FCF "," "," "},
+ {" "," BB~BB "," BBBBBBB ","BBBBBBBBB","BBBBBBBBB","BBBBBBBBB","BBBBBBBBB"," BBBBBBB "," BBBBB "," "}
+ }))
+ .addShape(STRUCTURE_PIECE_TIER2, transpose(new String[][] {
+ {" ", " ", " CC ", " CCCC ", " CCCC ", " CC ", " ", " "},
+ {" ", " ", " AA ", " ACCA ", " ACCA ", " AA ", " ", " "},
+ {" ", " ", " CC ", " CCCC ", " CCCC ", " CC ", " ", " "},
+ {" ", " ", " ", " CC ", " CC ", " ", " ", " "},
+ {" ", " ", " ", " CC ", " CC ", " ", " ", " "},
+ {" ", " ", " ", " CC ", " CC ", " ", " ", " "},
+ {" ", " ", " ", " CC ", " CC ", " ", " ", " "},
+ {" ", " ", " CC ", " CCCC ", " CCCC ", " CC ", " ", " "},
+ {" ", " ", " AA ", " ACCA ", " ACCA ", " AA ", " ", " "},
+ {" ", " ", " CC ", " CCCC ", " CCCC ", " CC ", " ", " "},
+ {" ", " ", " ", " CC ", " CC ", " ", " ", " "},
+ {" ", " ", " ", " CC ", " CC ", " ", " ", " "},
+ {" ", " ", " ", " CC ", " CC ", " ", " ", " "},
+ {" ", " ", " ", " CC ", " CC ", " ", " ", " "},
+ {" CCCCCC ", "CCCCCCCC", "CCCCCCCC", "CCCCCCCC", "CCCCCCCC", "CCCCCCCC", "CCCCCCCC", " CCCCCC "}
+ }))
+ .addShape(STRUCTURE_PIECE_TIER3, transpose(new String[][] {
+ {" ", " ", " CC ", " CCCC ", " CCCC ", " CC ", " ", " "},
+ {" ", " ", " FFAA ", " ACCA ", " ACCA ", " AAFF ", " ", " "},
+ {" ", " ", "F CC ", "F CCCC ", " CCCC F", " CC F", " ", " "},
+ {" ", " ", " F", " CC F", "F CC ", "F ", " ", " "},
+ {" ", " F ", " ", " CC ", " CC ", " ", " F ", " "},
+ {" FF ", " ", " ", " CC ", " CC ", " ", " ", " FF "},
+ {" FF ", " ", " ", " CC ", " CC ", " ", " ", " FF "},
+ {" ", " F ", " ", " CC ", " CC ", " ", " F ", " "},
+ {" ", " ", "F ", "F CC ", " CC F", " F", " ", " "},
+ {" ", " ", " CC F", " CCCC F", "F CCCC ", "F CC ", " ", " "},
+ {" ", " F ", " CC ", " CCCC ", " CCCC ", " CC ", " F ", " "},
+ {" FF ", " ", " CC ", " CCCC ", " CCCC ", " CC ", " ", " FF "},
+ {" FF ", " ", " CC ", " CCCC ", " CCCC ", " CC ", " ", " FF "},
+ {" ", " F ", " ", " CC ", " CC ", " ", " F ", " "},
+ {" ", " ", "F ", "F CC ", " CC F", " F", " ", " "},
+ {" ", " ", " F", " CC F", "F CC ", "F ", " ", " "},
+ {" ", " F ", " ", " CC ", " CC ", " ", " F ", " "},
+ {" FF ", " ", " ", " CC ", " CC ", " ", " ", " FF "},
+ {" FF ", " ", " ", " CC ", " CC ", " ", " ", " FF "},
+ {" ", " F ", " ", " CC ", " CC ", " ", " F ", " "},
+ {" ", " ", "F CC ", "F CCCC ", " CCCC F", " CC F", " ", " "},
+ {" ", " ", " AA F", " ACCA F", "F ACCA ", "F AA ", " ", " "},
+ {" ", " F ", " CC ", " CCCC ", " CCCC ", " CC ", " F ", " "},
+ {" FF ", " ", " ", " CC ", " CC ", " ", " ", " FF "},
+ {" FF ", " ", " ", " CC ", " CC ", " ", " ", " FF "},
+ {" ", " F ", " ", " CC ", " CC ", " ", " F ", " "},
+ {" CCCCCC ", "CCCCCCCC", "CCCCCCCC", "CCCCCCCC", "CCCCCCCC", "CCCCCCCC", "CCCCCCCC", " CCCCCC "}
+ }))
+ //spotless:on
+ .addElement('F', ofFrame(Materials.StellarAlloy))
+ .addElement('C', ofBlock(GregTech_API.sBlockCasings8, 10))
+ .addElement('A', ofBlock(GregTech_API.sBlockCasings2, 5))
+ .addElement(
+ 'B',
+ buildHatchAdder(GT_MetaTileEntity_NanoForge.class)
+ .atLeast(InputHatch, OutputBus, InputBus, Maintenance, Energy.or(ExoticEnergy))
+ .dot(1)
+ .casingIndex(((GT_Block_Casings8) GregTech_API.sBlockCasings8).getTextureIndex(10))
+ .buildAndChain(GregTech_API.sBlockCasings8, 10))
+ .build();
+ private byte mSpecialTier = 0;
+
+ public GT_MetaTileEntity_NanoForge(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_NanoForge(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.nanoForgeRecipes;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 4, 37, 1);
+ if (stackSize.stackSize > 1) {
+ buildPiece(STRUCTURE_PIECE_TIER2, stackSize, hintsOnly, -7, 14, 4);
+ }
+ if (stackSize.stackSize > 2) {
+ buildPiece(STRUCTURE_PIECE_TIER3, stackSize, hintsOnly, 14, 26, 4);
+ }
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_NanoForge(this.mName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ if (sideDirection == facingDirection) {
+ if (active) return new ITexture[] {
+ BlockIcons.getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings8, 10)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ASSEMBLY_LINE_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ASSEMBLY_LINE_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] {
+ BlockIcons.getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings8, 10)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ASSEMBLY_LINE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ASSEMBLY_LINE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] {
+ BlockIcons.getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings8, 10)) };
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_NanoForge> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic() {
+
+ @Override
+ protected @Nonnull CheckRecipeResult validateRecipe(@Nonnull GT_Recipe recipe) {
+ return recipe.mSpecialValue <= mSpecialTier ? CheckRecipeResultRegistry.SUCCESSFUL
+ : CheckRecipeResultRegistry.NO_RECIPE;
+ }
+
+ @Nonnull
+ @Override
+ protected GT_OverclockCalculator createOverclockCalculator(@Nonnull GT_Recipe recipe) {
+ return super.createOverclockCalculator(recipe)
+ .setDurationDecreasePerOC(mSpecialTier > recipe.mSpecialValue ? 4.0 : 2.0);
+ }
+ };
+ }
+
+ @Override
+ protected void setProcessingLogicPower(ProcessingLogic logic) {
+ logic.setAvailableVoltage(getMaxInputEu());
+ logic.setAvailableAmperage(1);
+ logic.setAmperageOC(false);
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ if (aBaseMetaTileEntity.isServerSide()) {
+ // TODO: Look for proper fix
+ // Updates every 10 sec
+ if (mUpdate <= -150) mUpdate = 50;
+ }
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ mSpecialTier = 0;
+ if (checkPiece(STRUCTURE_PIECE_MAIN, 4, 37, 1) && aStack != null) {
+ if (aStack.isItemEqual(Materials.Carbon.getNanite(1))) {
+ mSpecialTier = 1;
+ }
+
+ if (aStack.isItemEqual(Materials.Neutronium.getNanite(1)) && checkPiece(STRUCTURE_PIECE_TIER2, -7, 14, 4)) {
+ mSpecialTier = 2;
+ }
+
+ if (aStack.isItemEqual(MaterialsUEVplus.TranscendentMetal.getNanite(1))
+ && checkPiece(STRUCTURE_PIECE_TIER2, -7, 14, 4)
+ && checkPiece(STRUCTURE_PIECE_TIER3, 14, 26, 4)) {
+ mSpecialTier = 3;
+ }
+ }
+
+ if (mMaintenanceHatches.size() != 1) {
+ return false;
+ }
+
+ if (!checkExoticAndNormalEnergyHatches()) {
+ return false;
+ }
+
+ return mSpecialTier > 0;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ int built = survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 4, 37, 1, elementBudget, env, false, true);
+ if (built >= 0) return built;
+ if (stackSize.stackSize > 1) {
+ built += survivialBuildPiece(STRUCTURE_PIECE_TIER2, stackSize, -7, 14, 4, elementBudget, env, false, true);
+ }
+ if (stackSize.stackSize > 2) {
+ built += survivialBuildPiece(STRUCTURE_PIECE_TIER3, stackSize, 14, 26, 4, elementBudget, env, false, true);
+ }
+ return built;
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setByte("mSpecialTier", mSpecialTier);
+ }
+
+ @Override
+ public void loadNBTData(final NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ if (aNBT.hasKey("mSeparate")) {
+ // backward compatibility
+ inputSeparation = aNBT.getBoolean("mSeparate");
+ }
+ mSpecialTier = aNBT.getByte("mSpecialTier");
+ }
+
+ @Override
+ protected void drawTexts(DynamicPositionedColumn screenElements, SlotWidget inventorySlot) {
+ super.drawTexts(screenElements, inventorySlot);
+ screenElements
+ .widget(
+ new TextWidget(StatCollector.translateToLocal("GT5U.gui.button.tier") + " " + mSpecialTier)
+ .setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setEnabled(widget -> getBaseMetaTileEntity().getErrorDisplayID() == 0))
+ .widget(
+ new FakeSyncWidget.IntegerSyncer(
+ () -> (int) mSpecialTier,
+ val -> mSpecialTier = (byte) (val % Byte.MAX_VALUE)));
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Nanite Fabricator")
+ .addInfo("Controller block for the Nano Forge")
+ .addInfo("Requires insane amounts of power to create nanites. Each tier")
+ .addInfo("the multi gains a new building next to it. The nanite in the")
+ .addInfo("controller slot controls the tier.")
+ .addInfo("--------------------------------------------")
+ .addInfo("Requires a Carbon Nanite to use tier " + EnumChatFormatting.DARK_PURPLE + 1)
+ .addInfo("Requires a Neutronium Nanite to use tier " + EnumChatFormatting.DARK_PURPLE + 2)
+ .addInfo("Requires a Transcendent Metal Nanite to use tier " + EnumChatFormatting.DARK_PURPLE + 3)
+ .addInfo("--------------------------------------------")
+ .addInfo("If a recipe's tier is lower than the tier of the Nano Forge")
+ .addInfo("it gains " + EnumChatFormatting.RED + "perfect overclock" + EnumChatFormatting.GRAY + ".")
+ .addInfo(AuthorBlueWeabo)
+ .addSeparator()
+ .beginStructureBlock(30, 38, 13, false)
+ .addStructureInfo("Nano Forge Structure is too complex! See schematic for details.")
+ .addStructureInfo("--------------------------------------------")
+ .addStructureInfo("Tier " + EnumChatFormatting.DARK_PURPLE + 1 + EnumChatFormatting.GRAY)
+ .addStructureInfo(
+ EnumChatFormatting.GOLD + "527" + EnumChatFormatting.GRAY + " Radiant Naquadah Alloy Casing")
+ .addStructureInfo(EnumChatFormatting.GOLD + "171" + EnumChatFormatting.GRAY + " Stellar Alloy Frame Box")
+ .addStructureInfo("--------------------------------------------")
+ .addStructureInfo("Tier " + EnumChatFormatting.DARK_PURPLE + 2 + EnumChatFormatting.GRAY)
+ .addStructureInfo(
+ EnumChatFormatting.GOLD + "148" + EnumChatFormatting.GRAY + " Radiant Naquadah Alloy Casing")
+ .addStructureInfo(EnumChatFormatting.GOLD + "16" + EnumChatFormatting.GRAY + " Assembling Line Casing")
+ .addStructureInfo("--------------------------------------------")
+ .addStructureInfo("Tier " + EnumChatFormatting.DARK_PURPLE + 3 + EnumChatFormatting.GRAY)
+ .addStructureInfo(
+ EnumChatFormatting.GOLD + "228" + EnumChatFormatting.GRAY + " Radiant Naquadah Alloy Casing")
+ .addStructureInfo(EnumChatFormatting.GOLD + "84" + EnumChatFormatting.GRAY + " Stellar Alloy Frame Box")
+ .addStructureInfo(EnumChatFormatting.GOLD + "16" + EnumChatFormatting.GRAY + " Assembling Line Casing")
+ .addStructureInfo("--------------------------------------------")
+ .addStructureInfo(
+ "Requires " + EnumChatFormatting.GOLD
+ + "1"
+ + EnumChatFormatting.GRAY
+ + "-"
+ + EnumChatFormatting.GOLD
+ + "2"
+ + EnumChatFormatting.GRAY
+ + " energy hatches or "
+ + EnumChatFormatting.GOLD
+ + "1"
+ + EnumChatFormatting.GRAY
+ + " TT energy hatch.")
+ .addStructureInfo(
+ "Requires " + EnumChatFormatting.GOLD + "1" + EnumChatFormatting.GRAY + " maintenance hatch.")
+ .addStructureInfo(
+ "Requires " + EnumChatFormatting.GOLD
+ + 1
+ + EnumChatFormatting.GRAY
+ + "+"
+ + EnumChatFormatting.GRAY
+ + " input hatches.")
+ .addStructureInfo(
+ "Requires " + EnumChatFormatting.GOLD
+ + 0
+ + EnumChatFormatting.GRAY
+ + "+"
+ + EnumChatFormatting.GRAY
+ + " output hatches.")
+ .addStructureInfo(
+ "Requires " + EnumChatFormatting.GOLD
+ + 1
+ + EnumChatFormatting.GRAY
+ + "+"
+ + EnumChatFormatting.GRAY
+ + " input busses.")
+ .addStructureInfo(
+ "Requires " + EnumChatFormatting.GOLD
+ + 1
+ + EnumChatFormatting.GRAY
+ + "+"
+ + EnumChatFormatting.GRAY
+ + " output busses.")
+ .addStructureInfo("--------------------------------------------")
+ .toolTipFinisher("GregTech");
+ return tt;
+ }
+
+ @Override
+ public final void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ inputSeparation = !inputSeparation;
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ StatCollector.translateToLocal("GT5U.machines.separatebus") + " " + inputSeparation);
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsInputSeparation() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsBatchMode() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilCracker.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilCracker.java
new file mode 100644
index 0000000000..9ca671aa49
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilCracker.java
@@ -0,0 +1,423 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.onElementPass;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_OIL_CRACKER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_OIL_CRACKER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_OIL_CRACKER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_OIL_CRACKER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.casingTexturePages;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_StructureUtility.ofCoil;
+import static gregtech.api.util.GT_Utility.filterValidMTEs;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidStack;
+
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.GT_HatchElement;
+import gregtech.api.enums.HeatingCoilLevel;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_EnhancedMultiBlockBase;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Input;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_MultiInput;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Output;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.maps.OilCrackerBackend;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_Input_ME;
+import gregtech.common.tileentities.machines.IRecipeProcessingAwareHatch;
+
+public class GT_MetaTileEntity_OilCracker extends GT_MetaTileEntity_EnhancedMultiBlockBase<GT_MetaTileEntity_OilCracker>
+ implements ISurvivalConstructable {
+
+ private static final byte CASING_INDEX = 49;
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final IStructureDefinition<GT_MetaTileEntity_OilCracker> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_OilCracker>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ transpose(
+ new String[][] { { "lcmcr", "lcmcr", "lcmcr" }, { "lc~cr", "l---r", "lcmcr" },
+ { "lcmcr", "lcmcr", "lcmcr" }, }))
+ .addElement('c', ofCoil(GT_MetaTileEntity_OilCracker::setCoilLevel, GT_MetaTileEntity_OilCracker::getCoilLevel))
+ .addElement(
+ 'l',
+ buildHatchAdder(GT_MetaTileEntity_OilCracker.class)
+ .atLeast(
+ GT_HatchElement.InputHatch.withAdder(GT_MetaTileEntity_OilCracker::addLeftHatchToMachineList),
+ GT_HatchElement.Energy,
+ GT_HatchElement.Maintenance)
+ .dot(2)
+ .casingIndex(CASING_INDEX)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_OilCracker::onCasingAdded,
+ ofBlock(GregTech_API.sBlockCasings4, 1))))
+ .addElement(
+ 'r',
+ buildHatchAdder(GT_MetaTileEntity_OilCracker.class)
+ .atLeast(
+ GT_HatchElement.OutputHatch.withAdder(GT_MetaTileEntity_OilCracker::addRightHatchToMachineList),
+ GT_HatchElement.Energy,
+ GT_HatchElement.Maintenance)
+ .dot(3)
+ .casingIndex(CASING_INDEX)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_OilCracker::onCasingAdded,
+ ofBlock(GregTech_API.sBlockCasings4, 1))))
+ .addElement(
+ 'm',
+ buildHatchAdder(GT_MetaTileEntity_OilCracker.class)
+ .atLeast(
+ GT_HatchElement.InputHatch.withAdder(GT_MetaTileEntity_OilCracker::addMiddleInputToMachineList)
+ .withCount(t -> t.mMiddleInputHatches.size()),
+ GT_HatchElement.InputBus,
+ GT_HatchElement.Energy,
+ GT_HatchElement.Maintenance)
+ .dot(1)
+ .casingIndex(CASING_INDEX)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_OilCracker::onCasingAdded,
+ ofBlock(GregTech_API.sBlockCasings4, 1))))
+ .build();
+ private HeatingCoilLevel heatLevel;
+ protected final List<GT_MetaTileEntity_Hatch_Input> mMiddleInputHatches = new ArrayList<>();
+ // 0 -> left, 1 -> right, any other -> not found
+ protected int mInputOnSide;
+ protected int mOutputOnSide;
+ protected int mCasingAmount;
+
+ public GT_MetaTileEntity_OilCracker(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_OilCracker(String aName) {
+ super(aName);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Cracker")
+ .addInfo("Controller block for the Oil Cracking Unit")
+ .addInfo("Thermally cracks heavy hydrocarbons into lighter fractions")
+ .addInfo("More efficient than the Chemical Reactor")
+ .addInfo("Gives different benefits whether it hydro or steam-cracks:")
+ .addInfo("Hydro - Consumes 20% less Hydrogen and outputs 25% more cracked fluid")
+ .addInfo("Steam - Outputs 50% more cracked fluid")
+ .addInfo("(Values compared to cracking in the Chemical Reactor)")
+ .addInfo("Place the appropriate circuit in the controller or an input bus")
+ .addSeparator()
+ .beginStructureBlock(5, 3, 3, true)
+ .addController("Front center")
+ .addCasingInfoRange("Clean Stainless Steel Machine Casing", 18, 21, false)
+ .addOtherStructurePart("2 Rings of 8 Coils", "Each side of the controller")
+ .addInfo("Gets 10% EU/t reduction per coil tier, up to a maximum of 50%")
+ .addEnergyHatch("Any casing", 1, 2, 3)
+ .addMaintenanceHatch("Any casing", 1, 2, 3)
+ .addInputHatch("For cracking fluid (Steam/Hydrogen/etc.) ONLY, Any middle ring casing", 1)
+ .addInputHatch("Any left/right side casing", 2, 3)
+ .addOutputHatch("Any right/left side casing", 2, 3)
+ .addStructureInfo("Input/Output Hatches must be on opposite sides!")
+ .addInputBus("Any middle ring casing, optional for programmed circuit automation")
+ .addStructureHint("GT5U.cracker.io_side")
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ if (sideDirection == facingDirection) {
+ if (active) return new ITexture[] { casingTexturePages[0][CASING_INDEX], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_OIL_CRACKER_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_OIL_CRACKER_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { casingTexturePages[0][CASING_INDEX], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_OIL_CRACKER)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_OIL_CRACKER_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { casingTexturePages[0][CASING_INDEX] };
+ }
+
+ @Override
+ public RecipeMap<OilCrackerBackend> getRecipeMap() {
+ return RecipeMaps.crackingRecipes;
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic() {
+
+ @Nonnull
+ @Override
+ public CheckRecipeResult process() {
+ setEuModifier(1.0F - Math.min(0.1F * (heatLevel.getTier() + 1), 0.5F));
+ return super.process();
+ }
+ };
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_OilCracker> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ public HeatingCoilLevel getCoilLevel() {
+ return heatLevel;
+ }
+
+ public void setCoilLevel(HeatingCoilLevel aCoilLevel) {
+ heatLevel = aCoilLevel;
+ }
+
+ private boolean addMiddleInputToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Input tHatch) {
+ tHatch.updateTexture(aBaseCasingIndex);
+ tHatch.mRecipeMap = getRecipeMap();
+ return mMiddleInputHatches.add(tHatch);
+ }
+ return false;
+ }
+
+ private boolean addLeftHatchToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Input tHatch) {
+ if (mInputOnSide == 1) return false;
+ mInputOnSide = 0;
+ mOutputOnSide = 1;
+ tHatch.updateTexture(aBaseCasingIndex);
+ tHatch.mRecipeMap = getRecipeMap();
+ return mInputHatches.add(tHatch);
+ }
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Output tHatch) {
+ if (mOutputOnSide == 1) return false;
+ mInputOnSide = 1;
+ mOutputOnSide = 0;
+ tHatch.updateTexture(aBaseCasingIndex);
+ return mOutputHatches.add(tHatch);
+ }
+ return false;
+ }
+
+ private boolean addRightHatchToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Input tHatch) {
+ if (mInputOnSide == 0) return false;
+ mInputOnSide = 1;
+ mOutputOnSide = 0;
+ tHatch.updateTexture(aBaseCasingIndex);
+ tHatch.mRecipeMap = getRecipeMap();
+ return mInputHatches.add(tHatch);
+ }
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Output tHatch) {
+ if (mOutputOnSide == 0) return false;
+ mInputOnSide = 0;
+ mOutputOnSide = 1;
+ tHatch.updateTexture(aBaseCasingIndex);
+ return mOutputHatches.add(tHatch);
+ }
+ return false;
+ }
+
+ private void onCasingAdded() {
+ mCasingAmount++;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ setCoilLevel(HeatingCoilLevel.None);
+ mCasingAmount = 0;
+ mMiddleInputHatches.clear();
+ mInputOnSide = -1;
+ mOutputOnSide = -1;
+ replaceDeprecatedCoils(aBaseMetaTileEntity);
+ return checkPiece(STRUCTURE_PIECE_MAIN, 2, 1, 0) && mInputOnSide != -1
+ && mOutputOnSide != -1
+ && mCasingAmount >= 18
+ && mMaintenanceHatches.size() == 1
+ && !mMiddleInputHatches.isEmpty();
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_OilCracker(this.mName);
+ }
+
+ private void replaceDeprecatedCoils(IGregTechTileEntity aBaseMetaTileEntity) {
+ final int xDir = aBaseMetaTileEntity.getBackFacing().offsetX;
+ final int zDir = aBaseMetaTileEntity.getBackFacing().offsetZ;
+ final int tX = aBaseMetaTileEntity.getXCoord() + xDir;
+ final int tY = aBaseMetaTileEntity.getYCoord();
+ final int tZ = aBaseMetaTileEntity.getZCoord() + zDir;
+ for (int xPos = tX - 1; xPos <= tX + 1; xPos += (xDir != 0 ? 1 : 2))
+ for (int yPos = tY - 1; yPos <= tY + 1; yPos++)
+ for (int zPos = tZ - 1; zPos <= tZ + 1; zPos += (xDir != 0 ? 2 : 1)) {
+ if ((yPos == tY) && (xPos == tX || zPos == tZ)) continue;
+ final byte tUsedMeta = aBaseMetaTileEntity.getMetaID(xPos, yPos, zPos);
+ if (tUsedMeta < 12) continue;
+ if (tUsedMeta > 14) continue;
+ if (aBaseMetaTileEntity.getBlock(xPos, yPos, zPos) != GregTech_API.sBlockCasings1) continue;
+
+ aBaseMetaTileEntity.getWorld()
+ .setBlock(xPos, yPos, zPos, GregTech_API.sBlockCasings5, tUsedMeta - 12, 3);
+ }
+ }
+
+ @Override
+ public ArrayList<FluidStack> getStoredFluids() {
+ final ArrayList<FluidStack> rList = new ArrayList<>();
+ Map<Fluid, FluidStack> inputsFromME = new HashMap<>();
+ for (final GT_MetaTileEntity_Hatch_Input tHatch : filterValidMTEs(mInputHatches)) {
+ tHatch.mRecipeMap = getRecipeMap();
+ if (tHatch instanceof GT_MetaTileEntity_Hatch_Input_ME meHatch) {
+ for (FluidStack tFluid : meHatch.getStoredFluids()) {
+ if (tFluid != null && !getRecipeMap().getBackend()
+ .isValidCatalystFluid(tFluid)) {
+ inputsFromME.put(tFluid.getFluid(), tFluid);
+ }
+ }
+ } else if (tHatch instanceof GT_MetaTileEntity_Hatch_MultiInput) {
+ for (final FluidStack tFluid : ((GT_MetaTileEntity_Hatch_MultiInput) tHatch).getStoredFluid()) {
+ if (tFluid != null && !getRecipeMap().getBackend()
+ .isValidCatalystFluid(tFluid)) {
+ rList.add(tFluid);
+ }
+ }
+ } else {
+ if (tHatch.getFillableStack() != null) {
+ if (!getRecipeMap().getBackend()
+ .isValidCatalystFluid(tHatch.getFillableStack())) rList.add(tHatch.getFillableStack());
+ }
+ }
+ }
+ for (final GT_MetaTileEntity_Hatch_Input tHatch : filterValidMTEs(mMiddleInputHatches)) {
+ tHatch.mRecipeMap = getRecipeMap();
+ if (tHatch instanceof GT_MetaTileEntity_Hatch_Input_ME meHatch) {
+ for (FluidStack tFluid : meHatch.getStoredFluids()) {
+ if (tFluid != null && getRecipeMap().getBackend()
+ .isValidCatalystFluid(tFluid)) {
+ inputsFromME.put(tFluid.getFluid(), tFluid);
+ }
+ }
+ } else if (tHatch instanceof GT_MetaTileEntity_Hatch_MultiInput) {
+ for (final FluidStack tFluid : ((GT_MetaTileEntity_Hatch_MultiInput) tHatch).getStoredFluid()) {
+ if (tFluid != null && getRecipeMap().getBackend()
+ .isValidCatalystFluid(tFluid)) {
+ rList.add(tFluid);
+ }
+ }
+ } else {
+ if (tHatch.getFillableStack() != null) {
+ final FluidStack tStack = tHatch.getFillableStack();
+ if (getRecipeMap().getBackend()
+ .isValidCatalystFluid(tStack)) {
+ rList.add(tStack);
+ }
+ }
+ }
+ }
+ if (!inputsFromME.isEmpty()) {
+ rList.addAll(inputsFromME.values());
+ }
+ return rList;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 2, 1, 0);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 2, 1, 0, elementBudget, env, false, true);
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ @Override
+ protected void startRecipeProcessing() {
+ for (GT_MetaTileEntity_Hatch_Input hatch : filterValidMTEs(mMiddleInputHatches)) {
+ if (hatch instanceof IRecipeProcessingAwareHatch aware) {
+ aware.startRecipeProcessing();
+ }
+ }
+ super.startRecipeProcessing();
+ }
+
+ @Override
+ protected void endRecipeProcessing() {
+ super.endRecipeProcessing();
+ for (GT_MetaTileEntity_Hatch_Input hatch : filterValidMTEs(mMiddleInputHatches)) {
+ if (hatch instanceof IRecipeProcessingAwareHatch aware) {
+ setResultIfFailure(aware.endRecipeProcessing(this));
+ }
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrill1.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrill1.java
new file mode 100644
index 0000000000..1e37dcbac9
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrill1.java
@@ -0,0 +1,53 @@
+package gregtech.common.tileentities.machines.multi;
+
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+
+public class GT_MetaTileEntity_OilDrill1 extends GT_MetaTileEntity_OilDrillBase {
+
+ public GT_MetaTileEntity_OilDrill1(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_OilDrill1(String aName) {
+ super(aName);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ return createTooltip("I");
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_OilDrill1(mName);
+ }
+
+ @Override
+ protected ItemList getCasingBlockItem() {
+ return ItemList.Casing_SolidSteel;
+ }
+
+ @Override
+ protected Materials getFrameMaterial() {
+ return Materials.Steel;
+ }
+
+ @Override
+ protected int getCasingTextureIndex() {
+ return 16;
+ }
+
+ @Override
+ protected int getRangeInChunks() {
+ return 1;
+ }
+
+ @Override
+ protected int getMinTier() {
+ return 2;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrill2.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrill2.java
new file mode 100644
index 0000000000..10a64a6b1d
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrill2.java
@@ -0,0 +1,55 @@
+package gregtech.common.tileentities.machines.multi;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_OilDrill2 extends GT_MetaTileEntity_OilDrillBase {
+
+ public GT_MetaTileEntity_OilDrill2(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_OilDrill2(String aName) {
+ super(aName);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ return createTooltip("II");
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_OilDrill2(mName);
+ }
+
+ @Override
+ protected ItemList getCasingBlockItem() {
+ return ItemList.Casing_CleanStainlessSteel;
+ }
+
+ @Override
+ protected Materials getFrameMaterial() {
+ return Materials.StainlessSteel;
+ }
+
+ @Override
+ protected int getCasingTextureIndex() {
+ return GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings4, 1);
+ }
+
+ @Override
+ protected int getRangeInChunks() {
+ return 2;
+ }
+
+ @Override
+ protected int getMinTier() {
+ return 3;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrill3.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrill3.java
new file mode 100644
index 0000000000..bcbe42d794
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrill3.java
@@ -0,0 +1,55 @@
+package gregtech.common.tileentities.machines.multi;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_OilDrill3 extends GT_MetaTileEntity_OilDrillBase {
+
+ public GT_MetaTileEntity_OilDrill3(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_OilDrill3(String aName) {
+ super(aName);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ return createTooltip("III");
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_OilDrill3(mName);
+ }
+
+ @Override
+ protected ItemList getCasingBlockItem() {
+ return ItemList.Casing_StableTitanium;
+ }
+
+ @Override
+ protected Materials getFrameMaterial() {
+ return Materials.Titanium;
+ }
+
+ @Override
+ protected int getCasingTextureIndex() {
+ return GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings4, 2);
+ }
+
+ @Override
+ protected int getRangeInChunks() {
+ return 4;
+ }
+
+ @Override
+ protected int getMinTier() {
+ return 4;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrill4.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrill4.java
new file mode 100644
index 0000000000..802e9f1c5c
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrill4.java
@@ -0,0 +1,55 @@
+package gregtech.common.tileentities.machines.multi;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_OilDrill4 extends GT_MetaTileEntity_OilDrillBase {
+
+ public GT_MetaTileEntity_OilDrill4(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_OilDrill4(String aName) {
+ super(aName);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ return createTooltip("IV");
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_OilDrill4(mName);
+ }
+
+ @Override
+ protected ItemList getCasingBlockItem() {
+ return ItemList.Casing_RobustTungstenSteel;
+ }
+
+ @Override
+ protected Materials getFrameMaterial() {
+ return Materials.TungstenSteel;
+ }
+
+ @Override
+ protected int getCasingTextureIndex() {
+ return GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings4, 0);
+ }
+
+ @Override
+ protected int getRangeInChunks() {
+ return 8;
+ }
+
+ @Override
+ protected int getMinTier() {
+ return 5;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrillBase.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrillBase.java
new file mode 100644
index 0000000000..075b4e19a1
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrillBase.java
@@ -0,0 +1,489 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.GT_Values.VN;
+import static gregtech.api.enums.GT_Values.debugDriller;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_OIL_DRILL;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_OIL_DRILL_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_OIL_DRILL_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_OIL_DRILL_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.getCasingTextureForId;
+import static gregtech.common.GT_UndergroundOil.undergroundOil;
+import static gregtech.common.GT_UndergroundOil.undergroundOilReadInformation;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.annotation.Nonnegative;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.ChunkCoordIntPair;
+import net.minecraft.world.chunk.Chunk;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidRegistry;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.google.common.collect.ImmutableList;
+import com.gtnewhorizons.modularui.api.NumberFormatMUI;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.common.widget.DynamicPositionedColumn;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+
+import gregtech.api.enums.SoundResource;
+import gregtech.api.interfaces.IHatchElement;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetricsExporter;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.objects.GT_ChunkManager;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Log;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.ValidationResult;
+import gregtech.api.util.ValidationType;
+
+public abstract class GT_MetaTileEntity_OilDrillBase extends GT_MetaTileEntity_DrillerBase implements IMetricsExporter {
+
+ private final ArrayList<Chunk> mOilFieldChunks = new ArrayList<>();
+ private int mOilId = 0;
+ private int mOilFlow = 0;
+
+ private int chunkRangeConfig = getRangeInChunks();
+
+ public GT_MetaTileEntity_OilDrillBase(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_OilDrillBase(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side == aFacing) {
+ if (aActive) return new ITexture[] { getCasingTextureForId(casingTextureIndex), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_OIL_DRILL_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_OIL_DRILL_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { getCasingTextureForId(casingTextureIndex), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_OIL_DRILL)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_OIL_DRILL_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { getCasingTextureForId(casingTextureIndex) };
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setInteger("mOilId", mOilId);
+ aNBT.setInteger("chunkRangeConfig", chunkRangeConfig);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ mOilId = aNBT.getInteger("mOilId");
+ if (aNBT.hasKey("chunkRangeConfig")) chunkRangeConfig = aNBT.getInteger("chunkRangeConfig");
+ }
+
+ protected GT_Multiblock_Tooltip_Builder createTooltip(String tierSuffix) {
+ String casings = getCasingBlockItem().get(0)
+ .getDisplayName();
+
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Pump")
+ .addInfo("Controller Block for the Oil/Gas/Fluid Drilling Rig " + (tierSuffix != null ? tierSuffix : ""))
+ .addInfo("Works on " + getRangeInChunks() + "x" + getRangeInChunks() + " chunks")
+ .addInfo("Use a Screwdriver to configure range")
+ .addInfo("Use Programmed Circuits to ignore near exhausted oil field")
+ .addInfo("If total circuit # is greater than output amount it will halt. If it worked right.") // doesn't
+ // work
+ .addSeparator()
+ .beginStructureBlock(3, 7, 3, false)
+ .addController("Front bottom")
+ .addOtherStructurePart(casings, "form the 3x1x3 Base")
+ .addOtherStructurePart(casings, "1x3x1 pillar above the center of the base (2 minimum total)")
+ .addOtherStructurePart(getFrameMaterial().mName + " Frame Boxes", "Each pillar's side and 1x3x1 on top")
+ .addEnergyHatch("1x " + VN[getMinTier()] + "+, Any base casing", 1)
+ .addMaintenanceHatch("Any base casing", 1)
+ .addInputBus("Mining Pipes or Circuits, optional, any base casing", 1)
+ .addOutputHatch("Any base casing", 1)
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ protected abstract int getRangeInChunks();
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ super.onScrewdriverRightClick(side, aPlayer, aX, aY, aZ);
+ int oldChunkRange = chunkRangeConfig;
+ if (aPlayer.isSneaking()) {
+ if (chunkRangeConfig > 0) {
+ chunkRangeConfig--;
+ }
+ if (chunkRangeConfig == 0) chunkRangeConfig = getRangeInChunks();
+ } else {
+ if (chunkRangeConfig <= getRangeInChunks()) {
+ chunkRangeConfig++;
+ }
+ if (chunkRangeConfig > getRangeInChunks()) chunkRangeConfig = 1;
+ }
+ if (oldChunkRange != chunkRangeConfig) mOilFieldChunks.clear();
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ StatCollector.translateToLocal("GT5U.machines.workareaset") + " "
+ + chunkRangeConfig
+ + "x"
+ + chunkRangeConfig
+ + StatCollector.translateToLocal("GT5U.machines.chunks")); // TODO Add translation support
+ }
+
+ @Override
+ protected boolean checkHatches() {
+ return !mMaintenanceHatches.isEmpty() && !mOutputHatches.isEmpty() && mEnergyHatches.size() == 1;
+ }
+
+ @Override
+ protected List<IHatchElement<? super GT_MetaTileEntity_DrillerBase>> getAllowedHatches() {
+ return ImmutableList.of(InputBus, OutputHatch, Maintenance, Energy);
+ }
+
+ @Override
+ protected void setElectricityStats() {
+ this.mEfficiency = getCurrentEfficiency(null);
+ this.mEfficiencyIncrease = 10000;
+ int tier = Math.max(0, GT_Utility.getTier(getMaxInputVoltage()));
+ this.mEUt = -7 << (tier << 1); // (1/4) A of current tier when at bottom (7/8) A of current tier while mining
+ this.mMaxProgresstime = Math.max(
+ 1,
+ (workState == STATE_AT_BOTTOM ? (64 * (chunkRangeConfig * chunkRangeConfig)) >> (getMinTier() - 1) : 120)
+ >> tier);
+ }
+
+ protected float computeSpeed() {
+ return .5F + (GT_Utility.getTier(getMaxInputVoltage()) - getMinTier()) * .25F;
+ }
+
+ @Override
+ protected boolean workingAtBottom(ItemStack aStack, int xDrill, int yDrill, int zDrill, int xPipe, int zPipe,
+ int yHead, int oldYHead) {
+ switch (tryLowerPipeState(true)) {
+ case 0 -> {
+ workState = STATE_DOWNWARD;
+ setElectricityStats();
+ return true;
+ }
+ case 3 -> {
+ workState = STATE_UPWARD;
+ return true;
+ }
+ }
+
+ if (reachingVoidOrBedrock() && tryFillChunkList()) {
+ if (mWorkChunkNeedsReload) {
+ mCurrentChunk = new ChunkCoordIntPair(xDrill >> 4, zDrill >> 4);
+ GT_ChunkManager.requestChunkLoad((TileEntity) getBaseMetaTileEntity(), null);
+ mWorkChunkNeedsReload = false;
+ }
+ float speed = computeSpeed();
+ ValidationResult<FluidStack> pumpResult = tryPumpOil(speed);
+ if (pumpResult.getType() != ValidationType.VALID) {
+ mEUt = 0;
+ mMaxProgresstime = 0;
+ setRuntimeFailureReason(CheckRecipeResultRegistry.FLUID_OUTPUT_FULL);
+ return false;
+ }
+ FluidStack tFluid = pumpResult.getResult();
+ if (tFluid != null && tFluid.amount > getTotalConfigValue()) {
+ this.mOutputFluids = new FluidStack[] { tFluid };
+ return true;
+ }
+ }
+ GT_ChunkManager.releaseTicket((TileEntity) getBaseMetaTileEntity());
+ workState = STATE_UPWARD;
+ setShutdownReason(StatCollector.translateToLocal("GT5U.gui.text.drill_exhausted"));
+ return true;
+ }
+
+ private boolean tryFillChunkList() {
+ FluidStack tFluid, tOil;
+ if (mOilId <= 0) {
+ tFluid = undergroundOilReadInformation(getBaseMetaTileEntity());
+ if (tFluid == null) return false;
+ mOilId = tFluid.getFluidID();
+ }
+ if (debugDriller) {
+ GT_Log.out.println(" Driller on fluid = " + mOilId);
+ }
+
+ tOil = new FluidStack(FluidRegistry.getFluid(mOilId), 0);
+
+ if (mOilFieldChunks.isEmpty()) {
+ Chunk tChunk = getBaseMetaTileEntity().getWorld()
+ .getChunkFromBlockCoords(getBaseMetaTileEntity().getXCoord(), getBaseMetaTileEntity().getZCoord());
+ int range = chunkRangeConfig;
+ int xChunk = Math.floorDiv(tChunk.xPosition, range) * range; // Java was written by idiots. For negative
+ // values, / returns rounded towards zero.
+ // Fucking morons.
+ int zChunk = Math.floorDiv(tChunk.zPosition, range) * range;
+ if (debugDriller) {
+ GT_Log.out.println(
+ "tChunk.xPosition = " + tChunk.xPosition
+ + " tChunk.zPosition = "
+ + tChunk.zPosition
+ + " xChunk = "
+ + xChunk
+ + " zChunk = "
+ + zChunk);
+ }
+ for (int i = 0; i < range; i++) {
+ for (int j = 0; j < range; j++) {
+ if (debugDriller) {
+ GT_Log.out.println(" getChunkX = " + (xChunk + i) + " getChunkZ = " + (zChunk + j));
+ }
+ tChunk = getBaseMetaTileEntity().getWorld()
+ .getChunkFromChunkCoords(xChunk + i, zChunk + j);
+ tFluid = undergroundOilReadInformation(tChunk);
+ if (debugDriller) {
+ GT_Log.out.println(
+ " Fluid in chunk = " + tFluid.getFluid()
+ .getID());
+ }
+ if (tOil.isFluidEqual(tFluid) && tFluid.amount > 0) {
+ mOilFieldChunks.add(tChunk);
+ if (debugDriller) {
+ GT_Log.out.println(" Matching fluid, quantity = " + tFluid.amount);
+ }
+ }
+ }
+ }
+ }
+ if (debugDriller) {
+ GT_Log.out.println("mOilFieldChunks.size = " + mOilFieldChunks.size());
+ }
+ return !mOilFieldChunks.isEmpty();
+ }
+
+ /**
+ * Tries to pump oil, accounting for output space if void protection is enabled.
+ * <p>
+ * If pumped fluid will not fit in output hatches, it returns a result with INVALID.
+ * <p>
+ * If vein is depleted, it returns a result with VALID and null fluid.
+ */
+ protected ValidationResult<FluidStack> tryPumpOil(float speed) {
+ if (mOilId <= 0) return null;
+ if (debugDriller) {
+ GT_Log.out.println(" pump speed = " + speed);
+ }
+
+ // Even though it works fine without this check,
+ // it can save tiny amount of CPU time when void protection is disabled
+ if (protectsExcessFluid()) {
+ FluidStack simulatedOil = pumpOil(speed, true);
+ if (!canOutputAll(new FluidStack[] { simulatedOil })) {
+ return ValidationResult.of(ValidationType.INVALID, null);
+ }
+ }
+
+ FluidStack pumpedOil = pumpOil(speed, false);
+ mOilFlow = pumpedOil.amount;
+ return ValidationResult.of(ValidationType.VALID, pumpedOil.amount == 0 ? null : pumpedOil);
+ }
+
+ /**
+ * @param speed Speed to pump oil
+ * @param simulate If true, it actually does not consume vein
+ * @return Fluid pumped
+ */
+ protected FluidStack pumpOil(@Nonnegative float speed, boolean simulate) {
+ if (speed < 0) {
+ throw new IllegalArgumentException("Don't pass negative speed");
+ }
+
+ ArrayList<Chunk> emptyChunks = new ArrayList<>();
+ FluidStack returnOil = new FluidStack(FluidRegistry.getFluid(mOilId), 0);
+
+ for (Chunk tChunk : mOilFieldChunks) {
+ FluidStack pumped = undergroundOil(tChunk, simulate ? -speed : speed);
+ if (debugDriller) {
+ GT_Log.out.println(
+ " chunkX = " + tChunk.getChunkCoordIntPair().chunkXPos
+ + " chunkZ = "
+ + tChunk.getChunkCoordIntPair().chunkZPos);
+ if (pumped != null) {
+ GT_Log.out.println(" Fluid pumped = " + pumped.amount);
+ } else {
+ GT_Log.out.println(" No fluid pumped ");
+ }
+ }
+ if (pumped == null || pumped.amount < 1) {
+ emptyChunks.add(tChunk);
+ continue;
+ }
+ if (returnOil.isFluidEqual(pumped)) {
+ returnOil.amount += pumped.amount;
+ }
+ }
+ for (Chunk tChunk : emptyChunks) {
+ mOilFieldChunks.remove(tChunk);
+ }
+ return returnOil;
+ }
+
+ @Override
+ protected SoundResource getProcessStartSound() {
+ return SoundResource.IC2_MACHINES_PUMP_OP;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ List<String> l = new ArrayList<>(
+ Arrays.asList(
+ EnumChatFormatting.BLUE + StatCollector.translateToLocal("GT5U.machines.oilfluidpump")
+ + EnumChatFormatting.RESET,
+ StatCollector.translateToLocal("GT5U.machines.workarea") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(chunkRangeConfig)
+ + " x "
+ + GT_Utility.formatNumbers(chunkRangeConfig)
+ + EnumChatFormatting.RESET
+ + " "
+ + StatCollector.translateToLocal("GT5U.machines.chunks"),
+ "Drilling fluid: " + EnumChatFormatting.GREEN + getFluidName() + EnumChatFormatting.RESET,
+ "Drilling flow: " + EnumChatFormatting.GREEN
+ + getFlowRatePerTick()
+ + EnumChatFormatting.RESET
+ + " L/t"));
+ l.addAll(Arrays.asList(super.getInfoData()));
+ return l.toArray(new String[0]);
+ }
+
+ @Override
+ public @NotNull List<String> reportMetrics() {
+ final boolean machineIsActive = getBaseMetaTileEntity().isActive();
+ final String failureReason = getFailureReason()
+ .map(reason -> StatCollector.translateToLocalFormatted("GT5U.gui.text.drill_offline_reason", reason))
+ .orElseGet(() -> StatCollector.translateToLocalFormatted("GT5U.gui.text.drill_offline_generic"));
+
+ if (workState == STATE_AT_BOTTOM) {
+ final ImmutableList.Builder<String> builder = ImmutableList.builder();
+ builder.add(StatCollector.translateToLocalFormatted("GT5U.gui.text.pump_fluid_type", getFluidName()));
+
+ if (machineIsActive) {
+ builder.add(
+ StatCollector.translateToLocalFormatted(
+ "GT5U.gui.text.pump_rate.1",
+ EnumChatFormatting.AQUA + numberFormat.format(getFlowRatePerTick()))
+ + StatCollector.translateToLocal("GT5U.gui.text.pump_rate.2"),
+ mOilFlow + StatCollector.translateToLocal("GT5U.gui.text.pump_recovery.2"));
+ } else {
+ builder.add(failureReason);
+ }
+
+ return builder.build();
+ }
+
+ if (machineIsActive) {
+ return switch (workState) {
+ case STATE_DOWNWARD -> ImmutableList.of(StatCollector.translateToLocal("GT5U.gui.text.deploying_pipe"));
+ case STATE_UPWARD, STATE_ABORT -> ImmutableList
+ .of(StatCollector.translateToLocal("GT5U.gui.text.retracting_pipe"));
+ default -> ImmutableList.of();
+ };
+ }
+
+ return ImmutableList.of(failureReason);
+ }
+
+ protected int getFlowRatePerTick() {
+ return this.mMaxProgresstime > 0 ? (mOilFlow / this.mMaxProgresstime) : 0;
+ }
+
+ @NotNull
+ private String getFluidName() {
+ if (mOilId > 0) {
+ final Fluid fluid = FluidRegistry.getFluid(mOilId);
+ return fluid.getLocalizedName(new FluidStack(fluid, 0));
+ }
+ return "None";
+ }
+
+ private @NotNull String clientFluidType = "";
+ private int clientFlowPerTick = 0;
+ private int clientFlowPerOperation = 0;
+
+ protected static final NumberFormatMUI numberFormat = new NumberFormatMUI();
+
+ @Override
+ protected void drawTexts(DynamicPositionedColumn screenElements, SlotWidget inventorySlot) {
+ super.drawTexts(screenElements, inventorySlot);
+ screenElements
+ .widget(
+ new TextWidget()
+ .setStringSupplier(
+ () -> EnumChatFormatting.GRAY
+ + StatCollector.translateToLocalFormatted("GT5U.gui.text.pump_fluid_type", clientFluidType))
+ .setTextAlignment(Alignment.CenterLeft)
+ .setEnabled(widget -> getBaseMetaTileEntity().isActive() && workState == STATE_AT_BOTTOM))
+ .widget(
+ new TextWidget()
+ .setStringSupplier(
+ () -> EnumChatFormatting.GRAY
+ + StatCollector.translateToLocalFormatted(
+ "GT5U.gui.text.pump_rate.1",
+ EnumChatFormatting.AQUA + numberFormat.format(clientFlowPerTick))
+ + EnumChatFormatting.GRAY
+ + StatCollector.translateToLocal("GT5U.gui.text.pump_rate.2"))
+ .setTextAlignment(Alignment.CenterLeft)
+ .setEnabled(widget -> getBaseMetaTileEntity().isActive() && workState == STATE_AT_BOTTOM))
+ .widget(
+ new TextWidget()
+ .setStringSupplier(
+ () -> EnumChatFormatting.GRAY
+ + StatCollector.translateToLocalFormatted(
+ "GT5U.gui.text.pump_recovery.1",
+ EnumChatFormatting.AQUA + numberFormat.format(clientFlowPerOperation))
+ + EnumChatFormatting.GRAY
+ + StatCollector.translateToLocal("GT5U.gui.text.pump_recovery.2"))
+ .setTextAlignment(Alignment.CenterLeft)
+ .setEnabled(widget -> getBaseMetaTileEntity().isActive() && workState == STATE_AT_BOTTOM))
+ .widget(new FakeSyncWidget.IntegerSyncer(() -> workState, newInt -> workState = newInt))
+ .widget(new FakeSyncWidget.StringSyncer(this::getFluidName, newString -> clientFluidType = newString))
+ .widget(new FakeSyncWidget.IntegerSyncer(this::getFlowRatePerTick, newInt -> clientFlowPerTick = newInt))
+ .widget(new FakeSyncWidget.IntegerSyncer(() -> mOilFlow, newInt -> clientFlowPerOperation = newInt));
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrillInfinite.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrillInfinite.java
new file mode 100644
index 0000000000..056577e8e8
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrillInfinite.java
@@ -0,0 +1,87 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static gregtech.api.enums.GT_Values.VN;
+
+import net.minecraftforge.fluids.FluidStack;
+
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_OilDrillInfinite extends GT_MetaTileEntity_OilDrillBase {
+
+ public GT_MetaTileEntity_OilDrillInfinite(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_OilDrillInfinite(String aName) {
+ super(aName);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ String casings = getCasingBlockItem().get(0)
+ .getDisplayName();
+
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Pump")
+ .addInfo("Controller Block for the Infinite Oil/Gas/Fluid Drilling Rig ")
+ .addInfo("Works on " + getRangeInChunks() + "x" + getRangeInChunks() + " chunks")
+ .addSeparator()
+ .beginStructureBlock(3, 7, 3, false)
+ .addController("Front bottom")
+ .addOtherStructurePart(casings, "form the 3x1x3 Base")
+ .addOtherStructurePart(casings, "1x3x1 pillar above the center of the base (2 minimum total)")
+ .addOtherStructurePart(getFrameMaterial().mName + " Frame Boxes", "Each pillar's side and 1x3x1 on top")
+ .addEnergyHatch("1x " + VN[getMinTier()] + "+, Any base casing", 1)
+ .addMaintenanceHatch("Any base casing", 1)
+ .addInputBus("Mining Pipes or Circuits, optional, any base casing", 1)
+ .addOutputHatch("Any base casing", 1)
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_OilDrillInfinite(mName);
+ }
+
+ @Override
+ protected FluidStack pumpOil(float speed, boolean simulate) {
+ // always simulate to not deplete vein
+ return super.pumpOil(speed, true);
+ }
+
+ @Override
+ protected ItemList getCasingBlockItem() {
+ return ItemList.Casing_MiningNeutronium;
+ }
+
+ @Override
+ protected Materials getFrameMaterial() {
+ return Materials.Neutronium;
+ }
+
+ @Override
+ protected int getCasingTextureIndex() {
+ return 178;
+ }
+
+ @Override
+ protected int getRangeInChunks() {
+ return 8;
+ }
+
+ @Override
+ protected float computeSpeed() {
+ return .5F + (GT_Utility.getTier(getMaxInputVoltage()) - getMinTier() + 5) * .25F;
+ }
+
+ @Override
+ protected int getMinTier() {
+ return 9;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlant1.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlant1.java
new file mode 100644
index 0000000000..50edf8928a
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlant1.java
@@ -0,0 +1,60 @@
+package gregtech.common.tileentities.machines.multi;
+
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+
+public class GT_MetaTileEntity_OreDrillingPlant1 extends GT_MetaTileEntity_OreDrillingPlantBase {
+
+ public GT_MetaTileEntity_OreDrillingPlant1(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ mTier = 1;
+ }
+
+ public GT_MetaTileEntity_OreDrillingPlant1(String aName) {
+ super(aName);
+ mTier = 1;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ return createTooltip("I");
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_OreDrillingPlant1(mName);
+ }
+
+ @Override
+ protected ItemList getCasingBlockItem() {
+ return ItemList.Casing_SolidSteel;
+ }
+
+ @Override
+ protected Materials getFrameMaterial() {
+ return Materials.Steel;
+ }
+
+ @Override
+ protected int getCasingTextureIndex() {
+ return 16;
+ }
+
+ @Override
+ protected int getRadiusInChunks() {
+ return 3;
+ }
+
+ @Override
+ protected int getMinTier() {
+ return 2;
+ }
+
+ @Override
+ protected int getBaseProgressTime() {
+ return 960;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlant2.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlant2.java
new file mode 100644
index 0000000000..6a80c96d26
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlant2.java
@@ -0,0 +1,60 @@
+package gregtech.common.tileentities.machines.multi;
+
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+
+public class GT_MetaTileEntity_OreDrillingPlant2 extends GT_MetaTileEntity_OreDrillingPlantBase {
+
+ public GT_MetaTileEntity_OreDrillingPlant2(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ mTier = 2;
+ }
+
+ public GT_MetaTileEntity_OreDrillingPlant2(String aName) {
+ super(aName);
+ mTier = 2;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ return createTooltip("II");
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_OreDrillingPlant2(mName);
+ }
+
+ @Override
+ protected ItemList getCasingBlockItem() {
+ return ItemList.Casing_StableTitanium;
+ }
+
+ @Override
+ protected Materials getFrameMaterial() {
+ return Materials.Titanium;
+ }
+
+ @Override
+ protected int getCasingTextureIndex() {
+ return 50;
+ }
+
+ @Override
+ protected int getRadiusInChunks() {
+ return 4;
+ }
+
+ @Override
+ protected int getMinTier() {
+ return 3;
+ }
+
+ @Override
+ protected int getBaseProgressTime() {
+ return 800;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlant3.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlant3.java
new file mode 100644
index 0000000000..2bb25b2254
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlant3.java
@@ -0,0 +1,60 @@
+package gregtech.common.tileentities.machines.multi;
+
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+
+public class GT_MetaTileEntity_OreDrillingPlant3 extends GT_MetaTileEntity_OreDrillingPlantBase {
+
+ public GT_MetaTileEntity_OreDrillingPlant3(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ mTier = 3;
+ }
+
+ public GT_MetaTileEntity_OreDrillingPlant3(String aName) {
+ super(aName);
+ mTier = 3;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ return createTooltip("III");
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_OreDrillingPlant3(mName);
+ }
+
+ @Override
+ protected ItemList getCasingBlockItem() {
+ return ItemList.Casing_RobustTungstenSteel;
+ }
+
+ @Override
+ protected Materials getFrameMaterial() {
+ return Materials.TungstenSteel;
+ }
+
+ @Override
+ protected int getCasingTextureIndex() {
+ return 48;
+ }
+
+ @Override
+ protected int getRadiusInChunks() {
+ return 6;
+ }
+
+ @Override
+ protected int getMinTier() {
+ return 4;
+ }
+
+ @Override
+ protected int getBaseProgressTime() {
+ return 640;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlant4.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlant4.java
new file mode 100644
index 0000000000..7ba26b9500
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlant4.java
@@ -0,0 +1,60 @@
+package gregtech.common.tileentities.machines.multi;
+
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+
+public class GT_MetaTileEntity_OreDrillingPlant4 extends GT_MetaTileEntity_OreDrillingPlantBase {
+
+ public GT_MetaTileEntity_OreDrillingPlant4(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ mTier = 4;
+ }
+
+ public GT_MetaTileEntity_OreDrillingPlant4(String aName) {
+ super(aName);
+ mTier = 4;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ return createTooltip("IV");
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_OreDrillingPlant4(mName);
+ }
+
+ @Override
+ protected ItemList getCasingBlockItem() {
+ return ItemList.Casing_MiningOsmiridium;
+ }
+
+ @Override
+ protected Materials getFrameMaterial() {
+ return Materials.Osmiridium;
+ }
+
+ @Override
+ protected int getCasingTextureIndex() {
+ return 62;
+ }
+
+ @Override
+ protected int getRadiusInChunks() {
+ return 9;
+ }
+
+ @Override
+ protected int getMinTier() {
+ return 5;
+ }
+
+ @Override
+ protected int getBaseProgressTime() {
+ return 480;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlantBase.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlantBase.java
new file mode 100644
index 0000000000..b60bac4f84
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlantBase.java
@@ -0,0 +1,794 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_Values.TIER_COLORS;
+import static gregtech.api.enums.GT_Values.VN;
+import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.ChunkCoordIntPair;
+import net.minecraft.world.ChunkPosition;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.google.common.collect.ImmutableList;
+import com.gtnewhorizons.modularui.api.NumberFormatMUI;
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.DynamicPositionedColumn;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.gui.widgets.GT_LockedWhileActiveButton;
+import gregtech.api.interfaces.IHatchElement;
+import gregtech.api.interfaces.metatileentity.IMetricsExporter;
+import gregtech.api.objects.GT_ChunkManager;
+import gregtech.api.objects.ItemData;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_OreDictUnificator;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.blocks.GT_Block_Ores_Abstract;
+import gregtech.common.blocks.GT_TileEntity_Ores;
+import gregtech.crossmod.visualprospecting.GT_VisualProspecting_Database;
+
+public abstract class GT_MetaTileEntity_OreDrillingPlantBase extends GT_MetaTileEntity_DrillerBase
+ implements IMetricsExporter {
+
+ private final List<ChunkPosition> oreBlockPositions = new ArrayList<>();
+ protected int mTier = 1;
+ private int chunkRadiusConfig = getRadiusInChunks();
+ private boolean replaceWithCobblestone = true;
+
+ /** Used to drive the remaining ores count in the UI. */
+ private int clientOreListSize = 0;
+
+ /** Used to drive the current chunk number in the UI. */
+ private int clientCurrentChunk = 0;
+
+ /** Used to drive the total chunk count in the UI. */
+ private int clientTotalChunks = 0;
+
+ /** Used to drive the drill's y-level in the UI. */
+ private int clientYHead = 0;
+
+ /** Contains the name of the currently mined vein. Used for driving metrics cover output. */
+ private String veinName = null;
+
+ GT_MetaTileEntity_OreDrillingPlantBase(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ GT_MetaTileEntity_OreDrillingPlantBase(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setInteger("chunkRadiusConfig", chunkRadiusConfig);
+ aNBT.setBoolean("replaceWithCobblestone", replaceWithCobblestone);
+ if (veinName != null) {
+ aNBT.setString("veinName", veinName);
+ } else if (aNBT.hasKey("veinName")) {
+ aNBT.removeTag("veinName");
+ }
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ if (aNBT.hasKey("chunkRadiusConfig")) {
+ chunkRadiusConfig = aNBT.getInteger("chunkRadiusConfig");
+ }
+ if (aNBT.hasKey("replaceWithCobblestone")) {
+ replaceWithCobblestone = aNBT.getBoolean("replaceWithCobblestone");
+ }
+ if (aNBT.hasKey("veinName")) {
+ veinName = aNBT.getString("veinName");
+ } else {
+ veinName = null;
+ }
+ }
+
+ private void adjustChunkRadius(boolean increase) {
+ if (increase) {
+ if (chunkRadiusConfig <= getRadiusInChunks()) {
+ chunkRadiusConfig++;
+ }
+ if (chunkRadiusConfig > getRadiusInChunks()) chunkRadiusConfig = 1;
+ } else {
+ if (chunkRadiusConfig > 0) {
+ chunkRadiusConfig--;
+ }
+ if (chunkRadiusConfig == 0) chunkRadiusConfig = getRadiusInChunks();
+ }
+
+ if (mCurrentChunk != null && mChunkLoadingEnabled) {
+ GT_ChunkManager.releaseChunk((TileEntity) getBaseMetaTileEntity(), mCurrentChunk);
+ }
+
+ oreBlockPositions.clear();
+ createInitialWorkingChunk();
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ super.onScrewdriverRightClick(side, aPlayer, aX, aY, aZ);
+
+ if (getBaseMetaTileEntity().isActive()) {
+ GT_Utility.sendChatToPlayer(aPlayer, StatCollector.translateToLocal("GT5U.machines.workarea_fail"));
+ } else {
+ adjustChunkRadius(!aPlayer.isSneaking());
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ StatCollector.translateToLocal("GT5U.machines.workareaset") + " "
+ + GT_Utility.formatNumbers((long) chunkRadiusConfig << 4)
+ + " "
+ + StatCollector.translateToLocal("GT5U.machines.radius"));
+ }
+ }
+
+ @Override
+ public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer,
+ float aX, float aY, float aZ) {
+ replaceWithCobblestone = !replaceWithCobblestone;
+ GT_Utility.sendChatToPlayer(aPlayer, "Replace with cobblestone " + replaceWithCobblestone);
+ return true;
+ }
+
+ @Override
+ protected boolean workingDownward(ItemStack aStack, int xDrill, int yDrill, int zDrill, int xPipe, int zPipe,
+ int yHead, int oldYHead) {
+ if (yHead != oldYHead) oreBlockPositions.clear();
+
+ if (mWorkChunkNeedsReload && mChunkLoadingEnabled) { // ask to load machine itself
+ GT_ChunkManager.requestChunkLoad((TileEntity) getBaseMetaTileEntity(), null);
+ mWorkChunkNeedsReload = false;
+ }
+ fillMineListIfEmpty(xDrill, yDrill, zDrill, xPipe, zPipe, yHead);
+ if (oreBlockPositions.isEmpty()) {
+ if (veinName == null) {
+ updateVeinNameFromVP(getDrillCoords());
+ }
+
+ switch (tryLowerPipeState()) {
+ case 2 -> {
+ mMaxProgresstime = 0;
+ setRuntimeFailureReason(CheckRecipeResultRegistry.MISSING_MINING_PIPE);
+ return false;
+ }
+ case 3 -> {
+ workState = STATE_UPWARD;
+ return true;
+ }
+ case 1 -> {
+ workState = STATE_AT_BOTTOM;
+ return true;
+ }
+ }
+ // new layer - fill again
+ fillMineListIfEmpty(xDrill, yDrill, zDrill, xPipe, zPipe, yHead);
+ }
+ return tryProcessOreList();
+ }
+
+ private boolean tryProcessOreList() {
+ // Even though it works fine without this check,
+ // it can save tiny amount of CPU time when void protection is disabled
+ if (protectsExcessItem()) {
+ boolean simulateResult = processOreList(true);
+ if (!simulateResult) {
+ mEUt = 0;
+ mMaxProgresstime = 0;
+ return false;
+ }
+ }
+
+ boolean result = processOreList(false);
+ if (!result) {
+ mEUt = 0;
+ mMaxProgresstime = 0;
+ return false;
+ }
+ return true;
+ }
+
+ private boolean processOreList(boolean simulate) {
+ ChunkPosition oreBlockPos = null;
+ List<ChunkPosition> oreBlockPositions = simulate ? copyOreBlockPositions(this.oreBlockPositions)
+ : this.oreBlockPositions;
+ int x = 0, y = 0, z = 0;
+ Block oreBlock = null;
+ int oreBlockMetadata = 0;
+
+ while ((oreBlock == null || !GT_Utility.isOre(oreBlock, oreBlockMetadata)) && !oreBlockPositions.isEmpty()) {
+ oreBlockPos = oreBlockPositions.remove(0);
+ x = oreBlockPos.chunkPosX;
+ y = oreBlockPos.chunkPosY;
+ z = oreBlockPos.chunkPosZ;
+ if (GT_Utility.eraseBlockByFakePlayer(getFakePlayer(getBaseMetaTileEntity()), x, y, z, true))
+ oreBlock = getBaseMetaTileEntity().getBlock(x, y, z);
+ oreBlockMetadata = getBaseMetaTileEntity().getWorld()
+ .getBlockMetadata(x, y, z);
+ }
+
+ if (!tryConsumeDrillingFluid(simulate)) {
+ oreBlockPositions.add(0, oreBlockPos);
+ setRuntimeFailureReason(CheckRecipeResultRegistry.NO_DRILLING_FLUID);
+ return false;
+ }
+ if (oreBlock != null && GT_Utility.isOre(oreBlock, oreBlockMetadata)) {
+ short metaData = 0;
+ TileEntity tTileEntity = getBaseMetaTileEntity().getTileEntity(x, y, z);
+ if (tTileEntity instanceof GT_TileEntity_Ores) {
+ metaData = ((GT_TileEntity_Ores) tTileEntity).mMetaData;
+ }
+
+ Collection<ItemStack> oreBlockDrops = getBlockDrops(oreBlock, x, y, z);
+ ItemStack cobble = GT_Utility.getCobbleForOre(oreBlock, metaData);
+ if (!simulate) {
+ if (replaceWithCobblestone) {
+ getBaseMetaTileEntity().getWorld()
+ .setBlock(x, y, z, Block.getBlockFromItem(cobble.getItem()), cobble.getItemDamage(), 3);
+ } else {
+ getBaseMetaTileEntity().getWorld()
+ .setBlockToAir(oreBlockPos.chunkPosX, oreBlockPos.chunkPosY, oreBlockPos.chunkPosZ);
+ }
+ }
+ ItemStack[] toOutput = getOutputByDrops(oreBlockDrops);
+ if (simulate && !canOutputAll(toOutput)) {
+ setRuntimeFailureReason(CheckRecipeResultRegistry.ITEM_OUTPUT_FULL);
+ return false;
+ }
+ mOutputItems = toOutput;
+ }
+ return true;
+ }
+
+ private static List<ChunkPosition> copyOreBlockPositions(List<ChunkPosition> oreBlockPositions) {
+ List<ChunkPosition> ret = new ArrayList<>();
+ for (ChunkPosition chunkPosition : oreBlockPositions) {
+ ret.add(new ChunkPosition(chunkPosition.chunkPosX, chunkPosition.chunkPosY, chunkPosition.chunkPosZ));
+ }
+ return ret;
+ }
+
+ @Override
+ protected boolean workingAtBottom(ItemStack aStack, int xDrill, int yDrill, int zDrill, int xPipe, int zPipe,
+ int yHead, int oldYHead) {
+ if (!mChunkLoadingEnabled)
+ return super.workingAtBottom(aStack, xDrill, yDrill, zDrill, xPipe, zPipe, yHead, oldYHead);
+
+ if (mCurrentChunk == null) {
+ createInitialWorkingChunk();
+ return true;
+ }
+
+ if (mWorkChunkNeedsReload) {
+ GT_ChunkManager.requestChunkLoad((TileEntity) getBaseMetaTileEntity(), mCurrentChunk);
+ mWorkChunkNeedsReload = false;
+ return true;
+ }
+ if (oreBlockPositions.isEmpty()) {
+ fillChunkMineList(yHead, yDrill);
+ if (oreBlockPositions.isEmpty()) {
+ GT_ChunkManager.releaseChunk((TileEntity) getBaseMetaTileEntity(), mCurrentChunk);
+ if (!moveToNextChunk(xDrill >> 4, zDrill >> 4)) {
+ workState = STATE_UPWARD;
+ updateVeinNameFromVP();
+ }
+ return true;
+ }
+ }
+ return tryProcessOreList();
+ }
+
+ private void createInitialWorkingChunk() {
+ mCurrentChunk = getTopLeftChunkCoords();
+ updateVeinNameFromVP();
+ if (mChunkLoadingEnabled) {
+ GT_ChunkManager.requestChunkLoad((TileEntity) getBaseMetaTileEntity(), mCurrentChunk);
+ mWorkChunkNeedsReload = false;
+ }
+ }
+
+ @NotNull
+ private ChunkCoordIntPair getTopLeftChunkCoords() {
+ return getCornerCoords(-1, -1);
+ }
+
+ @NotNull
+ private ChunkCoordIntPair getBottomRightChunkCoords() {
+ return getCornerCoords(1, 1);
+ }
+
+ @NotNull
+ private ChunkCoordIntPair getCornerCoords(int xMultiplier, int zMultiplier) {
+ final ChunkCoordIntPair drillPos = getDrillCoords();
+ // use corner closest to the drill as mining area center
+ return new ChunkCoordIntPair(
+ drillPos.chunkXPos + xMultiplier * chunkRadiusConfig
+ + ((getXDrill() - (drillPos.chunkXPos << 4)) < 8 ? 0 : 1),
+ drillPos.chunkZPos + zMultiplier * chunkRadiusConfig
+ + ((getZDrill() - (drillPos.chunkZPos << 4)) < 8 ? 0 : 1));
+ }
+
+ @NotNull
+ private ChunkCoordIntPair getDrillCoords() {
+ return new ChunkCoordIntPair(getXDrill() >> 4, getZDrill() >> 4);
+ }
+
+ private int getTotalChunkCount() {
+ final ChunkCoordIntPair topLeft = getTopLeftChunkCoords();
+ final ChunkCoordIntPair bottomRight = getBottomRightChunkCoords();
+ return (bottomRight.chunkXPos - topLeft.chunkXPos) * (bottomRight.chunkZPos - topLeft.chunkZPos);
+ }
+
+ /**
+ * Returns a number corresponding to which chunk the drill is operating on. Only really useful for driving outputs
+ * in the controller UI.
+ *
+ * @return 0 if the miner is not in operation, positive integer corresponding to the chunk currently being drilled
+ */
+ @SuppressWarnings("ExtractMethodRecommender")
+ private int getChunkNumber() {
+ if (mCurrentChunk == null) {
+ return 0;
+ }
+
+ final ChunkCoordIntPair topLeft = getTopLeftChunkCoords();
+ final ChunkCoordIntPair drillPos = getDrillCoords();
+
+ if (workState == STATE_DOWNWARD) {
+ return 1;
+ } else if (workState == STATE_UPWARD) {
+ // Technically, the miner isn't mining anything now; it's retracting the pipes in preparation to end
+ // operation.
+ return 0;
+ }
+
+ int chunkNumber = (chunkRadiusConfig * 2) * (mCurrentChunk.chunkZPos - topLeft.chunkZPos)
+ + mCurrentChunk.chunkXPos
+ - topLeft.chunkXPos
+ + 1;
+
+ // Drills mine the chunk they're in first, so if we're not there yet, bump the number to indicate that it
+ // was already mined.
+ if (mCurrentChunk.chunkZPos < drillPos.chunkZPos
+ || (mCurrentChunk.chunkZPos == drillPos.chunkZPos && mCurrentChunk.chunkXPos < drillPos.chunkXPos)) {
+ chunkNumber += 1;
+ }
+ return chunkNumber;
+ }
+
+ @Override
+ protected boolean workingUpward(ItemStack aStack, int xDrill, int yDrill, int zDrill, int xPipe, int zPipe,
+ int yHead, int oldYHead) {
+ boolean result;
+ if (!mChunkLoadingEnabled || oreBlockPositions.isEmpty()) {
+ result = super.workingUpward(aStack, xDrill, yDrill, zDrill, xPipe, zPipe, yHead, oldYHead);
+ } else {
+ result = tryProcessOreList();
+ if (oreBlockPositions.isEmpty()) GT_ChunkManager.releaseTicket((TileEntity) getBaseMetaTileEntity());
+ }
+
+ if (!result) {
+ setShutdownReason(StatCollector.translateToLocal("GT5U.gui.text.drill_exhausted"));
+ }
+
+ return result;
+ }
+
+ @Override
+ protected void onAbort() {
+ oreBlockPositions.clear();
+ if (mCurrentChunk != null) {
+ GT_ChunkManager.releaseChunk((TileEntity) getBaseMetaTileEntity(), mCurrentChunk);
+ }
+ mCurrentChunk = null;
+ updateVeinNameFromVP();
+ }
+
+ private boolean moveToNextChunk(int centerX, int centerZ) {
+ if (mCurrentChunk == null) return false;
+ // use corner closest to the drill as mining area center
+ final int left = centerX - chunkRadiusConfig + ((getXDrill() - (centerX << 4)) < 8 ? 0 : 1);
+ final int right = left + chunkRadiusConfig * 2;
+ final int bottom = centerZ + chunkRadiusConfig + ((getZDrill() - (centerZ << 4)) < 8 ? 0 : 1);
+
+ int nextChunkX = mCurrentChunk.chunkXPos + 1;
+ int nextChunkZ = mCurrentChunk.chunkZPos;
+
+ // step to the next chunk
+ if (nextChunkX >= right) {
+ nextChunkX = left;
+ ++nextChunkZ;
+ }
+ // skip center chunk - dug in workingDownward()
+ if (nextChunkX == centerX && nextChunkZ == centerZ) {
+ ++nextChunkX;
+
+ if (nextChunkX >= right) {
+ nextChunkX = left;
+ ++nextChunkZ;
+ }
+ }
+
+ if (nextChunkZ >= bottom) {
+ mCurrentChunk = null;
+ return false;
+ }
+
+ mCurrentChunk = new ChunkCoordIntPair(nextChunkX, nextChunkZ);
+ updateVeinNameFromVP();
+
+ GT_ChunkManager
+ .requestChunkLoad((TileEntity) getBaseMetaTileEntity(), new ChunkCoordIntPair(nextChunkX, nextChunkZ));
+ return true;
+ }
+
+ private void updateVeinNameFromVP() {
+ updateVeinNameFromVP(mCurrentChunk);
+ }
+
+ private void updateVeinNameFromVP(@NotNull ChunkCoordIntPair coords) {
+ veinName = GT_VisualProspecting_Database
+ .getVeinName(getBaseMetaTileEntity().getWorld().provider.dimensionId, coords)
+ .orElse(null);
+ }
+
+ @Override
+ protected boolean checkHatches() {
+ return !mMaintenanceHatches.isEmpty() && !mInputHatches.isEmpty()
+ && !mOutputBusses.isEmpty()
+ && !mEnergyHatches.isEmpty();
+ }
+
+ @Override
+ protected List<IHatchElement<? super GT_MetaTileEntity_DrillerBase>> getAllowedHatches() {
+ return ImmutableList.of(InputHatch, InputBus, OutputBus, Maintenance, Energy);
+ }
+
+ @Override
+ protected void setElectricityStats() {
+ this.mEfficiency = getCurrentEfficiency(null);
+ this.mEfficiencyIncrease = 10000;
+ int tier = Math.max(1, GT_Utility.getTier(getMaxInputVoltage()));
+ this.mEUt = -3 * (1 << (tier << 1));
+ this.mMaxProgresstime = calculateMaxProgressTime(tier);
+ }
+
+ private int calculateMaxProgressTime(int tier) {
+ return Math.max(
+ 1,
+ ((workState == STATE_DOWNWARD || workState == STATE_AT_BOTTOM) ? getBaseProgressTime() : 80) / (1 << tier));
+ }
+
+ private ItemStack[] getOutputByDrops(Collection<ItemStack> oreBlockDrops) {
+ long voltage = getMaxInputVoltage();
+ Collection<ItemStack> outputItems = new HashSet<>();
+ oreBlockDrops.forEach(currentItem -> {
+ if (!doUseMaceratorRecipe(currentItem)) {
+ outputItems.add(multiplyStackSize(currentItem));
+ return;
+ }
+ GT_Recipe tRecipe = RecipeMaps.maceratorRecipes
+ .findRecipe(getBaseMetaTileEntity(), false, voltage, null, currentItem);
+ if (tRecipe == null) {
+ outputItems.add(currentItem);
+ return;
+ }
+ for (int i = 0; i < tRecipe.mOutputs.length; i++) {
+ ItemStack recipeOutput = tRecipe.mOutputs[i].copy();
+ if (getBaseMetaTileEntity().getRandomNumber(10000) < tRecipe.getOutputChance(i))
+ multiplyStackSize(recipeOutput);
+ outputItems.add(recipeOutput);
+ }
+ });
+ return outputItems.toArray(new ItemStack[0]);
+ }
+
+ private boolean doUseMaceratorRecipe(ItemStack currentItem) {
+ ItemData itemData = GT_OreDictUnificator.getItemData(currentItem);
+ return itemData == null || itemData.mPrefix != OrePrefixes.crushed && itemData.mPrefix != OrePrefixes.dustImpure
+ && itemData.mPrefix != OrePrefixes.dust
+ && itemData.mPrefix != OrePrefixes.gem
+ && itemData.mPrefix != OrePrefixes.gemChipped
+ && itemData.mPrefix != OrePrefixes.gemExquisite
+ && itemData.mPrefix != OrePrefixes.gemFlawed
+ && itemData.mPrefix != OrePrefixes.gemFlawless
+ && itemData.mMaterial.mMaterial != Materials.Oilsands;
+ }
+
+ private ItemStack multiplyStackSize(ItemStack itemStack) {
+ itemStack.stackSize *= getBaseMetaTileEntity().getRandomNumber(4) + 1;
+ return itemStack;
+ }
+
+ private Collection<ItemStack> getBlockDrops(final Block oreBlock, int posX, int posY, int posZ) {
+ final int blockMeta = getBaseMetaTileEntity().getMetaID(posX, posY, posZ);
+ if (oreBlock.canSilkHarvest(getBaseMetaTileEntity().getWorld(), null, posX, posY, posZ, blockMeta)) {
+ return Collections.singleton(new ItemStack(oreBlock, 1, blockMeta));
+ }
+ if (oreBlock instanceof GT_Block_Ores_Abstract) {
+ TileEntity tTileEntity = getBaseMetaTileEntity().getTileEntity(posX, posY, posZ);
+ if (tTileEntity instanceof GT_TileEntity_Ores && ((GT_TileEntity_Ores) tTileEntity).mMetaData >= 16000) {
+ // GT_Log.out.println("Running Small Ore");
+ return oreBlock.getDrops(getBaseMetaTileEntity().getWorld(), posX, posY, posZ, blockMeta, mTier + 3);
+ }
+ }
+ // GT_Log.out.println("Running Normal Ore");
+ return oreBlock.getDrops(getBaseMetaTileEntity().getWorld(), posX, posY, posZ, blockMeta, 0);
+ }
+
+ private boolean tryConsumeDrillingFluid(boolean simulate) {
+ return depleteInput(new FluidStack(ItemList.sDrillingFluid, 2000), simulate);
+ }
+
+ private void fillChunkMineList(int yHead, int yDrill) {
+ if (mCurrentChunk == null || !oreBlockPositions.isEmpty()) return;
+ final int minX = mCurrentChunk.chunkXPos << 4;
+ final int maxX = minX + 16;
+ final int minZ = mCurrentChunk.chunkZPos << 4;
+ final int maxZ = minZ + 16;
+ for (int x = minX; x < maxX; ++x)
+ for (int z = minZ; z < maxZ; ++z) for (int y = yHead; y < yDrill; ++y) tryAddOreBlockToMineList(x, y, z);
+ }
+
+ private void fillMineListIfEmpty(int xDrill, int yDrill, int zDrill, int xPipe, int zPipe, int yHead) {
+ if (!oreBlockPositions.isEmpty()) return;
+
+ tryAddOreBlockToMineList(xPipe, yHead - 1, zPipe);
+ if (yHead == yDrill) return; // skip controller block layer
+
+ if (mChunkLoadingEnabled) {
+ int startX = (xDrill >> 4) << 4;
+ int startZ = (zDrill >> 4) << 4;
+ for (int x = startX; x < (startX + 16); ++x)
+ for (int z = startZ; z < (startZ + 16); ++z) tryAddOreBlockToMineList(x, yHead, z);
+ } else {
+ int radius = chunkRadiusConfig << 4;
+ for (int xOff = -radius; xOff <= radius; xOff++) for (int zOff = -radius; zOff <= radius; zOff++)
+ tryAddOreBlockToMineList(xDrill + xOff, yHead, zDrill + zOff);
+ }
+ }
+
+ private void tryAddOreBlockToMineList(int x, int y, int z) {
+ Block block = getBaseMetaTileEntity().getBlock(x, y, z);
+ int blockMeta = getBaseMetaTileEntity().getMetaID(x, y, z);
+ ChunkPosition blockPos = new ChunkPosition(x, y, z);
+ if (!oreBlockPositions.contains(blockPos)) {
+ if (block instanceof GT_Block_Ores_Abstract) {
+ TileEntity tTileEntity = getBaseMetaTileEntity().getTileEntity(x, y, z);
+ if (tTileEntity instanceof GT_TileEntity_Ores && ((GT_TileEntity_Ores) tTileEntity).mNatural)
+ oreBlockPositions.add(blockPos);
+ } else if (GT_Utility.isOre(block, blockMeta)) oreBlockPositions.add(blockPos);
+ }
+ }
+
+ protected abstract int getRadiusInChunks();
+
+ protected abstract int getBaseProgressTime();
+
+ protected GT_Multiblock_Tooltip_Builder createTooltip(String tierSuffix) {
+ String casings = getCasingBlockItem().get(0)
+ .getDisplayName();
+
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ final int baseCycleTime = calculateMaxProgressTime(getMinTier());
+ tt.addMachineType("Miner")
+ .addInfo("Controller Block for the Ore Drilling Plant " + (tierSuffix != null ? tierSuffix : ""))
+ .addInfo("Use a Screwdriver to configure block radius")
+ .addInfo("Maximum radius is " + GT_Utility.formatNumbers((long) getRadiusInChunks() << 4) + " blocks")
+ .addInfo("Use Soldering iron to turn off chunk mode")
+ .addInfo("Use Wire Cutter to toggle replacing mined blocks with cobblestone")
+ .addInfo("In chunk mode, working area center is the chunk corner nearest to the drill")
+ .addInfo("Gives ~3x as much crushed ore vs normal processing")
+ .addInfo("Fortune bonus of " + GT_Utility.formatNumbers(mTier + 3) + ". Only works on small ores")
+ .addInfo("Minimum energy hatch tier: " + TIER_COLORS[getMinTier()] + VN[getMinTier()])
+ .addInfo(
+ "Base cycle time: " + (baseCycleTime < 20 ? GT_Utility.formatNumbers(baseCycleTime) + " ticks"
+ : GT_Utility.formatNumbers(baseCycleTime / 20) + " seconds"))
+ .addSeparator()
+ .beginStructureBlock(3, 7, 3, false)
+ .addController("Front bottom")
+ .addOtherStructurePart(casings, "form the 3x1x3 Base")
+ .addOtherStructurePart(casings, "1x3x1 pillar above the center of the base (2 minimum total)")
+ .addOtherStructurePart(getFrameMaterial().mName + " Frame Boxes", "Each pillar's side and 1x3x1 on top")
+ .addEnergyHatch(VN[getMinTier()] + "+, Any base casing", 1)
+ .addMaintenanceHatch("Any base casing", 1)
+ .addInputBus("Mining Pipes, optional, any base casing", 1)
+ .addInputHatch("Drilling Fluid, any base casing", 1)
+ .addOutputBus("Any base casing", 1)
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ protected static final NumberFormatMUI numberFormat = new NumberFormatMUI();
+
+ @Override
+ protected void drawTexts(DynamicPositionedColumn screenElements, SlotWidget inventorySlot) {
+ super.drawTexts(screenElements, inventorySlot);
+ screenElements
+ .widget(
+ new TextWidget()
+ .setStringSupplier(
+ () -> EnumChatFormatting.GRAY + StatCollector.translateToLocalFormatted(
+ "GT5U.gui.text.drill_ores_left_chunk",
+ numberFormat.format(clientOreListSize)))
+ .setTextAlignment(Alignment.CenterLeft)
+ .setEnabled(
+ widget -> getBaseMetaTileEntity().isActive() && clientOreListSize > 0
+ && workState == STATE_AT_BOTTOM))
+ .widget(
+ new TextWidget()
+ .setStringSupplier(
+ () -> EnumChatFormatting.GRAY + StatCollector.translateToLocalFormatted(
+ "GT5U.gui.text.drill_ores_left_layer",
+ numberFormat.format(clientYHead),
+ numberFormat.format(clientOreListSize)))
+ .setTextAlignment(Alignment.CenterLeft)
+ .setEnabled(
+ widget -> getBaseMetaTileEntity().isActive() && clientYHead > 0 && workState == STATE_DOWNWARD))
+ .widget(
+ new TextWidget()
+ .setStringSupplier(
+ () -> EnumChatFormatting.GRAY + StatCollector.translateToLocalFormatted(
+ "GT5U.gui.text.drill_chunks_left",
+ numberFormat.format(clientCurrentChunk),
+ numberFormat.format(clientTotalChunks)))
+ .setTextAlignment(Alignment.CenterLeft)
+ .setEnabled(
+ widget -> getBaseMetaTileEntity().isActive() && clientCurrentChunk > 0
+ && workState == STATE_AT_BOTTOM))
+ .widget(
+ new TextWidget()
+ .setStringSupplier(
+ () -> EnumChatFormatting.GRAY
+ + StatCollector.translateToLocalFormatted("GT5U.gui.text.drill_current_vein", veinName))
+ .setTextAlignment(Alignment.CenterLeft)
+ .setEnabled(
+ widget -> veinName != null && (workState == STATE_AT_BOTTOM || workState == STATE_DOWNWARD)))
+ .widget(new FakeSyncWidget.IntegerSyncer(oreBlockPositions::size, (newInt) -> clientOreListSize = newInt))
+ .widget(new FakeSyncWidget.IntegerSyncer(this::getTotalChunkCount, (newInt) -> clientTotalChunks = newInt))
+ .widget(new FakeSyncWidget.IntegerSyncer(this::getChunkNumber, (newInt) -> clientCurrentChunk = newInt))
+ .widget(new FakeSyncWidget.IntegerSyncer(() -> workState, (newInt) -> workState = newInt))
+ .widget(new FakeSyncWidget.IntegerSyncer(this::getYHead, (newInt) -> clientYHead = newInt))
+ .widget(new FakeSyncWidget.StringSyncer(() -> veinName, (newString) -> veinName = newString));
+ }
+
+ @Override
+ protected List<ButtonWidget> getAdditionalButtons(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ return ImmutableList.of(
+ (ButtonWidget) new GT_LockedWhileActiveButton(this.getBaseMetaTileEntity(), builder)
+ .setOnClick((clickData, widget) -> adjustChunkRadius(clickData.mouseButton == 0))
+ .setPlayClickSound(true)
+ .setBackground(GT_UITextures.BUTTON_STANDARD, GT_UITextures.OVERLAY_BUTTON_WORK_AREA)
+ .attachSyncer(
+ new FakeSyncWidget.IntegerSyncer(() -> chunkRadiusConfig, (val) -> chunkRadiusConfig = val),
+ builder,
+ (widget, val) -> widget.notifyTooltipChange())
+ .dynamicTooltip(
+ () -> ImmutableList.of(
+ StatCollector.translateToLocalFormatted(
+ "GT5U.gui.button.ore_drill_radius_1",
+ GT_Utility.formatNumbers((long) chunkRadiusConfig << 4)),
+ StatCollector.translateToLocal("GT5U.gui.button.ore_drill_radius_2")))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setSize(16, 16),
+ (ButtonWidget) new GT_LockedWhileActiveButton(this.getBaseMetaTileEntity(), builder)
+ .setOnClick((clickData, widget) -> replaceWithCobblestone = !replaceWithCobblestone)
+ .setPlayClickSound(true)
+ .setBackground(() -> {
+ if (replaceWithCobblestone) {
+ return new IDrawable[] { GT_UITextures.BUTTON_STANDARD_PRESSED,
+ GT_UITextures.OVERLAY_BUTTON_REPLACE_COBBLE_ON };
+ }
+ return new IDrawable[] { GT_UITextures.BUTTON_STANDARD,
+ GT_UITextures.OVERLAY_BUTTON_REPLACE_COBBLE_OFF };
+ })
+ .attachSyncer(
+ new FakeSyncWidget.BooleanSyncer(
+ () -> replaceWithCobblestone,
+ (val) -> replaceWithCobblestone = val),
+ builder,
+ (widget, val) -> widget.notifyTooltipChange())
+ .dynamicTooltip(
+ () -> ImmutableList.of(
+ StatCollector.translateToLocal(
+ replaceWithCobblestone ? "GT5U.gui.button.ore_drill_cobblestone_on"
+ : "GT5U.gui.button.ore_drill_cobblestone_off")))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setSize(16, 16));
+ }
+
+ @Override
+ protected SoundResource getProcessStartSound() {
+ return SoundResource.IC2_MACHINES_MINER_OP;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ final String diameter = GT_Utility.formatNumbers(chunkRadiusConfig * 2L);
+ return new String[] {
+ EnumChatFormatting.BLUE + StatCollector.translateToLocal("GT5U.machines.minermulti")
+ + EnumChatFormatting.RESET,
+ StatCollector.translateToLocal("GT5U.machines.workarea") + ": "
+ + EnumChatFormatting.GREEN
+ + diameter
+ + "x"
+ + diameter
+ + EnumChatFormatting.RESET
+ + " "
+ + StatCollector.translateToLocal("GT5U.machines.chunks") };
+ }
+
+ @Override
+ public @NotNull List<String> reportMetrics() {
+ if (getBaseMetaTileEntity().isActive()) {
+ return switch (workState) {
+ case STATE_AT_BOTTOM -> ImmutableList.of(
+ StatCollector.translateToLocalFormatted(
+ "GT5U.gui.text.drill_ores_left_chunk",
+ GT_Utility.formatNumbers(oreBlockPositions.size())),
+ StatCollector.translateToLocalFormatted(
+ "GT5U.gui.text.drill_chunks_left",
+ GT_Utility.formatNumbers(getChunkNumber()),
+ GT_Utility.formatNumbers(getTotalChunkCount())),
+ veinName == null ? ""
+ : StatCollector.translateToLocalFormatted("GT5U.gui.text.drill_current_vein", veinName));
+ case STATE_DOWNWARD -> ImmutableList.of(
+ StatCollector.translateToLocalFormatted(
+ "GT5U.gui.text.drill_ores_left_layer",
+ getYHead(),
+ GT_Utility.formatNumbers(oreBlockPositions.size())),
+ veinName == null ? ""
+ : StatCollector.translateToLocalFormatted("GT5U.gui.text.drill_current_vein", veinName));
+ case STATE_UPWARD, STATE_ABORT -> ImmutableList
+ .of(StatCollector.translateToLocal("GT5U.gui.text.retracting_pipe"));
+
+ default -> ImmutableList.of();
+ };
+ }
+
+ return ImmutableList.of(
+ getFailureReason()
+ .map(reason -> StatCollector.translateToLocalFormatted("GT5U.gui.text.drill_offline_reason", reason))
+ .orElseGet(() -> StatCollector.translateToLocalFormatted("GT5U.gui.text.drill_offline_generic")));
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PCBFactory.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PCBFactory.java
new file mode 100644
index 0000000000..d07ad238a2
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PCBFactory.java
@@ -0,0 +1,1290 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.ExoticEnergy;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_Values.AuthorBlueWeabo;
+import static gregtech.api.enums.GT_Values.VN;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ASSEMBLY_LINE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ASSEMBLY_LINE_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ASSEMBLY_LINE_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_ASSEMBLY_LINE_GLOW;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_StructureUtility.ofFrame;
+import static gregtech.api.util.GT_Utility.filterValidMTEs;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
+import com.gtnewhorizon.structurelib.alignment.enumerable.Flip;
+import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.drawable.Text;
+import com.gtnewhorizons.modularui.api.drawable.UITexture;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.math.Color;
+import com.gtnewhorizons.modularui.api.math.Pos2d;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.CycleButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.DynamicPositionedColumn;
+import com.gtnewhorizons.modularui.common.widget.DynamicPositionedRow;
+import com.gtnewhorizons.modularui.common.widget.MultiChildWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+import com.gtnewhorizons.modularui.common.widget.textfield.NumericWidget;
+
+import blockrenderer6343.client.world.ClientFakePlayer;
+import cpw.mods.fml.common.Loader;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.Textures.BlockIcons;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.GregTechTileClientEvents;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Input;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Muffler;
+import gregtech.api.multitileentity.multiblock.casing.Glasses;
+import gregtech.api.objects.ItemData;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.recipe.check.SimpleCheckRecipeResult;
+import gregtech.api.recipe.metadata.PCBFactoryTierKey;
+import gregtech.api.recipe.metadata.PCBFactoryUpgrade;
+import gregtech.api.recipe.metadata.PCBFactoryUpgradeKey;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_OreDictUnificator;
+import gregtech.api.util.GT_OverclockCalculator;
+import gregtech.api.util.GT_ParallelHelper;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.shutdown.ShutDownReasonRegistry;
+import gregtech.common.blocks.GT_Block_Casings8;
+
+@SuppressWarnings("SpellCheckingInspection")
+public class GT_MetaTileEntity_PCBFactory extends
+ GT_MetaTileEntity_ExtendedPowerMultiBlockBase<GT_MetaTileEntity_PCBFactory> implements ISurvivalConstructable {
+
+ private static final String tier1 = "tier1";
+ private static final String tier2 = "tier2";
+ private static final String tier3 = "tier3";
+ private static final String bioUpgrade = "bioUpgrade";
+ private static final String ocTier1Upgrade = "ocTier1Upgrade";
+ private static final String ocTier2Upgrade = "ocTier2Upgrade";
+ private float mRoughnessMultiplier = 1;
+ private int mTier = 1, mSetTier = 1, mUpgradesInstalled = 0, mCurrentParallel = 0, mMaxParallel = 0;
+ private boolean mBioUpgrade = false, mBioRotate = false, mOCTier1 = false, mOCTier2 = false;
+ private final int[] mBioOffsets = new int[] { -5, -1 };
+ private final int[] mOCTier1Offsets = new int[] { 2, -11 };
+ private final int[] mOCTier2Offsets = new int[] { 2, -11 };
+ private GT_MetaTileEntity_Hatch_Input mCoolantInputHatch;
+ private static final int mBioRotateBitMap = 0b1000000;
+ private static final int mOCTier2BitMap = 0b100000;
+ private static final int mOCTier1BitMap = 0b10000;
+ private static final int mBioBitMap = 0b1000;
+ private static final int mTier3BitMap = 0b100;
+ private static final int mTier2BitMap = 0b10;
+ private static final int mTier1BitMap = 0b1;
+ private static final int COOLANT_CONSUMED_PER_SEC = 10;
+ private static final IStructureDefinition<GT_MetaTileEntity_PCBFactory> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_PCBFactory>builder()
+ .addShape(
+ tier1,
+ transpose(
+ new String[][] {
+ // spotless:off
+ {" ","E E","E E","EEEEEEE","E E","E E"," "},
+ {"EEEEEEE","CAAAAAC","CAAAAAC","CCCCCCC","CCCCCCC","CCCCCCC","E E"},
+ {"EAAAAAE","C-----C","C-----C","C-----C","C-----C","C-----C","ECCCCCE"},
+ {"EAAAAAE","C-----C","B-----B","B-----B","B-----B","C-----C","ECCCCCE"},
+ {"EAAAAAE","C-----C","B-FFF-B","B-FFF-B","B-FFF-B","C-----C","EPPPPPE"},
+ {"ECC~CCE","CDDDDDC","CDDDDDC","CDDDDDC","CDDDDDC","CDDDDDC","EPPPPPE"}
+ //spotless:on
+ }))
+ .addShape(
+ tier2,
+ transpose(
+ new String[][] {
+ // spotless:off
+ {" "," "," ","HGGH","HGGH","HGGH","HGGH","HGGH"," "," "," "},
+ {" "," ","HGGH","GGGG","GGGG","GGGG","GGGG","GGGG","HGGH"," "," "},
+ {" ","HGGH","GGGG","G G","G G","G G","G G","G G","GGGG","HGGH"," "},
+ {" ","HGGH","G G","G G","G G","G G","G G","G G","G G","HGGH"," "},
+ {"HGGH","G G","G G","G G","G G","G G","G G","G G","G G","G G","HGGH"},
+ {"HGGH","G G","G G","G G","G G","G G","G G","G G","G G","G G","HGGH"},
+ {"HGGH","GGGG","GGGG","GGGG","GGGG","GGGG","GGGG","GGGG","GGGG","GGGG","HGGH"}
+ //spotless:on
+ }))
+ .addShape(
+ tier3,
+ transpose(
+ new String[][] {
+ // spotless:off
+ {" "," "," "," "," I "," I "," "," "," "," "},
+ {" "," "," "," I "," I "," I "," I "," "," "," "},
+ {" "," "," KKK "," KIK "," K K "," K K "," I "," "," "," "},
+ {" "," "," KKK "," K K "," K K "," K K "," I "," "," "," "},
+ {" "," III "," I I "," I I "," I I "," K K "," I "," "," "," "},
+ {" "," III "," I I "," I I "," I I "," K K "," I "," "," "," "},
+ {" "," III "," I I "," I I "," I I "," K K "," KIK "," "," "," "},
+ {" "," I I "," I K I "," I I "," I I "," K K "," KIK "," "," "," "},
+ {" "," I I "," I K I "," I I "," I I "," K K "," K K "," KKK "," "," "},
+ {" "," I I "," I K I "," I I "," I I "," K K "," K K "," KKK "," "," "},
+ {" "," III "," I I "," I I "," I I "," I I "," I I "," I I "," III "," "},
+ {" "," III "," I I "," K K "," K K "," K K "," K K "," I I "," III "," "},
+ {" "," III "," I I "," I I "," I I "," I I "," I I "," I I "," III "," "},
+ {" "," "," KKK "," K K "," K K "," I I "," I I "," I I "," III "," "},
+ {" "," "," KKK "," K K "," K K "," I I "," I I "," I I "," III "," "},
+ {" "," "," KKK "," K K "," K K "," I I "," I I "," I I "," III "," "},
+ {" "," III "," I I "," I I "," I I "," I I "," I I "," I I "," III "," "},
+ {" "," III "," I I "," I I "," I I "," I I "," I I "," I I "," III "," "},
+ {" "," III "," I I "," I I "," I I "," I I "," I I "," I I "," III "," "},
+ {" "," III "," I I "," I I "," I I "," I I "," I I "," I I "," III "," "},
+ {" "," III "," I I "," I I "," I I "," I I "," I I "," I I "," III "," "},
+ {" II~II ","IIJJJII","IJJJJJI","IJJJJJI","IJJJJJI","IJJJJJI","IJJJJJI","IJJJJJI","IIJJJII"," IIIII "}
+ //spotless:on
+ }))
+ .addShape(
+ bioUpgrade,
+ transpose(
+ new String[][] {
+ // spotless:off
+ {" "," "," LLLLLL "," "," "},
+ {" "," "," L L "," "," "},
+ {"E E E E"," LLL LLL "," LLL LLL "," LLL LLL ","E E E E"},
+ {"EAAAE EAAAE","A A A A","A A A A","A A A A","EAAAE EAAAE"},
+ {"EAAAE EAAAE","A A A A","A A A A","A A A A","EAAAE EAAAE"},
+ {"EAAAE EAAAE","A A A A","A A A A","A A A A","EAAAE EAAAE"},
+ {"ELLLE ELLLE","LLLLL LLLLL","LLLLL LLLLL","LLLLL LLLLL","ELLLE ELLLE"}
+ //spotless:on
+ }))
+ .addShape(
+ ocTier1Upgrade,
+ transpose(
+ new String[][] {
+ // spotless:off
+ {"EKKKE","K K","K K","K K","EKKKE"},
+ {"E E"," KKK "," K K "," KKK ","E E"},
+ {"E E"," NNN "," N N "," NNN ","E E"},
+ {"E E"," KKK "," K K "," KKK ","E E"},
+ {"E E"," KKK "," K K "," KKK ","E E"},
+ {"EOOOE","OKKKO","OK KO","OKKKO","EOOOE"},
+ {"E E"," KKK "," K K "," KKK ","E E"},
+ {"E E"," KKK "," K K "," KKK ","E E"},
+ {"ENNNE","NKKKN","NK KN","NKKKN","ENNNE"},
+ {"EGGGE","GGGGG","GGMGG","GGGGG","EGGGE"}
+ //spotless:on
+ }))
+ .addShape(
+ ocTier2Upgrade,
+ transpose(
+ new String[][] {
+ // spotless:off
+ {"RGGGR","G G","G G","G G","RGGGR"},
+ {"R R"," GGG "," GTG "," GGG ","R R"},
+ {"R R"," NNN "," NTN "," NNN ","R R"},
+ {"R R"," QQQ "," QTQ "," QQQ ","R R"},
+ {"R R"," QQQ "," QTQ "," QQQ ","R R"},
+ {"R R"," QQQ "," QTQ "," QQQ ","R R"},
+ {"R R"," QQQ "," QTQ "," QQQ ","R R"},
+ {"R R"," QQQ "," QTQ "," QQQ ","R R"},
+ {"RNNNR","NQQQN","NQTQN","NQQQN","RNNNR"},
+ {"RGGGR","GGGGG","GGSGG","GGGGG","RGGGR"}
+ //spotless:on
+ }))
+ .addElement('E', ofFrame(Materials.DamascusSteel))
+ .addElement('C', ofBlock(GregTech_API.sBlockCasings8, 11))
+ .addElement('D', ofBlock(GregTech_API.sBlockReinforced, 2))
+ .addElement('A', Glasses.chainAllGlasses())
+ .addElement('B', ofBlock(GregTech_API.sBlockCasings3, 10))
+ .addElement('F', ofFrame(Materials.VibrantAlloy))
+ .addElement(
+ 'P',
+ buildHatchAdder(GT_MetaTileEntity_PCBFactory.class)
+ .atLeast(InputHatch, OutputBus, InputBus, Maintenance, Energy.or(ExoticEnergy))
+ .dot(1)
+ .casingIndex(((GT_Block_Casings8) GregTech_API.sBlockCasings8).getTextureIndex(11))
+ .buildAndChain(GregTech_API.sBlockCasings8, 11))
+ .addElement('H', ofFrame(Materials.Duranium))
+ .addElement('G', ofBlock(GregTech_API.sBlockCasings8, 12))
+ .addElement('I', ofBlock(GregTech_API.sBlockCasings8, 13))
+ .addElement('K', ofBlock(GregTech_API.sBlockCasings8, 10))
+ .addElement(
+ 'J',
+ buildHatchAdder(GT_MetaTileEntity_PCBFactory.class)
+ .atLeast(InputHatch, OutputBus, InputBus, Maintenance, Energy.or(ExoticEnergy))
+ .dot(1)
+ .casingIndex(((GT_Block_Casings8) GregTech_API.sBlockCasings8).getTextureIndex(13))
+ .buildAndChain(GregTech_API.sBlockCasings8, 13))
+ .addElement('L', ofBlock(GregTech_API.sBlockCasings4, 1))
+ .addElement(
+ 'M',
+ buildHatchAdder(GT_MetaTileEntity_PCBFactory.class).hatchClass(GT_MetaTileEntity_Hatch_Input.class)
+ .adder(GT_MetaTileEntity_PCBFactory::addCoolantInputToMachineList)
+ .casingIndex(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings8, 12))
+ .dot(2)
+ .buildAndChain(GregTech_API.sBlockCasings8, 12))
+ .addElement('N', ofBlock(GregTech_API.sBlockCasings2, 15))
+ .addElement('O', ofBlock(GregTech_API.sBlockCasings8, 4))
+ .addElement(
+ 'S',
+ buildHatchAdder(GT_MetaTileEntity_PCBFactory.class).hatchClass(GT_MetaTileEntity_Hatch_Input.class)
+ .adder(GT_MetaTileEntity_PCBFactory::addCoolantInputToMachineList)
+ .casingIndex(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings8, 12))
+ .dot(2)
+ .buildAndChain(GregTech_API.sBlockCasings8, 12))
+ .addElement('R', ofFrame(Materials.Americium))
+ .addElement('Q', ofBlock(GregTech_API.sBlockCasings8, 14))
+ .addElement('T', ofBlock(GregTech_API.sBlockCasings1, 15))
+ .build();
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ if (mSetTier < 3) {
+ buildPiece(tier1, stackSize, hintsOnly, 3, 5, 0);
+ if (mSetTier == 2) {
+ buildPiece(tier2, stackSize, hintsOnly, 7, 6, 2);
+ }
+ } else {
+ buildPiece(tier3, stackSize, hintsOnly, 3, 21, 0);
+ }
+
+ if (mBioUpgrade) {
+ if (mBioRotate) {
+ final IGregTechTileEntity tTile = getBaseMetaTileEntity();
+ getStructureDefinition().buildOrHints(
+ this,
+ stackSize,
+ bioUpgrade,
+ tTile.getWorld(),
+ transformFacing(getExtendedFacing()),
+ tTile.getXCoord(),
+ tTile.getYCoord(),
+ tTile.getZCoord(),
+ mBioOffsets[1],
+ 6,
+ mBioOffsets[0],
+ hintsOnly);
+ } else {
+ buildPiece(bioUpgrade, stackSize, hintsOnly, mBioOffsets[0], 6, mBioOffsets[1]);
+ }
+ }
+
+ if (mOCTier1 && !mOCTier2) {
+ buildPiece(ocTier1Upgrade, stackSize, hintsOnly, mOCTier1Offsets[0], 9, mOCTier1Offsets[1]);
+ }
+
+ if (!mOCTier1 && mOCTier2) {
+ buildPiece(ocTier2Upgrade, stackSize, hintsOnly, mOCTier2Offsets[0], 9, mOCTier2Offsets[1]);
+ }
+ }
+
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ int built = 0;
+ if (Loader.isModLoaded("blockrenderer6343") && env.getActor() instanceof ClientFakePlayer) {
+ if (stackSize.stackSize < 3) {
+ built += survivialBuildPiece(tier1, stackSize, 3, 5, 0, elementBudget, env, false, false);
+ if (stackSize.stackSize == 2) {
+ built += survivialBuildPiece(tier2, stackSize, 7, 6, 2, elementBudget, env, false, false);
+ }
+ } else {
+ built += survivialBuildPiece(tier3, stackSize, 3, 21, 0, elementBudget, env, false, false);
+ }
+ return built;
+ }
+ if (mSetTier < 3) {
+ built += survivialBuildPiece(tier1, stackSize, 3, 5, 0, elementBudget, env, false, true);
+ if (mSetTier == 2) {
+ built += survivialBuildPiece(tier2, stackSize, 7, 6, 2, elementBudget, env, false, true);
+ }
+ } else {
+ built += survivialBuildPiece(tier3, stackSize, 3, 21, 0, elementBudget, env, false, true);
+ }
+
+ if (mBioUpgrade) {
+ if (mBioRotate) {
+ final IGregTechTileEntity tTile = getBaseMetaTileEntity();
+ getStructureDefinition().survivalBuild(
+ this,
+ stackSize,
+ bioUpgrade,
+ tTile.getWorld(),
+ transformFacing(getExtendedFacing()),
+ tTile.getXCoord(),
+ tTile.getYCoord(),
+ tTile.getZCoord(),
+ mBioOffsets[1],
+ 6,
+ mBioOffsets[0],
+ elementBudget,
+ env,
+ false);
+ } else {
+ built += survivialBuildPiece(
+ bioUpgrade,
+ stackSize,
+ mBioOffsets[0],
+ 6,
+ mBioOffsets[1],
+ elementBudget,
+ env,
+ false,
+ true);
+ }
+ }
+
+ if (mOCTier1 && !mOCTier2) {
+ built += survivialBuildPiece(
+ ocTier1Upgrade,
+ stackSize,
+ mOCTier1Offsets[0],
+ 9,
+ mOCTier1Offsets[1],
+ elementBudget,
+ env,
+ false,
+ true);
+ }
+ if (!mOCTier1 && mOCTier2) {
+ built += survivialBuildPiece(
+ ocTier2Upgrade,
+ stackSize,
+ mOCTier2Offsets[0],
+ 9,
+ mOCTier2Offsets[1],
+ elementBudget,
+ env,
+ false,
+ true);
+ }
+
+ return built;
+ }
+
+ public GT_MetaTileEntity_PCBFactory(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_PCBFactory(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_PCBFactory(this.mName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ if (sideDirection == facingDirection) {
+ if (active)
+ return new ITexture[] {
+ BlockIcons.getCasingTextureForId(
+ getTier() < 3 ? GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings8, 11)
+ : GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings8, 13)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ASSEMBLY_LINE_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ASSEMBLY_LINE_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] {
+ BlockIcons.getCasingTextureForId(
+ getTier() < 3 ? GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings8, 11)
+ : GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings8, 13)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ASSEMBLY_LINE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_ASSEMBLY_LINE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { BlockIcons.getCasingTextureForId(
+ mSetTier < 3 ? ((GT_Block_Casings8) GregTech_API.sBlockCasings8).getTextureIndex(11)
+ : ((GT_Block_Casings8) GregTech_API.sBlockCasings8).getTextureIndex(13)) };
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_PCBFactory> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ mTier = 0;
+ mUpgradesInstalled = 0;
+ mCoolantInputHatch = null;
+ if (mSetTier < 3) {
+ if (!checkPiece(tier1, 3, 5, 0)) {
+ return false;
+ }
+ if (mSetTier == 2) {
+ if (!checkPiece(tier2, 7, 6, 2)) {
+ return false;
+ }
+ mTier = 2;
+ } else {
+ mTier = 1;
+ }
+ } else {
+ if (!checkPiece(tier3, 3, 21, 0)) {
+ return false;
+ }
+ mTier = 3;
+ }
+
+ if (mBioUpgrade) {
+ if (mBioRotate) {
+ final IGregTechTileEntity tTile = getBaseMetaTileEntity();
+ if (!getStructureDefinition().check(
+ this,
+ bioUpgrade,
+ tTile.getWorld(),
+ transformFacing(getExtendedFacing()),
+ tTile.getXCoord(),
+ tTile.getYCoord(),
+ tTile.getZCoord(),
+ mBioOffsets[1],
+ 6,
+ mBioOffsets[0],
+ !mMachine)) {
+ return false;
+ }
+ } else {
+ if (!checkPiece(bioUpgrade, mBioOffsets[0], 6, mBioOffsets[1])) {
+ return false;
+ }
+ }
+ mUpgradesInstalled++;
+ }
+
+ if (mOCTier1 && !mOCTier2) {
+ if (!checkPiece(ocTier1Upgrade, mOCTier1Offsets[0], 9, mOCTier1Offsets[1])) {
+ return false;
+ }
+ if (mCoolantInputHatch == null) {
+ return false;
+ }
+ mUpgradesInstalled++;
+ }
+
+ if (mOCTier2 && !mOCTier1) {
+ if (!checkPiece(ocTier2Upgrade, mOCTier2Offsets[0], 9, mOCTier2Offsets[1])) {
+ return false;
+ }
+ if (mCoolantInputHatch == null) {
+ return false;
+ }
+ mUpgradesInstalled++;
+ }
+
+ getBaseMetaTileEntity().sendBlockEvent(GregTechTileClientEvents.CHANGE_CUSTOM_DATA, getUpdateData());
+
+ if (mMaintenanceHatches.size() != 1) {
+ return false;
+ }
+
+ if (!checkExoticAndNormalEnergyHatches()) {
+ return false;
+ }
+
+ return mTier > 0;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.pcbFactoryRecipes;
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic() {
+
+ @NotNull
+ @Override
+ protected CheckRecipeResult validateRecipe(@Nonnull GT_Recipe recipe) {
+ // Here we check the dynamic parallels, which depend on the recipe
+ int numberOfNanites = 0;
+ ItemStack aNanite = recipe.getRepresentativeInput(1);
+ ItemData naniteData = GT_OreDictUnificator.getAssociation(aNanite);
+ if (naniteData != null && naniteData.mPrefix != null && naniteData.mPrefix.equals(OrePrefixes.nanite)) {
+ for (ItemStack aItem : inputItems) {
+ if (aItem != null && aItem.isItemEqual(aNanite)) {
+ numberOfNanites += aItem.stackSize;
+ }
+ }
+ }
+ maxParallel = (int) Math.max(Math.ceil(Math.log(numberOfNanites) / Math.log(2) + 0.00001), 1);
+ mMaxParallel = maxParallel;
+
+ PCBFactoryUpgrade requiredUpgrade = recipe.getMetadata(PCBFactoryUpgradeKey.INSTANCE);
+ if (requiredUpgrade == PCBFactoryUpgrade.BIO && !mBioUpgrade)
+ return SimpleCheckRecipeResult.ofFailure("bio_upgrade_missing");
+
+ int requiredPCBTier = recipe.getMetadataOrDefault(PCBFactoryTierKey.INSTANCE, 1);
+ if (requiredPCBTier > mTier) return CheckRecipeResultRegistry.insufficientMachineTier(requiredPCBTier);
+
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ }
+
+ @Nonnull
+ @Override
+ protected GT_OverclockCalculator createOverclockCalculator(@Nonnull GT_Recipe recipe) {
+ return super.createOverclockCalculator(recipe).setNoOverclock(isNoOC())
+ .setEUtDiscount((float) Math.sqrt(mUpgradesInstalled == 0 ? 1 : mUpgradesInstalled))
+ .setSpeedBoost(getDurationMultiplierFromRoughness())
+ .setDurationDecreasePerOC(mOCTier2 ? 4.0 : 2.0);
+ }
+
+ @Nonnull
+ @Override
+ protected GT_ParallelHelper createParallelHelper(@Nonnull GT_Recipe recipe) {
+ return super.createParallelHelper(recipe)
+ .setEUtModifier((float) Math.sqrt(mUpgradesInstalled == 0 ? 1 : mUpgradesInstalled))
+ .setChanceMultiplier(mRoughnessMultiplier);
+ }
+ };
+ }
+
+ private boolean isNoOC() {
+ return !mOCTier1 && !mOCTier2;
+ }
+
+ private float getDurationMultiplierFromRoughness() {
+ return (float) Math.pow(mRoughnessMultiplier, 2);
+ }
+
+ private int ticker = 0;
+
+ @Override
+ public boolean onRunningTick(ItemStack aStack) {
+ if (!super.onRunningTick(aStack)) {
+ return false;
+ }
+
+ if (ticker % 20 == 0) {
+ if (!isNoOC()) {
+ FluidStack tFluid = mOCTier1 ? GT_ModHandler.getDistilledWater(COOLANT_CONSUMED_PER_SEC)
+ : Materials.SuperCoolant.getFluid(COOLANT_CONSUMED_PER_SEC);
+ if (!drain(mCoolantInputHatch, tFluid, true)) {
+ stopMachine(ShutDownReasonRegistry.outOfFluid(tFluid));
+ return false;
+ }
+ }
+ ticker = 0;
+ }
+
+ ticker++;
+
+ return true;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ if (aBaseMetaTileEntity.isServerSide()) {
+ // TODO: Look for proper fix
+ // Updates every 10 sec
+ if (mUpdate <= -150) mUpdate = 50;
+ }
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return (int) (10000f * mRoughnessMultiplier);
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ private int getTier() {
+ return mSetTier;
+ }
+
+ @Override
+ public void receiveClientEvent(byte aEventID, byte aValue) {
+ if (aEventID == 1) {
+ if ((aValue & mTier1BitMap) == mTier1BitMap) {
+ mSetTier = 1;
+ }
+
+ if ((aValue & mTier2BitMap) == mTier2BitMap) {
+ mSetTier = 2;
+ }
+
+ if ((aValue & mTier3BitMap) == mTier3BitMap) {
+ mSetTier = 3;
+ }
+
+ if ((aValue & mBioBitMap) == mBioBitMap) {
+ mBioUpgrade = true;
+ }
+
+ if ((aValue & mBioRotateBitMap) == mBioRotateBitMap) {
+ mBioRotate = true;
+ }
+
+ if ((aValue & mOCTier1BitMap) == mOCTier1BitMap) {
+ mOCTier1 = true;
+ }
+
+ if ((aValue & mOCTier2BitMap) == mOCTier2BitMap) {
+ mOCTier2 = true;
+ }
+ }
+ }
+
+ private ExtendedFacing transformFacing(ExtendedFacing facing) {
+ ForgeDirection curDirection = facing.getDirection();
+ Rotation curRotation = facing.getRotation();
+ Flip curFlip = facing.getFlip();
+ ForgeDirection newDirection = curDirection;
+ Rotation newRotation = curRotation;
+ Flip newFlip = curFlip;
+
+ if (curDirection == ForgeDirection.UP || curDirection == ForgeDirection.DOWN) {
+ switch (curRotation) {
+ case CLOCKWISE, COUNTER_CLOCKWISE -> {
+ newFlip = curFlip == Flip.NONE ? Flip.HORIZONTAL : Flip.NONE;
+ newDirection = curDirection == ForgeDirection.UP ? ForgeDirection.NORTH : ForgeDirection.SOUTH;
+ }
+ case NORMAL -> {
+ newRotation = curDirection == ForgeDirection.UP ? Rotation.CLOCKWISE : Rotation.COUNTER_CLOCKWISE;
+ newDirection = curDirection == ForgeDirection.UP ? ForgeDirection.EAST : ForgeDirection.WEST;
+ newFlip = Flip.NONE;
+ }
+ case UPSIDE_DOWN -> {
+ newRotation = curDirection == ForgeDirection.UP ? Rotation.COUNTER_CLOCKWISE : Rotation.CLOCKWISE;
+ newDirection = curDirection == ForgeDirection.UP ? ForgeDirection.EAST : ForgeDirection.WEST;
+ newFlip = Flip.NONE;
+ }
+ }
+ } else if (curRotation == Rotation.CLOCKWISE || curRotation == Rotation.COUNTER_CLOCKWISE) {
+ newFlip = curRotation == Rotation.CLOCKWISE ? curFlip == Flip.NONE ? Flip.NONE : Flip.HORIZONTAL
+ : curFlip != Flip.NONE ? Flip.NONE : Flip.HORIZONTAL;
+ newDirection = curRotation == Rotation.CLOCKWISE ? ForgeDirection.UP : ForgeDirection.DOWN;
+ } else {
+ newDirection = switch (curDirection) {
+ case EAST -> ForgeDirection.SOUTH;
+ case NORTH -> ForgeDirection.EAST;
+ case WEST -> ForgeDirection.NORTH;
+ case SOUTH -> ForgeDirection.WEST;
+ default -> curDirection;
+ };
+ }
+
+ if (curRotation == Rotation.UPSIDE_DOWN) {
+ if (curDirection != ForgeDirection.UP && curDirection != ForgeDirection.DOWN) {
+ newFlip = curFlip == Flip.NONE ? Flip.HORIZONTAL : Flip.NONE;
+ }
+ }
+
+ return ExtendedFacing.of(newDirection, newRotation, newFlip);
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ public boolean addCoolantInputToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Input) {
+ ((GT_MetaTileEntity_Hatch) aMetaTileEntity).updateTexture(aBaseCasingIndex);
+ ((GT_MetaTileEntity_Hatch_Input) aMetaTileEntity).mRecipeMap = null;
+ mCoolantInputHatch = (GT_MetaTileEntity_Hatch_Input) aMetaTileEntity;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected long getActualEnergyUsage() {
+ return (-this.lEUt * 10000) / Math.min(Math.max(1000, mEfficiency), 10000);
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ inputSeparation = !inputSeparation;
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ StatCollector.translateToLocal("GT5U.machines.separatebus") + " " + inputSeparation);
+ }
+
+ @Override
+ public String[] getInfoData() {
+ int mPollutionReduction = 0;
+ for (GT_MetaTileEntity_Hatch_Muffler tHatch : filterValidMTEs(mMufflerHatches)) {
+ mPollutionReduction = Math.max(tHatch.calculatePollutionReduction(100), mPollutionReduction);
+ }
+
+ long storedEnergy = 0;
+ long maxEnergy = 0;
+ for (GT_MetaTileEntity_Hatch tHatch : getExoticAndNormalEnergyHatchList()) {
+ storedEnergy += tHatch.getBaseMetaTileEntity()
+ .getStoredEU();
+ maxEnergy += tHatch.getBaseMetaTileEntity()
+ .getEUCapacity();
+ }
+ long voltage = getAverageInputVoltage();
+ long amps = getMaxInputAmps();
+
+ return new String[] {
+ /* 1 */ StatCollector.translateToLocal("GT5U.multiblock.Progress") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(mProgresstime / 20)
+ + EnumChatFormatting.RESET
+ + " s / "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(mMaxProgresstime / 20)
+ + EnumChatFormatting.RESET
+ + " s",
+ /* 2 */ StatCollector.translateToLocal("GT5U.multiblock.energy") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(storedEnergy)
+ + EnumChatFormatting.RESET
+ + " EU / "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(maxEnergy)
+ + EnumChatFormatting.RESET
+ + " EU",
+ /* 3 */ StatCollector.translateToLocal("GT5U.multiblock.usage") + ": "
+ + EnumChatFormatting.RED
+ + GT_Utility.formatNumbers(getActualEnergyUsage())
+ + EnumChatFormatting.RESET
+ + " EU/t",
+ /* 4 */ StatCollector.translateToLocal("GT5U.multiblock.mei") + ": "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(voltage)
+ + EnumChatFormatting.RESET
+ + " EU/t(*"
+ + amps
+ + " A)"
+ + StatCollector.translateToLocal("GT5U.machines.tier")
+ + ": "
+ + EnumChatFormatting.YELLOW
+ + VN[GT_Utility.getTier(voltage)]
+ + EnumChatFormatting.RESET,
+ /* 5 */ StatCollector.translateToLocal("GT5U.multiblock.problems") + ": "
+ + EnumChatFormatting.RED
+ + (getIdealStatus() - getRepairStatus())
+ + EnumChatFormatting.RESET
+ + " "
+ + StatCollector.translateToLocal("GT5U.multiblock.efficiency")
+ + ": "
+ + EnumChatFormatting.YELLOW
+ + mEfficiency / 100.0F
+ + EnumChatFormatting.RESET
+ + " %",
+ /* 6 */ StatCollector.translateToLocal("GT5U.multiblock.pollution") + ": "
+ + EnumChatFormatting.GREEN
+ + mPollutionReduction
+ + EnumChatFormatting.RESET
+ + " %",
+ /* 7 */ StatCollector.translateToLocal("GT5U.multiblock.parallelism") + ": "
+ + EnumChatFormatting.GREEN
+ + mMaxParallel,
+ /* 8 */ StatCollector.translateToLocal("GT5U.multiblock.curparallelism") + ": "
+ + EnumChatFormatting.GREEN
+ + mCurrentParallel };
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Circuit Board Fabricator")
+ .addInfo("Controller for the PCB Factory")
+ .addInfo(
+ EnumChatFormatting.GOLD.toString() + EnumChatFormatting.BOLD
+ + "IMPORTANT!"
+ + " Check the configuration menu before building.")
+ .addInfo("Tier of the machine determines the available recipes.")
+ .addInfo("Machine tier (1-3) is set in the controller GUI.")
+ .addInfo("The configuration menu can be used to add upgrades.")
+ .addInfo("Each tier and upgrade requires additional structures.")
+ .addInfo("Power consumption is multiplied by Sqrt(structures).")
+ .addInfo("Tier 2 and 3 allow parallel by using extra nanites.")
+ .addInfo("Every doubling of nanites adds one parallel.")
+ .addInfo("1x->1, 2x->2, 4x->3, 8x->4 with no limit.")
+ .addInfo("Recipes require a cooling upgrade to be overclocked.")
+ .addInfo("Liquid Cooling uses 10 L/s of distilled water and enables default overclocks.")
+ .addInfo("Thermosink uses 10 L/s of Super Coolant and enables perfect overclocks.")
+ .addInfo("Trace size can be changed to modify the material usage and machine speed.")
+ .addInfo(AuthorBlueWeabo)
+ .beginStructureBlock(30, 38, 13, false)
+ .addSeparator()
+ .addMaintenanceHatch(EnumChatFormatting.GOLD + "1", 1)
+ .addEnergyHatch(
+ EnumChatFormatting.GOLD + "1"
+ + EnumChatFormatting.GRAY
+ + "-"
+ + EnumChatFormatting.GOLD
+ + "2"
+ + EnumChatFormatting.GRAY
+ + " or "
+ + EnumChatFormatting.GOLD
+ + "1"
+ + EnumChatFormatting.GRAY
+ + " TT energy hatch.",
+ 1)
+ .addInputBus(EnumChatFormatting.GOLD + "0" + EnumChatFormatting.GRAY + "+", 1)
+ .addOutputBus(EnumChatFormatting.GOLD + "0" + EnumChatFormatting.GRAY + "+", 1)
+ .addInputHatch(EnumChatFormatting.GOLD + "0" + EnumChatFormatting.GRAY + "+", 1)
+ .addStructureInfo(
+ "Coolant Hatch (Input Hatch): " + EnumChatFormatting.GOLD
+ + "1"
+ + EnumChatFormatting.GRAY
+ + " Center of the Liquid Cooling/Thermosink")
+ .addStructureInfo(
+ EnumChatFormatting.BLUE + "Base Multi (Tier "
+ + EnumChatFormatting.DARK_PURPLE
+ + 1
+ + EnumChatFormatting.BLUE
+ + "):")
+ .addStructureInfo(EnumChatFormatting.GOLD + "40" + EnumChatFormatting.GRAY + " Damascus Steel Frame Box")
+ .addStructureInfo(EnumChatFormatting.GOLD + "9" + EnumChatFormatting.GRAY + " Vibrant Alloy Frame Box")
+ .addStructureInfo(EnumChatFormatting.GOLD + "25" + EnumChatFormatting.GRAY + " Reinforced Glass")
+ .addStructureInfo(
+ EnumChatFormatting.GOLD + "77" + EnumChatFormatting.GRAY + " Basic Photolithography Framework Casing")
+ .addStructureInfo(EnumChatFormatting.GOLD + "12" + EnumChatFormatting.GRAY + " Grate Machine Casing")
+ .addStructureInfo(EnumChatFormatting.GOLD + "25" + EnumChatFormatting.GRAY + " Plascrete Block")
+ .addStructureInfo(
+ EnumChatFormatting.BLUE + "Tier "
+ + EnumChatFormatting.DARK_PURPLE
+ + 2
+ + EnumChatFormatting.BLUE
+ + " (Adds to Tier "
+ + EnumChatFormatting.DARK_PURPLE
+ + 1
+ + EnumChatFormatting.BLUE
+ + "):")
+ .addStructureInfo(EnumChatFormatting.GOLD + "34" + EnumChatFormatting.GRAY + " Duranium Frame Box")
+ .addStructureInfo(
+ EnumChatFormatting.GOLD + "158"
+ + EnumChatFormatting.GRAY
+ + " Reinforced Photolithography Framework Casing")
+ .addStructureInfo(
+ EnumChatFormatting.BLUE + "Tier " + EnumChatFormatting.DARK_PURPLE + 3 + EnumChatFormatting.BLUE + ":")
+ .addStructureInfo(
+ EnumChatFormatting.GOLD + "292"
+ + EnumChatFormatting.GRAY
+ + " Radiation Proof Photolithography Framework Casing")
+ .addStructureInfo(
+ EnumChatFormatting.GOLD + "76" + EnumChatFormatting.GRAY + " Radiant Naquadah Alloy Casing")
+ .addStructureInfo(EnumChatFormatting.BLUE + "Biochamber Upgrade")
+ .addStructureInfo(
+ EnumChatFormatting.GOLD + "68" + EnumChatFormatting.GRAY + " Clean Stainless Steel Machine Casing")
+ .addStructureInfo(EnumChatFormatting.GOLD + "40" + EnumChatFormatting.GRAY + " Damascus Steel Frame Box")
+ .addStructureInfo(EnumChatFormatting.GOLD + "72" + EnumChatFormatting.GRAY + " Reinforced Glass")
+ .addStructureInfo(
+ EnumChatFormatting.BLUE + "Liquid Cooling Tower (Tier "
+ + EnumChatFormatting.DARK_PURPLE
+ + 1
+ + EnumChatFormatting.BLUE
+ + "):")
+ .addStructureInfo(EnumChatFormatting.GOLD + "40" + EnumChatFormatting.GRAY + " Damascus Steel Frame Box")
+ .addStructureInfo(
+ EnumChatFormatting.GOLD + "68" + EnumChatFormatting.GRAY + " Radiant Naquadah Alloy Casing")
+ .addStructureInfo(
+ EnumChatFormatting.GOLD + "12" + EnumChatFormatting.GRAY + " Extreme Engine Intake Casing")
+ .addStructureInfo(EnumChatFormatting.GOLD + "20" + EnumChatFormatting.GRAY + " Tungstensteel Pipe Casing")
+ .addStructureInfo(
+ EnumChatFormatting.GOLD + "21"
+ + EnumChatFormatting.GRAY
+ + " Reinforced Photolithography Framework Casing")
+ .addStructureInfo(
+ EnumChatFormatting.BLUE + "Thermosink Radiator(Tier "
+ + EnumChatFormatting.DARK_PURPLE
+ + 2
+ + EnumChatFormatting.BLUE
+ + "):")
+ .addStructureInfo(EnumChatFormatting.GOLD + "40" + EnumChatFormatting.GRAY + " Americium Frame Box")
+ .addStructureInfo(
+ EnumChatFormatting.GOLD + "41"
+ + EnumChatFormatting.GRAY
+ + " Reinforced Photolithography Framework Casing")
+ .addStructureInfo(EnumChatFormatting.GOLD + "8" + EnumChatFormatting.GRAY + " Superconducting Coil Block")
+ .addStructureInfo(EnumChatFormatting.GOLD + "20" + EnumChatFormatting.GRAY + " Tungstensteel Pipe Casing")
+ .addStructureInfo(EnumChatFormatting.GOLD + "48" + EnumChatFormatting.GRAY + " Infinity Cooled Casing")
+ .toolTipFinisher("GregTech");
+ return tt;
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setBoolean("mBioUpgrade", mBioUpgrade);
+ aNBT.setBoolean("mBioRotate", mBioRotate);
+ aNBT.setInteger("mBioOffsetX", mBioOffsets[0]);
+ aNBT.setInteger("mBioOffsetZ", mBioOffsets[1]);
+ aNBT.setBoolean("mOCTier1Upgrade", mOCTier1);
+ aNBT.setInteger("mOCTier1OffsetX", mOCTier1Offsets[0]);
+ aNBT.setInteger("mOCTier1OffsetZ", mOCTier1Offsets[1]);
+ aNBT.setBoolean("mOCTier2Upgrade", mOCTier2);
+ aNBT.setInteger("mOCTier2OffsetX", mOCTier2Offsets[0]);
+ aNBT.setInteger("mOCTier2OffsetZ", mOCTier2Offsets[1]);
+ aNBT.setFloat("mRoughnessMultiplier", mRoughnessMultiplier);
+ aNBT.setInteger("mSetTier", mSetTier);
+ }
+
+ @Override
+ public void loadNBTData(final NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ if (aNBT.hasKey("mSeparate")) {
+ // backward compatibility
+ inputSeparation = aNBT.getBoolean("mSeparate");
+ }
+ mBioUpgrade = aNBT.getBoolean("mBioUpgrade");
+ mBioRotate = aNBT.getBoolean("mBioRotate");
+ mBioOffsets[0] = aNBT.getInteger("mBioOffsetX");
+ mBioOffsets[1] = aNBT.getInteger("mBioOffsetZ");
+ mOCTier1 = aNBT.getBoolean("mOCTier1Upgrade");
+ mOCTier1Offsets[0] = aNBT.getInteger("mOCTier1OffsetX");
+ mOCTier1Offsets[1] = aNBT.getInteger("mOCTier1OffsetZ");
+ mOCTier2 = aNBT.getBoolean("mOCTier2Upgrade");
+ mOCTier2Offsets[0] = aNBT.getInteger("mOCTier2OffsetX");
+ mOCTier2Offsets[1] = aNBT.getInteger("mOCTier2OffsetZ");
+ mRoughnessMultiplier = aNBT.getFloat("mRoughnessMultiplier");
+ mSetTier = aNBT.getInteger("mSetTier");
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ protected SoundResource getProcessStartSound() {
+ return SoundResource.IC2_MACHINES_MAGNETIZER_LOOP;
+ }
+
+ @Override
+ public byte getUpdateData() {
+ byte data = 0;
+ if (mSetTier == 1) {
+ data += mTier1BitMap;
+ } else if (mSetTier == 2) {
+ data += mTier2BitMap;
+ } else {
+ data += mTier3BitMap;
+ }
+
+ if (mBioUpgrade) {
+ data += mBioBitMap;
+ }
+
+ if (mBioRotate) {
+ data += mBioRotateBitMap;
+ }
+
+ if (mOCTier1) {
+ data += mOCTier1BitMap;
+ }
+
+ if (mOCTier2) {
+ data += mOCTier2BitMap;
+ }
+
+ return data;
+ }
+
+ @Override
+ public Pos2d getStructureUpdateButtonPos() {
+ return new Pos2d(80, 91);
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ super.addUIWidgets(builder, buildContext);
+ buildContext.addSyncedWindow(10, this::createConfigurationWindow);
+ builder.widget(
+ new ButtonWidget().setOnClick(
+ (clickData, widget) -> {
+ if (!widget.isClient()) widget.getContext()
+ .openSyncedWindow(10);
+ })
+ .setSize(16, 16)
+ .setBackground(() -> {
+ List<UITexture> ret = new ArrayList<>();
+ ret.add(GT_UITextures.BUTTON_STANDARD);
+ ret.add(GT_UITextures.OVERLAY_BUTTON_CYCLIC);
+ return ret.toArray(new IDrawable[0]);
+ })
+ .addTooltip("Configuration Menu")
+ .setPos(174, 130))
+ .widget(
+ new TextWidget(new Text("Tier")).setTextAlignment(Alignment.Center)
+ .setScale(0.91f)
+ .setSize(20, 16)
+ .setPos(173, 98))
+ .widget(
+ new NumericWidget().setGetter(() -> mSetTier)
+ .setSetter(val -> mSetTier = (int) val)
+ .setBounds(1, 3)
+ .setTextColor(Color.WHITE.normal)
+ .setTextAlignment(Alignment.Center)
+ .addTooltip("PCB Factory Tier")
+ .setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD)
+ .setSize(18, 18)
+ .setPos(173, 110));
+ }
+
+ protected ModularWindow createConfigurationWindow(final EntityPlayer player) {
+ ModularWindow.Builder builder = ModularWindow.builder(200, 160);
+ builder.setBackground(GT_UITextures.BACKGROUND_SINGLEBLOCK_DEFAULT);
+ builder.setGuiTint(getGUIColorization());
+ builder.widget(
+ new DrawableWidget().setDrawable(GT_UITextures.OVERLAY_BUTTON_CYCLIC)
+ .setPos(5, 5)
+ .setSize(16, 16))
+ .widget(new TextWidget("Configuration Menu").setPos(25, 9))
+ .widget(
+ ButtonWidget.closeWindowButton(true)
+ .setPos(185, 3))
+ .widget(
+ new DynamicPositionedColumn().setSynced(false)
+ .widget(
+ new MultiChildWidget().addChild(new CycleButtonWidget().setToggle(() -> mBioUpgrade, val -> {
+ mBioUpgrade = val;
+ if (!mBioUpgrade) {
+ GT_Utility
+ .sendChatToPlayer(player, GT_Utility.trans("339.1", "Biochamber Upgrade Disabled"));
+ } else {
+ GT_Utility
+ .sendChatToPlayer(player, GT_Utility.trans("339", "Biochamber Upgrade Enabled"));
+ }
+ })
+ .setVariableBackground(GT_UITextures.BUTTON_STANDARD_TOGGLE)
+ .setSize(90, 18)
+ .addTooltip(
+ "Enables nanites to construct organic circuitry. Required for Bioware and Wetware boards."))
+ .addChild(
+ new DrawableWidget().setDrawable(GT_UITextures.OVERLAY_BUTTON_CYCLIC)
+ .setSize(18, 18))
+ .addChild(
+ new TextWidget("Biochamber").setTextAlignment(Alignment.Center)
+ .setPos(23, 5))
+ .setEnabled(widget -> !getBaseMetaTileEntity().isActive()))
+ .widget(new MultiChildWidget().addChild(new CycleButtonWidget().setToggle(() -> mBioRotate, val -> {
+ mBioRotate = val;
+ if (!mBioRotate) {
+ GT_Utility
+ .sendChatToPlayer(player, GT_Utility.trans("340.1", "Rotated biochamber disabled"));
+ } else {
+ GT_Utility.sendChatToPlayer(player, GT_Utility.trans("340", "Rotated biochamber enabled"));
+ }
+ })
+ .setVariableBackground(GT_UITextures.BUTTON_STANDARD_TOGGLE)
+ .setSize(90, 18)
+ .addTooltip("Rotates the biochamber by 90 degrees."))
+ .addChild(
+ new DrawableWidget().setDrawable(GT_UITextures.OVERLAY_BUTTON_CYCLIC)
+ .setSize(18, 18))
+ .addChild(
+ new TextWidget("Bio Rotation").setTextAlignment(Alignment.Center)
+ .setPos(23, 5))
+ .setEnabled(widget -> !getBaseMetaTileEntity().isActive()))
+ .widget(new MultiChildWidget().addChild(new CycleButtonWidget().setToggle(() -> mOCTier1, val -> {
+ mOCTier1 = val;
+ mOCTier2 = false;
+ if (!mOCTier1) {
+ GT_Utility.sendChatToPlayer(player, GT_Utility.trans("341.1", "Tier 1 cooling disabled"));
+ } else {
+ GT_Utility.sendChatToPlayer(player, GT_Utility.trans("341", "Tier 1 cooling enabled"));
+ }
+ })
+ .setVariableBackground(GT_UITextures.BUTTON_STANDARD_TOGGLE)
+ .setSize(90, 18)
+ .addTooltip(
+ "Allows for overclocking. Requires 10 L/s of distilled water. Cooling upgrades are mutually exclusive."))
+ .addChild(
+ new DrawableWidget().setDrawable(GT_UITextures.OVERLAY_BUTTON_CYCLIC)
+ .setSize(18, 18))
+ .addChild(
+ new TextWidget("Liquid Cooling").setTextAlignment(Alignment.Center)
+ .setPos(20, 5))
+ .setEnabled(widget -> !getBaseMetaTileEntity().isActive()))
+ .widget(new MultiChildWidget().addChild(new CycleButtonWidget().setToggle(() -> mOCTier2, val -> {
+ mOCTier2 = val;
+ mOCTier1 = false;
+ if (!mOCTier2) {
+ GT_Utility.sendChatToPlayer(player, GT_Utility.trans("342.1", "Tier 2 cooling disabled"));
+ } else {
+ GT_Utility.sendChatToPlayer(player, GT_Utility.trans("342", "Tier 2 cooling enabled"));
+ }
+ })
+ .setVariableBackground(GT_UITextures.BUTTON_STANDARD_TOGGLE)
+ .setSize(90, 18)
+ .addTooltip(
+ "Enables perfect overclocking by allowing nanites to work with extreme speed and efficiency. Uses 10 L/s of Super Coolant."))
+ .addChild(
+ new DrawableWidget().setDrawable(GT_UITextures.OVERLAY_BUTTON_CYCLIC)
+ .setSize(18, 18))
+ .addChild(
+ new TextWidget("Thermosink").setTextAlignment(Alignment.Center)
+ .setPos(20, 5))
+ .setEnabled(widget -> !getBaseMetaTileEntity().isActive()))
+ .widget(
+ new TextWidget(new Text("Trace Size")).setSize(90, 18)
+ .setEnabled(widget -> !getBaseMetaTileEntity().isActive())
+ .setPos(0, 4))
+ .widget(
+ new NumericWidget().setGetter(() -> (int) ((1f / mRoughnessMultiplier) * 100f))
+ .setSetter(val -> mRoughnessMultiplier = 100f / (int) val)
+ .setBounds(50, 200)
+ .setTextColor(Color.WHITE.normal)
+ .setTextAlignment(Alignment.Center)
+ .addTooltip(
+ "Set the trace size. Smaller traces allow material savings but take longer to fabricate. Larger traces waste material but are fast. 50-200 μm.")
+ .setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD)
+ .setSize(90, 16))
+ .widget(
+ new DrawableWidget().setDrawable(GT_UITextures.OVERLAY_BUTTON_CROSS)
+ .setSize(18, 18)
+ .addTooltip(new Text("Can't change configuration when running !").color(Color.RED.dark(3)))
+ .setEnabled(widget -> getBaseMetaTileEntity().isActive()))
+ .setPos(10, 25))
+ .widget(
+ new DynamicPositionedColumn().setSynced(false)
+ .widget(
+ new TextWidget(new Text("Bio Upgrade Offsets")).setSize(72, 18)
+ .setEnabled(widget -> !getBaseMetaTileEntity().isActive()))
+ .widget(
+ new DynamicPositionedRow().setSynced(false)
+ .widget(
+ new NumericWidget().setGetter(() -> mBioOffsets[0])
+ .setSetter(val -> mBioOffsets[0] = (int) val)
+ .setBounds(-16, 16)
+ .setTextColor(Color.WHITE.normal)
+ .setTextAlignment(Alignment.Center)
+ .addTooltip("X Offset")
+ .setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD)
+ .setSize(36, 18))
+ .widget(
+ new NumericWidget().setGetter(() -> mBioOffsets[1])
+ .setSetter(val -> mBioOffsets[1] = (int) val)
+ .setBounds(-16, 16)
+ .setTextColor(Color.WHITE.normal)
+ .setTextAlignment(Alignment.Center)
+ .addTooltip("Z Offset")
+ .setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD)
+ .setSize(36, 18))
+ .setEnabled(widget -> !getBaseMetaTileEntity().isActive()))
+ .widget(
+ new TextWidget(new Text("Cooler Tier 1 Offsets")).setSize(72, 18)
+ .setEnabled(widget -> !getBaseMetaTileEntity().isActive()))
+ .widget(
+ new DynamicPositionedRow().setSynced(false)
+ .widget(
+ new NumericWidget().setGetter(() -> mOCTier1Offsets[0])
+ .setSetter(val -> mOCTier1Offsets[0] = (int) val)
+ .setBounds(-16, 16)
+ .setTextColor(Color.WHITE.normal)
+ .setTextAlignment(Alignment.Center)
+ .addTooltip("X Offset")
+ .setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD)
+ .setSize(36, 18))
+ .widget(
+ new NumericWidget().setGetter(() -> mOCTier1Offsets[1])
+ .setSetter(val -> mOCTier1Offsets[1] = (int) val)
+ .setBounds(-16, 16)
+ .setTextColor(Color.WHITE.normal)
+ .setTextAlignment(Alignment.Center)
+ .addTooltip("Z Offset")
+ .setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD)
+ .setSize(36, 18))
+ .setEnabled(widget -> !getBaseMetaTileEntity().isActive()))
+ .widget(
+ new TextWidget(new Text("Cooler Tier 2 Offsets")).setSize(72, 18)
+ .setEnabled(widget -> !getBaseMetaTileEntity().isActive()))
+ .widget(
+ new DynamicPositionedRow().setSynced(false)
+ .widget(
+ new NumericWidget().setGetter(() -> mOCTier2Offsets[0])
+ .setSetter(val -> mOCTier2Offsets[0] = (int) val)
+ .setBounds(-16, 16)
+ .setTextColor(Color.WHITE.normal)
+ .setTextAlignment(Alignment.Center)
+ .addTooltip("X Offset")
+ .setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD)
+ .setSize(36, 18))
+ .widget(
+ new NumericWidget().setGetter(() -> mOCTier2Offsets[1])
+ .setSetter(val -> mOCTier2Offsets[1] = (int) val)
+ .setBounds(-16, 16)
+ .setTextColor(Color.WHITE.normal)
+ .setTextAlignment(Alignment.Center)
+ .addTooltip("Z Offset")
+ .setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD)
+ .setSize(36, 18))
+ .setEnabled(widget -> !getBaseMetaTileEntity().isActive()))
+ .setPos(110, 25));
+ return builder.build();
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsInputSeparation() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsSingleRecipeLocking() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsBatchMode() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PlasmaForge.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PlasmaForge.java
new file mode 100644
index 0000000000..d23ea912bb
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PlasmaForge.java
@@ -0,0 +1,1263 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.ExoticEnergy;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.GT_Values.AuthorColen;
+import static gregtech.api.enums.GT_Values.VN;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_DTPF_OFF;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_DTPF_ON;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FUSION1_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_RAINBOWSCREEN_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.casingTexturePages;
+import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_StructureUtility.ofCoil;
+import static gregtech.api.util.GT_Utility.filterValidMTEs;
+import static net.minecraft.util.StatCollector.translateToLocal;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.ChunkCoordIntPair;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.jetbrains.annotations.NotNull;
+
+import com.github.technus.tectech.thing.gui.TecTechUITextures;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.drawable.UITexture;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.math.Color;
+import com.gtnewhorizons.modularui.api.math.Size;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+import com.gtnewhorizons.modularui.common.widget.textfield.NumericWidget;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.HeatingCoilLevel;
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.MaterialsUEVplus;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.IIconContainer;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.GregTechTileClientEvents;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Energy;
+import gregtech.api.objects.GT_ChunkManager;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_OverclockCalculator;
+import gregtech.api.util.GT_ParallelHelper;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_PlasmaForge extends
+ GT_MetaTileEntity_ExtendedPowerMultiBlockBase<GT_MetaTileEntity_PlasmaForge> implements ISurvivalConstructable {
+
+ // 3600 seconds in an hour, 8 hours, 20 ticks in a second.
+ private static final double max_efficiency_time_in_ticks = 3600d * 8d * 20d;
+ // Multiplier for the efficiency decay rate
+ private static final double efficiency_decay_rate = 100;
+ private static final double maximum_discount = 0.5d;
+ private static final int CONVERGENCE_BITMAP = 0b1;
+ private static final int DISCOUNT_BITMAP = 0b10;
+
+ // Valid fuels which the discount will get applied to.
+ private static final FluidStack[] valid_fuels = { MaterialsUEVplus.ExcitedDTCC.getFluid(1L),
+ MaterialsUEVplus.ExcitedDTPC.getFluid(1L), MaterialsUEVplus.ExcitedDTRC.getFluid(1L),
+ MaterialsUEVplus.ExcitedDTEC.getFluid(1L), MaterialsUEVplus.ExcitedDTSC.getFluid(1L) };
+
+ private static final HashMap<Fluid, Pair<Long, Float>> FUEL_ENERGY_VALUES = new HashMap<>() {
+
+ {
+ put(
+ MaterialsUEVplus.ExcitedDTCC.getFluid(1L)
+ .getFluid(),
+ Pair.of(14_514_983L, 1 / 8f));
+ put(
+ MaterialsUEVplus.ExcitedDTPC.getFluid(1L)
+ .getFluid(),
+ Pair.of(66_768_460L, 1 / 4f));
+ put(
+ MaterialsUEVplus.ExcitedDTRC.getFluid(1L)
+ .getFluid(),
+ Pair.of(269_326_451L, 1 / 2f));
+ put(
+ MaterialsUEVplus.ExcitedDTEC.getFluid(1L)
+ .getFluid(),
+ Pair.of(1_073_007_393L, 1f));
+ put(
+ MaterialsUEVplus.ExcitedDTSC.getFluid(1L)
+ .getFluid(),
+ Pair.of(4_276_767_521L, 2f));
+ }
+ };
+
+ private static final int min_input_hatch = 0;
+ private static final int max_input_hatch = 7;
+ private static final int min_output_hatch = 0;
+ private static final int max_output_hatch = 2;
+ private static final int min_input_bus = 0;
+ private static final int max_input_bus = 6;
+ private static final int min_output_bus = 0;
+ private static final int max_output_bus = 1;
+
+ // Current discount rate. 1 = 0%, 0 = 100%.
+ private double discount = 1;
+ private int mHeatingCapacity = 0;
+ private long running_time = 0;
+ private boolean convergence = false;
+ private HeatingCoilLevel mCoilLevel;
+ private GT_OverclockCalculator overclockCalculator;
+
+ @SuppressWarnings("SpellCheckingInspection")
+ private static final String[][] structure_string = new String[][] { { " ",
+ " N N N N ", " N N N N ", " N N N N ",
+ " ", " ", " ",
+ " N N N N ", " N N N N ", " NNN NNN N N NNN NNN ",
+ " ", " ", " ",
+ " NNN NNN NNN NNN ", " ", " ",
+ " ", " ", " ",
+ " NNN NNN NNN NNN ", " ", " ",
+ " ", " NNN NNN NNN NNN " },
+ { " N N N N ", " bCCCb bCCCb ", " bCCCb bCCCb ",
+ " bCCCb bCCCb ", " N N N N ",
+ " ", " N N N N ",
+ " bCCCb bCCCb ", " bCCCb bCCCb ",
+ "NbbbN NbbNCCCb bCCCNbbN NbbbN", " CCC CCC N N CCC CCC ",
+ " CCC CCC CCC CCC ", " CCC CCC CCC CCC ",
+ "NbbbN NbbbN NbbbN NbbbN", " N N N N ",
+ " N N N N ", " ",
+ " N N N N ", " N N N N ",
+ "NbbbN NbbbN NbbbN NbbbN", " CCC CCC CCC CCC ",
+ " CCC CCC CCC CCC ", " CCC CCC CCC CCC ",
+ "NbbbN NbbbN N N NbbbN NbbbN", },
+ { " N N N N ", " bCCCb bCCCb ", " NNNbbbbbNNsNNbbbbbNNN ",
+ " ss bCCCb bCCCb ss ", " s N N N N s ",
+ " s s ", " N N N N N N ",
+ " N bCCCb bCCCb N ", " N sbbbbbNNsNNbbbbbs N ",
+ "NbbbN NbbNCCCb bCCCNbbN NbbbN", " CbC CbC N N CbC CbC ",
+ " CbC CbC CbC CbC ", " CbC CbC CbC CbC ",
+ "NbbbN NbbbN NbbbN NbbbN", " NNN NNN NNN NNN ",
+ " NNN NNN NNN NNN ", " s s s s ",
+ " NNN NNN NNN NNN ", " NNN NNN NNN NNN ",
+ "NbbbN NbbbN NbbbN NbbbN", " CbC CbC CbC CbC ",
+ " CbC CbC CbC CbC ", " CbC CbC CbC CbC ",
+ "NbbbN NbbbNNNNNsNsNNNNNbbbN NbbbN", },
+ { " N N N N ", " bCCCb bCCCb ", " ss bCCCb bCCCb ss ",
+ " bCCCb bCCCb ", " s NCCCN NCCCN s ",
+ " s NCCCN NCCCN s ", " NCCCN NCCCN ",
+ " bCCCb bCCCb ", " bCCCb bCCCb ",
+ "NbbbNNNbbNCCCb bCCCNbbNNNbbbN", " CCCCCCCCC N N CCCCCCCCC ",
+ " CCCCCCCCC CCCCCCCCC ", " CCCCCCCCC CCCCCCCCC ",
+ "NbbbNNNbbbN NbbbNNNbbbN", " N N N N ",
+ " N N N N ", " ",
+ " N N N N ", " N N N N ",
+ "NbbbNNNbbbN NbbbNNNbbbN", " CCCCCCCCC CCCCCCCCC ",
+ " CCCCCCCCC CCCCCCCCC ", " CCCCCCCCC CCCCCCCCC ",
+ "NbbbNNNbbbN NbN NbbbNNNbbbN", },
+ { " ", " N N N N ", " s N N N N s ",
+ " s NCCCN NCCCN s ", " ",
+ " ", " ",
+ " NCCCN NCCCN ", " N N N N ",
+ " NNN NN N N NN NNN ", " C C C C ",
+ " C C C C ", " C C C C ",
+ " NNN NNN NNN NNN ", " ",
+ " ", " ",
+ " ", " ",
+ " NNN NNN NNN NNN ", " C C C C ",
+ " C C C C ", " C C C C ",
+ " NNN NNN NbN NNN NNN ", },
+ { " ", " ", " s s ",
+ " s NCCCN NCCCN s ", " ",
+ " ", " ",
+ " NCCCN NCCCN ", " ",
+ " N N N N ", " C C C C ",
+ " C C C C ", " C C C C ",
+ " N N N N ", " ",
+ " ", " ",
+ " ", " ",
+ " N N N N ", " C C C C ",
+ " C C C C ", " C C C C ",
+ " N N NbN N N ", },
+ { " ", " N N N N ", " N N N N N N ",
+ " NCCCN NCCCN ", " ",
+ " ", " ",
+ " NCCCN NCCCN ", " N N N N ",
+ " NNN NN N N NN NNN ", " C C C C ",
+ " C C C C ", " C C C C ",
+ " NNN NNN NNN NNN ", " ",
+ " ", " ",
+ " ", " ",
+ " NNN NNN NNN NNN ", " C C C C ",
+ " C C C C ", " C C C C ",
+ " NNN NNN NbN NNN NNN ", },
+ { " N N N N ", " bCCCb bCCCb ", " N bCCCb bCCCb N ",
+ " bCCCb bCCCb ", " NCCCN NCCCN ",
+ " NCCCN NCCCN ", " NCCCN NCCCN ",
+ " bCCCb bCCCb ", " bCCCb bCCCb ",
+ "NbbbNNNbbNCCCb bCCCNbbNNNbbbN", " CCCCCCCCC N N CCCCCCCCC ",
+ " CCCCCCCCC CCCCCCCCC ", " CCCCCCCCC CCCCCCCCC ",
+ "NbbbNNNbbbN NbbbNNNbbbN", " N N N N ",
+ " N N N N ", " ",
+ " N N N N ", " N N N N ",
+ "NbbbNNNbbbN NbbbNNNbbbN", " CCCCCCCCC CCCCCCCCC ",
+ " CCCCCCCCC CCCCCCCCC ", " CCCCCCCCC CCCCCCCCC ",
+ "NbbbNNNbbbN NbN NbbbNNNbbbN", },
+ { " N N N N ", " bCCCb bCCCb ", " N sbbbbbNNsNNbbbbbs N ",
+ " bCCCb bCCCb ", " N N N N ",
+ " ", " N N N N ",
+ " bCCCb bCCCb ", " s sbbbbbNNsNNbbbbbs s ",
+ "NbbbN NbbNCCCb bCCCNbbN NbbbN", " CbC CbC N N CbC CbC ",
+ " CbC CbC CbC CbC ", " CbC CbC CbC CbC ",
+ "NbbbN NbbbN NbbbN NbbbN", " NNN NNN NNN NNN ",
+ " NNN NNN NNN NNN ", " s s s s ",
+ " NNN NNN NNN NNN ", " NNN NNN NNN NNN ",
+ "NbbbN NbbbN NbbbN NbbbN", " CbC CbC CbC CbC ",
+ " CbC CbC CbC CbC ", " CbC CbC CbC CbC ",
+ "NbbbN NbbbNNNNNsNsNNNNNbbbN NbbbN", },
+ { " NNN NNN N N NNN NNN ", "NbbbN NbbNCCCb bCCCNbbN NbbbN", "NbbbN NbbNCCCb bCCCNbbN NbbbN",
+ "NbbbNNNbbNCCCb bCCCNbbNNNbbbN", " NNN NNN N N NNN NNN ",
+ " N N N N ", " NNN NNN N N NNN NNN ",
+ "NbbbNNNbbNCCCb bCCCNbbNNNbbbN", "NbbbN NbbNCCCb bCCCNbbN NbbbN",
+ "NNNN NNNCCCb bCCCNNN NNNN", " CCC CCC N N CCC CCC ",
+ " CCC CCC CCC CCC ", " CCC CCC CCC CCC ",
+ "NbbbN NbbbN NbbbN NbbbN", " N N N N ",
+ " N N N N ", " ",
+ " N N N N ", " N N N N ",
+ "NbbbN NbbbN NbbbN NbbbN", " CCC CCC CCC CCC ",
+ " CCC CCC CCC CCC ", " CCC CCC CCC CCC ",
+ "NbbbN NbbbN NbN NbbbN NbbbN", },
+ { " ", " CCC CCC N N CCC CCC ", " CbC CbC N N CbC CbC ",
+ " CCCCCCCCC N N CCCCCCCCC ", " C C C C ",
+ " C C C C ", " C C C C ",
+ " CCCCCCCCC N N CCCCCCCCC ", " CbC CbC N N CbC CbC ",
+ " CCC CCC N N CCC CCC ", " ",
+ " ", " ",
+ " NNN NNN NNN NNN ", " ",
+ " ", " ",
+ " ", " ",
+ " NNN NNN NNN NNN ", " ",
+ " ", " ",
+ " NNN NNN NbN NNN NNN ", },
+ { " ", " CCC CCC CCC CCC ", " CbC CbC CbC CbC ",
+ " CCCCCCCCC CCCCCCCCC ", " C C C C ",
+ " C C C C ", " C C C C ",
+ " CCCCCCCCC CCCCCCCCC ", " CbC CbC CbC CbC ",
+ " CCC CCC CCC CCC ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " N N NbN N N ", },
+ { " ", " CCC CCC CCC CCC ", " CbC CbC CbC CbC ",
+ " CCCCCCCCC CCCCCCCCC ", " C C C C ",
+ " C C C C ", " C C C C ",
+ " CCCCCCCCC CCCCCCCCC ", " CbC CbC CbC CbC ",
+ " CCC CCC CCC CCC ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " N N NbN N N ", },
+ { " NNN NNN NNN NNN ", "NbbbN NbbbN NbbbN NbbbN", "NbbbN NbbbN NbbbN NbbbN",
+ "NbbbNNNbbbN NbbbNNNbbbN", " NNN NNN NNN NNN ",
+ " N N N N ", " NNN NNN NNN NNN ",
+ "NbbbNNNbbbN NbbbNNNbbbN", "NbbbN NbbbN NbbbN NbbbN",
+ "NbbbN NbbbN NbbbN NbbbN", " NNN NNN NNN NNN ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " N N NsNsN N N ", },
+ { " ", " ", " N N N N ",
+ " ", " ",
+ " ", " ",
+ " ", " N N N N ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " N N NbbbbbN N N ", },
+ { " ", " ", " N N N N ",
+ " ", " ",
+ " ", " ",
+ " ", " N N N N ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " N ",
+ " NsNNNNNsNNNNsbbbbbsNNNNsNNNNNsN ", },
+ { " ", " ", " s s s s ",
+ " ", " ",
+ " ", " ",
+ " ", " s s s s ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ~ ", " NNN ",
+ " NbbbbbNbbbbNbbbbbNbbbbNbbbbbN ", },
+ { " ", " ", " N N N N ",
+ " ", " ",
+ " ", " ",
+ " ", " N N N N ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " N ",
+ " NsNNNNNsNNNNsbbbbbsNNNNsNNNNNsN ", },
+ { " ", " ", " N N N N ",
+ " ", " ",
+ " ", " ",
+ " ", " N N N N ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " N N NbbbbbN N N ", },
+ { " NNN NNN NNN NNN ", "NbbbN NbbbN NbbbN NbbbN", "NbbbN NbbbN NbbbN NbbbN",
+ "NbbbNNNbbbN NbbbNNNbbbN", " NNN NNN NNN NNN ",
+ " N N N N ", " NNN NNN NNN NNN ",
+ "NbbbNNNbbbN NbbbNNNbbbN", "NbbbN NbbbN NbbbN NbbbN",
+ "NbbbN NbbbN NbbbN NbbbN", " NNN NNN NNN NNN ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " N N NsNsN N N ", },
+ { " ", " CCC CCC CCC CCC ", " CbC CbC CbC CbC ",
+ " CCCCCCCCC CCCCCCCCC ", " C C C C ",
+ " C C C C ", " C C C C ",
+ " CCCCCCCCC CCCCCCCCC ", " CbC CbC CbC CbC ",
+ " CCC CCC CCC CCC ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " N N NbN N N ", },
+ { " ", " CCC CCC CCC CCC ", " CbC CbC CbC CbC ",
+ " CCCCCCCCC CCCCCCCCC ", " C C C C ",
+ " C C C C ", " C C C C ",
+ " CCCCCCCCC CCCCCCCCC ", " CbC CbC CbC CbC ",
+ " CCC CCC CCC CCC ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " ", " ",
+ " N N NbN N N ", },
+ { " ", " CCC CCC N N CCC CCC ", " CbC CbC N N CbC CbC ",
+ " CCCCCCCCC N N CCCCCCCCC ", " C C C C ",
+ " C C C C ", " C C C C ",
+ " CCCCCCCCC N N CCCCCCCCC ", " CbC CbC N N CbC CbC ",
+ " CCC CCC N N CCC CCC ", " ",
+ " ", " ",
+ " NNN NNN NNN NNN ", " ",
+ " ", " ",
+ " ", " ",
+ " NNN NNN NNN NNN ", " ",
+ " ", " ",
+ " NNN NNN NbN NNN NNN ", },
+ { " NNN NNN N N NNN NNN ", "NbbbN NbbNCCCb bCCCNbbN NbbbN", "NbbbN NbbNCCCb bCCCNbbN NbbbN",
+ "NbbbNNNbbNCCCb bCCCNbbNNNbbbN", " NNN NNN N N NNN NNN ",
+ " N N N N ", " NNN NNN N N NNN NNN ",
+ "NbbbNNNbbNCCCb bCCCNbbNNNbbbN", "NbbbN NbbNCCCb bCCCNbbN NbbbN",
+ "NNNN NNNCCCb bCCCNNN NNNN", " CCC CCC N N CCC CCC ",
+ " CCC CCC CCC CCC ", " CCC CCC CCC CCC ",
+ "NbbbN NbbbN NbbbN NbbbN", " N N N N ",
+ " N N N N ", " ",
+ " N N N N ", " N N N N ",
+ "NbbbN NbbbN NbbbN NbbbN", " CCC CCC CCC CCC ",
+ " CCC CCC CCC CCC ", " CCC CCC CCC CCC ",
+ "NbbbN NbbbN NbN NbbbN NbbbN", },
+ { " N N N N ", " bCCCb bCCCb ", " N sbbbbbNNsNNbbbbbs N ",
+ " bCCCb bCCCb ", " N N N N ",
+ " ", " N N N N ",
+ " bCCCb bCCCb ", " s sbbbbbNNsNNbbbbbs s ",
+ "NbbbN NbbNCCCb bCCCNbbN NbbbN", " CbC CbC N N CbC CbC ",
+ " CbC CbC CbC CbC ", " CbC CbC CbC CbC ",
+ "NbbbN NbbbN NbbbN NbbbN", " NNN NNN NNN NNN ",
+ " NNN NNN NNN NNN ", " s s s s ",
+ " NNN NNN NNN NNN ", " NNN NNN NNN NNN ",
+ "NbbbN NbbbN NbbbN NbbbN", " CbC CbC CbC CbC ",
+ " CbC CbC CbC CbC ", " CbC CbC CbC CbC ",
+ "NbbbN NbbbNNNNNsNsNNNNNbbbN NbbbN", },
+ { " N N N N ", " bCCCb bCCCb ", " N bCCCb bCCCb N ",
+ " bCCCb bCCCb ", " NCCCN NCCCN ",
+ " NCCCN NCCCN ", " NCCCN NCCCN ",
+ " bCCCb bCCCb ", " bCCCb bCCCb ",
+ "NbbbNNNbbNCCCb bCCCNbbNNNbbbN", " CCCCCCCCC N N CCCCCCCCC ",
+ " CCCCCCCCC CCCCCCCCC ", " CCCCCCCCC CCCCCCCCC ",
+ "NbbbNNNbbbN NbbbNNNbbbN", " N N N N ",
+ " N N N N ", " ",
+ " N N N N ", " N N N N ",
+ "NbbbNNNbbbN NbbbNNNbbbN", " CCCCCCCCC CCCCCCCCC ",
+ " CCCCCCCCC CCCCCCCCC ", " CCCCCCCCC CCCCCCCCC ",
+ "NbbbNNNbbbN NbN NbbbNNNbbbN", },
+ { " ", " N N N N ", " N N N N N N ",
+ " NCCCN NCCCN ", " ",
+ " ", " ",
+ " NCCCN NCCCN ", " N N N N ",
+ " NNN NN N N NN NNN ", " C C C C ",
+ " C C C C ", " C C C C ",
+ " NNN NNN NNN NNN ", " ",
+ " ", " ",
+ " ", " ",
+ " NNN NNN NNN NNN ", " C C C C ",
+ " C C C C ", " C C C C ",
+ " NNN NNN NbN NNN NNN ", },
+ { " ", " ", " s s ",
+ " s NCCCN NCCCN s ", " ",
+ " ", " ",
+ " NCCCN NCCCN ", " ",
+ " N N N N ", " C C C C ",
+ " C C C C ", " C C C C ",
+ " N N N N ", " ",
+ " ", " ",
+ " ", " ",
+ " N N N N ", " C C C C ",
+ " C C C C ", " C C C C ",
+ " N N NbN N N ", },
+ { " ", " N N N N ", " s N N N N s ",
+ " s NCCCN NCCCN s ", " ",
+ " ", " ",
+ " NCCCN NCCCN ", " N N N N ",
+ " NNN NN N N NN NNN ", " C C C C ",
+ " C C C C ", " C C C C ",
+ " NNN NNN NNN NNN ", " ",
+ " ", " ",
+ " ", " ",
+ " NNN NNN NNN NNN ", " C C C C ",
+ " C C C C ", " C C C C ",
+ " NNN NNN NbN NNN NNN ", },
+ { " N N N N ", " bCCCb bCCCb ", " ss bCCCb bCCCb ss ",
+ " bCCCb bCCCb ", " s NCCCN NCCCN s ",
+ " s NCCCN NCCCN s ", " NCCCN NCCCN ",
+ " bCCCb bCCCb ", " bCCCb bCCCb ",
+ "NbbbNNNbbNCCCb bCCCNbbNNNbbbN", " CCCCCCCCC N N CCCCCCCCC ",
+ " CCCCCCCCC CCCCCCCCC ", " CCCCCCCCC CCCCCCCCC ",
+ "NbbbNNNbbbN NbbbNNNbbbN", " N N N N ",
+ " N N N N ", " ",
+ " N N N N ", " N N N N ",
+ "NbbbNNNbbbN NbbbNNNbbbN", " CCCCCCCCC CCCCCCCCC ",
+ " CCCCCCCCC CCCCCCCCC ", " CCCCCCCCC CCCCCCCCC ",
+ "NbbbNNNbbbN NbN NbbbNNNbbbN", },
+ { " N N N N ", " bCCCb bCCCb ", " NNNbbbbbNNsNNbbbbbNNN ",
+ " ss bCCCb bCCCb ss ", " s N N N N s ",
+ " s s ", " N N N N N N ",
+ " N bCCCb bCCCb N ", " N sbbbbbNNsNNbbbbbs N ",
+ "NbbbN NbbNCCCb bCCCNbbN NbbbN", " CbC CbC N N CbC CbC ",
+ " CbC CbC CbC CbC ", " CbC CbC CbC CbC ",
+ "NbbbN NbbbN NbbbN NbbbN", " NNN NNN NNN NNN ",
+ " NNN NNN NNN NNN ", " s s s s ",
+ " NNN NNN NNN NNN ", " NNN NNN NNN NNN ",
+ "NbbbN NbbbN NbbbN NbbbN", " CbC CbC CbC CbC ",
+ " CbC CbC CbC CbC ", " CbC CbC CbC CbC ",
+ "NbbbN NbbbNNNNNsNsNNNNNbbbN NbbbN", },
+ { " N N N N ", " bCCCb bCCCb ", " bCCCb bCCCb ",
+ " bCCCb bCCCb ", " N N N N ",
+ " ", " N N N N ",
+ " bCCCb bCCCb ", " bCCCb bCCCb ",
+ "NbbbN NbbNCCCb bCCCNbbN NbbbN", " CCC CCC N N CCC CCC ",
+ " CCC CCC CCC CCC ", " CCC CCC CCC CCC ",
+ "NbbbN NbbbN NbbbN NbbbN", " N N N N ",
+ " N N N N ", " ",
+ " N N N N ", " N N N N ",
+ "NbbbN NbbbN NbbbN NbbbN", " CCC CCC CCC CCC ",
+ " CCC CCC CCC CCC ", " CCC CCC CCC CCC ",
+ "NbbbN NbbbN N N NbbbN NbbbN", },
+ { " ", " N N N N ", " N N N N ",
+ " N N N N ", " ",
+ " ", " ",
+ " N N N N ", " N N N N ",
+ " NNN NNN N N NNN NNN ", " ",
+ " ", " ",
+ " NNN NNN NNN NNN ", " ",
+ " ", " ",
+ " ", " ",
+ " NNN NNN NNN NNN ", " ",
+ " ", " ",
+ " NNN NNN NNN NNN ", } };
+
+ protected static final int DIM_TRANS_CASING = 12;
+ protected static final int DIM_INJECTION_CASING = 13;
+ protected static final int DIM_BRIDGE_CASING = 14;
+
+ private boolean isMultiChunkloaded = true;
+
+ protected static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final IStructureDefinition<GT_MetaTileEntity_PlasmaForge> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_PlasmaForge>builder()
+ .addShape(STRUCTURE_PIECE_MAIN, structure_string)
+ .addElement(
+ 'C',
+ ofCoil(GT_MetaTileEntity_PlasmaForge::setCoilLevel, GT_MetaTileEntity_PlasmaForge::getCoilLevel))
+ .addElement(
+ 'b',
+ buildHatchAdder(GT_MetaTileEntity_PlasmaForge.class)
+ .atLeast(InputHatch, OutputHatch, InputBus, OutputBus, Energy, ExoticEnergy, Maintenance)
+ .casingIndex(DIM_INJECTION_CASING)
+ .dot(3)
+ .buildAndChain(GregTech_API.sBlockCasings1, DIM_INJECTION_CASING))
+ .addElement('N', ofBlock(GregTech_API.sBlockCasings1, DIM_TRANS_CASING))
+ .addElement('s', ofBlock(GregTech_API.sBlockCasings1, DIM_BRIDGE_CASING))
+ .build();
+
+ public GT_MetaTileEntity_PlasmaForge(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_PlasmaForge(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_PlasmaForge(mName);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Plasma Forge, DTPF")
+ .addInfo("Transcending Dimensional Boundaries.")
+ .addInfo(
+ "Takes " + EnumChatFormatting.RED
+ + GT_Utility.formatNumbers(max_efficiency_time_in_ticks / (3600 * 20))
+ + EnumChatFormatting.GRAY
+ + " hours of continuous run time to fully breach dimensional")
+ .addInfo(
+ "boundaries and achieve maximum efficiency, reducing fuel consumption by up to "
+ + EnumChatFormatting.RED
+ + GT_Utility.formatNumbers(100 * maximum_discount)
+ + "%"
+ + EnumChatFormatting.GRAY
+ + ".")
+ .addInfo("Multidimensional spaces can be perfectly aligned and synchronized in this state, ")
+ .addInfo(
+ "allowing " + EnumChatFormatting.GOLD
+ + "Dimensional Convergence "
+ + EnumChatFormatting.GRAY
+ + "to occur. To reach the required stability threshold,")
+ .addInfo(
+ "a " + EnumChatFormatting.AQUA
+ + "Transdimensional Alignment Matrix "
+ + EnumChatFormatting.GRAY
+ + "must be placed in the controller.")
+ .addInfo(
+ "When " + EnumChatFormatting.GOLD
+ + "Convergence "
+ + EnumChatFormatting.GRAY
+ + "is active, it allows the forge to perform "
+ + EnumChatFormatting.RED
+ + "Perfect Overclocks"
+ + EnumChatFormatting.GRAY
+ + ",")
+ .addInfo("but the extra power cost is instead added in form of increased catalyst amounts.")
+ .addInfo(
+ "When no recipe is running, fuel discount decays x" + EnumChatFormatting.RED
+ + GT_Utility.formatNumbers(efficiency_decay_rate)
+ + EnumChatFormatting.GRAY
+ + " as fast as it builds up.")
+ .addInfo(AuthorColen)
+ .addSeparator()
+ .beginStructureBlock(33, 24, 33, false)
+ .addStructureInfo("DTPF Structure is too complex! See schematic for details.")
+ .addStructureInfo(EnumChatFormatting.GOLD + "2,112" + EnumChatFormatting.GRAY + " Heating coils required.")
+ .addStructureInfo(
+ EnumChatFormatting.GOLD + "120" + EnumChatFormatting.GRAY + " Dimensional bridge blocks required.")
+ .addStructureInfo(
+ EnumChatFormatting.GOLD + "1,270"
+ + EnumChatFormatting.GRAY
+ + " Dimensional injection casings required.")
+ .addStructureInfo(
+ EnumChatFormatting.GOLD + "2,121"
+ + EnumChatFormatting.GRAY
+ + " Dimensionally transcendent casings required.")
+ .addStructureInfo("--------------------------------------------")
+ .addStructureInfo(
+ "Requires " + EnumChatFormatting.GOLD
+ + "1"
+ + EnumChatFormatting.GRAY
+ + "-"
+ + EnumChatFormatting.GOLD
+ + "2"
+ + EnumChatFormatting.GRAY
+ + " energy hatches or "
+ + EnumChatFormatting.GOLD
+ + "1"
+ + EnumChatFormatting.GRAY
+ + " TT energy hatch.")
+ .addStructureInfo(
+ "Requires " + EnumChatFormatting.GOLD
+ + min_input_hatch
+ + EnumChatFormatting.GRAY
+ + "-"
+ + EnumChatFormatting.GOLD
+ + max_input_hatch
+ + EnumChatFormatting.GRAY
+ + " input hatches.")
+ .addStructureInfo(
+ "Requires " + EnumChatFormatting.GOLD
+ + min_output_hatch
+ + EnumChatFormatting.GRAY
+ + "-"
+ + EnumChatFormatting.GOLD
+ + max_output_hatch
+ + EnumChatFormatting.GRAY
+ + " output hatches.")
+ .addStructureInfo(
+ "Requires " + EnumChatFormatting.GOLD
+ + min_input_bus
+ + EnumChatFormatting.GRAY
+ + "-"
+ + EnumChatFormatting.GOLD
+ + max_input_bus
+ + EnumChatFormatting.GRAY
+ + " input busses.")
+ .addStructureInfo(
+ "Requires " + EnumChatFormatting.GOLD
+ + min_output_bus
+ + EnumChatFormatting.GRAY
+ + "-"
+ + EnumChatFormatting.GOLD
+ + max_input_bus
+ + EnumChatFormatting.GRAY
+ + " output busses.")
+ .addStructureInfo("--------------------------------------------")
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public boolean addToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ boolean exotic = addExoticEnergyInputToMachineList(aTileEntity, aBaseCasingIndex);
+ return super.addToMachineList(aTileEntity, aBaseCasingIndex) || exotic;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ IIconContainer glow = OVERLAY_FUSION1_GLOW;
+ if (convergence && discount == maximum_discount) {
+ glow = OVERLAY_RAINBOWSCREEN_GLOW;
+ }
+ if (side == aFacing) {
+ if (aActive) return new ITexture[] { casingTexturePages[0][DIM_BRIDGE_CASING], TextureFactory.builder()
+ .addIcon(OVERLAY_DTPF_ON)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(glow)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { casingTexturePages[0][DIM_BRIDGE_CASING], TextureFactory.builder()
+ .addIcon(OVERLAY_DTPF_OFF)
+ .extFacing()
+ .build() };
+ }
+ return new ITexture[] { casingTexturePages[0][DIM_BRIDGE_CASING] };
+ }
+
+ @Override
+ public int getPollutionPerSecond(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.plasmaForgeRecipes;
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_PlasmaForge> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ @NotNull
+ public CheckRecipeResult checkProcessing() {
+ CheckRecipeResult recipe_process = super.checkProcessing();
+ if (recipe_process.wasSuccessful()) {
+ running_time = running_time + mMaxProgresstime;
+ }
+ return recipe_process;
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic() {
+
+ @Nonnull
+ @Override
+ protected GT_OverclockCalculator createOverclockCalculator(@Nonnull GT_Recipe recipe) {
+ overclockCalculator = super.createOverclockCalculator(recipe).setRecipeHeat(recipe.mSpecialValue)
+ .setMachineHeat(mHeatingCapacity);
+ if (discount == maximum_discount && convergence) {
+ overclockCalculator = overclockCalculator.enablePerfectOC();
+ }
+ return overclockCalculator;
+ }
+
+ @NotNull
+ @Override
+ protected GT_ParallelHelper createParallelHelper(@Nonnull GT_Recipe recipe) {
+ return super.createParallelHelper(recipeAfterAdjustments(recipe));
+ }
+
+ @Override
+ protected @Nonnull CheckRecipeResult validateRecipe(@Nonnull GT_Recipe recipe) {
+ return recipe.mSpecialValue <= mHeatingCapacity ? CheckRecipeResultRegistry.SUCCESSFUL
+ : CheckRecipeResultRegistry.insufficientHeat(recipe.mSpecialValue);
+ }
+ };
+ }
+
+ @Nonnull
+ protected GT_Recipe recipeAfterAdjustments(@Nonnull GT_Recipe recipe) {
+ GT_Recipe tRecipe = recipe.copy();
+ boolean adjusted = false;
+ outside: for (int i = 0; i < recipe.mFluidInputs.length; i++) {
+ for (FluidStack fuel : valid_fuels) {
+ if (tRecipe.mFluidInputs[i].isFluidEqual(fuel)) {
+ recalculateDiscount();
+ if (discount == maximum_discount && convergence
+ && overclockCalculator != null
+ && overclockCalculator.getCalculationStatus()) {
+ calculateCatalystIncrease(tRecipe, i, false);
+ getBaseMetaTileEntity()
+ .sendBlockEvent(GregTechTileClientEvents.CHANGE_CUSTOM_DATA, getUpdateData());
+ }
+ tRecipe.mFluidInputs[i].amount = (int) Math.round(tRecipe.mFluidInputs[i].amount * discount);
+ adjusted = true;
+ break outside;
+ }
+ }
+ }
+ // Convergence adjusts the recipe even if it has no catalyst input
+ if (!adjusted && discount == maximum_discount
+ && convergence
+ && overclockCalculator != null
+ && overclockCalculator.getCalculationStatus()) {
+ recalculateDiscount();
+ calculateCatalystIncrease(tRecipe, 0, true);
+ getBaseMetaTileEntity().sendBlockEvent(GregTechTileClientEvents.CHANGE_CUSTOM_DATA, getUpdateData());
+ }
+ return tRecipe;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+
+ // Reset heating capacity.
+ mHeatingCapacity = 0;
+
+ // Get heating capacity from coils in structure.
+ setCoilLevel(HeatingCoilLevel.None);
+
+ // Check the main structure
+ if (!checkPiece(STRUCTURE_PIECE_MAIN, 16, 21, 16)) return false;
+
+ if (getCoilLevel() == HeatingCoilLevel.None) return false;
+
+ // Item input bus check.
+ if (mInputBusses.size() > max_input_bus) return false;
+
+ // Item output bus check.
+ if (mOutputBusses.size() > max_output_bus) return false;
+
+ // Fluid input hatch check.
+ if (mInputHatches.size() > max_input_hatch) return false;
+
+ // Fluid output hatch check.
+ if (mOutputHatches.size() > max_output_hatch) return false;
+
+ // If there is more than 1 TT energy hatch, the structure check will fail.
+ // If there is a TT hatch and a normal hatch, the structure check will fail.
+ if (mExoticEnergyHatches.size() > 0) {
+ if (mEnergyHatches.size() > 0) return false;
+ if (mExoticEnergyHatches.size() > 1) return false;
+ }
+
+ // If there is 0 or more than 2 energy hatches structure check will fail.
+ if (mEnergyHatches.size() > 0) {
+ if (mEnergyHatches.size() > 2) return false;
+
+ // Check will also fail if energy hatches are not of the same tier.
+ byte tier_of_hatch = mEnergyHatches.get(0).mTier;
+ for (GT_MetaTileEntity_Hatch_Energy energyHatch : mEnergyHatches) {
+ if (energyHatch.mTier != tier_of_hatch) {
+ return false;
+ }
+ }
+ }
+
+ // If there are no energy hatches or TT energy hatches, structure will fail to form.
+ if ((mEnergyHatches.size() == 0) && (mExoticEnergyHatches.size() == 0)) return false;
+
+ // Maintenance hatch not required but left for compatibility.
+ // Don't allow more than 1, no free casing spam!
+ if (mMaintenanceHatches.size() > 1) return false;
+
+ // Heat capacity of coils used on multi. No free heat from extra EU!
+ mHeatingCapacity = (int) getCoilLevel().getHeat();
+
+ // All structure checks passed, return true.
+ return true;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public void clearHatches() {
+ super.clearHatches();
+ mExoticEnergyHatches.clear();
+ }
+
+ @Override
+ public boolean addOutput(FluidStack aLiquid) {
+ if (aLiquid == null) return false;
+ FluidStack tLiquid = aLiquid.copy();
+
+ return dumpFluid(mOutputHatches, tLiquid, true) || dumpFluid(mOutputHatches, tLiquid, false);
+ }
+
+ @Override
+ public String[] getInfoData() {
+
+ long storedEnergy = 0;
+ long maxEnergy = 0;
+
+ for (GT_MetaTileEntity_Hatch tHatch : filterValidMTEs(mExoticEnergyHatches)) {
+ storedEnergy += tHatch.getBaseMetaTileEntity()
+ .getStoredEU();
+ maxEnergy += tHatch.getBaseMetaTileEntity()
+ .getEUCapacity();
+ }
+ long voltage = getAverageInputVoltage();
+ long amps = getMaxInputAmps();
+
+ // Calculate discount to make sure it is shown properly even when machine is off but decaying
+ recalculateDiscount();
+
+ return new String[] { "------------ Critical Information ------------",
+ StatCollector.translateToLocal("GT5U.multiblock.Progress") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(mProgresstime)
+ + EnumChatFormatting.RESET
+ + "t / "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(mMaxProgresstime)
+ + EnumChatFormatting.RESET
+ + "t",
+ StatCollector.translateToLocal("GT5U.multiblock.energy") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(storedEnergy)
+ + EnumChatFormatting.RESET
+ + " EU / "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(maxEnergy)
+ + EnumChatFormatting.RESET
+ + " EU",
+ StatCollector.translateToLocal("GT5U.multiblock.usage") + ": "
+ + EnumChatFormatting.RED
+ + GT_Utility.formatNumbers(getActualEnergyUsage())
+ + EnumChatFormatting.RESET
+ + " EU/t",
+ StatCollector.translateToLocal("GT5U.multiblock.mei") + ": "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(voltage)
+ + EnumChatFormatting.RESET
+ + " EU/t(*"
+ + EnumChatFormatting.YELLOW
+ + amps
+ + EnumChatFormatting.RESET
+ + "A) "
+ + StatCollector.translateToLocal("GT5U.machines.tier")
+ + ": "
+ + EnumChatFormatting.YELLOW
+ + VN[GT_Utility.getTier(voltage)]
+ + EnumChatFormatting.RESET,
+ StatCollector.translateToLocal("GT5U.EBF.heat") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(mHeatingCapacity)
+ + EnumChatFormatting.RESET
+ + " K",
+ "Ticks run: " + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(running_time)
+ + EnumChatFormatting.RESET
+ + ", Fuel Discount: "
+ + EnumChatFormatting.RED
+ + GT_Utility.formatNumbers(100 * (1 - discount))
+ + EnumChatFormatting.RESET
+ + "%",
+ "Convergence: " + (convergence ? EnumChatFormatting.GREEN + "Active" : EnumChatFormatting.RED + "Inactive"),
+ "-----------------------------------------" };
+ }
+
+ private void recalculateDiscount() {
+ double time_percentage = running_time / max_efficiency_time_in_ticks;
+ time_percentage = Math.min(time_percentage, 1.0d);
+ // Multiplied by 0.5 because that is the maximum achievable discount
+ discount = 1 - time_percentage * 0.5;
+ discount = Math.max(maximum_discount, discount);
+ }
+
+ private int catalystTypeForRecipesWithoutCatalyst = 1;
+
+ private void calculateCatalystIncrease(GT_Recipe recipe, int index, boolean withoutCatalyst) {
+ long machineConsumption = overclockCalculator.getConsumption();
+ int numberOfOverclocks = (int) Math.ceil(calculateTier(machineConsumption) - GT_Utility.getTier(recipe.mEUt));
+ double recipeDuration = recipe.mDuration / Math.pow(4, numberOfOverclocks);
+ // Power difference between regular and perfect OCs for this recipe duration
+ long extraPowerNeeded = (long) ((Math.pow(2, numberOfOverclocks) - 1) * machineConsumption * recipeDuration);
+ int inputFluids = recipe.mFluidInputs.length;
+ int outputFluids = recipe.mFluidOutputs.length;
+ int extraCatalystNeeded;
+ Fluid validFuel;
+ if (!withoutCatalyst) {
+ validFuel = recipe.mFluidInputs[index].getFluid();
+ extraCatalystNeeded = (int) (extraPowerNeeded / FUEL_ENERGY_VALUES.get(validFuel)
+ .getLeft());
+ recipe.mFluidInputs[index].amount += extraCatalystNeeded;
+ // Increase present catalyst and residue by calculated amount
+ for (int j = 0; j < outputFluids; j++) {
+ if (recipe.mFluidOutputs[j]
+ .isFluidEqual(MaterialsUEVplus.DimensionallyTranscendentResidue.getFluid(1))) {
+ recipe.mFluidOutputs[j].amount += (int) (extraCatalystNeeded * FUEL_ENERGY_VALUES.get(validFuel)
+ .getRight());
+ }
+ }
+ } else {
+ // Add chosen catalyst as recipe input
+ validFuel = valid_fuels[catalystTypeForRecipesWithoutCatalyst - 1].getFluid();
+ extraCatalystNeeded = (int) (extraPowerNeeded / FUEL_ENERGY_VALUES.get(validFuel)
+ .getLeft());
+ FluidStack[] newInputFluids = new FluidStack[inputFluids + 1];
+ for (int i = 0; i < inputFluids; i++) {
+ newInputFluids[i] = recipe.mFluidInputs[i].copy();
+ }
+ newInputFluids[inputFluids] = new FluidStack(validFuel, extraCatalystNeeded / 2);
+ recipe.mFluidInputs = newInputFluids;
+ // Add residue as recipe output
+ FluidStack[] newOutputFluids = new FluidStack[outputFluids + 1];
+ for (int i = 0; i < outputFluids; i++) {
+ newOutputFluids[i] = recipe.mFluidOutputs[i].copy();
+ }
+ newOutputFluids[outputFluids] = new FluidStack(
+ MaterialsUEVplus.DimensionallyTranscendentResidue.getFluid(1),
+ (int) (extraCatalystNeeded * FUEL_ENERGY_VALUES.get(validFuel)
+ .getRight()));
+ recipe.mFluidOutputs = newOutputFluids;
+ }
+ }
+
+ private static final double LN2 = Math.log(2);
+
+ private double calculateTier(long voltage) {
+ return (1 + Math.max(0, (Math.log(voltage) / LN2) - 5) / 2);
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aBaseMetaTileEntity.isServerSide() && !aBaseMetaTileEntity.isAllowedToWork()) {
+ // If machine has stopped, stop chunkloading.
+ GT_ChunkManager.releaseTicket((TileEntity) aBaseMetaTileEntity);
+ isMultiChunkloaded = false;
+ } else if (aBaseMetaTileEntity.isServerSide() && aBaseMetaTileEntity.isAllowedToWork() && !isMultiChunkloaded) {
+ // Load a 3x3 area centered on controller when machine is running.
+ GT_ChunkManager.releaseTicket((TileEntity) aBaseMetaTileEntity);
+
+ int ControllerXCoordinate = ((TileEntity) aBaseMetaTileEntity).xCoord;
+ int ControllerZCoordinate = ((TileEntity) aBaseMetaTileEntity).zCoord;
+
+ GT_ChunkManager.requestChunkLoad(
+ (TileEntity) aBaseMetaTileEntity,
+ new ChunkCoordIntPair(ControllerXCoordinate, ControllerZCoordinate));
+ GT_ChunkManager.requestChunkLoad(
+ (TileEntity) aBaseMetaTileEntity,
+ new ChunkCoordIntPair(ControllerXCoordinate + 16, ControllerZCoordinate));
+ GT_ChunkManager.requestChunkLoad(
+ (TileEntity) aBaseMetaTileEntity,
+ new ChunkCoordIntPair(ControllerXCoordinate - 16, ControllerZCoordinate));
+ GT_ChunkManager.requestChunkLoad(
+ (TileEntity) aBaseMetaTileEntity,
+ new ChunkCoordIntPair(ControllerXCoordinate, ControllerZCoordinate + 16));
+ GT_ChunkManager.requestChunkLoad(
+ (TileEntity) aBaseMetaTileEntity,
+ new ChunkCoordIntPair(ControllerXCoordinate, ControllerZCoordinate - 16));
+ GT_ChunkManager.requestChunkLoad(
+ (TileEntity) aBaseMetaTileEntity,
+ new ChunkCoordIntPair(ControllerXCoordinate + 16, ControllerZCoordinate + 16));
+ GT_ChunkManager.requestChunkLoad(
+ (TileEntity) aBaseMetaTileEntity,
+ new ChunkCoordIntPair(ControllerXCoordinate + 16, ControllerZCoordinate - 16));
+ GT_ChunkManager.requestChunkLoad(
+ (TileEntity) aBaseMetaTileEntity,
+ new ChunkCoordIntPair(ControllerXCoordinate - 16, ControllerZCoordinate + 16));
+ GT_ChunkManager.requestChunkLoad(
+ (TileEntity) aBaseMetaTileEntity,
+ new ChunkCoordIntPair(ControllerXCoordinate - 16, ControllerZCoordinate - 16));
+
+ isMultiChunkloaded = true;
+ }
+
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+
+ if (aBaseMetaTileEntity.isServerSide()) {
+ if (mMaxProgresstime == 0) {
+ running_time = Math.max(0, running_time - (long) efficiency_decay_rate);
+ }
+ if (aTick % 100 == 0) {
+ ItemStack controllerStack = this.getControllerSlot();
+ if (convergence && (controllerStack == null
+ || !controllerStack.isItemEqual(ItemList.Transdimensional_Alignment_Matrix.get(1)))) {
+ convergence = false;
+ getBaseMetaTileEntity()
+ .sendBlockEvent(GregTechTileClientEvents.CHANGE_CUSTOM_DATA, getUpdateData());
+ }
+ }
+ }
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 16, 21, 16);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ int realBudget = elementBudget >= 200 ? elementBudget : Math.min(200, elementBudget * 5);
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 16, 21, 16, realBudget, env, false, true);
+ }
+
+ @SideOnly(Side.CLIENT)
+ @Override
+ protected ResourceLocation getActivitySoundLoop() {
+ return SoundResource.GT_MACHINES_PLASMAFORGE_LOOP.resourceLocation;
+ }
+
+ @Override
+ public byte getUpdateData() {
+ byte data = 0;
+ if (discount == maximum_discount) {
+ data += DISCOUNT_BITMAP;
+ }
+ if (convergence) {
+ data += CONVERGENCE_BITMAP;
+ }
+ return data;
+ }
+
+ @Override
+ public void receiveClientEvent(byte aEventID, byte aValue) {
+ if (aEventID == GregTechTileClientEvents.CHANGE_CUSTOM_DATA) {
+ convergence = (aValue & CONVERGENCE_BITMAP) == CONVERGENCE_BITMAP;
+ if ((aValue & DISCOUNT_BITMAP) == DISCOUNT_BITMAP) {
+ discount = maximum_discount;
+ }
+ }
+ }
+
+ private static final int CATALYST_WINDOW_ID = 10;
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ buildContext.addSyncedWindow(CATALYST_WINDOW_ID, this::createCatalystWindow);
+ builder.widget(new ButtonWidget().setOnClick((clickData, widget) -> {
+ ItemStack controllerStack = this.getControllerSlot();
+ if (clickData.mouseButton == 0) {
+ if (controllerStack != null
+ && controllerStack.isItemEqual(ItemList.Transdimensional_Alignment_Matrix.get(1))) {
+ convergence = !convergence;
+ }
+ } else if (clickData.mouseButton == 1 && !widget.isClient()) {
+ widget.getContext()
+ .openSyncedWindow(CATALYST_WINDOW_ID);
+ }
+ })
+ .setPlayClickSound(true)
+ .setBackground(() -> {
+ List<UITexture> ret = new ArrayList<>();
+ if (convergence) {
+ ret.add(GT_UITextures.BUTTON_STANDARD_PRESSED);
+ ret.add(TecTechUITextures.OVERLAY_BUTTON_SAFE_VOID_ON);
+ } else {
+ ret.add(GT_UITextures.BUTTON_STANDARD);
+ ret.add(TecTechUITextures.OVERLAY_BUTTON_SAFE_VOID_OFF);
+ }
+ return ret.toArray(new IDrawable[0]);
+ })
+ .addTooltip(translateToLocal("GT5U.DTPF.convergencebutton"))
+ .addTooltip(EnumChatFormatting.GRAY + translateToLocal("GT5U.DTPF.convergencebuttontooltip.0"))
+ .addTooltip(EnumChatFormatting.GRAY + translateToLocal("GT5U.DTPF.convergencebuttontooltip.1"))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setPos(174, 112)
+ .setSize(16, 16)
+ .attachSyncer(new FakeSyncWidget.BooleanSyncer(() -> convergence, (val) -> convergence = val), builder));
+ super.addUIWidgets(builder, buildContext);
+ }
+
+ protected ModularWindow createCatalystWindow(final EntityPlayer player) {
+ final int WIDTH = 58;
+ final int HEIGHT = 52;
+ final int PARENT_WIDTH = getGUIWidth();
+ final int PARENT_HEIGHT = getGUIHeight();
+ ModularWindow.Builder builder = ModularWindow.builder(WIDTH, HEIGHT);
+ builder.setBackground(GT_UITextures.BACKGROUND_SINGLEBLOCK_DEFAULT);
+ builder.setGuiTint(getGUIColorization());
+ builder.setDraggable(true);
+ builder.setPos(
+ (size, window) -> Alignment.Center.getAlignedPos(size, new Size(PARENT_WIDTH, PARENT_HEIGHT))
+ .add(
+ Alignment.BottomRight.getAlignedPos(new Size(PARENT_WIDTH, PARENT_HEIGHT), new Size(WIDTH, HEIGHT))
+ .add(WIDTH - 3, 0)
+ .subtract(0, 10)));
+ builder.widget(
+ TextWidget.localised("GT5U.DTPF.catalysttier")
+ .setPos(3, 4)
+ .setSize(50, 20))
+ .widget(
+ new NumericWidget().setSetter(val -> catalystTypeForRecipesWithoutCatalyst = (int) val)
+ .setGetter(() -> catalystTypeForRecipesWithoutCatalyst)
+ .setBounds(1, 5)
+ .setDefaultValue(1)
+ .setScrollValues(1, 4, 64)
+ .setTextAlignment(Alignment.Center)
+ .setTextColor(Color.WHITE.normal)
+ .setSize(50, 18)
+ .setPos(4, 25)
+ .setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD)
+ .addTooltip(translateToLocal("GT5U.DTPF.catalystinfotooltip"))
+ .attachSyncer(
+ new FakeSyncWidget.IntegerSyncer(
+ () -> catalystTypeForRecipesWithoutCatalyst,
+ (val) -> catalystTypeForRecipesWithoutCatalyst = val),
+ builder));
+ return builder.build();
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ aNBT.setLong("eRunningTime", running_time);
+ aNBT.setDouble("eLongDiscountValue", discount);
+ aNBT.setInteger("catalystType", catalystTypeForRecipesWithoutCatalyst);
+ aNBT.setBoolean("convergence", convergence);
+ super.saveNBTData(aNBT);
+ }
+
+ @Override
+ public void loadNBTData(final NBTTagCompound aNBT) {
+ running_time = aNBT.getLong("eRunningTime");
+ discount = aNBT.getDouble("eLongDiscountValue");
+ catalystTypeForRecipesWithoutCatalyst = aNBT.getInteger("catalystType");
+ convergence = aNBT.getBoolean("convergence");
+ super.loadNBTData(aNBT);
+ }
+
+ public HeatingCoilLevel getCoilLevel() {
+ return mCoilLevel;
+ }
+
+ public void setCoilLevel(HeatingCoilLevel aCoilLevel) {
+ mCoilLevel = aCoilLevel;
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsBatchMode() {
+ return true;
+ }
+
+ @Override
+ public boolean getDefaultHasMaintenanceChecks() {
+ return false;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PrimitiveBlastFurnace.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PrimitiveBlastFurnace.java
new file mode 100644
index 0000000000..d28aae81ce
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PrimitiveBlastFurnace.java
@@ -0,0 +1,521 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.isAir;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.lazy;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlockAnyMeta;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.objects.XSTR.XSTR_INSTANCE;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizon.structurelib.StructureLibAPI;
+import com.gtnewhorizon.structurelib.alignment.IAlignment;
+import com.gtnewhorizon.structurelib.alignment.IAlignmentLimits;
+import com.gtnewhorizon.structurelib.alignment.IAlignmentProvider;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.ProgressBar;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.ParticleFX;
+import gregtech.api.enums.SteamVariant;
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.gui.modularui.GUITextureSet;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.modularui.IGetTitleColor;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.interfaces.tileentity.RecipeMapWorkable;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.objects.GT_ItemStack;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.WorldSpawnedEventBuilder;
+import gregtech.api.util.WorldSpawnedEventBuilder.ParticleEventBuilder;
+import gregtech.common.GT_Pollution;
+
+public abstract class GT_MetaTileEntity_PrimitiveBlastFurnace extends MetaTileEntity
+ implements IAlignment, ISurvivalConstructable, RecipeMapWorkable, IAddUIWidgets, IGetTitleColor {
+
+ public static final int INPUT_SLOTS = 3, OUTPUT_SLOTS = 3;
+ private static final ClassValue<IStructureDefinition<GT_MetaTileEntity_PrimitiveBlastFurnace>> STRUCTURE_DEFINITION = new ClassValue<>() {
+
+ @Override
+ protected IStructureDefinition<GT_MetaTileEntity_PrimitiveBlastFurnace> computeValue(Class<?> type) {
+ return IStructureDefinition.<GT_MetaTileEntity_PrimitiveBlastFurnace>builder()
+ .addShape(
+ "main",
+ transpose(
+ new String[][] { { "ccc", "c-c", "ccc" }, { "ccc", "clc", "ccc" }, { "c~c", "clc", "ccc" },
+ { "ccc", "ccc", "ccc" }, }))
+ .addElement('c', lazy(t -> ofBlock(t.getCasingBlock(), t.getCasingMetaID())))
+ .addElement(
+ 'l',
+ ofChain(isAir(), ofBlockAnyMeta(Blocks.lava, 1), ofBlockAnyMeta(Blocks.flowing_lava, 1)))
+ .build();
+ }
+ };
+
+ public int mMaxProgresstime = 0;
+ private volatile boolean mUpdated;
+ public int mUpdate = 5;
+ public int mProgresstime = 0;
+ public boolean mMachine = false;
+
+ public ItemStack[] mOutputItems = new ItemStack[OUTPUT_SLOTS];
+
+ public GT_MetaTileEntity_PrimitiveBlastFurnace(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional, INPUT_SLOTS + OUTPUT_SLOTS);
+ }
+
+ public GT_MetaTileEntity_PrimitiveBlastFurnace(String aName) {
+ super(aName, INPUT_SLOTS + OUTPUT_SLOTS);
+ }
+
+ @Override
+ public boolean isTeleporterCompatible() {
+ return false;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return (facing.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public int getProgresstime() {
+ return this.mProgresstime;
+ }
+
+ @Override
+ public int maxProgresstime() {
+ return this.mMaxProgresstime;
+ }
+
+ @Override
+ public int increaseProgress(int aProgress) {
+ this.mProgresstime += aProgress;
+ return this.mMaxProgresstime - this.mProgresstime;
+ }
+
+ @Override
+ public boolean allowCoverOnSide(ForgeDirection side, GT_ItemStack aCoverID) {
+ return (GregTech_API.getCoverBehaviorNew(aCoverID.toStack())
+ .isSimpleCover()) && (super.allowCoverOnSide(side, aCoverID));
+ }
+
+ @Override
+ public abstract MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity);
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ aNBT.setInteger("mProgresstime", this.mProgresstime);
+ aNBT.setInteger("mMaxProgresstime", this.mMaxProgresstime);
+ if (this.mOutputItems != null) {
+ for (int i = 0; i < mOutputItems.length; i++) {
+ if (this.mOutputItems[i] != null) {
+ NBTTagCompound tNBT = new NBTTagCompound();
+ this.mOutputItems[i].writeToNBT(tNBT);
+ aNBT.setTag("mOutputItem" + i, tNBT);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ this.mUpdate = 5;
+ this.mProgresstime = aNBT.getInteger("mProgresstime");
+ this.mMaxProgresstime = aNBT.getInteger("mMaxProgresstime");
+ this.mOutputItems = new ItemStack[OUTPUT_SLOTS];
+ for (int i = 0; i < OUTPUT_SLOTS; i++) {
+ this.mOutputItems[i] = GT_Utility.loadItem(aNBT, "mOutputItem" + i);
+ }
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+
+ @Override
+ public ExtendedFacing getExtendedFacing() {
+ return ExtendedFacing.of(getBaseMetaTileEntity().getFrontFacing());
+ }
+
+ @Override
+ public void setExtendedFacing(ExtendedFacing alignment) {
+ getBaseMetaTileEntity().setFrontFacing(alignment.getDirection());
+ }
+
+ @Override
+ public IAlignmentLimits getAlignmentLimits() {
+ return (d, r, f) -> (d.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0 && r.isNotRotated()
+ && f.isNotFlipped();
+ }
+
+ private boolean checkMachine() {
+ return STRUCTURE_DEFINITION.get(this.getClass())
+ .check(
+ this,
+ "main",
+ getBaseMetaTileEntity().getWorld(),
+ getExtendedFacing(),
+ getBaseMetaTileEntity().getXCoord(),
+ getBaseMetaTileEntity().getYCoord(),
+ getBaseMetaTileEntity().getZCoord(),
+ 1,
+ 2,
+ 0,
+ !mMachine);
+ }
+
+ protected abstract Block getCasingBlock();
+
+ protected abstract int getCasingMetaID();
+
+ @Override
+ public void onMachineBlockUpdate() {
+ mUpdated = true;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) {
+ final int lavaX = aBaseMetaTileEntity.getOffsetX(aBaseMetaTileEntity.getBackFacing(), 1);
+ final int lavaZ = aBaseMetaTileEntity.getOffsetZ(aBaseMetaTileEntity.getBackFacing(), 1);
+ if ((aBaseMetaTileEntity.isClientSide()) && (aBaseMetaTileEntity.isActive())) {
+
+ new WorldSpawnedEventBuilder.ParticleEventBuilder().setMotion(0D, 0.3D, 0D)
+ .setIdentifier(ParticleFX.LARGE_SMOKE)
+ .setPosition(
+ lavaX + XSTR_INSTANCE.nextFloat(),
+ aBaseMetaTileEntity.getOffsetY(aBaseMetaTileEntity.getBackFacing(), 1),
+ lavaZ + XSTR_INSTANCE.nextFloat())
+ .setWorld(getBaseMetaTileEntity().getWorld())
+ .run();
+ }
+ if (aBaseMetaTileEntity.isServerSide()) {
+ if (mUpdated) {
+ // duct tape fix for too many updates on an overloaded server, causing the structure check to not run
+ if (mUpdate < 0) mUpdate = 5;
+ mUpdated = false;
+ }
+ if (this.mUpdate-- == 0) {
+ this.mMachine = checkMachine();
+ }
+ if (this.mMachine) {
+ if (this.mMaxProgresstime > 0) {
+ if (++this.mProgresstime >= this.mMaxProgresstime) {
+ addOutputProducts();
+ this.mOutputItems = null;
+ this.mProgresstime = 0;
+ this.mMaxProgresstime = 0;
+ GT_Mod.achievements.issueAchievement(
+ aBaseMetaTileEntity.getWorld()
+ .getPlayerEntityByName(aBaseMetaTileEntity.getOwnerName()),
+ "steel");
+ }
+ } else if (aBaseMetaTileEntity.isAllowedToWork()) {
+ checkRecipe();
+ }
+ }
+ if (this.mMaxProgresstime > 0 && (aTimer % 20L == 0L)) {
+ GT_Pollution.addPollution(
+ this.getBaseMetaTileEntity(),
+ GT_Mod.gregtechproxy.mPollutionPrimitveBlastFurnacePerSecond);
+ }
+
+ aBaseMetaTileEntity.setActive((this.mMaxProgresstime > 0) && (this.mMachine));
+ final short lavaY = aBaseMetaTileEntity.getYCoord();
+ if (aBaseMetaTileEntity.isActive()) {
+ if (aBaseMetaTileEntity.getAir(lavaX, lavaY, lavaZ)) {
+ aBaseMetaTileEntity.getWorld()
+ .setBlock(lavaX, lavaY, lavaZ, Blocks.lava, 1, 2);
+ this.mUpdate = 1;
+ }
+ if (aBaseMetaTileEntity.getAir(lavaX, lavaY + 1, lavaZ)) {
+ aBaseMetaTileEntity.getWorld()
+ .setBlock(lavaX, lavaY + 1, lavaZ, Blocks.lava, 1, 2);
+ this.mUpdate = 1;
+ }
+ } else {
+ Block lowerLava = aBaseMetaTileEntity.getBlock(lavaX, lavaY, lavaZ);
+ Block upperLava = aBaseMetaTileEntity.getBlock(lavaX, lavaY + 1, lavaZ);
+ if (lowerLava == Blocks.lava) {
+ aBaseMetaTileEntity.getWorld()
+ .setBlock(lavaX, lavaY, lavaZ, Blocks.air, 0, 2);
+ this.mUpdate = 1;
+ }
+ if (upperLava == Blocks.lava) {
+ aBaseMetaTileEntity.getWorld()
+ .setBlock(lavaX, lavaY + 1, lavaZ, Blocks.air, 0, 2);
+ this.mUpdate = 1;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ super.onFirstTick(aBaseMetaTileEntity);
+ if (aBaseMetaTileEntity.isClientSide())
+ StructureLibAPI.queryAlignment((IAlignmentProvider) aBaseMetaTileEntity);
+ }
+
+ /**
+ * Draws random flames and smoke particles in front of Primitive Blast Furnace when active
+ *
+ * @param aBaseMetaTileEntity The entity that will handle the {@link Block#randomDisplayTick}
+ */
+ @SideOnly(Side.CLIENT)
+ @Override
+ public void onRandomDisplayTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ if (aBaseMetaTileEntity.isActive()) {
+
+ final ForgeDirection frontFacing = aBaseMetaTileEntity.getFrontFacing();
+
+ final double oX = aBaseMetaTileEntity.getOffsetX(frontFacing, 1) + 0.5D;
+ final double oY = aBaseMetaTileEntity.getOffsetY(frontFacing, 1);
+ final double oZ = aBaseMetaTileEntity.getOffsetZ(frontFacing, 1) + 0.5D;
+ final double offset = -0.48D;
+ final double horizontal = XSTR_INSTANCE.nextFloat() * 8D / 16D - 4D / 16D;
+
+ final double x, y, z;
+
+ y = oY + XSTR_INSTANCE.nextFloat() * 10D / 16D + 5D / 16D;
+
+ if (frontFacing == ForgeDirection.WEST) {
+ x = oX - offset;
+ z = oZ + horizontal;
+ } else if (frontFacing == ForgeDirection.EAST) {
+ x = oX + offset;
+ z = oZ + horizontal;
+ } else if (frontFacing == ForgeDirection.NORTH) {
+ x = oX + horizontal;
+ z = oZ - offset;
+ } else // if (frontFacing == ForgeDirection.SOUTH.ordinal())
+ {
+ x = oX + horizontal;
+ z = oZ + offset;
+ }
+
+ ParticleEventBuilder particleEventBuilder = (new ParticleEventBuilder()).setMotion(0D, 0D, 0D)
+ .setPosition(x, y, z)
+ .setWorld(getBaseMetaTileEntity().getWorld());
+ particleEventBuilder.setIdentifier(ParticleFX.SMOKE)
+ .run();
+ particleEventBuilder.setIdentifier(ParticleFX.FLAME)
+ .run();
+ }
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.primitiveBlastRecipes;
+ }
+
+ private void addOutputProducts() {
+ if (this.mOutputItems == null) {
+ return;
+ }
+ int limit = Math.min(mOutputItems.length, OUTPUT_SLOTS);
+ for (int i = 0; i < limit; i++) {
+ int absi = INPUT_SLOTS + i;
+ if (this.mInventory[absi] == null) {
+ this.mInventory[absi] = GT_Utility.copyOrNull(this.mOutputItems[i]);
+ } else if (GT_Utility.areStacksEqual(this.mInventory[absi], this.mOutputItems[i])) {
+ this.mInventory[absi].stackSize = Math.min(
+ this.mInventory[absi].getMaxStackSize(),
+ this.mInventory[absi].stackSize + this.mOutputItems[i].stackSize);
+ }
+ }
+ }
+
+ private boolean spaceForOutput(ItemStack outputStack, int relativeOutputSlot) {
+ int absoluteSlot = relativeOutputSlot + INPUT_SLOTS;
+ if (this.mInventory[absoluteSlot] == null || outputStack == null) {
+ return true;
+ }
+ return ((this.mInventory[absoluteSlot].stackSize + outputStack.stackSize
+ <= this.mInventory[absoluteSlot].getMaxStackSize())
+ && (GT_Utility.areStacksEqual(this.mInventory[absoluteSlot], outputStack)));
+ }
+
+ private boolean checkRecipe() {
+ if (!this.mMachine) {
+ return false;
+ }
+ ItemStack[] inputs = new ItemStack[INPUT_SLOTS];
+ System.arraycopy(mInventory, 0, inputs, 0, INPUT_SLOTS);
+ GT_Recipe recipe = getRecipeMap().findRecipe(getBaseMetaTileEntity(), false, 0, null, inputs);
+ if (recipe == null) {
+ this.mOutputItems = null;
+ return false;
+ }
+ for (int i = 0; i < OUTPUT_SLOTS; i++) {
+ if (!spaceForOutput(recipe.getOutput(i), i)) {
+ this.mOutputItems = null;
+ return false;
+ }
+ }
+
+ if (!recipe.isRecipeInputEqual(true, null, inputs)) {
+ this.mOutputItems = null;
+ return false;
+ }
+ for (int i = 0; i < INPUT_SLOTS; i++) {
+ if (mInventory[i] != null && mInventory[i].stackSize == 0) {
+ mInventory[i] = null;
+ }
+ }
+
+ this.mMaxProgresstime = recipe.mDuration;
+ this.mOutputItems = recipe.mOutputs;
+ return true;
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return aIndex > INPUT_SLOTS;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return !GT_Utility.areStacksEqual(aStack, this.mInventory[0]);
+ }
+
+ @Override
+ public byte getTileEntityBaseType() {
+ return 0;
+ }
+
+ public abstract String getName();
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return STRUCTURE_DEFINITION.get(getClass())
+ .survivalBuild(
+ this,
+ stackSize,
+ "main",
+ getBaseMetaTileEntity().getWorld(),
+ getExtendedFacing(),
+ getBaseMetaTileEntity().getXCoord(),
+ getBaseMetaTileEntity().getYCoord(),
+ getBaseMetaTileEntity().getZCoord(),
+ 1,
+ 2,
+ 0,
+ elementBudget,
+ env,
+ false);
+ }
+
+ @Override
+ public IStructureDefinition<?> getStructureDefinition() {
+ return STRUCTURE_DEFINITION.get(getClass());
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ STRUCTURE_DEFINITION.get(getClass())
+ .buildOrHints(
+ this,
+ stackSize,
+ "main",
+ getBaseMetaTileEntity().getWorld(),
+ getExtendedFacing(),
+ getBaseMetaTileEntity().getXCoord(),
+ getBaseMetaTileEntity().getYCoord(),
+ getBaseMetaTileEntity().getZCoord(),
+ 1,
+ 2,
+ 0,
+ hintsOnly);
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ builder
+ .widget(
+ new SlotWidget(inventoryHandler, 0)
+ .setBackground(
+ getGUITextureSet().getItemSlot(),
+ GT_UITextures.OVERLAY_SLOT_INGOT_STEAM.get(getSteamVariant()))
+ .setPos(33, 15))
+ .widget(
+ new SlotWidget(inventoryHandler, 1)
+ .setBackground(
+ getGUITextureSet().getItemSlot(),
+ GT_UITextures.OVERLAY_SLOT_DUST_STEAM.get(getSteamVariant()))
+ .setPos(33, 33))
+ .widget(
+ new SlotWidget(inventoryHandler, 2)
+ .setBackground(
+ getGUITextureSet().getItemSlot(),
+ GT_UITextures.OVERLAY_SLOT_FURNACE_STEAM.get(getSteamVariant()))
+ .setPos(33, 51))
+ .widget(
+ new SlotWidget(inventoryHandler, 3).setAccess(true, false)
+ .setBackground(
+ getGUITextureSet().getItemSlot(),
+ GT_UITextures.OVERLAY_SLOT_INGOT_STEAM.get(getSteamVariant()))
+ .setPos(85, 24))
+ .widget(
+ new SlotWidget(inventoryHandler, 4).setAccess(true, false)
+ .setBackground(
+ getGUITextureSet().getItemSlot(),
+ GT_UITextures.OVERLAY_SLOT_DUST_STEAM.get(getSteamVariant()))
+ .setPos(103, 24))
+ .widget(
+ new SlotWidget(inventoryHandler, 5).setAccess(true, false)
+ .setBackground(
+ getGUITextureSet().getItemSlot(),
+ GT_UITextures.OVERLAY_SLOT_DUST_STEAM.get(getSteamVariant()))
+ .setPos(121, 24))
+ .widget(
+ new ProgressBar().setTexture(GT_UITextures.PROGRESSBAR_ARROW_2_STEAM.get(getSteamVariant()), 20)
+ .setProgress(() -> (float) mProgresstime / mMaxProgresstime)
+ .setNEITransferRect(
+ getRecipeMap().getFrontend()
+ .getUIProperties().neiTransferRectId)
+ .setPos(58, 24)
+ .setSize(20, 18));
+ }
+
+ @Override
+ public GUITextureSet getGUITextureSet() {
+ return GUITextureSet.STEAM.apply(getSteamVariant());
+ }
+
+ @Override
+ public int getTitleColor() {
+ return getSteamVariant() == SteamVariant.BRONZE ? COLOR_TITLE.get() : COLOR_TITLE_WHITE.get();
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ProcessingArray.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ProcessingArray.java
new file mode 100644
index 0000000000..abb0605df7
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ProcessingArray.java
@@ -0,0 +1,550 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.lazy;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.onElementPass;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.ExoticEnergy;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.GT_Values.VN;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PROCESSING_ARRAY;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PROCESSING_ARRAY_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PROCESSING_ARRAY_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PROCESSING_ARRAY_GLOW;
+import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY;
+import static gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine.isValidForLowGravity;
+import static gregtech.api.util.GT_Utility.filterValidMTEs;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.google.common.collect.ImmutableList;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.Textures;
+import gregtech.api.enums.Textures.BlockIcons;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.IHatchElement;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Input;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_InputBus;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_TieredMachineBlock;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.recipe.check.SimpleCheckRecipeResult;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_ExoticEnergyInputHelper;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_ProcessingArray_Manager;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_StructureUtility;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.blocks.GT_Item_Machines;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+@Deprecated
+public class GT_MetaTileEntity_ProcessingArray extends
+ GT_MetaTileEntity_ExtendedPowerMultiBlockBase<GT_MetaTileEntity_ProcessingArray> implements ISurvivalConstructable {
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final IStructureDefinition<GT_MetaTileEntity_ProcessingArray> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_ProcessingArray>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ transpose(new String[][] { { "hhh", "hhh", "hhh" }, { "h~h", "h-h", "hhh" }, { "hhh", "hhh", "hhh" }, }))
+ .addElement(
+ 'h',
+ ofChain(
+ lazy(
+ t -> GT_StructureUtility.<GT_MetaTileEntity_ProcessingArray>buildHatchAdder()
+ .atLeastList(t.getAllowedHatches())
+ .casingIndex(48)
+ .dot(1)
+ .build()),
+ onElementPass(t -> t.mCasingAmount++, ofBlock(GregTech_API.sBlockCasings4, 0))))
+ .build();
+
+ private int mCasingAmount = 0;
+
+ private RecipeMap<?> mLastRecipeMap;
+ private ItemStack lastControllerStack;
+ private int tTier = 0;
+ private int mMult = 0;
+ private boolean downtierUEV = true;
+
+ public GT_MetaTileEntity_ProcessingArray(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_ProcessingArray(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_ProcessingArray(this.mName);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Processing Array")
+ .addInfo("Runs supplied machines as if placed in the world")
+ .addInfo("Place up to 64 singleblock GT machines into the controller")
+ .addInfo("Note that you still need to supply power to them all")
+ .addInfo("Use a screwdriver to enable separate input busses")
+ .addInfo("Use a wire cutter to disable UEV+ downtiering")
+ .addInfo("Doesn't work on certain machines, deal with it")
+ .addInfo("Use it if you hate GT++, or want even more speed later on")
+ .addInfo(
+ EnumChatFormatting.GOLD
+ + "On the way to be slowly removed. Use it strictly if you have no alternative.")
+ .addSeparator()
+ .beginStructureBlock(3, 3, 3, true)
+ .addController("Front center")
+ .addCasingInfoRange("Robust Tungstensteel Machine Casing", 14, 24, false)
+ .addEnergyHatch("Any casing", 1)
+ .addMaintenanceHatch("Any casing", 1)
+ .addInputBus("Any casing", 1)
+ .addInputHatch("Any casing", 1)
+ .addOutputBus("Any casing", 1)
+ .addOutputHatch("Any casing", 1)
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public boolean addToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ return super.addToMachineList(aTileEntity, aBaseCasingIndex)
+ || addExoticEnergyInputToMachineList(aTileEntity, aBaseCasingIndex);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side == aFacing) {
+ if (aActive) return new ITexture[] { BlockIcons.casingTexturePages[0][48], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_PROCESSING_ARRAY_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_PROCESSING_ARRAY_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { BlockIcons.casingTexturePages[0][48], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_PROCESSING_ARRAY)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_PROCESSING_ARRAY_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { Textures.BlockIcons.casingTexturePages[0][48] };
+ }
+
+ private RecipeMap<?> fetchRecipeMap() {
+ if (isCorrectMachinePart(getControllerSlot())) {
+ // Gets the recipe map for the given machine through its unlocalized name
+ return GT_ProcessingArray_Manager
+ .giveRecipeMap(GT_ProcessingArray_Manager.getMachineName(getControllerSlot()));
+ }
+ return null;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return mLastRecipeMap;
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return aStack != null && aStack.getUnlocalizedName()
+ .startsWith("gt.blockmachines.");
+ }
+
+ @Override
+ protected void sendStartMultiBlockSoundLoop() {
+ SoundResource sound = GT_ProcessingArray_Manager
+ .getSoundResource(GT_ProcessingArray_Manager.getMachineName(getControllerSlot()));
+ if (sound != null) {
+ sendLoopStart((byte) sound.id);
+ }
+ }
+
+ @Override
+ public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) {
+ super.startSoundLoop(aIndex, aX, aY, aZ);
+ SoundResource sound = SoundResource.get(aIndex < 0 ? aIndex + 256 : 0);
+ if (sound != null) {
+ GT_Utility.doSoundAtClient(sound, getTimeBetweenProcessSounds(), 1.0F, aX, aY, aZ);
+ }
+ }
+
+ @Override
+ @NotNull
+ public CheckRecipeResult checkProcessing() {
+ if (!GT_Utility.areStacksEqual(lastControllerStack, getControllerSlot())) {
+ // controller slot has changed
+ lastControllerStack = getControllerSlot();
+ mLastRecipeMap = fetchRecipeMap();
+ setTierAndMult();
+ }
+ if (mLastRecipeMap == null) return SimpleCheckRecipeResult.ofFailure("no_machine");
+ if (mLockedToSingleRecipe && mSingleRecipeCheck != null) {
+ if (mSingleRecipeCheck.getRecipeMap() != mLastRecipeMap) {
+ return SimpleCheckRecipeResult.ofFailure("machine_mismatch");
+ }
+ }
+
+ return super.checkProcessing();
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic() {
+
+ @Nonnull
+ @Override
+ protected CheckRecipeResult validateRecipe(@Nonnull GT_Recipe recipe) {
+ if (GT_Mod.gregtechproxy.mLowGravProcessing && recipe.mSpecialValue == -100
+ && !isValidForLowGravity(recipe, getBaseMetaTileEntity().getWorld().provider.dimensionId)) {
+ return SimpleCheckRecipeResult.ofFailure("high_gravity");
+ }
+ if (recipe.mEUt > availableVoltage) return CheckRecipeResultRegistry.insufficientPower(recipe.mEUt);
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ }
+ }.setMaxParallelSupplier(this::getMaxParallel);
+ }
+
+ @Override
+ protected boolean canUseControllerSlotForRecipe() {
+ return false;
+ }
+
+ @Override
+ protected void setProcessingLogicPower(ProcessingLogic logic) {
+ logic.setAvailableVoltage(GT_Values.V[tTier] * (mLastRecipeMap != null ? mLastRecipeMap.getAmperage() : 1));
+ logic.setAvailableAmperage(getMaxParallel());
+ logic.setAmperageOC(false);
+ }
+
+ private void setTierAndMult() {
+ IMetaTileEntity aMachine = GT_Item_Machines.getMetaTileEntity(getControllerSlot());
+ if (aMachine instanceof GT_MetaTileEntity_TieredMachineBlock) {
+ tTier = ((GT_MetaTileEntity_TieredMachineBlock) aMachine).mTier;
+ } else {
+ tTier = 0;
+ }
+ mMult = 0;
+ if (downtierUEV && tTier > 9) {
+ tTier--; // Lowers down the tier by 1 to allow for bigger parallel
+ mMult = 2; // Multiplies Parallels by 4x, keeping the energy cost
+ }
+ }
+
+ private int getMaxParallel() {
+ if (getControllerSlot() == null) {
+ return 0;
+ }
+ return getControllerSlot().stackSize << mMult;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ if (mMachine && aTick % 20 == 0) {
+ for (GT_MetaTileEntity_Hatch_InputBus tInputBus : mInputBusses) {
+ tInputBus.mRecipeMap = mLastRecipeMap;
+ }
+ for (GT_MetaTileEntity_Hatch_Input tInputHatch : mInputHatches) {
+ tInputHatch.mRecipeMap = mLastRecipeMap;
+ }
+ }
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_ProcessingArray> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public void construct(ItemStack aStack, boolean aHintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, aStack, aHintsOnly, 1, 1, 0);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 1, 1, 0, elementBudget, env, false, true);
+ }
+
+ private boolean checkHatches() {
+ return mMaintenanceHatches.size() == 1;
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setBoolean("downtierUEV", downtierUEV);
+ }
+
+ @Override
+ public void loadNBTData(final NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ if (aNBT.hasKey("mSeparate")) {
+ // backward compatibility
+ inputSeparation = aNBT.getBoolean("mSeparate");
+ }
+ if (aNBT.hasKey("mUseMultiparallelMode")) {
+ // backward compatibility
+ batchMode = aNBT.getBoolean("mUseMultiparallelMode");
+ }
+ downtierUEV = aNBT.getBoolean("downtierUEV");
+ }
+
+ @Override
+ public final void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ if (aPlayer.isSneaking()) {
+ // Lock to single recipe
+ super.onScrewdriverRightClick(side, aPlayer, aX, aY, aZ);
+ } else {
+ inputSeparation = !inputSeparation;
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ StatCollector.translateToLocal("GT5U.machines.separatebus") + " " + inputSeparation);
+ }
+ }
+
+ @Override
+ public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer,
+ float aX, float aY, float aZ) {
+ if (aPlayer.isSneaking()) {
+ batchMode = !batchMode;
+ if (batchMode) {
+ GT_Utility.sendChatToPlayer(aPlayer, "Batch recipes");
+ } else {
+ GT_Utility.sendChatToPlayer(aPlayer, "Don't batch recipes");
+ }
+ } else {
+ downtierUEV = !downtierUEV;
+ GT_Utility.sendChatToPlayer(aPlayer, "Treat UEV+ machines as multiple UHV " + downtierUEV);
+ }
+ return true;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ private List<IHatchElement<? super GT_MetaTileEntity_ProcessingArray>> getAllowedHatches() {
+ return ImmutableList.of(InputHatch, OutputHatch, InputBus, OutputBus, Maintenance, Energy, ExoticEnergy);
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ mExoticEnergyHatches.clear();
+ mCasingAmount = 0;
+ return checkPiece(STRUCTURE_PIECE_MAIN, 1, 1, 0) && mCasingAmount >= 14 && checkHatches();
+ }
+
+ @Override
+ public String[] getInfoData() {
+ long storedEnergy = 0;
+ long maxEnergy = 0;
+ for (GT_MetaTileEntity_Hatch tHatch : filterValidMTEs(mExoticEnergyHatches)) {
+ storedEnergy += tHatch.getBaseMetaTileEntity()
+ .getStoredEU();
+ maxEnergy += tHatch.getBaseMetaTileEntity()
+ .getEUCapacity();
+ }
+
+ return new String[] {
+ StatCollector.translateToLocal("GT5U.multiblock.Progress") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(mProgresstime / 20)
+ + EnumChatFormatting.RESET
+ + " s / "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(mMaxProgresstime / 20)
+ + EnumChatFormatting.RESET
+ + " s",
+ StatCollector.translateToLocal("GT5U.multiblock.energy") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(storedEnergy)
+ + EnumChatFormatting.RESET
+ + " EU / "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(maxEnergy)
+ + EnumChatFormatting.RESET
+ + " EU",
+ StatCollector.translateToLocal("GT5U.multiblock.usage") + ": "
+ + EnumChatFormatting.RED
+ + GT_Utility.formatNumbers(-lEUt)
+ + EnumChatFormatting.RESET
+ + " EU/t",
+ StatCollector.translateToLocal("GT5U.multiblock.mei") + ": "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(
+ GT_ExoticEnergyInputHelper.getMaxInputVoltageMulti(getExoticAndNormalEnergyHatchList()))
+ + EnumChatFormatting.RESET
+ + " EU/t(*"
+ + GT_Utility
+ .formatNumbers(GT_ExoticEnergyInputHelper.getMaxInputAmpsMulti(getExoticAndNormalEnergyHatchList()))
+ + "A) "
+ + StatCollector.translateToLocal("GT5U.machines.tier")
+ + ": "
+ + EnumChatFormatting.YELLOW
+ + VN[GT_Utility
+ .getTier(GT_ExoticEnergyInputHelper.getMaxInputVoltageMulti(getExoticAndNormalEnergyHatchList()))]
+ + EnumChatFormatting.RESET,
+ StatCollector.translateToLocal("GT5U.multiblock.problems") + ": "
+ + EnumChatFormatting.RED
+ + (getIdealStatus() - getRepairStatus())
+ + EnumChatFormatting.RESET
+ + " "
+ + StatCollector.translateToLocal("GT5U.multiblock.efficiency")
+ + ": "
+ + EnumChatFormatting.YELLOW
+ + mEfficiency / 100.0F
+ + EnumChatFormatting.RESET
+ + " %",
+ StatCollector.translateToLocal("GT5U.PA.machinetier") + ": "
+ + EnumChatFormatting.GREEN
+ + tTier
+ + EnumChatFormatting.RESET
+ + " "
+ + StatCollector.translateToLocal("GT5U.PA.discount")
+ + ": "
+ + EnumChatFormatting.GREEN
+ + 1
+ + EnumChatFormatting.RESET
+ + " x",
+ StatCollector.translateToLocal("GT5U.PA.parallel") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(getMaxParallel())
+ + EnumChatFormatting.RESET };
+ }
+
+ @Override
+ public boolean supportsInputSeparation() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsBatchMode() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsSingleRecipeLocking() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ @Override
+ protected boolean supportsSlotAutomation(int aSlot) {
+ return aSlot == getControllerSlotIndex();
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ super.addUIWidgets(builder, buildContext);
+
+ builder.widget(new ButtonWidget().setOnClick((clickData, widget) -> {
+ downtierUEV = !downtierUEV;
+ setTierAndMult();
+ })
+ .setPlayClickSound(true)
+ .setBackground(() -> {
+ if (downtierUEV) {
+ return new IDrawable[] { GT_UITextures.BUTTON_STANDARD_PRESSED,
+ GT_UITextures.OVERLAY_BUTTON_DOWN_TIERING_ON };
+ } else {
+ return new IDrawable[] { GT_UITextures.BUTTON_STANDARD,
+ GT_UITextures.OVERLAY_BUTTON_DOWN_TIERING_OFF };
+ }
+ })
+ .setPos(80, 91)
+ .setSize(16, 16)
+ .addTooltip(StatCollector.translateToLocal("GT5U.gui.button.down_tier"))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY))
+ .widget(new FakeSyncWidget.BooleanSyncer(() -> downtierUEV, val -> downtierUEV = val));
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ if (mLastRecipeMap != null && getControllerSlot() != null) {
+ tag.setString("type", getControllerSlot().getDisplayName());
+ }
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ super.getWailaBody(itemStack, currentTip, accessor, config);
+ final NBTTagCompound tag = accessor.getNBTData();
+ if (tag.hasKey("type")) {
+ currentTip.add("Machine: " + EnumChatFormatting.YELLOW + tag.getString("type"));
+ } else {
+ currentTip.add("Machine: " + EnumChatFormatting.YELLOW + "None");
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PyrolyseOven.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PyrolyseOven.java
new file mode 100644
index 0000000000..ff84a69b44
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PyrolyseOven.java
@@ -0,0 +1,263 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlockUnlocalizedName;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.onElementPass;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.Muffler;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.Mods.NewHorizonsCoreMod;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PYROLYSE_OVEN;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PYROLYSE_OVEN_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PYROLYSE_OVEN_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PYROLYSE_OVEN_GLOW;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_StructureUtility.ofCoil;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.IStructureElement;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.HeatingCoilLevel;
+import gregtech.api.enums.Textures;
+import gregtech.api.enums.Textures.BlockIcons;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_EnhancedMultiBlockBase;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+
+public class GT_MetaTileEntity_PyrolyseOven
+ extends GT_MetaTileEntity_EnhancedMultiBlockBase<GT_MetaTileEntity_PyrolyseOven> implements ISurvivalConstructable {
+
+ private HeatingCoilLevel coilHeat;
+ private static final int CASING_INDEX = 1090;
+ private static final IStructureDefinition<GT_MetaTileEntity_PyrolyseOven> STRUCTURE_DEFINITION = createStructureDefinition();
+
+ private static IStructureDefinition<GT_MetaTileEntity_PyrolyseOven> createStructureDefinition() {
+ IStructureElement<GT_MetaTileEntity_PyrolyseOven> tCasingElement = NewHorizonsCoreMod.isModLoaded()
+ ? ofBlockUnlocalizedName(NewHorizonsCoreMod.ID, "gt.blockcasingsNH", 2)
+ : ofBlock(GregTech_API.sBlockCasings1, 0);
+
+ return StructureDefinition.<GT_MetaTileEntity_PyrolyseOven>builder()
+ .addShape(
+ "main",
+ transpose(
+ new String[][] { { "ccccc", "ctttc", "ctttc", "ctttc", "ccccc" },
+ { "ccccc", "c---c", "c---c", "c---c", "ccccc" },
+ { "ccccc", "c---c", "c---c", "c---c", "ccccc" },
+ { "bb~bb", "bCCCb", "bCCCb", "bCCCb", "bbbbb" }, }))
+ .addElement('c', onElementPass(GT_MetaTileEntity_PyrolyseOven::onCasingAdded, tCasingElement))
+ .addElement(
+ 'C',
+ ofCoil(GT_MetaTileEntity_PyrolyseOven::setCoilLevel, GT_MetaTileEntity_PyrolyseOven::getCoilLevel))
+ .addElement(
+ 'b',
+ buildHatchAdder(GT_MetaTileEntity_PyrolyseOven.class)
+ .atLeast(OutputBus, OutputHatch, Energy, Maintenance)
+ .casingIndex(CASING_INDEX)
+ .dot(1)
+ .buildAndChain(onElementPass(GT_MetaTileEntity_PyrolyseOven::onCasingAdded, tCasingElement)))
+ .addElement(
+ 't',
+ buildHatchAdder(GT_MetaTileEntity_PyrolyseOven.class).atLeast(InputBus, InputHatch, Muffler)
+ .casingIndex(CASING_INDEX)
+ .dot(1)
+ .buildAndChain(onElementPass(GT_MetaTileEntity_PyrolyseOven::onCasingAdded, tCasingElement)))
+ .build();
+ }
+
+ private int mCasingAmount;
+
+ public GT_MetaTileEntity_PyrolyseOven(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_PyrolyseOven(String aName) {
+ super(aName);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Coke Oven")
+ .addInfo("Controller block for the Pyrolyse Oven")
+ .addInfo("Industrial Charcoal producer")
+ .addInfo("Processing speed scales linearly with Coil tier:")
+ .addInfo("CuNi: 50%, FeAlCr: 100%, Ni4Cr: 150%, TPV: 200%, etc.")
+ .addInfo("EU/t is not affected by Coil tier")
+ .addPollutionAmount(getPollutionPerSecond(null))
+ .addSeparator()
+ .beginStructureBlock(5, 4, 5, true)
+ .addController("Front center")
+ .addCasingInfoRange("Pyrolyse Oven Casing", 60, 80, false)
+ .addOtherStructurePart("Heating Coils", "Center 3x1x3 of the bottom layer")
+ .addEnergyHatch("Any bottom layer casing", 1)
+ .addMaintenanceHatch("Any bottom layer casing", 1)
+ .addMufflerHatch("Center 3x1x3 area in top layer", 2)
+ .addInputBus("Center 3x1x3 area in top layer", 2)
+ .addInputHatch("Center 3x1x3 area in top layer", 2)
+ .addOutputBus("Any bottom layer casing", 1)
+ .addOutputHatch("Any bottom layer casing", 1)
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ if (sideDirection == facingDirection) {
+ if (active) return new ITexture[] { BlockIcons.casingTexturePages[8][66], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_PYROLYSE_OVEN_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_PYROLYSE_OVEN_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { BlockIcons.casingTexturePages[8][66], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_PYROLYSE_OVEN)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_PYROLYSE_OVEN_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { Textures.BlockIcons.casingTexturePages[8][66] };
+ }
+
+ @Override
+ public boolean supportsSingleRecipeLocking() {
+ return true;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.pyrolyseRecipes;
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic() {
+
+ @NotNull
+ @Override
+ public CheckRecipeResult process() {
+ setSpeedBonus(2f / (1 + coilHeat.getTier()));
+ return super.process();
+ }
+ };
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_PyrolyseOven> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ private void onCasingAdded() {
+ mCasingAmount++;
+ }
+
+ public HeatingCoilLevel getCoilLevel() {
+ return coilHeat;
+ }
+
+ private void setCoilLevel(HeatingCoilLevel aCoilLevel) {
+ coilHeat = aCoilLevel;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ coilHeat = HeatingCoilLevel.None;
+ mCasingAmount = 0;
+ replaceDeprecatedCoils(aBaseMetaTileEntity);
+ return checkPiece("main", 2, 3, 0) && mCasingAmount >= 60
+ && mMaintenanceHatches.size() == 1
+ && !mMufflerHatches.isEmpty();
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getPollutionPerSecond(ItemStack aStack) {
+ return GT_Mod.gregtechproxy.mPollutionPyrolyseOvenPerSecond;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_PyrolyseOven(this.mName);
+ }
+
+ private void replaceDeprecatedCoils(IGregTechTileEntity aBaseMetaTileEntity) {
+ final int xDir = aBaseMetaTileEntity.getBackFacing().offsetX;
+ final int zDir = aBaseMetaTileEntity.getBackFacing().offsetZ;
+ final int tX = aBaseMetaTileEntity.getXCoord() + xDir * 2;
+ final int tY = aBaseMetaTileEntity.getYCoord();
+ final int tZ = aBaseMetaTileEntity.getZCoord() + zDir * 2;
+ for (int xPos = tX - 1; xPos <= tX + 1; xPos++) {
+ for (int zPos = tZ - 1; zPos <= tZ + 1; zPos++) {
+ if (aBaseMetaTileEntity.getBlock(xPos, tY, zPos) == GregTech_API.sBlockCasings1
+ && aBaseMetaTileEntity.getMetaID(xPos, tY, zPos) == 13) {
+ aBaseMetaTileEntity.getWorld()
+ .setBlock(xPos, tY, zPos, GregTech_API.sBlockCasings5, 1, 3);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece("main", stackSize, hintsOnly, 2, 3, 0);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece("main", stackSize, 2, 3, 0, elementBudget, env, false, true);
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_TranscendentPlasmaMixer.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_TranscendentPlasmaMixer.java
new file mode 100644
index 0000000000..749a94df57
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_TranscendentPlasmaMixer.java
@@ -0,0 +1,389 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.GT_Values.AuthorColen;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_DTPF_OFF;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_DTPF_ON;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FUSION1_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.casingTexturePages;
+import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_Utility.formatNumbers;
+import static gregtech.common.misc.WirelessNetworkManager.addEUToGlobalEnergyMap;
+import static gregtech.common.misc.WirelessNetworkManager.getUserEU;
+import static gregtech.common.misc.WirelessNetworkManager.processInitialSettings;
+import static gregtech.common.tileentities.machines.multi.GT_MetaTileEntity_PlasmaForge.DIM_BRIDGE_CASING;
+import static gregtech.common.tileentities.machines.multi.GT_MetaTileEntity_PlasmaForge.DIM_INJECTION_CASING;
+import static gregtech.common.tileentities.machines.multi.GT_MetaTileEntity_PlasmaForge.DIM_TRANS_CASING;
+import static net.minecraft.util.EnumChatFormatting.GOLD;
+import static net.minecraft.util.EnumChatFormatting.GRAY;
+import static net.minecraft.util.StatCollector.translateToLocal;
+import static util.Util.toStandardForm;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.drawable.UITexture;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.math.Color;
+import com.gtnewhorizons.modularui.api.math.Size;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+import com.gtnewhorizons.modularui.common.widget.textfield.NumericWidget;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_EnhancedMultiBlockBase;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_OverclockCalculator;
+import gregtech.api.util.GT_Recipe;
+
+public class GT_MetaTileEntity_TranscendentPlasmaMixer
+ extends GT_MetaTileEntity_EnhancedMultiBlockBase<GT_MetaTileEntity_TranscendentPlasmaMixer>
+ implements ISurvivalConstructable {
+
+ private static final String[][] structure = new String[][] {
+ { " CAC ", " ABA ", " ABA ", " A~A ", " ABA ", " ABA ", " CAC " },
+ { "CBBBC", "A A", "A A", "A A", "A A", "A A", "CBBBC" },
+ { "ABBBA", "B B", "B B", "B B", "B B", "B B", "ABBBA" },
+ { "CBBBC", "A A", "A A", "A A", "A A", "A A", "CBBBC" },
+ { " CAC ", " ABA ", " ABA ", " ABA ", " ABA ", " ABA ", " CAC " } };
+
+ private static final String STRUCTURE_PIECE_MAIN = "MAIN";
+ private static final IStructureDefinition<GT_MetaTileEntity_TranscendentPlasmaMixer> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_TranscendentPlasmaMixer>builder()
+ .addShape(STRUCTURE_PIECE_MAIN, structure)
+ .addElement(
+ 'B',
+ buildHatchAdder(GT_MetaTileEntity_TranscendentPlasmaMixer.class)
+ .atLeast(InputHatch, OutputHatch, InputBus, Maintenance)
+ .casingIndex(DIM_INJECTION_CASING)
+ .dot(1)
+ .buildAndChain(GregTech_API.sBlockCasings1, DIM_INJECTION_CASING))
+ .addElement('A', ofBlock(GregTech_API.sBlockCasings1, DIM_TRANS_CASING))
+ .addElement('C', ofBlock(GregTech_API.sBlockCasings1, DIM_BRIDGE_CASING))
+ .build();
+
+ private UUID ownerUUID;
+
+ public GT_MetaTileEntity_TranscendentPlasmaMixer(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_TranscendentPlasmaMixer(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_TranscendentPlasmaMixer> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Transcendent Mixer")
+ .addInfo("Assisting in all your DTPF needs.")
+ .addInfo("This multiblock will run in parallel according to the amount set")
+ .addInfo("in the parallel menu. All inputs will scale, except time.")
+ .addInfo("All EU is deducted from wireless EU networks only.")
+ .addInfo(AuthorColen)
+ .addSeparator()
+ .beginStructureBlock(5, 7, 5, false)
+ .addStructureInfo(GOLD + "1+ " + GRAY + "Input Hatch")
+ .addStructureInfo(GOLD + "1+ " + GRAY + "Output Hatch")
+ .addStructureInfo(GOLD + "1+ " + GRAY + "Input Bus")
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_TranscendentPlasmaMixer(mName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side == aFacing) {
+ if (aActive) return new ITexture[] { casingTexturePages[0][DIM_TRANS_CASING], TextureFactory.builder()
+ .addIcon(OVERLAY_DTPF_ON)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FUSION1_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { casingTexturePages[0][DIM_TRANS_CASING], TextureFactory.builder()
+ .addIcon(OVERLAY_DTPF_OFF)
+ .extFacing()
+ .build() };
+ }
+
+ return new ITexture[] { casingTexturePages[0][DIM_TRANS_CASING] };
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ int multiplier = 1;
+ BigInteger finalConsumption = BigInteger.ZERO;
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.transcendentPlasmaMixerRecipes;
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic() {
+
+ BigInteger recipeEU;
+
+ @NotNull
+ @Override
+ protected CheckRecipeResult validateRecipe(@Nonnull GT_Recipe recipe) {
+ BigInteger availableEU = getUserEU(ownerUUID);
+ recipeEU = BigInteger.valueOf(10L * recipe.mEUt * recipe.mDuration);
+ if (availableEU.compareTo(recipeEU) < 0) {
+ finalConsumption = BigInteger.ZERO;
+ return CheckRecipeResultRegistry.insufficientStartupPower(recipeEU);
+ }
+ maxParallel = availableEU.divide(recipeEU)
+ .min(BigInteger.valueOf(maxParallel))
+ .intValue();
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ }
+
+ @NotNull
+ @Override
+ protected CheckRecipeResult onRecipeStart(@Nonnull GT_Recipe recipe) {
+ finalConsumption = recipeEU.multiply(BigInteger.valueOf(-calculatedParallels));
+ // This will void the inputs if wireless energy has dropped
+ // below the required amount between validateRecipe and here.
+ if (!addEUToGlobalEnergyMap(ownerUUID, finalConsumption)) {
+ return CheckRecipeResultRegistry.insufficientStartupPower(finalConsumption);
+ }
+ // Energy consumed all at once from wireless net.
+ setCalculatedEut(0);
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ }
+
+ @Nonnull
+ @Override
+ protected GT_OverclockCalculator createOverclockCalculator(@Nonnull GT_Recipe recipe) {
+ return GT_OverclockCalculator.ofNoOverclock(recipe);
+ }
+ }.setMaxParallelSupplier(() -> multiplier);
+ }
+
+ @Override
+ protected void setProcessingLogicPower(ProcessingLogic logic) {
+ // The voltage is only used for recipe finding
+ logic.setAvailableVoltage(Long.MAX_VALUE);
+ logic.setAvailableAmperage(1);
+ logic.setAmperageOC(false);
+ }
+
+ private static final int HORIZONTAL_OFFSET = 2;
+ private static final int VERTICAL_OFFSET = 3;
+ private static final int DEPTH_OFFSET = 0;
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, HORIZONTAL_OFFSET, VERTICAL_OFFSET, DEPTH_OFFSET);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ return survivialBuildPiece(
+ STRUCTURE_PIECE_MAIN,
+ stackSize,
+ HORIZONTAL_OFFSET,
+ VERTICAL_OFFSET,
+ DEPTH_OFFSET,
+ elementBudget,
+ env,
+ false,
+ true);
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+
+ // Check the main structure
+ if (!checkPiece(STRUCTURE_PIECE_MAIN, HORIZONTAL_OFFSET, VERTICAL_OFFSET, DEPTH_OFFSET)) {
+ return false;
+ }
+
+ // Maintenance hatch not required but left for compatibility.
+ // Don't allow more than 1, no free casing spam!
+ return (mMaintenanceHatches.size() <= 1);
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+
+ super.onPreTick(aBaseMetaTileEntity, aTick);
+
+ if (aBaseMetaTileEntity.isServerSide() && (aTick == 1)) {
+ // Adds player to the wireless network if they do not already exist on it.
+ ownerUUID = processInitialSettings(aBaseMetaTileEntity);
+ }
+ }
+
+ private static final int PARALLEL_WINDOW_ID = 10;
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ buildContext.addSyncedWindow(PARALLEL_WINDOW_ID, this::createParallelWindow);
+ builder.widget(new ButtonWidget().setOnClick((clickData, widget) -> {
+ if (!widget.isClient()) {
+ widget.getContext()
+ .openSyncedWindow(PARALLEL_WINDOW_ID);
+ }
+ })
+ .setPlayClickSound(true)
+ .setBackground(() -> {
+ List<UITexture> ret = new ArrayList<>();
+ ret.add(GT_UITextures.BUTTON_STANDARD);
+ ret.add(GT_UITextures.OVERLAY_BUTTON_BATCH_MODE_ON);
+ return ret.toArray(new IDrawable[0]);
+ })
+ .addTooltip(translateToLocal("GT5U.tpm.parallelwindow"))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setPos(174, 112)
+ .setSize(16, 16));
+ super.addUIWidgets(builder, buildContext);
+ }
+
+ protected ModularWindow createParallelWindow(final EntityPlayer player) {
+ final int WIDTH = 158;
+ final int HEIGHT = 52;
+ final int PARENT_WIDTH = getGUIWidth();
+ final int PARENT_HEIGHT = getGUIHeight();
+ ModularWindow.Builder builder = ModularWindow.builder(WIDTH, HEIGHT);
+ builder.setBackground(GT_UITextures.BACKGROUND_SINGLEBLOCK_DEFAULT);
+ builder.setGuiTint(getGUIColorization());
+ builder.setDraggable(true);
+ builder.setPos(
+ (size, window) -> Alignment.Center.getAlignedPos(size, new Size(PARENT_WIDTH, PARENT_HEIGHT))
+ .add(
+ Alignment.BottomRight.getAlignedPos(new Size(PARENT_WIDTH, PARENT_HEIGHT), new Size(WIDTH, HEIGHT))
+ .add(WIDTH - 3, 0)
+ .subtract(0, 10)));
+ builder.widget(
+ TextWidget.localised("GTPP.CC.parallel")
+ .setPos(3, 4)
+ .setSize(150, 20))
+ .widget(
+ new NumericWidget().setSetter(val -> multiplier = (int) val)
+ .setGetter(() -> multiplier)
+ .setBounds(1, Integer.MAX_VALUE)
+ .setDefaultValue(1)
+ .setScrollValues(1, 4, 64)
+ .setTextAlignment(Alignment.Center)
+ .setTextColor(Color.WHITE.normal)
+ .setSize(150, 18)
+ .setPos(4, 25)
+ .setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD)
+ .attachSyncer(
+ new FakeSyncWidget.IntegerSyncer(() -> multiplier, (val) -> multiplier = val),
+ builder));
+ return builder.build();
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ aNBT.setInteger("eMultiplier", multiplier);
+ super.saveNBTData(aNBT);
+ }
+
+ @Override
+ public void loadNBTData(final NBTTagCompound aNBT) {
+ multiplier = aNBT.getInteger("eMultiplier");
+ super.loadNBTData(aNBT);
+ }
+
+ @Override
+ public String[] getInfoData() {
+ return new String[] {
+ StatCollector.translateToLocal("GT5U.multiblock.Progress") + ": "
+ + EnumChatFormatting.GREEN
+ + formatNumbers(mProgresstime / 20)
+ + EnumChatFormatting.RESET
+ + " s / "
+ + EnumChatFormatting.YELLOW
+ + formatNumbers(mMaxProgresstime / 20)
+ + EnumChatFormatting.RESET
+ + " s",
+ StatCollector.translateToLocal("GT5U.multiblock.usage") + ": "
+ + EnumChatFormatting.RED
+ + (mMaxProgresstime == 0 ? "0"
+ : toStandardForm(finalConsumption.divide(BigInteger.valueOf(-mMaxProgresstime))))
+ + EnumChatFormatting.RESET
+ + " EU/t" };
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ @Override
+ public boolean getDefaultHasMaintenanceChecks() {
+ return false;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_VacuumFreezer.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_VacuumFreezer.java
new file mode 100644
index 0000000000..499da54bc3
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_VacuumFreezer.java
@@ -0,0 +1,163 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_VACUUM_FREEZER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_VACUUM_FREEZER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_VACUUM_FREEZER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_VACUUM_FREEZER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.casingTexturePages;
+
+import java.util.List;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.google.common.collect.ImmutableList;
+import com.gtnewhorizon.structurelib.structure.IStructureElement;
+import com.gtnewhorizon.structurelib.structure.StructureUtility;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.interfaces.IHatchElement;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_CubicMultiBlockBase;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+
+public class GT_MetaTileEntity_VacuumFreezer
+ extends GT_MetaTileEntity_CubicMultiBlockBase<GT_MetaTileEntity_VacuumFreezer> {
+
+ public GT_MetaTileEntity_VacuumFreezer(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_VacuumFreezer(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_VacuumFreezer(this.mName);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Vacuum Freezer")
+ .addInfo("Controller Block for the Vacuum Freezer")
+ .addInfo("Cools hot ingots and cells")
+ .addSeparator()
+ .beginStructureBlock(3, 3, 3, true)
+ .addController("Front center")
+ .addCasingInfoRange("Frost Proof Machine Casing", 16, 24, false)
+ .addEnergyHatch("Any casing", 1)
+ .addMaintenanceHatch("Any casing", 1)
+ .addInputHatch("Any casing", 1)
+ .addOutputHatch("Any casing", 1)
+ .addInputBus("Any casing", 1)
+ .addOutputBus("Any casing", 1)
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ ITexture[] rTexture;
+ if (side == aFacing) {
+ if (aActive) {
+ rTexture = new ITexture[] { casingTexturePages[0][17], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_VACUUM_FREEZER_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_VACUUM_FREEZER_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ } else {
+ rTexture = new ITexture[] { casingTexturePages[0][17], TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_VACUUM_FREEZER)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_VACUUM_FREEZER_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ } else {
+ rTexture = new ITexture[] { casingTexturePages[0][17] };
+ }
+ return rTexture;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.vacuumFreezerRecipes;
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic();
+ }
+
+ @Override
+ protected IStructureElement<GT_MetaTileEntity_CubicMultiBlockBase<?>> getCasingElement() {
+ return StructureUtility.ofBlock(GregTech_API.sBlockCasings2, 1);
+ }
+
+ @Override
+ protected List<IHatchElement<? super GT_MetaTileEntity_CubicMultiBlockBase<?>>> getAllowedHatches() {
+ return ImmutableList.of(InputHatch, OutputHatch, InputBus, OutputBus, Maintenance, Energy);
+ }
+
+ @Override
+ protected int getHatchTextureIndex() {
+ return 17;
+ }
+
+ @Override
+ protected int getRequiredCasingCount() {
+ return 16;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsBatchMode() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_WormholeGenerator.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_WormholeGenerator.java
new file mode 100644
index 0000000000..eeb5770800
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_WormholeGenerator.java
@@ -0,0 +1,1119 @@
+package gregtech.common.tileentities.machines.multi;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.lazy;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.withChannel;
+import static goodgenerator.util.DescTextLocalization.BLUE_PRINT_INFO;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_Values.V;
+import static gregtech.api.enums.GT_Values.VN;
+import static gregtech.api.enums.Textures.BlockIcons.getCasingTextureForId;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.github.bartimaeusnek.bartworks.API.BorosilicateGlass;
+import com.github.technus.tectech.thing.casing.GT_Block_CasingsTT;
+import com.github.technus.tectech.thing.casing.TT_Container_Casings;
+import com.github.technus.tectech.thing.metaTileEntity.hatch.GT_MetaTileEntity_Hatch_DynamoMulti;
+import com.github.technus.tectech.thing.metaTileEntity.hatch.GT_MetaTileEntity_Hatch_EnergyMulti;
+import com.google.common.collect.MapMaker;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+import com.gtnewhorizons.modularui.common.widget.DynamicPositionedColumn;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+
+import appeng.api.AEApi;
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.TierEU;
+import gregtech.api.interfaces.IHatchElement;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.interfaces.tileentity.IHasWorldObjectAndCoords;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_EnhancedMultiBlockBase;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.ResultMissingItem;
+import gregtech.api.recipe.check.SimpleCheckRecipeResult;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.IGT_HatchAdder;
+import gregtech.common.tileentities.render.TileWormhole;
+import gtPlusPlus.xmod.gregtech.common.blocks.textures.TexturesGtBlock;
+
+public class GT_MetaTileEntity_WormholeGenerator extends
+ GT_MetaTileEntity_EnhancedMultiBlockBase<GT_MetaTileEntity_WormholeGenerator> implements ISurvivalConstructable {
+
+ /**
+ * Number of seconds to average the wormhole energy over.
+ */
+ public static int WH_ENERGY_AVG_WINDOW = 90;
+
+ /**
+ * The amount of EU received per EU sent.
+ */
+ public static double TRANSFER_EFFICIENCY = (1.0 - 0.00_0004 * 64);
+
+ /**
+ * The amount of EU to lose every second the wormhole decays.
+ */
+ public static double DECAY_RATE = 0.25;
+
+ /**
+ * The amount of EU that the wormhole collapses at.
+ */
+ public static double COLLAPSE_THRESHOLD = 32;
+
+ /**
+ * The wormhole render radius percent of the max size when specified wormhole power is reached (purely aesthetical)
+ */
+ public static double RENDER_PERCENT_TARGET = .1;
+
+ /**
+ * Wormhole energy to specify reach the specified size percentage (purely aesthetical)
+ */
+ public static double RENDER_TARGET_ENERGY = TierEU.IV * 256;
+
+ /**
+ * Maximum wormhole radius (purely aesthetical)
+ */
+ public static double RENDER_MAX_RADIUS = 2.999 / Math.sqrt(3);
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final byte GLASS_TIER_UNSET = -2;
+
+ private static final int TT_CASING_INDEX = GT_Block_CasingsTT.textureOffset;
+
+ private static final int TOP_HATCH = 0, BOTTOM_HATCH = 1, LEFT_HATCH = 2, RIGHT_HATCH = 3, BACK_HATCH = 4,
+ FRONT_HATCH = 5, MAX_HATCHES = 6;
+
+ private static final int[] OPPOSITES = { BOTTOM_HATCH, TOP_HATCH, RIGHT_HATCH, LEFT_HATCH, FRONT_HATCH,
+ BACK_HATCH, };
+
+ private static final String[] HATCH_NAMES = { "Top", "Bottom", "Left", "Right", "Back", "Front" };
+ private static final boolean[] HATCH_MASK = { true, true, true, true, false, false };
+
+ private byte mGlassTier = -2;
+ private boolean mStructureBadGlassTier = false;
+
+ private final GT_MetaTileEntity_Hatch_EnergyMulti[] mSendHatches = new GT_MetaTileEntity_Hatch_EnergyMulti[MAX_HATCHES];
+ private final GT_MetaTileEntity_Hatch_DynamoMulti[] mReceiveHatches = new GT_MetaTileEntity_Hatch_DynamoMulti[MAX_HATCHES];
+
+ private final ItemStack singularity = AEApi.instance()
+ .definitions()
+ .materials()
+ .singularity()
+ .maybeStack(1)
+ .get();
+ private final ItemStack qeSingularity = AEApi.instance()
+ .definitions()
+ .materials()
+ .qESingularity()
+ .maybeStack(1)
+ .get();
+
+ private WormholeLink mLink;
+ private final WeakReference<GT_MetaTileEntity_WormholeGenerator> mSelfReference = new WeakReference<>(this);
+
+ private double mWormholeEnergy_UI = 0;
+
+ private boolean mIsUnloading = false;
+
+ public GT_MetaTileEntity_WormholeGenerator(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_WormholeGenerator(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_WormholeGenerator(mName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, ForgeDirection facing,
+ int colorIndex, boolean active, boolean redstoneLevel) {
+ if (side == facing) {
+ if (active) {
+ return new ITexture[] { getCasingTextureForId(TT_CASING_INDEX), TextureFactory.builder()
+ .addIcon(TexturesGtBlock.Overlay_Machine_Controller_Advanced_Active)
+ .extFacing()
+ .build() };
+ } else {
+ return new ITexture[] { getCasingTextureForId(TT_CASING_INDEX), TextureFactory.builder()
+ .addIcon(TexturesGtBlock.Overlay_Machine_Controller_Advanced)
+ .extFacing()
+ .build() };
+ }
+ }
+ return new ITexture[] { getCasingTextureForId(TT_CASING_INDEX) };
+ }
+
+ // #region Structure
+
+ // spotless:off
+ private static final IStructureDefinition<GT_MetaTileEntity_WormholeGenerator> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_WormholeGenerator>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ transpose(
+ new String[][]{
+ {" "," F "," EEE "," FEtEF "," EEE "," F "," "},
+ {" F "," AADAA "," A B A ","FDBBBDF"," A B A "," AADAA "," F "},
+ {" EEE "," A B A ","E E","EB BE","E E"," A B A "," EEE "},
+ {" FE~EF ","FA B AF","E E","lB Br","E E","FA B AF"," FEEEF "},
+ {" EEE "," A B A ","E E","EB BE","E E"," A B A "," EEE "},
+ {" F "," AADAA "," A B A ","FDBBBDF"," A B A "," AADAA "," F "},
+ {" "," F "," EEE "," FEbEF "," EEE "," F "," "}
+ }))
+ .addElement('E',
+ buildHatchAdder(GT_MetaTileEntity_WormholeGenerator.class)
+ .atLeast(Maintenance, InputBus)
+ .casingIndex(TT_CASING_INDEX) // High Power Casing
+ .dot(1)
+ .buildAndChain(lazy(() -> ofBlock(TT_Container_Casings.sBlockCasingsTT, 0))) // High Power Casing
+ )
+ .addElement(
+ 'A',
+ withChannel(
+ "glass",
+ BorosilicateGlass.ofBoroGlass(GLASS_TIER_UNSET, (te, t) -> te.mGlassTier = t, te -> te.mGlassTier))
+ )
+ .addElement('D', ofBlock(GregTech_API.sBlockCasings8, 5)) // Europium Reinforced Radiation Proof Machine Casing
+ .addElement('B', ofBlock(GregTech_API.sBlockCasings4, 7)) // Fusion Coil Block
+ .addElement('F', lazy(() -> ofBlock(TT_Container_Casings.sBlockCasingsTT, 4))) // Molecular Casing
+ .addElement('t',
+ buildHatchAdder(GT_MetaTileEntity_WormholeGenerator.class)
+ .anyOf(new TransferHatch(TOP_HATCH))
+ .casingIndex(TT_CASING_INDEX) // High Power Casing
+ .dot(2)
+ .buildAndChain(lazy(() -> ofBlock(TT_Container_Casings.sBlockCasingsTT, 0))) // High Power Casing
+ )
+ .addElement('b',
+ buildHatchAdder(GT_MetaTileEntity_WormholeGenerator.class)
+ .anyOf(new TransferHatch(BOTTOM_HATCH))
+ .casingIndex(TT_CASING_INDEX) // High Power Casing
+ .dot(2)
+ .buildAndChain(lazy(() -> ofBlock(TT_Container_Casings.sBlockCasingsTT, 0))) // High Power Casing
+ )
+ .addElement('l',
+ buildHatchAdder(GT_MetaTileEntity_WormholeGenerator.class)
+ .anyOf(new TransferHatch(LEFT_HATCH))
+ .casingIndex(TT_CASING_INDEX) // High Power Casing
+ .dot(2)
+ .buildAndChain(lazy(() -> ofBlock(TT_Container_Casings.sBlockCasingsTT, 0))) // High Power Casing
+ )
+ .addElement('r',
+ buildHatchAdder(GT_MetaTileEntity_WormholeGenerator.class)
+ .anyOf(new TransferHatch(RIGHT_HATCH))
+ .casingIndex(TT_CASING_INDEX) // High Power Casing
+ .dot(2)
+ .buildAndChain(lazy(() -> ofBlock(TT_Container_Casings.sBlockCasingsTT, 0))) // High Power Casing
+ )
+ .build();
+ // spotless:on
+
+ private static class TransferHatch implements IHatchElement<GT_MetaTileEntity_WormholeGenerator> {
+
+ public final int mIndex;
+
+ public TransferHatch(int index) {
+ this.mIndex = index;
+ }
+
+ @Override
+ public List<? extends Class<? extends IMetaTileEntity>> mteClasses() {
+ return Arrays.asList(GT_MetaTileEntity_Hatch_EnergyMulti.class, GT_MetaTileEntity_Hatch_DynamoMulti.class);
+ }
+
+ @Override
+ public IGT_HatchAdder<? super GT_MetaTileEntity_WormholeGenerator> adder() {
+ return (tile, aTileEntity, aBaseCasingIndex) -> {
+ if (aTileEntity == null) return false;
+
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+
+ if (aMetaTileEntity == null) return false;
+
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_EnergyMulti input) {
+ input.updateTexture(aBaseCasingIndex);
+ input.updateCraftingIcon(tile.getMachineCraftingIcon());
+ tile.mSendHatches[mIndex] = input;
+ return true;
+ } else if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_DynamoMulti output) {
+ output.updateTexture(aBaseCasingIndex);
+ output.updateCraftingIcon(tile.getMachineCraftingIcon());
+ tile.mReceiveHatches[mIndex] = output;
+ return true;
+ }
+
+ return false;
+ };
+ }
+
+ @Override
+ public String name() {
+ return "TransferHatch";
+ }
+
+ @Override
+ public long count(GT_MetaTileEntity_WormholeGenerator t) {
+ return t.mExoticEnergyHatches.size();
+ }
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ protected SoundResource getProcessStartSound() {
+ return SoundResource.GT_MACHINES_FUSION_LOOP;
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_WormholeGenerator> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ if (!checkPiece(STRUCTURE_PIECE_MAIN, 3, 3, 0)) return false;
+
+ mStructureBadGlassTier = false;
+
+ for (var energyHatch : mExoticEnergyHatches) {
+ if (energyHatch.getBaseMetaTileEntity() == null) {
+ continue;
+ }
+
+ if (energyHatch.getTierForStructure() > mGlassTier) {
+ mStructureBadGlassTier = true;
+ }
+ }
+
+ return !mStructureBadGlassTier;
+ }
+
+ @Override
+ public void clearHatches() {
+ super.clearHatches();
+
+ Arrays.fill(mSendHatches, null);
+ Arrays.fill(mReceiveHatches, null);
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 3, 3, 0);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 3, 3, 0, elementBudget, env, false, true);
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ // #endregion
+ @Override
+ public void onBlockDestroyed() {
+ super.onBlockDestroyed();
+ destroyRenderBlock();
+ }
+
+ @Override
+ public void onDisableWorking() {
+ super.onDisableWorking();
+ // destroyRenderBlock();
+ }
+
+ @Override
+ public void onStructureChange() {
+ super.onStructureChange();
+ if (!checkStructure(false, this.getBaseMetaTileEntity())) {
+ destroyRenderBlock();
+ }
+ }
+
+ private void destroyRenderBlock() {
+ IGregTechTileEntity gregTechTileEntity = this.getBaseMetaTileEntity();
+ if (gregTechTileEntity.getWorld() == null) {
+ return;
+ }
+
+ int x = gregTechTileEntity.getXCoord();
+ int y = gregTechTileEntity.getYCoord();
+ int z = gregTechTileEntity.getZCoord();
+
+ int xOffset = 3 * getExtendedFacing().getRelativeBackInWorld().offsetX;
+ int yOffset = 3 * getExtendedFacing().getRelativeBackInWorld().offsetY;
+ int zOffset = 3 * getExtendedFacing().getRelativeBackInWorld().offsetZ;
+
+ int xTarget = x + xOffset;
+ int yTarget = y + yOffset;
+ int zTarget = z + zOffset;
+
+ Optional.of(gregTechTileEntity)
+ .map(IHasWorldObjectAndCoords::getWorld)
+ .ifPresent(w -> w.setBlock(xTarget, yTarget, zTarget, Blocks.air));
+ }
+
+ @Nullable
+ private TileWormhole createRenderBlock() {
+
+ IGregTechTileEntity gregTechTileEntity = this.getBaseMetaTileEntity();
+ World world = gregTechTileEntity.getWorld();
+
+ if (world == null) {
+ return null;
+ }
+
+ int x = gregTechTileEntity.getXCoord();
+ int y = gregTechTileEntity.getYCoord();
+ int z = gregTechTileEntity.getZCoord();
+
+ int xOffset = 3 * getExtendedFacing().getRelativeBackInWorld().offsetX;
+ int yOffset = 3 * getExtendedFacing().getRelativeBackInWorld().offsetY;
+ int zOffset = 3 * getExtendedFacing().getRelativeBackInWorld().offsetZ;
+
+ int xTarget = x + xOffset;
+ int yTarget = y + yOffset;
+ int zTarget = z + zOffset;
+
+ world.setBlock(xTarget, yTarget, zTarget, Blocks.air);
+ world.setBlock(xTarget, yTarget, zTarget, GregTech_API.sWormholeRender);
+
+ TileWormhole wormhole = (TileWormhole) world.getTileEntity(xTarget, yTarget, zTarget);
+
+ if (wormhole == null) {
+ return null;
+ }
+ return wormhole;
+ }
+
+ @Nullable
+ private TileWormhole getRenderBlock() {
+ IGregTechTileEntity gregTechTileEntity = this.getBaseMetaTileEntity();
+
+ int x = gregTechTileEntity.getXCoord();
+ int y = gregTechTileEntity.getYCoord();
+ int z = gregTechTileEntity.getZCoord();
+
+ double xOffset = 3 * getExtendedFacing().getRelativeBackInWorld().offsetX;
+ double zOffset = 3 * getExtendedFacing().getRelativeBackInWorld().offsetZ;
+ double yOffset = 3 * getExtendedFacing().getRelativeBackInWorld().offsetY;
+
+ int wX = (int) (x + xOffset);
+ int wY = (int) (y + yOffset);
+ int wZ = (int) (z + zOffset);
+
+ TileEntity tile = Optional.ofNullable(gregTechTileEntity.getWorld())
+ .map(w -> w.getTileEntity(wX, wY, wZ))
+ .orElse(null);
+ if (tile instanceof TileWormhole wormhole) return wormhole;
+ return null;
+
+ }
+
+ public void updateRenderDim() {
+ TileWormhole temp = getRenderBlock();
+
+ World target = Optional.ofNullable(mLink)
+ .map(link -> link.getDest(mSelfReference))
+ .map(MetaTileEntity::getBaseMetaTileEntity)
+ .map(IHasWorldObjectAndCoords::getWorld)
+ .orElse(null);
+
+ TileWormhole hole = getRenderBlock();
+ if (hole == null) hole = createRenderBlock();
+
+ if (hole != null) {
+ hole.setDimFromWorld(target);
+ }
+ }
+
+ public void updateRenderRadius(double radius) {
+ TileWormhole hole = getRenderBlock();
+ if (hole == null) hole = createRenderBlock();
+
+ if (hole != null) {
+ hole.setRadius(radius);
+ }
+ }
+
+ private static double wormholeRadiusCalc(double energy) {
+ if (energy < COLLAPSE_THRESHOLD) return 0;
+
+ double offset = RENDER_TARGET_ENERGY + (COLLAPSE_THRESHOLD - RENDER_TARGET_ENERGY) / (RENDER_PERCENT_TARGET);
+ return RENDER_MAX_RADIUS * (COLLAPSE_THRESHOLD - energy) / (offset - energy);
+ }
+
+ // #region Logic
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public void onUnload() {
+ super.onUnload();
+ mIsUnloading = true;
+ if (mLink != null) {
+ mLink.disconnect(mSelfReference);
+ }
+ }
+
+ @Override
+ protected void runMachine(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ checkFrequency();
+
+ mMaxProgresstime = 20;
+ mEfficiency = Math.max(0, getMaxEfficiency(mInventory[1]) - ((getIdealStatus() - getRepairStatus()) * 1000));
+
+ if (doRandomMaintenanceDamage()) {
+ if (onRunningTick(mInventory[1])) {
+ markDirty();
+
+ if (++mProgresstime >= mMaxProgresstime) {
+ mProgresstime = 0;
+
+ if (mLink != null) {
+ mLink.update(mSelfReference);
+ }
+ checkRecipe();
+ }
+ }
+ }
+ }
+
+ @Override
+ @Nonnull
+ public CheckRecipeResult checkProcessing() {
+ if (mLink == null || !mLink.isConnected(mSelfReference)
+ || getBaseMetaTileEntity() == null
+ || !getBaseMetaTileEntity().isAllowedToWork()) {
+ return SimpleCheckRecipeResult.ofFailure("none");
+ }
+
+ if (mLink.isActive()) {
+ for (int i = 0; i < MAX_HATCHES; i++) {
+ if (!HATCH_MASK[i]) continue;
+ long optimal = mLink.mWormholeEnergy > Long.MAX_VALUE ? Long.MAX_VALUE : ((long) mLink.mWormholeEnergy);
+ if (getTransferable(i) > 0) {
+ if (mLink.mWormholeEnergy <= 0) {
+ var singularityStack = singularity.copy();
+
+ if (!depleteInput(singularityStack)) {
+ return new ResultMissingItem(singularityStack);
+ }
+ mLink.mWormholeEnergy = 1;
+ optimal = 1;
+ }
+ transferPower(optimal, i);
+ }
+ }
+ }
+
+ if (mLink.mWormholeEnergy > 0) {
+ return SimpleCheckRecipeResult.ofSuccess("none");
+ }
+ return SimpleCheckRecipeResult.ofFailure("none");
+ }
+
+ private void checkFrequency() {
+ if (mIsUnloading) {
+ return;
+ }
+
+ ItemStack link = null;
+
+ for (var slot : mInventory) {
+ if (slot != null && qeSingularity.getItem() == slot.getItem()
+ && qeSingularity.getItemDamage() == slot.getItemDamage()) {
+ link = slot;
+ break;
+ }
+ }
+
+ Long freq = link != null && link.getTagCompound() != null
+ && link.getTagCompound()
+ .hasKey("freq", 4 /* Long */) ? link.getTagCompound()
+ .getLong("freq") : null;
+
+ if (!Objects.equals(freq, mLink == null ? null : mLink.mFrequency)) {
+ if (mLink != null) {
+ mLink.disconnect(mSelfReference);
+ mLink = null;
+ }
+
+ if (freq != null) {
+ mLink = WormholeLink.get(freq);
+ mLink.connect(mSelfReference);
+ }
+ }
+ }
+
+ private long getTransferable(int index) {
+ var dest = mLink.getDest(mSelfReference);
+
+ if (dest == null || mMaxProgresstime == 0 || dest.mMaxProgresstime == 0) {
+ return 0;
+ }
+
+ var inputHatch = mSendHatches[index];
+ var outputHatch = dest.mReceiveHatches[OPPOSITES[index]];
+
+ if (inputHatch == null || outputHatch == null) {
+ return 0;
+ }
+
+ long available = inputHatch.getEUVar();
+ long empty = outputHatch.maxEUStore() - outputHatch.getEUVar();
+
+ return Math.min(available, empty);
+ }
+
+ private void transferPower(long optimal, int index) {
+ var dest = mLink.getDest(mSelfReference);
+ if (dest == null) {
+ return;
+ }
+
+ var inputHatch = mSendHatches[index];
+ var outputHatch = dest.mReceiveHatches[OPPOSITES[index]];
+
+ if (inputHatch == null || outputHatch == null) {
+ return;
+ }
+
+ long available = inputHatch.getEUVar();
+ long empty = outputHatch.maxEUStore() - outputHatch.getEUVar();
+ long maxSend = inputHatch.maxAmperesIn() * V[inputHatch.mTier] * 20;
+ long maxReceive = outputHatch.maxAmperesOut() * V[outputHatch.mTier] * 20;
+
+ // spotless:off
+ long toSend = (long)(Math.min(Math.min(Math.min(available, empty), maxSend), maxReceive) * (1.0 - (getIdealStatus() - getRepairStatus()) * 0.1));
+
+ double overclocks = Math.max(Math.log((double)toSend / (double)optimal) / Math.log(4.0), 0.0);
+
+ long toReceive = (long) (
+ toSend *
+ (1.0 / Math.pow(4.0, overclocks)) *
+ Math.pow(2.0, overclocks) *
+ (1.0 - (dest.getIdealStatus() - dest.getRepairStatus()) * 0.1) *
+ TRANSFER_EFFICIENCY
+ );
+ // spotless:on
+
+ inputHatch.setEUVar(inputHatch.getEUVar() - toSend);
+ outputHatch.setEUVar(outputHatch.getEUVar() + toReceive);
+
+ double size = wormholeRadiusCalc((double) optimal / 20);
+ this.updateRenderRadius(size);
+ dest.updateRenderRadius(size);
+
+ mLink.onEnergyTransferred(toSend);
+ mLink.mSendAmounts[index] += toSend;
+ mLink.mReceiveAmounts[OPPOSITES[index]] += toReceive;
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+
+ try {
+ if (mLink != null) {
+ mLink.tryPromote();
+
+ NBTTagCompound link = new NBTTagCompound();
+
+ link.setLong("mFrequency", mLink.mFrequency);
+ link.setDouble("mWormholeEnergy", mLink.mWormholeEnergy);
+
+ if (mLink.isMaster(mSelfReference)) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos);
+
+ dos.writeInt(mLink.mReceiveAmounts.length);
+ for (var x : mLink.mReceiveAmounts) {
+ dos.writeLong(x);
+ }
+
+ dos.writeInt(mLink.mSendAmounts.length);
+ for (var x : mLink.mSendAmounts) {
+ dos.writeLong(x);
+ }
+
+ link.setByteArray("data", baos.toByteArray());
+ }
+
+ aNBT.setTag("mLink", link);
+ }
+ } catch (Throwable t) {
+ GT_Mod.GT_FML_LOGGER.error("Could not save GT_MetaTileEntity_WormholeGenerator", t);
+ }
+
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+
+ checkFrequency();
+
+ if (aNBT.hasKey("mLink") && mLink != null) {
+ NBTTagCompound link = aNBT.getCompoundTag("mLink");
+
+ long freq = link.getLong("mFrequency");
+
+ try {
+ if (freq == mLink.mFrequency && mLink.isMaster(mSelfReference)) {
+ mLink.mWormholeEnergy = link.getDouble("mWormholeEnergy");
+
+ ByteArrayInputStream bais = new ByteArrayInputStream(link.getByteArray("data"));
+ DataInputStream dis = new DataInputStream(bais);
+
+ long[] recv_amounts = new long[dis.readInt()];
+ for (int i = 0; i < recv_amounts.length; i++) {
+ recv_amounts[i] = dis.readLong();
+ }
+
+ System.arraycopy(
+ recv_amounts,
+ 0,
+ mLink.mReceiveAmounts,
+ 0,
+ Math.min(recv_amounts.length, mLink.mReceiveAmounts.length));
+
+ long[] send_amounts = new long[dis.readInt()];
+ for (int i = 0; i < send_amounts.length; i++) {
+ send_amounts[i] = dis.readLong();
+ }
+
+ System.arraycopy(
+ send_amounts,
+ 0,
+ mLink.mSendAmounts,
+ 0,
+ Math.min(send_amounts.length, mLink.mSendAmounts.length));
+ }
+ } catch (Throwable t) {
+ GT_Mod.GT_FML_LOGGER.error("Could not load GT_MetaTileEntity_WormholeGenerator", t);
+ }
+ }
+ }
+
+ private static class WormholeLink {
+
+ private final static Map<Long, WormholeLink> WORMHOLE_GENERATORS = new MapMaker().weakValues()
+ .makeMap();
+
+ public final long mFrequency;
+
+ public WeakReference<GT_MetaTileEntity_WormholeGenerator> mMaster, mSlave;
+
+ public final long[] mSendAmounts = new long[MAX_HATCHES];
+ public final long[] mReceiveAmounts = new long[MAX_HATCHES];
+
+ public double mWormholeEnergy;
+ private double mPendingEnergy;
+
+ private WormholeLink(long frequency) {
+ mFrequency = frequency;
+ }
+
+ public static WormholeLink get(long frequency) {
+ return WORMHOLE_GENERATORS.computeIfAbsent(frequency, WormholeLink::new);
+ }
+
+ public void update(WeakReference<GT_MetaTileEntity_WormholeGenerator> updater) {
+ tryPromote();
+
+ if (isMaster(updater)) {
+ if (isActive() && mPendingEnergy > 0) {
+ var delta = mPendingEnergy / WH_ENERGY_AVG_WINDOW;
+
+ if (mPendingEnergy < mWormholeEnergy) {
+ // if the wormhole is shrinking and the next tick would take it below the pending energy, just
+ // use the pending energy
+ if (mWormholeEnergy - delta < mPendingEnergy) {
+ mWormholeEnergy = mPendingEnergy;
+ } else {
+ mWormholeEnergy -= delta;
+ }
+ } else if (mPendingEnergy > mWormholeEnergy) {
+ // if the wormhole is growing and the next tick would take it above the pending energy, just use
+ // the pending energy
+ if (mWormholeEnergy + delta > mPendingEnergy) {
+ mWormholeEnergy = mPendingEnergy;
+ } else {
+ mWormholeEnergy += delta;
+ }
+ }
+
+ mPendingEnergy = 0;
+ } else {
+ mWormholeEnergy *= (1.0 - DECAY_RATE);
+
+ if (mWormholeEnergy < COLLAPSE_THRESHOLD) {
+ mWormholeEnergy = 0;
+
+ }
+ }
+ Arrays.fill(mSendAmounts, 0);
+ Arrays.fill(mReceiveAmounts, 0);
+ }
+ }
+
+ public void onEnergyTransferred(long amount) {
+ mPendingEnergy += amount;
+ }
+
+ public boolean isMaster(WeakReference<GT_MetaTileEntity_WormholeGenerator> tile) {
+ return mMaster == tile;
+ }
+
+ public boolean isConnected(WeakReference<GT_MetaTileEntity_WormholeGenerator> tile) {
+ return mMaster == tile || mSlave == tile;
+ }
+
+ public boolean isFormed() {
+ return mMaster != null && mSlave != null;
+ }
+
+ public boolean connect(WeakReference<GT_MetaTileEntity_WormholeGenerator> tile) {
+ tryPromote();
+
+ if (mMaster == null) {
+ mMaster = tile;
+ Optional.ofNullable(mMaster)
+ .map(Reference::get)
+ .ifPresent(GT_MetaTileEntity_WormholeGenerator::updateRenderDim);
+
+ Optional.ofNullable(mSlave)
+ .map(Reference::get)
+ .ifPresent(GT_MetaTileEntity_WormholeGenerator::updateRenderDim);
+ return true;
+ }
+ if (mSlave == null) {
+ mSlave = tile;
+
+ Optional.of(mMaster)
+ .map(Reference::get)
+ .ifPresent(GT_MetaTileEntity_WormholeGenerator::updateRenderDim);
+
+ Optional.ofNullable(mSlave)
+ .map(Reference::get)
+ .ifPresent(GT_MetaTileEntity_WormholeGenerator::updateRenderDim);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public void disconnect(WeakReference<GT_MetaTileEntity_WormholeGenerator> tile) {
+ Objects.requireNonNull(tile.get())
+ .destroyRenderBlock();
+
+ if (tile == mMaster) mMaster = null;
+ if (tile == mSlave) mSlave = null;
+
+ tryPromote();
+
+ if (mMaster == null && mSlave == null) {
+ WORMHOLE_GENERATORS.remove(mFrequency, this);
+ }
+ }
+
+ public void tryPromote() {
+ mMaster = tryClean(mMaster);
+ mSlave = tryClean(mSlave);
+
+ if (mMaster == null && mSlave != null) {
+ mMaster = mSlave;
+ mSlave = null;
+ }
+ }
+
+ private static WeakReference<GT_MetaTileEntity_WormholeGenerator> tryClean(
+ WeakReference<GT_MetaTileEntity_WormholeGenerator> tileReference) {
+ if (tileReference != null) {
+ var tile = tileReference.get();
+
+ if (tile == null) {
+ return null;
+ } else {
+ var base = tile.getBaseMetaTileEntity();
+
+ if (base == null || base.isDead()) {
+ return null;
+ }
+ }
+ }
+
+ return tileReference;
+ }
+
+ public GT_MetaTileEntity_WormholeGenerator getDest(WeakReference<GT_MetaTileEntity_WormholeGenerator> tile) {
+ if (tile == mMaster) {
+ return mSlave != null ? mSlave.get() : null;
+ }
+
+ if (tile == mSlave) {
+ return mMaster != null ? mMaster.get() : null;
+ }
+
+ return null;
+ }
+
+ public boolean isActive() {
+ boolean masterCanWork = Optional.ofNullable(mMaster)
+ .map(WeakReference::get)
+ .map(GT_MetaTileEntity_WormholeGenerator::getBaseMetaTileEntity)
+ .map(IGregTechTileEntity::isAllowedToWork)
+ .orElse(false);
+
+ boolean slaveCanWork = Optional.ofNullable(mSlave)
+ .map(WeakReference::get)
+ .map(GT_MetaTileEntity_WormholeGenerator::getBaseMetaTileEntity)
+ .map(IGregTechTileEntity::isAllowedToWork)
+ .orElse(false);
+
+ return masterCanWork && slaveCanWork;
+ }
+ }
+
+ // #endregion
+
+ // #region UI
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+
+ // spotless:off
+ tt.addMachineType("Wormhole Generator")
+ .addInfo("Controller for the Miniature Wormhole Generator.")
+ .addInfo("Transfers EU between two wormhole generators.")
+ .addInfo("Wormholes are linked by placing an AE2 Entangled Singularity in each controller slot.")
+ .addInfo("The transfer rate is limited by the wormhole size, and the wormhole size is governed by the transfer rate.")
+ .addInfo("If the transfer rate is completely stable, the transfer efficiency is " + String.format("%.3f", TRANSFER_EFFICIENCY * 100.0) + "%.")
+ .addInfo("EU will only be transferred if there is space in the laser source hatch.")
+ .addInfo("Each laser target must have a laser source on the §oother§7 controller, on the §oopposite§7 side.")
+ .addInfo("Consumes an AE2 Singularity from an input bus each time the wormhole is kick-started.")
+ .addInfo("The structure is too complex!")
+ .addInfo(BLUE_PRINT_INFO)
+ .beginStructureBlock(7, 9, 7, false)
+ .addSeparator()
+ .addCasingInfoExactly("Molecular Casing", 2 * 12, false)
+ .addCasingInfoExactly("Europium Reinforced Radiation Proof Machine Casing", 4, false)
+ .addCasingInfoExactly("Fusion Coil Block", 3 * 4 + 5 * 2, false)
+ .addCasingInfoRange("High Power Casing", 8 * 6 + 1, 8 * 6 + 1 + 4, false)
+ .addCasingInfoExactly("Borosilicate Glass (any)", 9 * 4, true)
+ .addMaintenanceHatch("§61§r (dot 1)")
+ .addInputBus("§61§r (dot 1)")
+ .addDynamoHatch("§60§r - §64§r (laser only, dot 2)")
+ .addEnergyHatch("§60§r - §64§r (laser only, dot 2)")
+ .addStructureInfo("§rThe glass tier limits the hatch tier.")
+ .addSubChannelUsage("glass", "Borosilicate Glass Tier")
+ .toolTipFinisher("Gregtech");
+ // spotless:on
+
+ return tt;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ List<String> data = new ArrayList<>();
+
+ data.addAll(Arrays.asList(super.getInfoData()));
+
+ data.add("-----------------------");
+ data.add("Wormhole Generator Info");
+
+ if (mStructureBadGlassTier) {
+ data.add(String.format("§cStructure errors:§r"));
+
+ if (mStructureBadGlassTier) {
+ data.add(String.format("§cGlass tier must be greater than or equal to the energy hatch tiers.§r"));
+ }
+ }
+
+ if (mLink == null) {
+ data.add("An entangled singularity must be present in the controller slot");
+ } else {
+ if (!mLink.isFormed()) {
+ data.add("Wormhole status: §cNo destination§f");
+ } else {
+ if (mLink.mWormholeEnergy > 0) {
+ if (mLink.isActive()) {
+ data.add("Wormhole status: §bActive§f");
+ } else {
+ data.add("Wormhole status: §6Decaying§f");
+ }
+ } else {
+ boolean anyTransferable = false;
+
+ for (int i = 0; i < MAX_HATCHES; i++) {
+ if (!HATCH_MASK[i]) continue;
+
+ if (getTransferable(i) > 0) {
+ anyTransferable = true;
+ break;
+ }
+ }
+
+ if (anyTransferable) {
+ data.add("Wormhole status: §7Inactive§f");
+ } else {
+ data.add("Wormhole status: §7No energy in input hatches§f");
+ }
+ }
+
+ double radius = Math.sqrt(mLink.mWormholeEnergy / 20.0 / 32.0);
+ data.add(String.format("Wormhole diameter: §b%,d§r ångström", (long) (radius * 2)));
+
+ data.add(String.format("Optimal transfer speed: §b%,.0f§r EU/t", mLink.mWormholeEnergy / 20));
+ }
+ }
+
+ for (int i = 0; i < MAX_HATCHES; i++) {
+ if (!HATCH_MASK[i]) continue;
+
+ var inputHatch = mSendHatches[i];
+ var outputHatch = mReceiveHatches[i];
+
+ // spotless:off
+ if(inputHatch != null) {
+ data.add(String.format(
+ "%s hatch (%,dA/t %s) transferred §b%,d§f EU (equivalent to %,dA/t) with an efficiency of %.3f%% in the last second",
+ HATCH_NAMES[i],
+ inputHatch.Amperes,
+ VN[inputHatch.mTier],
+ mLink != null ? mLink.mSendAmounts[i] : 0,
+ mLink != null ? mLink.mSendAmounts[i] / 20 / V[inputHatch.mTier] : 0,
+ mLink != null && mLink.mSendAmounts[i] > 0 ? ((double)mLink.mReceiveAmounts[OPPOSITES[i]]) / ((double)mLink.mSendAmounts[i]) * 100 : 0
+ ));
+ } else if(outputHatch != null) {
+ data.add(String.format(
+ "%s hatch (%,dA/t %s) received §b%,d§f EU (equivalent to %,dA/t) with an efficiency of %.3f%% in the last second",
+ HATCH_NAMES[i],
+ outputHatch.Amperes,
+ VN[outputHatch.mTier],
+ mLink != null ? mLink.mReceiveAmounts[i] : 0,
+ mLink != null ? mLink.mReceiveAmounts[i] / 20 / V[outputHatch.mTier] : 0,
+ mLink != null && mLink.mSendAmounts[OPPOSITES[i]] > 0 ? ((double)mLink.mReceiveAmounts[i]) / ((double)mLink.mSendAmounts[OPPOSITES[i]]) * 100 : 0
+ ));
+ } else {
+ data.add(String.format("%s hatch is not present", HATCH_NAMES[i]));
+ }
+ // spotless:on
+ }
+
+ data.add("-----------------------");
+
+ return data.toArray(new String[data.size()]);
+ }
+
+ @Override
+ protected void drawTexts(DynamicPositionedColumn screenElements, SlotWidget inventorySlot) {
+ super.drawTexts(screenElements, inventorySlot);
+
+ screenElements.widgets(TextWidget.dynamicString(() -> {
+ if (mLink == null) {
+ return String.format("§7Missing Entangled Singularity§f");
+ }
+
+ if (!mLink.isFormed()) {
+ return String.format("§7Wormhole status: §cNo destination§f");
+ }
+
+ if (mLink.mWormholeEnergy > 0 && !mLink.isActive()) {
+ return String.format("§7Wormhole status: §6Decaying§f");
+ }
+
+ if (mLink.mWormholeEnergy > 0) {
+ return String.format("§7Wormhole status: §bActive§f");
+ }
+
+ return String.format("§7Wormhole status: Inactive§f");
+ }),
+
+ TextWidget.dynamicString(() -> {
+ if (mLink == null) {
+ return "";
+ }
+
+ // LV power = 1 angstrom in diameter
+ double radius = Math.sqrt(mLink.mWormholeEnergy / 20.0 / 32.0);
+
+ return String.format("§7Wormhole diameter: §b%,d§7 ŧf", (long) (radius * 2));
+ })
+ .setEnabled(w -> mWormholeEnergy_UI > 0),
+
+ TextWidget.dynamicString(() -> {
+ if (mLink == null) {
+ return "";
+ }
+
+ if (mLink.mWormholeEnergy >= 1e10) {
+ return String.format("§7Max I/O per hatch: §b%3.3e§7 EU/t§f", mLink.mWormholeEnergy / 20);
+ } else {
+ return String.format("§7Max I/O per hatch: §b%,d§7 EU/t§f", (long) (mLink.mWormholeEnergy / 20));
+ }
+ })
+ .setEnabled(w -> mWormholeEnergy_UI > 0),
+
+ new FakeSyncWidget.DoubleSyncer(
+ () -> mLink != null ? mLink.mWormholeEnergy : 0,
+ val -> mWormholeEnergy_UI = val));
+ }
+
+ // #endregion
+
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/compressor/GT_MetaTileEntity_BlackHoleCompressor.java b/src/main/java/gregtech/common/tileentities/machines/multi/compressor/GT_MetaTileEntity_BlackHoleCompressor.java
new file mode 100644
index 0000000000..0b36d35cf8
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/compressor/GT_MetaTileEntity_BlackHoleCompressor.java
@@ -0,0 +1,539 @@
+package gregtech.common.tileentities.machines.multi.compressor;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.*;
+import static gregtech.api.enums.GT_HatchElement.*;
+import static gregtech.api.enums.GT_Values.AuthorFourIsTheNumber;
+import static gregtech.api.enums.GT_Values.Ollie;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_COMPRESSOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_COMPRESSOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_COMPRESSOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_COMPRESSOR_GLOW;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_StructureUtility.ofFrame;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Stream;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.MaterialsUEVplus;
+import gregtech.api.enums.Textures;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Input;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.recipe.metadata.CompressionTierKey;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.blocks.GT_Block_Casings10;
+import gregtech.common.items.GT_MetaGenerated_Item_01;
+import gtPlusPlus.core.util.minecraft.PlayerUtils;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public class GT_MetaTileEntity_BlackHoleCompressor
+ extends GT_MetaTileEntity_ExtendedPowerMultiBlockBase<GT_MetaTileEntity_BlackHoleCompressor>
+ implements ISurvivalConstructable {
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final IStructureDefinition<GT_MetaTileEntity_BlackHoleCompressor> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_BlackHoleCompressor>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ // spotless:off
+ transpose(new String[][]{
+ {" "," "," "," "," "," "," "," "," "," CC CC "," CC CC "," CC CC "," CCC CCC "," CCC CCC "," CCCCC CCCCC "," CCCC CCCC "," "," "," "," CCCC CCCC "," CCCCC CCCCC "," CCC CCC "," CCC CCC "," CC CC "," CC CC "," CC CC "," "," "," "," "," "," "," "," "," "},
+ {" "," "," "," "," "," "," "," CC CC "," CC CC "," CCC CCC "," CCC CCC "," CCCC CCCC "," CCCCC CCCCC "," CCCCCC CCCCCC "," CCCCCCC CCCCCCC "," CCCCCC CCCCCC "," "," "," "," CCCCCC CCCCCC "," CCCCCCC CCCCCCC "," CCCCCC CCCCCC "," CCCCC CCCCC "," CCCC CCCC "," CCC CCC "," CCC CCC "," CC CC "," CC CC "," "," "," "," "," "," "," "},
+ {" "," "," "," "," "," "," CCBBBCC "," BBCCCCCCCBB "," BCCCCCCCCCCCB "," BCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCCCCCB "," BCCCCCCCBBBBBCCCCCCCB "," BCCCCCCB BCCCCCCB "," CCCCCCCB BCCCCCCC "," CCCCCCB BBB BCCCCCC "," BCCCCCB B B BCCCCCB "," BCCCCCB B B BCCCCCB "," BCCCCCB B B BCCCCCB "," CCCCCCB BBB BCCCCCC "," CCCCCCCB BCCCCCCC "," BCCCCCCB BCCCCCCB "," BCCCCCCCBBBBBCCCCCCCB "," BCCCCCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCB "," BCCCCCCCCCCCB "," BBCCCCCCCBB "," CCBBBCC "," "," "," "," "," "," "},
+ {" "," "," "," "," "," CC CC "," CCBBBCC "," BBCCCCCCCBB "," BCCCCCCCCCCCB "," BCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCCCCCB "," BCCCCCCCBBBBBCCCCCCCB "," BCCCCCCBCCCCCBCCCCCCB "," CCCCCCCCBCCCCCCCBCCCCCCCC "," CCCCCCCBCCCCCCCCCBCCCCCCC "," BCCCCCBCCCCCCCCCBCCCCCB "," BCCCCCBCCCCCCCCCBCCCCCB "," BCCCCCBCCCCCCCCCBCCCCCB "," CCCCCCCBCCCCCCCCCBCCCCCCC "," CCCCCCCCBCCCCCCCBCCCCCCCC "," BCCCCCCBCCCCCBCCCCCCB "," BCCCCCCCBBBBBCCCCCCCB "," BCCCCCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCB "," BCCCCCCCCCCCB "," BBCCCCCCCBB "," CCBBBCC "," CC CC "," "," "," "," "," "},
+ {" "," "," "," "," CC CC "," CCDDDCC "," CCBBBCC "," BBCCCCCCCBB "," BCCCCCCCCCCCB "," BCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCCCCCB "," BCCCCCCCBBBBBCCCCCCCB "," BCCCCCCB BCCCCCCB "," CCCCCCCCCB BCCCCCCCCC "," CCCCCCCCB BCCCCCCCC "," DBCCCCCB BCCCCCBD "," DBCCCCCB BCCCCCBD "," DBCCCCCB BCCCCCBD "," CCCCCCCCB BCCCCCCCC "," CCCCCCCCCB BCCCCCCCCC "," BCCCCCCB BCCCCCCB "," BCCCCCCCBBBBBCCCCCCCB "," BCCCCCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCB "," BCCCCCCCCCCCB "," BBCCCCCCCBB "," CCBBBCC "," CCDDDCC "," CC CC "," "," "," "," "},
+ {" "," "," "," CC CC "," CC CC "," CC CC "," "," "," "," BB BB "," BBBB CCCCC BBBB "," BBBBCC CCBBBB "," BBC CBB "," C C "," CCC C C CCC "," CCC C C CCC "," C C "," C C "," C C "," CCC C C CCC "," CCC C C CCC "," C C "," BBC CBB "," BBBBCC CCBBBB "," BBBB CCCCC BBBB "," BB BB "," "," "," "," CC CC "," CC CC "," CC CC "," "," "," "},
+ {" "," "," "," CC CC "," CCDDDCC "," "," "," "," "," "," "," BBB CCCCC BBB "," BBCC CCBB "," BC CB "," CC C C CC "," CC C C CC "," D C C D "," D C C D "," D C C D "," CC C C CC "," CC C C CC "," BC CB "," BBCC CCBB "," BBB CCCCC BBB "," "," "," "," "," "," "," CCDDDCC "," CC CC "," "," "," "},
+ {" "," "," CC CC "," CC CC "," CC CC "," "," "," "," "," "," "," "," BBBCCCCCBBB "," BBC CBB "," CCC BC CB CCC "," CCC C C CCC "," C C "," C C "," C C "," CCC C C CCC "," CCC BC CB CCC "," BBC CBB "," BBBCCCCCBBB "," "," "," "," "," "," "," "," CC CC "," CC CC "," CC CC "," "," "},
+ {" "," "," CC CC "," CCDDDCC "," "," "," "," "," "," "," "," "," "," BBCCCCCBB "," CC BC CB CC "," CC C C CC "," D C C D "," D C C D "," D C C D "," CC C C CC "," CC BC CB CC "," BBCCCCCBB "," "," "," "," "," "," "," "," "," "," CCDDDCC "," CC CC "," "," "},
+ {" "," "," CC CC "," CC CC "," "," "," "," "," "," "," "," "," "," BCCCCCB "," CC BC CB CC "," CC C C CC "," C C "," C C "," C C "," CC C C CC "," CC BC CB CC "," BCCCCCB "," "," "," "," "," "," "," "," "," "," CC CC "," CC CC "," "," "},
+ {" "," CC CC "," CCDDDCC "," CC CC "," "," "," "," "," "," "," "," "," "," "," CCC BBCCCBB CCC "," CCC BC CB CCC "," D C C D "," D C C D "," D C C D "," CCC BC CB CCC "," CCC BBCCCBB CCC "," "," "," "," "," "," "," "," "," "," "," CC CC "," CCDDDCC "," CC CC "," "},
+ {" "," CC CC "," CC CC "," "," "," "," "," "," "," "," "," "," "," "," CC BCCCB CC "," CC BC CB CC "," C C "," C C "," C C "," CC BC CB CC "," CC BCCCB CC "," "," "," "," "," "," "," "," "," "," "," "," CC CC "," CC CC "," "},
+ {" "," CCDDDCC "," CC CC "," "," "," "," "," "," "," "," "," "," "," "," CC BBB CC "," CC BCCCB CC "," D BCCCCCB D "," D BCCCCCB D "," D BCCCCCB D "," CC BCCCB CC "," CC BBB CC "," "," "," "," "," "," "," "," "," "," "," "," CC CC "," CCDDDCC "," "},
+ {" "," CC CC "," CCBBBCC "," CCC "," "," "," "," "," "," "," "," "," "," "," CC CC "," CC BBB CC "," BC B B CB "," BC B B CB "," BC B B CB "," CC BBB CC "," CC CC "," "," "," "," "," "," "," "," "," "," "," CCC "," CCBBBCC "," CC CC "," "},
+ {" CCDDDCC "," CC CC "," CBBBBBC "," C C "," "," "," "," "," "," "," "," "," "," ","CCC CCC","CCBC CBCC","D B B D","D B B D","D B B D","CCBC CBCC","CCC CCC"," "," "," "," "," "," "," "," "," "," "," C C "," CBBBBBC "," CC CC "," CCDDDCC "},
+ {" CC CC "," CCCCCCC "," BBBBBBB "," C ABA C "," ABA "," A "," A "," "," "," "," "," "," "," ","CCBC CBCC","CCB BCC"," CBAA AABC "," CBBBAA AABBBC "," CBAA AABC ","CCB BCC","CCBC CBCC"," "," "," "," "," "," "," "," A "," A "," ABA "," C ABA C "," BBBBBBB "," CCCCCCC "," CC CC "},
+ {" CC CC "," CCCECCC "," BBBBBBB "," C BBB C "," BBB "," ABA "," ABA "," B "," B "," "," "," "," "," ","CCBC CBCC","CCB BCC"," CBBBAA AABBBC "," EBBBBBBB BBBBBBBE "," CBBBAA AABBBC ","CCB BCC","CCBC CBCC"," "," "," "," "," "," B "," B "," ABA "," ABA "," BBB "," C BBB C "," BBBBBBB "," CCCECCC "," CC CC "},
+ {" CC CC "," CCCCCCC "," BBBBBBB "," C ABA C "," ABA "," A "," A "," "," "," "," "," "," "," ","CCBC CBCC","CCB BCC"," CBAA AABC "," CBBBAA AABBBC "," CBAA AABC ","CCB BCC","CCBC CBCC"," "," "," "," "," "," "," "," A "," A "," ABA "," C ABA C "," BBBBBBB "," CCCCCCC "," CC CC "},
+ {" CCDDDCC "," CC CC "," CBBBBBC "," C C "," "," "," "," "," "," "," "," "," "," ","CCC CCC","CCBC CBCC","D B B D","D B B D","D B B D","CCBC CBCC","CCC CCC"," "," "," "," "," "," "," "," "," "," "," C C "," CBBBBBC "," CC CC "," CCDDDCC "},
+ {" "," CC CC "," CCBBBCC "," CCC "," "," "," "," "," "," "," "," "," "," "," CC CC "," CC BBB CC "," BC B B CB "," BC B B CB "," BC B B CB "," CC BBB CC "," CC CC "," "," "," "," "," "," "," "," "," "," "," CCC "," CCBBBCC "," CC CC "," "},
+ {" "," CCDDDCC "," CC CC "," "," "," "," "," "," "," "," "," "," "," "," CC BBB CC "," CC BCCCB CC "," D BCCCCCB D "," D BCCCCCB D "," D BCCCCCB D "," CC BCCCB CC "," CC BBB CC "," "," "," "," "," "," "," "," "," "," "," "," CC CC "," CCDDDCC "," "},
+ {" "," CC CC "," CC CC "," "," "," "," "," "," "," "," "," "," "," "," CC BCCCB CC "," CC BC CB CC "," C C "," C C "," C C "," CC BC CB CC "," CC BCCCB CC "," "," "," "," "," "," "," "," "," "," "," "," CC CC "," CC CC "," "},
+ {" "," CC CC "," CCDDDCC "," CC CC "," "," "," "," "," "," "," "," "," "," "," CCC BBCCCBB CCC "," CCC BC CB CCC "," D C C D "," D C C D "," D C C D "," CCC BC CB CCC "," CCC BBCCCBB CCC "," "," "," "," "," "," "," "," "," "," "," CC CC "," CCDDDCC "," CC CC "," "},
+ {" "," "," CC CC "," CC CC "," "," "," "," "," "," "," "," "," "," BCCCCCB "," CC BC CB CC "," CC C C CC "," C C "," C C "," C C "," CC C C CC "," CC BC CB CC "," BCCCCCB "," "," "," "," "," "," "," "," "," "," CC CC "," CC CC "," "," "},
+ {" "," "," CC CC "," CCDDDCC "," "," "," "," "," "," "," "," "," "," BBCCCCCBB "," CC BC CB CC "," CC C C CC "," D C C D "," D C C D "," D C C D "," CC C C CC "," CC BC CB CC "," BBCCCCCBB "," "," "," "," "," "," "," "," "," "," CCDDDCC "," CC CC "," "," "},
+ {" "," "," CC CC "," CC CC "," CC CC "," "," "," "," "," "," "," "," BBBCCCCCBBB "," BBC CBB "," CCC BC CB CCC "," CCC C C CCC "," C C "," C C "," C C "," CCC C C CCC "," CCC BC CB CCC "," BBC CBB "," BBBCCCCCBBB "," "," "," "," "," "," "," "," CC CC "," CC CC "," CC CC "," "," "},
+ {" "," "," "," CC CC "," CCDDDCC "," "," "," "," "," "," "," BBB CCCCC BBB "," BBCC CCBB "," BC CB "," CC C C CC "," CC C C CC "," D C C D "," D C C D "," D C C D "," CC C C CC "," CC C C CC "," BC CB "," BBCC CCBB "," BBB CCCCC BBB "," "," "," "," "," "," "," CCDDDCC "," CC CC "," "," "," "},
+ {" "," "," "," CC CC "," CC CC "," CC CC "," "," "," "," BB BB "," BBBB CC~CC BBBB "," BBBBCC CCBBBB "," BBC CBB "," C C "," CCC C C CCC "," CCC C C CCC "," C C "," C C "," C C "," CCC C C CCC "," CCC C C CCC "," C C "," BBC CBB "," BBBBCC CCBBBB "," BBBB CCCCC BBBB "," BB BB "," "," "," "," CC CC "," CC CC "," CC CC "," "," "," "},
+ {" "," "," "," "," CC CC "," CCDDDCC "," CCBBBCC "," BBCCCCCCCBB "," BCCCCCCCCCCCB "," BCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCCCCCB "," BCCCCCCCBBBBBCCCCCCCB "," BCCCCCCB BCCCCCCB "," CCCCCCCCCB BCCCCCCCCC "," CCCCCCCCB BCCCCCCCC "," DBCCCCCB BCCCCCBD "," DBCCCCCB BCCCCCBD "," DBCCCCCB BCCCCCBD "," CCCCCCCCB BCCCCCCCC "," CCCCCCCCCB BCCCCCCCCC "," BCCCCCCB BCCCCCCB "," BCCCCCCCBBBBBCCCCCCCB "," BCCCCCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCB "," BCCCCCCCCCCCB "," BBCCCCCCCBB "," CCBBBCC "," CCDDDCC "," CC CC "," "," "," "," "},
+ {" "," "," "," "," "," CC CC "," CCBBBCC "," BBCCCCCCCBB "," BCCCCCCCCCCCB "," BCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCCCCCB "," BCCCCCCCBBBBBCCCCCCCB "," BCCCCCCBCCCCCBCCCCCCB "," CCCCCCCCBCCCCCCCBCCCCCCCC "," CCCCCCCBCCCCCCCCCBCCCCCCC "," BCCCCCBCCCCCCCCCBCCCCCB "," BCCCCCBCCCCCCCCCBCCCCCB "," BCCCCCBCCCCCCCCCBCCCCCB "," CCCCCCCBCCCCCCCCCBCCCCCCC "," CCCCCCCCBCCCCCCCBCCCCCCCC "," BCCCCCCBCCCCCBCCCCCCB "," BCCCCCCCBBBBBCCCCCCCB "," BCCCCCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCB "," BCCCCCCCCCCCB "," BBCCCCCCCBB "," CCBBBCC "," CC CC "," "," "," "," "," "},
+ {" "," "," "," "," "," "," CCBBBCC "," BBCCCCCCCBB "," BCCCCCCCCCCCB "," BCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCCCCCB "," BCCCCCCCBBBBBCCCCCCCB "," BCCCCCCB BCCCCCCB "," CCCCCCCB BCCCCCCC "," CCCCCCB BBB BCCCCCC "," BCCCCCB B B BCCCCCB "," BCCCCCB B B BCCCCCB "," BCCCCCB B B BCCCCCB "," CCCCCCB BBB BCCCCCC "," CCCCCCCB BCCCCCCC "," BCCCCCCB BCCCCCCB "," BCCCCCCCBBBBBCCCCCCCB "," BCCCCCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCCCB "," BCCCCCCCCCCCCCB "," BCCCCCCCCCCCB "," BBCCCCCCCBB "," CCBBBCC "," "," "," "," "," "," "},
+ {" "," "," "," "," "," "," "," CC CC "," CC CC "," CCC CCC "," CCC CCC "," CCCC CCCC "," CCCCC CCCCC "," CCCCCC CCCCCC "," CCCCCCC CCCCCCC "," CCCCCC CCCCCC "," "," "," "," CCCCCC CCCCCC "," CCCCCCC CCCCCCC "," CCCCCC CCCCCC "," CCCCC CCCCC "," CCCC CCCC "," CCC CCC "," CCC CCC "," CC CC "," CC CC "," "," "," "," "," "," "," "},
+ {" "," "," "," "," "," "," "," "," "," CC CC "," CC CC "," CC CC "," CCC CCC "," CCC CCC "," CCCCC CCCCC "," CCCC CCCC "," "," "," "," CCCC CCCC "," CCCCC CCCCC "," CCC CCC "," CCC CCC "," CC CC "," CC CC "," CC CC "," "," "," "," "," "," "," "," "," "}
+ }))
+ //spotless:on
+ .addElement('A', ofBlock(GregTech_API.sBlockGlass1, 4))
+ .addElement(
+ 'B',
+ buildHatchAdder(GT_MetaTileEntity_BlackHoleCompressor.class).atLeast(Maintenance, Energy)
+ .casingIndex(((GT_Block_Casings10) GregTech_API.sBlockCasings10).getTextureIndex(12))
+ .dot(2)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_BlackHoleCompressor::onCasingAdded,
+ ofBlock(GregTech_API.sBlockCasings10, 12))))
+ .addElement('C', ofBlock(GregTech_API.sBlockCasings10, 11))
+ .addElement('D', ofFrame(Materials.NaquadahAlloy))
+ .addElement(
+ 'E',
+ buildHatchAdder(GT_MetaTileEntity_BlackHoleCompressor.class).atLeast(InputBus, OutputBus, InputHatch)
+ .casingIndex(((GT_Block_Casings10) GregTech_API.sBlockCasings10).getTextureIndex(11))
+ .dot(1)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_BlackHoleCompressor::onCasingAdded,
+ ofBlock(GregTech_API.sBlockCasings10, 11))))
+
+ .build();
+
+ private boolean blackholeOn = false;
+ private int catalyzingCounter = 0;
+ private float blackHoleStability = 100;
+
+ private final FluidStack blackholeCatalyzingCost = (MaterialsUEVplus.SpaceTime).getMolten(1);
+ private int catalyzingCostModifier = 1;
+
+ public GT_MetaTileEntity_BlackHoleCompressor(final int aID, final String aName, final String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_BlackHoleCompressor(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_BlackHoleCompressor> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_BlackHoleCompressor(this.mName);
+ }
+
+ @Override
+ public boolean supportsMachineModeSwitch() {
+ return true;
+ }
+
+ @Override
+ public void setMachineModeIcons() {
+ machineModeIcons.add(GT_UITextures.OVERLAY_BUTTON_MACHINEMODE_COMPRESSING);
+ machineModeIcons.add(GT_UITextures.OVERLAY_BUTTON_MACHINEMODE_SINGULARITY);
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ setMachineMode(nextMachineMode());
+ PlayerUtils.messagePlayer(
+ aPlayer,
+ String.format(StatCollector.translateToLocal("GT5U.MULTI_MACHINE_CHANGE"), getMachineModeName()));
+ }
+
+ @Override
+ public String getMachineModeName() {
+ return StatCollector.translateToLocal("GT5U.COMPRESSION_TIER.mode." + machineMode);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ ITexture[] rTexture;
+ if (side == aFacing) {
+ if (aActive) {
+ rTexture = new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings10, 11)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_COMPRESSOR_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_COMPRESSOR_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ } else {
+ rTexture = new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings10, 11)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_COMPRESSOR)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_COMPRESSOR_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ } else {
+ rTexture = new ITexture[] { Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings10, 11)) };
+ }
+ return rTexture;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Compressor/Advanced Neutronium Compressor")
+ .addInfo("Controller Block for the Semi-Stable Black Hole Containment Field")
+ .addInfo(EnumChatFormatting.LIGHT_PURPLE + "Uses the immense power of the event horizon to compress things")
+ .addInfo("No longer requires heat management to perform perfect compression")
+ .addInfo("Can create advanced singularities!")
+ .addSeparator()
+ .addInfo(
+ "Insert a " + EnumChatFormatting.WHITE
+ + "Black Hole Activation Catalyst"
+ + EnumChatFormatting.GRAY
+ + " to open a black hole")
+ .addInfo(
+ "The black hole will begin its life at " + EnumChatFormatting.RED
+ + "100%"
+ + EnumChatFormatting.GRAY
+ + " stability and slowly decay")
+ .addSeparator()
+ .addInfo("Natural decay takes " + EnumChatFormatting.RED + "100" + EnumChatFormatting.GRAY + " seconds")
+ .addInfo("Running recipes in the machine will slow the decay by " + EnumChatFormatting.RED + "25%")
+ .addInfo(
+ "The decay can be " + EnumChatFormatting.BOLD
+ + "halted"
+ + EnumChatFormatting.RESET
+ + EnumChatFormatting.GRAY
+ + " by inserting spacetime")
+ .addInfo(
+ "Every " + EnumChatFormatting.RED
+ + "30"
+ + EnumChatFormatting.GRAY
+ + " seconds saved by spacetime insertion will "
+ + EnumChatFormatting.RED
+ + "double"
+ + EnumChatFormatting.GRAY
+ + " the cost per second!")
+ .addInfo("Once the black hole becomes unstable, it will void all inputs for recipes which require it")
+ .addInfo(
+ "Insert a " + EnumChatFormatting.WHITE
+ + "Black Hole Deactivation Catalyst"
+ + EnumChatFormatting.GRAY
+ + " to close the black hole")
+ .addSeparator()
+ .addInfo(
+ "Recipes not utilizing the black hole have their lengths " + EnumChatFormatting.RED
+ + "doubled"
+ + EnumChatFormatting.GRAY
+ + " if it becomes unstable")
+ .addInfo("400% faster than singleblock machines of the same voltage when black hole is open")
+ .addInfo("Only uses 70% of the EU/t normally required")
+ .addInfo("Gains 8 parallels per voltage tier")
+ .addInfo(
+ "Parallels are " + EnumChatFormatting.RED
+ + "doubled"
+ + EnumChatFormatting.GRAY
+ + " when stability is BELOW "
+ + EnumChatFormatting.RED
+ + "50%")
+ .addInfo(
+ "Parallels are " + EnumChatFormatting.RED
+ + "quadrupled"
+ + EnumChatFormatting.GRAY
+ + " when stability is BELOW "
+ + EnumChatFormatting.RED
+ + "20%")
+ .addInfo(AuthorFourIsTheNumber + EnumChatFormatting.RESET + " & " + Ollie)
+ .addSeparator()
+ .beginStructureBlock(35, 33, 35, false)
+ .addCasingInfoMin("Background Radiation Absorbent Casing", 985, false)
+ .addCasingInfoExactly("Extreme Density Space-Bending Casing", 3667, false)
+ .addCasingInfoExactly("Hawking Radiation Realignment Focus", 64, false)
+ .addCasingInfoExactly("Naquadah Alloy Frame Box", 144, false)
+ .addInputBus("Behind Laser", 1)
+ .addOutputBus("Behind Laser", 1)
+ .addInputHatch("Behind Laser", 1)
+ .addEnergyHatch("Any Radiation Absorbent Casing", 2)
+ .addMaintenanceHatch("Any Radiation Absorbent Casing", 2)
+ .toolTipFinisher("GregTech");
+ return tt;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 17, 27, 10);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 17, 27, 10, elementBudget, env, false, true);
+ }
+
+ private int mCasingAmount;
+
+ private void onCasingAdded() {
+ mCasingAmount++;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ mCasingAmount = 0;
+ mEnergyHatches.clear();
+
+ if (!checkPiece(STRUCTURE_PIECE_MAIN, 17, 27, 10)) return false;
+ if (mCasingAmount < 0) return false;
+
+ return true;
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ aNBT.setInteger("catalyzingCostModifier", catalyzingCostModifier);
+ aNBT.setInteger("catalyzingCounter", catalyzingCounter);
+ aNBT.setBoolean("blackholeOn", blackholeOn);
+ aNBT.setFloat("blackholeStability", blackHoleStability);
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ if (aNBT.hasKey("catalyzingCounter")) catalyzingCostModifier = aNBT.getInteger("catalyzingCounter");
+ if (aNBT.hasKey("catalyzingCostModifier")) catalyzingCostModifier = aNBT.getInteger("catalyzingCostModifier");
+ if (aNBT.hasKey("blackholeOn")) blackholeOn = aNBT.getBoolean("blackholeOn");
+ if (aNBT.hasKey("blackholeStability")) blackHoleStability = aNBT.getFloat("blackholeStability");
+ }
+
+ @Override
+ protected void setProcessingLogicPower(ProcessingLogic logic) {
+ logic.setAvailableVoltage(GT_Utility.roundUpVoltage(this.getMaxInputVoltage()));
+ logic.setAvailableAmperage(1L);
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ tag.setBoolean("blackholeOn", blackholeOn);
+ tag.setFloat("blackHoleStability", blackHoleStability);
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ super.getWailaBody(itemStack, currentTip, accessor, config);
+ final NBTTagCompound tag = accessor.getNBTData();
+ if (tag.getBoolean("blackholeOn")) {
+ if (tag.getFloat("blackHoleStability") > 0) {
+ currentTip.add(EnumChatFormatting.DARK_PURPLE + "Black Hole Active");
+ currentTip.add(
+ EnumChatFormatting.DARK_PURPLE + " Stability: "
+ + EnumChatFormatting.BOLD
+ + Math.round(tag.getFloat("blackHoleStability"))
+ + "%");
+ } else {
+ currentTip.add(EnumChatFormatting.RED + "BLACK HOLE UNSTABLE");
+ }
+ } else currentTip.add(EnumChatFormatting.DARK_PURPLE + "Black Hole Offline");
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic() {
+
+ @NotNull
+ @Override
+ protected Stream<GT_Recipe> findRecipeMatches(@Nullable RecipeMap<?> map) {
+
+ // Loop through all items and look for the Activation and Deactivation Catalysts
+ // Deactivation resets stability to 100 and catalyzing cost to 1
+ for (ItemStack inputItem : inputItems) {
+ if (inputItem.getItem() instanceof GT_MetaGenerated_Item_01) {
+ if (inputItem.getItemDamage() == 32418 && !blackholeOn) {
+ inputItem.stackSize -= 1;
+ blackholeOn = true;
+ break;
+ } else if (inputItem.getItemDamage() == 32419 && blackholeOn) {
+ inputItem.stackSize -= 1;
+ blackholeOn = false;
+ blackHoleStability = 100;
+ catalyzingCostModifier = 1;
+ break;
+ }
+ }
+ }
+ return super.findRecipeMatches(map);
+ }
+
+ @NotNull
+ @Override
+ protected CheckRecipeResult validateRecipe(@NotNull GT_Recipe recipe) {
+
+ // Default speed bonus
+ setSpeedBonus(1F);
+
+ // If recipe needs a black hole and one is not open, just wait
+ // If the recipe doesn't require black hole, incur a 0.5x speed penalty
+ // If recipe doesn't require black hole but one is open, give 5x speed bonus
+ if (recipe.getMetadataOrDefault(CompressionTierKey.INSTANCE, 1) > 0) {
+ if (!blackholeOn) return CheckRecipeResultRegistry.NO_BLACK_HOLE;
+ } else {
+ if (blackHoleStability <= 0) setSpeedBonus(5F);
+ else if (blackholeOn) setSpeedBonus(0.2F);
+ }
+ return super.validateRecipe(recipe);
+ }
+
+ @Nonnull
+ protected CheckRecipeResult onRecipeStart(@Nonnull GT_Recipe recipe) {
+ // If recipe needs a black hole and one is active but unstable, continuously void items
+ if (blackHoleStability <= 0 && recipe.getMetadataOrDefault(CompressionTierKey.INSTANCE, 1) > 0) {
+ return CheckRecipeResultRegistry.UNSTABLE_BLACK_HOLE;
+ }
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ }
+ }.setMaxParallelSupplier(this::getMaxParallelRecipes)
+ .setEuModifier(0.7F);
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+
+ if (aTick % 20 == 0) {
+ if (blackholeOn && blackHoleStability >= 0) {
+ float stabilityDecrease = 1F;
+ // If the machine is running, reduce stability loss by 25%
+ if (this.maxProgresstime() != 0) {
+ stabilityDecrease = 0.75F;
+ }
+ // Search all hatches for catalyst fluid
+ // If found enough, drain it and reduce stability loss to 0
+ // Every 30 drains, double the cost
+ FluidStack totalCost = new FluidStack(blackholeCatalyzingCost, catalyzingCostModifier);
+ for (GT_MetaTileEntity_Hatch_Input hatch : mInputHatches) {
+ if (drain(hatch, totalCost, false)) {
+ drain(hatch, totalCost, true);
+ catalyzingCounter += 1;
+ stabilityDecrease = 0;
+ if (catalyzingCounter >= 30) {
+ catalyzingCostModifier *= 2;
+ catalyzingCounter = 0;
+ }
+ }
+ }
+ if (blackHoleStability >= 0) blackHoleStability -= stabilityDecrease;
+ else blackHoleStability = 0;
+ }
+ }
+ }
+
+ public int getMaxParallelRecipes() {
+ int parallels = (8 * GT_Utility.getTier(this.getMaxInputVoltage()));
+ if (blackHoleStability < 60) {
+ parallels *= 2;
+ if (blackHoleStability < 20) parallels *= 2;
+ }
+ return parallels;
+ }
+
+ private static final int MACHINEMODE_COMPRESSOR = 0;
+ private static final int MACHINEMODE_BLACKHOLE = 1;
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return (machineMode == MACHINEMODE_COMPRESSOR) ? RecipeMaps.compressorRecipes
+ : RecipeMaps.neutroniumCompressorRecipes;
+ }
+
+ @Nonnull
+ @Override
+ public Collection<RecipeMap<?>> getAvailableRecipeMaps() {
+ return Arrays.asList(RecipeMaps.compressorRecipes, RecipeMaps.neutroniumCompressorRecipes);
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsBatchMode() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsInputSeparation() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsSingleRecipeLocking() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/compressor/GT_MetaTileEntity_HIPCompressor.java b/src/main/java/gregtech/common/tileentities/machines/multi/compressor/GT_MetaTileEntity_HIPCompressor.java
new file mode 100644
index 0000000000..bb79cf692b
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/compressor/GT_MetaTileEntity_HIPCompressor.java
@@ -0,0 +1,525 @@
+package gregtech.common.tileentities.machines.multi.compressor;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.*;
+import static gregtech.api.enums.GT_HatchElement.*;
+import static gregtech.api.enums.GT_Values.AuthorFourIsTheNumber;
+import static gregtech.api.enums.GT_Values.Ollie;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_COMPRESSOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_COMPRESSOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_COMPRESSOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_COMPRESSOR_COOLING;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_COMPRESSOR_COOLING_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_COMPRESSOR_GLOW;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_StructureUtility.ofCoil;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.github.bartimaeusnek.bartworks.util.MathUtils;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.HeatingCoilLevel;
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.IHatchElement;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase;
+import gregtech.api.multitileentity.multiblock.casing.Glasses;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.metadata.CompressionTierKey;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.IGT_HatchAdder;
+import gregtech.api.util.shutdown.SimpleShutDownReason;
+import gregtech.common.blocks.GT_Block_Casings10;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public class GT_MetaTileEntity_HIPCompressor extends
+ GT_MetaTileEntity_ExtendedPowerMultiBlockBase<GT_MetaTileEntity_HIPCompressor> implements ISurvivalConstructable {
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final IStructureDefinition<GT_MetaTileEntity_HIPCompressor> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_HIPCompressor>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ // spotless:off
+ transpose(new String[][]{
+ {" "," "," "," CCCCCC DDDDDD "," "," "," "},
+ {" "," "," "," C C D D "," "," "," "},
+ {" "," HHH "," HHHHH "," C HHHHH D "," HHHHH "," HHH "," "},
+ {" BBB "," BBBBB "," BBBBBBB "," C BBBBBBB D "," BBBBBBB "," BBBBB "," BBB "},
+ {" HHH "," B B "," BF FB "," C BF FB D "," BF FB "," B B "," HHH "},
+ {" HAH "," B B "," GF FG "," C GF FG D "," GF FG "," B B "," HAH "},
+ {" HAH "," B B "," E GF FG E ","EEE GF FG EEE"," E GF FG E "," B B "," HAH "},
+ {" HAH "," B B "," A GF FG A ","A A GF FG A A"," A GF FG A "," B B "," HAH "},
+ {" HHH "," B B "," A BF FB A ","A A BF FB A A"," A BF FB A "," B B "," HHH "},
+ {" B~B "," BBBBB "," E BBBBBBB E ","EEE BBBBBBB EEE"," E BBBBBBB E "," BBBBB "," BBB "}
+ }))
+ //spotless:on
+ .addElement('A', Glasses.chainAllGlasses())
+ .addElement(
+ 'B',
+ buildHatchAdder(GT_MetaTileEntity_HIPCompressor.class)
+ .atLeast(Maintenance, Energy, SpecialHatchElement.HeatSensor)
+ .casingIndex(((GT_Block_Casings10) GregTech_API.sBlockCasings10).getTextureIndex(4))
+ .dot(1)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_HIPCompressor::onCasingAdded,
+ ofBlock(GregTech_API.sBlockCasings10, 4))))
+ .addElement('C', ofBlock(GregTech_API.sBlockCasings10, 9))
+ .addElement('D', ofBlock(GregTech_API.sBlockCasings10, 10))
+ .addElement('E', ofBlock(GregTech_API.sBlockCasings4, 1))
+ .addElement(
+ 'F',
+ ofCoil(GT_MetaTileEntity_HIPCompressor::setCoilLevel, GT_MetaTileEntity_HIPCompressor::getCoilLevel))
+ .addElement(
+ 'G',
+ buildHatchAdder(GT_MetaTileEntity_HIPCompressor.class).atLeast(InputBus, OutputBus)
+ .casingIndex(((GT_Block_Casings10) GregTech_API.sBlockCasings10).getTextureIndex(5))
+ .dot(1)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_HIPCompressor::onCasingAdded,
+ ofBlock(GregTech_API.sBlockCasings10, 5))))
+ .addElement('H', ofBlock(GregTech_API.sBlockCasings10, 5))
+ .build();
+
+ private final ArrayList<GT_MetaTileEntity_HeatSensor> sensorHatches = new ArrayList<>();
+
+ private HeatingCoilLevel heatLevel;
+ private int coilTier = 0;
+
+ private float heat = 0;
+ private boolean cooling = false;
+
+ public GT_MetaTileEntity_HIPCompressor(final int aID, final String aName, final String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_HIPCompressor(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_HIPCompressor> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_HIPCompressor(this.mName);
+ }
+
+ @Override
+ public void onValueUpdate(byte aValue) {
+ boolean oCooling = cooling;
+ cooling = (aValue & 1) == 1;
+ if (oCooling != cooling) getBaseMetaTileEntity().issueTextureUpdate();
+ }
+
+ @Override
+ public byte getUpdateData() {
+ return (byte) (cooling ? 1 : 0);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ ITexture[] rTexture;
+ if (side == aFacing) {
+ if (cooling) {
+ rTexture = new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings10, 4)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_COMPRESSOR_COOLING)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_COMPRESSOR_COOLING_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ } else if (aActive) {
+ rTexture = new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings10, 4)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_COMPRESSOR_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_COMPRESSOR_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ } else {
+ rTexture = new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings10, 4)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_COMPRESSOR)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_COMPRESSOR_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ } else {
+ rTexture = new ITexture[] { Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings10, 4)) };
+ }
+ return rTexture;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Compressor")
+ .addInfo("Controller Block for the Hot Isostatic Pressurization Unit")
+ .addInfo("HIP Unit heats up while running")
+ .addInfo(
+ "When it reaches maximum heat, it becomes " + EnumChatFormatting.DARK_RED
+ + EnumChatFormatting.BOLD
+ + "overheated!")
+ .addInfo("This is only resolved by letting the machine fully cool down")
+ .addInfo(
+ "When " + EnumChatFormatting.DARK_RED
+ + "overheated"
+ + EnumChatFormatting.GRAY
+ + ", recipes are slowed down drastically")
+ .addSeparator()
+ .addInfo(
+ "Some recipes " + EnumChatFormatting.BOLD
+ + "require"
+ + EnumChatFormatting.RESET
+ + EnumChatFormatting.GRAY
+ + " HIP")
+ .addInfo(
+ "If the machine " + EnumChatFormatting.DARK_RED
+ + "overheats"
+ + EnumChatFormatting.GRAY
+ + " during these recipes, recipe will be voided!")
+ .addInfo("Read the current heat using Heat Sensor Hatches")
+ .addSeparator()
+ .addInfo("More advanced coils allow better heat control - the unit will take longer to overheat")
+ .addInfo(
+ "Unit heats by " + EnumChatFormatting.GREEN
+ + "(5% x 0.90 ^ (Coil Tier - 1))"
+ + EnumChatFormatting.GRAY
+ + " every second while running")
+ .addInfo(
+ "Unit cools by " + EnumChatFormatting.GREEN
+ + "2%"
+ + EnumChatFormatting.GRAY
+ + " every second while not running")
+ .addSeparator()
+ .addInfo(
+ "250% " + EnumChatFormatting.RED
+ + "faster"
+ + EnumChatFormatting.GRAY
+ + "/"
+ + EnumChatFormatting.BLUE
+ + "slower"
+ + EnumChatFormatting.GRAY
+ + " than singleblock machines of the same voltage")
+ .addInfo(
+ "Uses " + EnumChatFormatting.RED
+ + "75%"
+ + EnumChatFormatting.GRAY
+ + "/"
+ + EnumChatFormatting.BLUE
+ + "110%"
+ + EnumChatFormatting.GRAY
+ + " the EU/t normally required")
+ .addInfo(
+ "Gains " + EnumChatFormatting.RED
+ + "4"
+ + EnumChatFormatting.GRAY
+ + "/"
+ + EnumChatFormatting.BLUE
+ + "1"
+ + EnumChatFormatting.GRAY
+ + " parallels per voltage tier")
+ .addInfo(AuthorFourIsTheNumber + EnumChatFormatting.RESET + " & " + Ollie)
+ .addSeparator()
+ .beginStructureBlock(7, 5, 7, true)
+ .addController("Front Center")
+ .addCasingInfoMin("Electric Compressor Casing", 95, false)
+ .addCasingInfoMin("Compressor Pipe Casing", 45, false)
+ .addCasingInfoExactly("Coolant Duct", 12, false)
+ .addCasingInfoExactly("Heating Duct", 12, false)
+ .addCasingInfoExactly("EV+ Glass", 22, false)
+ .addCasingInfoExactly("Clean Stainless Steel Machine Casing", 20, false)
+ .addCasingInfoExactly("Coil", 30, true)
+ .addInputBus("Pipe Casings on Side", 2)
+ .addOutputBus("Pipe Casings on Side", 2)
+ .addEnergyHatch("Any Electric Compressor Casing", 1)
+ .addMaintenanceHatch("Any Electric Compressor Casing", 1)
+ .toolTipFinisher("GregTech");
+ return tt;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 7, 9, 0);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 7, 9, 0, elementBudget, env, false, true);
+ }
+
+ private int mCasingAmount;
+
+ private void onCasingAdded() {
+ mCasingAmount++;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ setCoilLevel(HeatingCoilLevel.None);
+ mCasingAmount = 0;
+ return checkPiece(STRUCTURE_PIECE_MAIN, 7, 9, 0) && mCasingAmount >= 95;
+ }
+
+ @Override
+ protected void setProcessingLogicPower(ProcessingLogic logic) {
+ logic.setAvailableVoltage(GT_Utility.roundUpVoltage(this.getMaxInputVoltage()));
+ logic.setAvailableAmperage(1L);
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ aNBT.setFloat("heat", heat);
+ aNBT.setBoolean("cooling", cooling);
+ aNBT.setInteger("coilTier", coilTier);
+ aNBT.setBoolean("doingHIP", doingHIP);
+ super.saveNBTData(aNBT);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ if (aNBT.hasKey("heat")) heat = aNBT.getFloat("heat");
+ if (aNBT.hasKey("cooling")) cooling = aNBT.getBoolean("cooling");
+ if (aNBT.hasKey("coilTier")) coilTier = aNBT.getInteger("coilTier");
+ if (aNBT.hasKey("doingHIP")) doingHIP = aNBT.getBoolean("doingHIP");
+ super.loadNBTData(aNBT);
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ tag.setInteger("heat", Math.round(heat));
+ tag.setBoolean("cooling", cooling);
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ super.getWailaBody(itemStack, currentTip, accessor, config);
+ final NBTTagCompound tag = accessor.getNBTData();
+ if (tag.getBoolean("cooling")) currentTip.add(
+ "HIP Heat: " + EnumChatFormatting.RED
+ + EnumChatFormatting.BOLD
+ + tag.getInteger("heat")
+ + "%"
+ + EnumChatFormatting.RESET);
+ else currentTip.add(
+ "HIP Heat: " + EnumChatFormatting.AQUA
+ + EnumChatFormatting.BOLD
+ + tag.getInteger("heat")
+ + "%"
+ + EnumChatFormatting.RESET);
+ }
+
+ private boolean doingHIP = false;
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic() {
+
+ @NotNull
+ @Override
+ protected CheckRecipeResult validateRecipe(@NotNull GT_Recipe recipe) {
+ doingHIP = false;
+ setSpeedBonus(1F / 1.25F);
+ setEuModifier(0.75F);
+
+ if (cooling) {
+ setSpeedBonus(2.5F);
+ setEuModifier(1.1F);
+ }
+
+ if (recipe.getMetadataOrDefault(CompressionTierKey.INSTANCE, 1) > 0) doingHIP = true;
+ return super.validateRecipe(recipe);
+ }
+ }.setMaxParallelSupplier(this::getMaxParallelRecipes);
+ }
+
+ @Override
+ public boolean onRunningTick(ItemStack aStack) {
+ if (cooling && doingHIP) {
+ stopMachine(SimpleShutDownReason.ofCritical("overheated"));
+ doingHIP = false;
+ }
+ return super.onRunningTick(aStack);
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+
+ if (aTick % 20 == 0) {
+
+ // Default to cooling by 2%
+ float heatMod = -2;
+
+ // If the machine is running, heat by 5% x 0.90 ^ (Coil Tier)
+ // Cupronickel is 0, so base will be 5% increase
+ if (this.maxProgresstime() != 0) {
+ heatMod = (float) (5 * Math.pow(0.9, coilTier));
+ }
+
+ heat = MathUtils.clamp(heat + heatMod, 0, 100);
+
+ if ((cooling && heat <= 0) || (!cooling && heat >= 100)) {
+ cooling = !cooling;
+ }
+ }
+
+ // Update all the sensors
+ for (GT_MetaTileEntity_HeatSensor hatch : sensorHatches) {
+ hatch.updateRedstoneOutput(heat);
+ }
+
+ }
+
+ public int getMaxParallelRecipes() {
+ return cooling ? GT_Utility.getTier(this.getMaxInputVoltage())
+ : (4 * GT_Utility.getTier(this.getMaxInputVoltage()));
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.compressorRecipes;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsBatchMode() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsInputSeparation() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsSingleRecipeLocking() {
+ return true;
+ }
+
+ public HeatingCoilLevel getCoilLevel() {
+ return heatLevel;
+ }
+
+ public void setCoilLevel(HeatingCoilLevel aCoilLevel) {
+ heatLevel = aCoilLevel;
+ coilTier = aCoilLevel.getTier();
+ }
+
+ public boolean addSensorHatchToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_HeatSensor sensor) {
+ sensor.updateTexture(aBaseCasingIndex);
+ return this.sensorHatches.add(sensor);
+ }
+ return false;
+ }
+
+ private enum SpecialHatchElement implements IHatchElement<GT_MetaTileEntity_HIPCompressor> {
+
+ HeatSensor(GT_MetaTileEntity_HIPCompressor::addSensorHatchToMachineList, GT_MetaTileEntity_HeatSensor.class) {
+
+ @Override
+ public long count(GT_MetaTileEntity_HIPCompressor gtMetaTileEntityHIPCompressor) {
+ return gtMetaTileEntityHIPCompressor.sensorHatches.size();
+ }
+ };
+
+ private final List<Class<? extends IMetaTileEntity>> mteClasses;
+ private final IGT_HatchAdder<GT_MetaTileEntity_HIPCompressor> adder;
+
+ @SafeVarargs
+ SpecialHatchElement(IGT_HatchAdder<GT_MetaTileEntity_HIPCompressor> adder,
+ Class<? extends IMetaTileEntity>... mteClasses) {
+ this.mteClasses = Collections.unmodifiableList(Arrays.asList(mteClasses));
+ this.adder = adder;
+ }
+
+ @Override
+ public List<? extends Class<? extends IMetaTileEntity>> mteClasses() {
+ return mteClasses;
+ }
+
+ public IGT_HatchAdder<? super GT_MetaTileEntity_HIPCompressor> adder() {
+ return adder;
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/compressor/GT_MetaTileEntity_HeatSensor.java b/src/main/java/gregtech/common/tileentities/machines/multi/compressor/GT_MetaTileEntity_HeatSensor.java
new file mode 100644
index 0000000000..e4721428fc
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/compressor/GT_MetaTileEntity_HeatSensor.java
@@ -0,0 +1,188 @@
+package gregtech.common.tileentities.machines.multi.compressor;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.StatCollector;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.math.Color;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+import com.gtnewhorizons.modularui.common.widget.textfield.NumericWidget;
+
+import gregtech.api.enums.Textures;
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.IIconContainer;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.gui.modularui.widget.CoverCycleButtonWidget;
+
+public class GT_MetaTileEntity_HeatSensor extends GT_MetaTileEntity_Hatch {
+
+ protected float threshold = 0;
+ protected boolean inverted = false;
+ private boolean isOn = false;
+
+ private static final IIconContainer textureFont = Textures.BlockIcons.OVERLAY_HATCH_BLACKHOLE;
+ private static final IIconContainer textureFont_Glow = Textures.BlockIcons.OVERLAY_HATCH_BLACKHOLE_GLOW;
+
+ public GT_MetaTileEntity_HeatSensor(int aID, String aName, String aNameRegional, int aTier) {
+ super(aID, aName, aNameRegional, aTier, 0, "Reads heat from HIP Unit.");
+ }
+
+ public GT_MetaTileEntity_HeatSensor(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 0, aDescription, aTextures);
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return false;
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public boolean allowGeneralRedstoneOutput() {
+ return true;
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection Side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public void initDefaultModes(NBTTagCompound aNBT) {
+ getBaseMetaTileEntity().setActive(true);
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer, ForgeDirection side,
+ float aX, float aY, float aZ) {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+
+ @Override
+ public String[] getDescription() {
+ return new String[] { "Reads heat of Hot Isostatic Pressurization Unit.",
+ "Right click to open the GUI and change settings." };
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ threshold = aNBT.getFloat("mThreshold");
+ inverted = aNBT.getBoolean("mInverted");
+ super.loadNBTData(aNBT);
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ aNBT.setFloat("mThreshold", threshold);
+ aNBT.setBoolean("mInverted", inverted);
+ super.saveNBTData(aNBT);
+ }
+
+ /**
+ * Updates redstone output strength based on the heat of the HIP unit.
+ */
+ public void updateRedstoneOutput(float heat) {
+ isOn = (heat > threshold) ^ inverted;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (isOn) {
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ aBaseMetaTileEntity.setStrongOutputRedstoneSignal(side, (byte) 15);
+ }
+ } else {
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ aBaseMetaTileEntity.setStrongOutputRedstoneSignal(side, (byte) 0);
+ }
+ }
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_HeatSensor(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(textureFont), TextureFactory.builder()
+ .addIcon(textureFont_Glow)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(textureFont) };
+ }
+
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ final String INVERTED = GT_Utility.trans("INVERTED", "Inverted");
+ final String NORMAL = GT_Utility.trans("NORMAL", "Normal");
+
+ builder.widget(
+ new CoverCycleButtonWidget().setToggle(() -> inverted, (val) -> inverted = val)
+ .setTextureGetter(
+ (state) -> state == 1 ? GT_UITextures.OVERLAY_BUTTON_REDSTONE_ON
+ : GT_UITextures.OVERLAY_BUTTON_REDSTONE_OFF)
+ .addTooltip(0, NORMAL)
+ .addTooltip(1, INVERTED)
+ .setPos(10, 8))
+ .widget(
+ new TextWidget().setStringSupplier(() -> inverted ? INVERTED : NORMAL)
+ .setDefaultColor(COLOR_TEXT_GRAY.get())
+ .setTextAlignment(Alignment.CenterLeft)
+ .setPos(28, 12))
+ .widget(
+ new NumericWidget().setBounds(0, 100)
+ .setGetter(() -> (double) threshold)
+ .setSetter((value) -> threshold = (float) value)
+ .setScrollValues(0.1, 0.01, 1.0)
+ .setMaximumFractionDigits(2)
+ .setTextColor(Color.WHITE.dark(1))
+ .setTextAlignment(Alignment.CenterLeft)
+ .setFocusOnGuiOpen(true)
+ .setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD.withOffset(-1, -1, 2, 2))
+ .setPos(10, 28)
+ .setSize(77, 12))
+ .widget(
+ new TextWidget(StatCollector.translateToLocal("GT5U.gui.text.heat_sensor"))
+ .setDefaultColor(COLOR_TEXT_GRAY.get())
+ .setTextAlignment(Alignment.CenterLeft)
+ .setPos(90, 30));
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/compressor/GT_MetaTileEntity_IndustrialCompressor.java b/src/main/java/gregtech/common/tileentities/machines/multi/compressor/GT_MetaTileEntity_IndustrialCompressor.java
new file mode 100644
index 0000000000..489841c5d4
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/compressor/GT_MetaTileEntity_IndustrialCompressor.java
@@ -0,0 +1,244 @@
+package gregtech.common.tileentities.machines.multi.compressor;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.*;
+import static gregtech.api.enums.GT_HatchElement.*;
+import static gregtech.api.enums.GT_Values.AuthorFourIsTheNumber;
+import static gregtech.api.enums.GT_Values.Ollie;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_COMPRESSOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_COMPRESSOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_COMPRESSOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_COMPRESSOR_GLOW;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase;
+import gregtech.api.multitileentity.multiblock.casing.Glasses;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.blocks.GT_Block_Casings10;
+
+public class GT_MetaTileEntity_IndustrialCompressor
+ extends GT_MetaTileEntity_ExtendedPowerMultiBlockBase<GT_MetaTileEntity_IndustrialCompressor>
+ implements ISurvivalConstructable {
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final IStructureDefinition<GT_MetaTileEntity_IndustrialCompressor> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_IndustrialCompressor>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ // spotless:off
+ transpose(new String[][]{
+ {" "," DDD "," DDDDD "," DDDDD "," DDDDD "," DDD "," "},
+ {" BBB "," BBBBB ","BBBBBBB","BBBBBBB","BBBBBBB"," BBBBB "," BBB "},
+ {" DDD "," B B ","B B","B B","B B"," B B "," DDD "},
+ {" DAD "," B B ","C C","C C","C C"," B B "," DAD "},
+ {" DAD "," B B ","C C","C C","C C"," B B "," DAD "},
+ {" DAD "," B B ","C C","C C","C C"," B B "," DAD "},
+ {" DDD "," B B ","B B","B B","B B"," B B "," DDD "},
+ {" B~B "," BBBBB ","BBBBBBB","BBBBBBB","BBBBBBB"," BBBBB "," BBB "}
+ }))
+ //spotless:on
+ .addElement(
+ 'C',
+ buildHatchAdder(GT_MetaTileEntity_IndustrialCompressor.class).atLeast(InputBus, OutputBus)
+ .casingIndex(((GT_Block_Casings10) GregTech_API.sBlockCasings10).getTextureIndex(5))
+ .dot(1)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_IndustrialCompressor::onCasingAdded,
+ ofBlock(GregTech_API.sBlockCasings10, 5))))
+ .addElement(
+ 'B',
+ buildHatchAdder(GT_MetaTileEntity_IndustrialCompressor.class).atLeast(Maintenance, Energy)
+ .casingIndex(((GT_Block_Casings10) GregTech_API.sBlockCasings10).getTextureIndex(4))
+ .dot(1)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_IndustrialCompressor::onCasingAdded,
+ ofBlock(GregTech_API.sBlockCasings10, 4))))
+ .addElement('A', Glasses.chainAllGlasses())
+ .addElement('D', ofBlock(GregTech_API.sBlockCasings10, 5))
+ .build();
+
+ public GT_MetaTileEntity_IndustrialCompressor(final int aID, final String aName, final String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_IndustrialCompressor(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_IndustrialCompressor> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_IndustrialCompressor(this.mName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ ITexture[] rTexture;
+ if (side == aFacing) {
+ if (aActive) {
+ rTexture = new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings10, 4)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_COMPRESSOR_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_COMPRESSOR_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ } else {
+ rTexture = new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings10, 4)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_COMPRESSOR)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_COMPRESSOR_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ } else {
+ rTexture = new ITexture[] { Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings10, 4)) };
+ }
+ return rTexture;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Compressor")
+ .addInfo("Controller Block for the Large Electric Compressor")
+ .addInfo("100% faster than singleblock machines of the same voltage")
+ .addInfo("Only uses 90% of the EU/t normally required")
+ .addInfo("Gains 2 parallels per voltage tier")
+ .addInfo(AuthorFourIsTheNumber + EnumChatFormatting.RESET + " & " + Ollie)
+ .addSeparator()
+ .beginStructureBlock(7, 5, 7, true)
+ .addController("Front Center")
+ .addCasingInfoMin("Electric Compressor Casing", 95, false)
+ .addCasingInfoMin("Compressor Pipe Casing", 45, false)
+ .addCasingInfoExactly("EV+ Glass", 6, false)
+ .addInputBus("Pipe Casings on Side", 2)
+ .addOutputBus("Pipe Casings on Side", 2)
+ .addEnergyHatch("Any Electric Compressor Casing", 1)
+ .addMaintenanceHatch("Any Electric Compressor Casing", 1)
+ .toolTipFinisher("GregTech");
+ return tt;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 3, 7, 0);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 3, 7, 0, elementBudget, env, false, true);
+ }
+
+ private int mCasingAmount;
+
+ private void onCasingAdded() {
+ mCasingAmount++;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ mCasingAmount = 0;
+ return checkPiece(STRUCTURE_PIECE_MAIN, 3, 7, 0) && mCasingAmount >= 95;
+ }
+
+ @Override
+ protected void setProcessingLogicPower(ProcessingLogic logic) {
+ logic.setAvailableVoltage(GT_Utility.roundUpVoltage(this.getMaxInputVoltage()));
+ logic.setAvailableAmperage(1L);
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+ return new ProcessingLogic().setSpeedBonus(1F / 2F)
+ .setMaxParallelSupplier(this::getMaxParallelRecipes)
+ .setEuModifier(0.9F);
+ }
+
+ public int getMaxParallelRecipes() {
+ return (2 * GT_Utility.getTier(this.getMaxInputVoltage()));
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.compressorRecipes;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsBatchMode() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsInputSeparation() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsSingleRecipeLocking() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/compressor/GT_MetaTileEntity_NeutroniumCompressor.java b/src/main/java/gregtech/common/tileentities/machines/multi/compressor/GT_MetaTileEntity_NeutroniumCompressor.java
new file mode 100644
index 0000000000..90406bcb50
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/compressor/GT_MetaTileEntity_NeutroniumCompressor.java
@@ -0,0 +1,261 @@
+package gregtech.common.tileentities.machines.multi.compressor;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.onElementPass;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_Values.AuthorFourIsTheNumber;
+import static gregtech.api.enums.GT_Values.Ollie;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_COMPRESSOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_COMPRESSOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_COMPRESSOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_MULTI_COMPRESSOR_GLOW;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_StructureUtility.ofFrame;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase;
+import gregtech.api.multitileentity.multiblock.casing.Glasses;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.blocks.GT_Block_Casings10;
+
+public class GT_MetaTileEntity_NeutroniumCompressor
+ extends GT_MetaTileEntity_ExtendedPowerMultiBlockBase<GT_MetaTileEntity_NeutroniumCompressor>
+ implements ISurvivalConstructable {
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final IStructureDefinition<GT_MetaTileEntity_NeutroniumCompressor> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_NeutroniumCompressor>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ // spotless:off
+ transpose(new String[][]{
+ {" CEEEEEC "," CE EC ","CE EC","E E","E E","E E","E E","E E","CE EC"," CE EC "," CEEEEEC "},
+ {" CE EC ","C BBBBB C","E BBBBBBB E"," BBCCCCCBB "," BBCBBBCBB "," BBCCCCCBB "," BBCBBBCBB "," BBCCCCCBB ","E BBBBBBB E","C BBBBB C"," CE EC "},
+ {"CE EC","E BBBBBBB E"," B B "," D D "," B B "," B B "," D D "," B B "," B B ","E BDDDBDB E","CE EC"},
+ {"E E"," BBAAAAABB "," B B "," D D "," B B "," D D "," D D "," B B "," B B "," BBDBBBDBB ","E E"},
+ {"E E"," BBAAAAABB "," B B "," D D "," B B "," B B "," D D "," D D "," D D "," BDDBDDDDB ","E E"},
+ {"E E"," BBAAAAABB "," D D "," D D "," D D "," B B "," B B "," B B "," B B "," BBDBBBBDB ","E E"},
+ {"E E"," BBAAAAABB "," B B "," B B "," D D "," B B "," B B "," D D "," B B "," BBDBBDBBB ","E E"},
+ {"E E"," BBAAAAABB "," B B "," D D "," D D "," B B "," D D "," D D "," D D "," BBBBBDDDB ","E E"},
+ {"CE EC","E BBBBBBB E"," B B "," D D "," B B "," B B "," D D "," B B "," B B ","E BDDBBBB E","CE EC"},
+ {" CE EC ","C BB~BB C","E BBBBBBB E"," BBBBBBBBB "," BBBBBBBBB "," BBBBBBBBB "," BBBBBBBBB "," BBBBBBBBB ","E BBBBBBB E","C BBBBB C"," CE EC "},
+ {" CEEEEEC "," CE EC ","CE EC","E E","E E","E E","E E","E E","CE EC"," CE EC "," CEEEEEC "}
+ }))
+ //spotless:on
+ .addElement('A', Glasses.chainAllGlasses())
+ .addElement(
+ 'B',
+ buildHatchAdder(GT_MetaTileEntity_NeutroniumCompressor.class)
+ .atLeast(InputBus, OutputBus, Maintenance, Energy)
+ .casingIndex(((GT_Block_Casings10) GregTech_API.sBlockCasings10).getTextureIndex(6))
+ .dot(1)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_NeutroniumCompressor::onCasingAdded,
+ ofBlock(GregTech_API.sBlockCasings10, 6))))
+ .addElement('C', ofBlock(GregTech_API.sBlockCasings10, 8))
+ .addElement('D', ofBlock(GregTech_API.sBlockCasings10, 7))
+ .addElement('E', ofFrame(Materials.NaquadahAlloy))
+ .build();
+
+ public GT_MetaTileEntity_NeutroniumCompressor(final int aID, final String aName, final String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_NeutroniumCompressor(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_NeutroniumCompressor> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_NeutroniumCompressor(this.mName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ ITexture[] rTexture;
+ if (side == aFacing) {
+ if (aActive) {
+ rTexture = new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings10, 6)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_COMPRESSOR_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_COMPRESSOR_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ } else {
+ rTexture = new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings10, 6)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_COMPRESSOR)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_MULTI_COMPRESSOR_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ } else {
+ rTexture = new ITexture[] { Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings10, 6)) };
+ }
+ return rTexture;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Neutronium Compressor")
+ .addInfo("Controller Block for the Neutronium Compressor")
+ .addInfo("Has a static 8 parallels")
+ .addInfo("Capable of compressing matter into " + EnumChatFormatting.GOLD + "singularities")
+ .addInfo("More advanced singularities will require even stronger compression...")
+ .addInfo(AuthorFourIsTheNumber + EnumChatFormatting.RESET + " & " + Ollie)
+ .addSeparator()
+ .beginStructureBlock(11, 11, 11, true)
+ .addController("Front Center")
+ .addCasingInfoMin("Neutronium Casing", 220, false)
+ .addCasingInfoExactly("Active Neutronium Casing", 63, false)
+ .addCasingInfoExactly("Any Glass", 25, false)
+ .addCasingInfoExactly("Naquadah Alloy Frame Box", 108, false)
+ .addCasingInfoExactly("Neutronium Stabilization Casing", 67, false)
+ .addInputBus("Any Neutronium Casing", 1)
+ .addOutputBus("Any Neutronium Casing", 1)
+ .addEnergyHatch("Any Neutronium Casing", 1)
+ .addMaintenanceHatch("Any Neutronium Casing", 1)
+ .toolTipFinisher("GregTech");
+ return tt;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 5, 9, 1);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 5, 9, 1, elementBudget, env, false, true);
+ }
+
+ private int mCasingAmount;
+
+ private void onCasingAdded() {
+ mCasingAmount++;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ mCasingAmount = 0;
+
+ return (checkPiece(STRUCTURE_PIECE_MAIN, 5, 9, 1)) && mCasingAmount >= 220;
+ }
+
+ @Override
+ protected void setProcessingLogicPower(ProcessingLogic logic) {
+ logic.setAvailableVoltage(GT_Utility.roundUpVoltage(this.getMaxInputVoltage()));
+ logic.setAvailableAmperage(1L);
+ }
+
+ @Override
+ protected ProcessingLogic createProcessingLogic() {
+
+ return new ProcessingLogic() {
+
+ @NotNull
+ @Override
+ protected CheckRecipeResult validateRecipe(@NotNull GT_Recipe recipe) {
+ if (recipe.mSpecialValue > 0) {
+ return CheckRecipeResultRegistry.NO_BLACK_HOLE;
+ }
+ return super.validateRecipe(recipe);
+ }
+ }.setMaxParallel(8);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.neutroniumCompressorRecipes;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsBatchMode() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsInputSeparation() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsSingleRecipeLocking() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/drone/DroneConnection.java b/src/main/java/gregtech/common/tileentities/machines/multi/drone/DroneConnection.java
new file mode 100644
index 0000000000..92b1c65032
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/drone/DroneConnection.java
@@ -0,0 +1,143 @@
+package gregtech.common.tileentities.machines.multi.drone;
+
+import static gregtech.GT_Mod.gregtechproxy;
+
+import java.util.Optional;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.ChunkCoordinates;
+import net.minecraft.world.World;
+import net.minecraftforge.common.DimensionManager;
+
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_MultiBlockBase;
+import gregtech.api.util.GT_LanguageManager;
+import gregtech.api.util.GT_Util;
+import gregtech.api.util.GT_Utility;
+
+public class DroneConnection {
+
+ String customName;
+ String unlocalizedName;
+ GT_MetaTileEntity_MultiBlockBase machine;
+ ItemStack machineItem;
+ ChunkCoordinates machineCoord;
+ GT_MetaTileEntity_DroneCentre centre;
+ ChunkCoordinates centreCoord;
+ World world;
+
+ public DroneConnection(GT_MetaTileEntity_MultiBlockBase machine, GT_MetaTileEntity_DroneCentre centre) {
+ this.machine = machine;
+ this.machineItem = machine.getStackForm(1);
+ machineCoord = machine.getBaseMetaTileEntity()
+ .getCoords();
+ this.centre = centre;
+ centreCoord = centre.getBaseMetaTileEntity()
+ .getCoords();
+ this.world = centre.getBaseMetaTileEntity()
+ .getWorld();
+ unlocalizedName = machine.mName;
+ customName = Optional.ofNullable(centre.tempNameList.remove(machineCoord.toString()))
+ .orElse(machine.getLocalName());
+ }
+
+ public DroneConnection(NBTTagCompound aNBT) {
+ NBTTagCompound machineTag = aNBT.getCompoundTag("machine");
+ NBTTagCompound centreTag = aNBT.getCompoundTag("centre");
+ if (!gregtechproxy.isClientSide()) {
+ this.world = DimensionManager.getWorld(aNBT.getInteger("worldID"));
+ } else {
+ this.world = Minecraft.getMinecraft().thePlayer.worldObj;
+ }
+ machineItem = ItemStack.loadItemStackFromNBT(aNBT.getCompoundTag("item"));
+ machineCoord = new ChunkCoordinates(
+ machineTag.getInteger("x"),
+ machineTag.getInteger("y"),
+ machineTag.getInteger("z"));
+ this.machine = getLoadedGT_BaseMachineAt(machineCoord, world, true);
+ centreCoord = new ChunkCoordinates(
+ centreTag.getInteger("x"),
+ centreTag.getInteger("y"),
+ centreTag.getInteger("z"));
+ this.centre = (GT_MetaTileEntity_DroneCentre) getLoadedGT_BaseMachineAt(centreCoord, world, true);
+ this.customName = aNBT.getString("name");
+ this.unlocalizedName = aNBT.getString("unlocalizedName");
+ }
+
+ public GT_MetaTileEntity_MultiBlockBase getMachine() {
+ return machine;
+ }
+
+ public boolean reCheckConnection() {
+ if (machine == null) this.machine = getLoadedGT_BaseMachineAt(machineCoord, world, true);
+ if (centre == null)
+ this.centre = (GT_MetaTileEntity_DroneCentre) getLoadedGT_BaseMachineAt(centreCoord, world, true);
+ if (machine != null && centre != null
+ && !centre.getConnectionList()
+ .contains(this))
+ centre.getConnectionList()
+ .add(this);
+ return isValid();
+ }
+
+ public String getCustomName(boolean localized) {
+ if (localized) return GT_LanguageManager.getTranslation("gt.blockmachines." + unlocalizedName + ".name");
+ return customName;
+ }
+
+ public float getDistanceSquared() {
+ return centreCoord.getDistanceSquaredToChunkCoordinates(machineCoord);
+ }
+
+ public void setCustomName(String name) {
+ customName = name;
+ }
+
+ public boolean isMachineShutdown() {
+ return machine != null && machine.shouldDisplayShutDownReason()
+ && !machine.getBaseMetaTileEntity()
+ .isActive()
+ && GT_Utility.isStringValid(
+ machine.getBaseMetaTileEntity()
+ .getLastShutDownReason()
+ .getDisplayString())
+ && machine.getBaseMetaTileEntity()
+ .wasShutdown();
+ }
+
+ public NBTTagCompound transConnectionToNBT() {
+ NBTTagCompound aNBT = new NBTTagCompound();
+ aNBT.setTag("machine", transCoordsToNBT(machineCoord));
+ aNBT.setTag("centre", transCoordsToNBT(centreCoord));
+ aNBT.setTag("item", machineItem.writeToNBT(new NBTTagCompound()));
+ aNBT.setInteger(
+ "worldID",
+ centre.getBaseMetaTileEntity()
+ .getWorld().provider.dimensionId);
+ aNBT.setString("name", getCustomName(false));
+ aNBT.setString("unlocalizedName", unlocalizedName);
+ return aNBT;
+ }
+
+ public GT_MetaTileEntity_MultiBlockBase getLoadedGT_BaseMachineAt(ChunkCoordinates coords, World world,
+ boolean isLoaded) {
+ TileEntity te = GT_Util.getTileEntity(world, coords, isLoaded);
+ if (te == null) return null;
+ return (GT_MetaTileEntity_MultiBlockBase) ((IGregTechTileEntity) te).getMetaTileEntity();
+ }
+
+ private NBTTagCompound transCoordsToNBT(ChunkCoordinates coord) {
+ NBTTagCompound tag = new NBTTagCompound();
+ tag.setInteger("x", coord.posX);
+ tag.setInteger("y", coord.posY);
+ tag.setInteger("z", coord.posZ);
+ return tag;
+ }
+
+ public boolean isValid() {
+ return machine != null && machine.isValid() && centre != null && centre.isValid();
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/drone/GT_MetaTileEntity_DroneCentre.java b/src/main/java/gregtech/common/tileentities/machines/multi/drone/GT_MetaTileEntity_DroneCentre.java
new file mode 100644
index 0000000000..8d2eaa0480
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/drone/GT_MetaTileEntity_DroneCentre.java
@@ -0,0 +1,917 @@
+package gregtech.common.tileentities.machines.multi.drone;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.onElementPass;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_Values.AuthorSilverMoon;
+import static gregtech.api.multitileentity.multiblock.casing.Glasses.chainAllGlasses;
+import static gregtech.api.objects.XSTR.XSTR_INSTANCE;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+
+import java.io.IOException;
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.entity.item.EntityItem;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.network.PacketBuffer;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.ChatComponentTranslation;
+import net.minecraft.util.ChunkCoordinates;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.google.common.collect.HashMultimap;
+import com.gtnewhorizon.structurelib.alignment.IAlignmentLimits;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+import com.gtnewhorizon.structurelib.util.Vec3Impl;
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.drawable.UITexture;
+import com.gtnewhorizons.modularui.api.forge.ItemStackHandler;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.math.Color;
+import com.gtnewhorizons.modularui.api.math.MainAxisAlignment;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.DynamicPositionedRow;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.Scrollable;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+import com.gtnewhorizons.modularui.common.widget.textfield.TextFieldWidget;
+
+import appeng.api.util.DimensionalCoord;
+import appeng.api.util.WorldCoord;
+import appeng.client.render.BlockPosHighlighter;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.Textures;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.IIconContainer;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_MultiBlockBase;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.SimpleCheckRecipeResult;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Log;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.shutdown.ShutDownReason;
+import gregtech.api.util.shutdown.ShutDownReasonRegistry;
+import gregtech.common.gui.modularui.widget.ShutDownReasonSyncer;
+import gregtech.common.items.GT_TierDrone;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public class GT_MetaTileEntity_DroneCentre extends
+ GT_MetaTileEntity_ExtendedPowerMultiBlockBase<GT_MetaTileEntity_DroneCentre> implements ISurvivalConstructable {
+
+ private static final IIconContainer ACTIVE = new Textures.BlockIcons.CustomIcon("iconsets/DRONE_CENTRE_ACTIVE");
+ private static final IIconContainer FACE = new Textures.BlockIcons.CustomIcon("iconsets/DRONE_CENTRE_FACE");
+ private static final IIconContainer INACTIVE = new Textures.BlockIcons.CustomIcon("iconsets/DRONE_CENTRE_INACTIVE");
+ private final int MACHINE_LIST_WINDOW_ID = 10;
+ private final int CUSTOM_NAME_WINDOW_ID = 11;
+ private static final int CASINGS_MIN = 85;
+ private int mCasingAmount = 0;
+ private Vec3Impl centreCoord;
+ private int droneLevel = 0;
+ private int buttonID;
+ private String searchFilter = "";
+ private boolean useRender = true;
+ private boolean showLocalizedName = false;
+ private String sort = "distance";
+ private List<DroneConnection> connectionList = new ArrayList<>();
+ public HashMap<String, String> tempNameList = new HashMap<>();
+ // Save centre by dimID
+ private static final HashMultimap<Integer, GT_MetaTileEntity_DroneCentre> droneMap = HashMultimap.create();
+ // spotless off
+ private static final IStructureDefinition<GT_MetaTileEntity_DroneCentre> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_DroneCentre>builder()
+ .addShape(
+ "main",
+ transpose(
+ new String[][] { { " ", " ", " ", " ", "CCCCC", "CCCCC", "CCCCC", "CCCCC", "CCCCC" },
+ { "CC~CC", "C C", "C C", "C C", "CAAAC", "CCCCC", "CAAAC", "C C", "CCCCC" },
+ { "CCCCC", "CBBBC", "CBDBC", "CBBBC", "CCCCC", "CCCCC", "CCCCC", "CCCCC", "CCCCC" },
+ { "C C", " ", " ", " ", " ", " ", " ", " ", "C C" } }))
+ .addElement(
+ 'C',
+ buildHatchAdder(GT_MetaTileEntity_DroneCentre.class).atLeast(InputBus)
+ .casingIndex(59)
+ .dot(1)
+ .buildAndChain(
+ onElementPass(
+ GT_MetaTileEntity_DroneCentre::onCasingAdded,
+ ofBlock(GregTech_API.sBlockCasings4, 2))))
+ .addElement('A', chainAllGlasses())
+ .addElement('B', ofBlock(GregTech_API.sBlockCasings1, 11))
+ .addElement('D', ofBlock(GregTech_API.sBlockCasings4, 0))
+ .build();
+
+ // spotless on
+ public GT_MetaTileEntity_DroneCentre(String name) {
+ super(name);
+ }
+
+ public GT_MetaTileEntity_DroneCentre(int ID, String Name, String NameRegional) {
+ super(ID, Name, NameRegional);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_DroneCentre(super.mName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side == aFacing) {
+ if (getBaseMetaTileEntity().isActive()) {
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(59), TextureFactory.builder()
+ .addIcon(ACTIVE)
+ .extFacing()
+ .build() };
+ } else {
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(59), TextureFactory.builder()
+ .addIcon(INACTIVE)
+ .extFacing()
+ .build() };
+ }
+ } else if (side == aFacing.getOpposite()) {
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(59), TextureFactory.builder()
+ .addIcon(FACE)
+ .extFacing()
+ .build() };
+ }
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(59) };
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_DroneCentre> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Drone Centre")
+ .addInfo("Drone Center Controller")
+ .addInfo(EnumChatFormatting.AQUA + "Drone #10032, cleared for takeoff!")
+ .addInfo("Monitors multiblock machines in range.")
+ .addInfo("Replace maintenance hatch on other multi with drone downlink module.")
+ .addInfo("Provides maintenance, power control, monitoring and etc.")
+ .addInfo("Range is determined by drone tier: T1-128, T2-512, T3-4096")
+ .addInfo("Place drones in input bus; only one needed to operate.")
+ .addInfo("Automatically upgrade based on the drone level in the input bus.")
+ .addInfo("There is a chance per second that the drone will crash.")
+ .addInfo("Chance is determined by drone tier: T1-1/28800, T2-1/172800, T3-0")
+ .addInfo("If machine is too far, remote control would not available")
+ .addInfo(AuthorSilverMoon)
+ .addSeparator()
+ .beginStructureBlock(5, 4, 9, false)
+ .addController("Front center")
+ .addCasingInfoRange("Stable Titanium Machine Casing", CASINGS_MIN, 91, false)
+ .addCasingInfoExactly("Heat Proof Machine Casing", 8, false)
+ .addCasingInfoExactly("Robust Tungstensteel Machine Casing", 1, false)
+ .addCasingInfoExactly("Any tiered glass", 6, false)
+ .addInputBus("Any Titanium Casing", 1)
+ .addStructureInfo("No maintenance hatch needed")
+ .addSeparator()
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece("main", stackSize, hintsOnly, 2, 1, 0);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stack, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece("main", stack, 2, 1, 0, elementBudget, env, false, true);
+ }
+
+ @Override
+ protected IAlignmentLimits getInitialAlignmentLimits() {
+ // I don't think a drone can take off HORIZONTALLY!
+ return (d, r, f) -> (d.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0 && r.isNotRotated()
+ && !f.isVerticallyFliped();
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ private void onCasingAdded() {
+ mCasingAmount++;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ mCasingAmount = 0;
+ return checkPiece("main", 2, 1, 0) && mCasingAmount >= CASINGS_MIN;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public final void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ super.onScrewdriverRightClick(side, aPlayer, aX, aY, aZ);
+ useRender = !useRender;
+ aPlayer.addChatComponentMessage(
+ new ChatComponentTranslation(
+ "GT5U.machines.dronecentre." + (useRender ? "enableRender" : "disableRender")));
+ if (useRender) {
+ createRenderBlock();
+ } else {
+ destroyRenderBlock();
+ }
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public void stopMachine(@NotNull ShutDownReason reason) {
+ destroyRenderBlock();
+ super.stopMachine(reason);
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aBaseMetaTileEntity.isServerSide()) {
+ if (aTick % 20 == 0) {
+ if (switch (droneLevel) {
+ case 1 -> getBaseMetaTileEntity().getRandomNumber(28800);
+ case 2 -> getBaseMetaTileEntity().getRandomNumber(172800);
+ default -> 1;
+ } == 0) {
+ droneLevel = 0;
+ startRecipeProcessing();
+ if (!tryConsumeDrone()) stopMachine(ShutDownReasonRegistry.outOfStuff("Any Drone", 1));
+ endRecipeProcessing();
+ }
+ }
+ // Clean invalid connections every 4 seconds
+ if (aTick % 80 == 0) connectionList.removeIf(v -> !v.isValid());
+ }
+ if (mMaxProgresstime > 0 && mMaxProgresstime - mProgresstime == 1) destroyRenderBlock();
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ droneLevel = aNBT.getInteger("drone");
+ useRender = aNBT.getBoolean("useRender");
+ sort = aNBT.getString("sort");
+ NBTTagCompound nameList = aNBT.getCompoundTag("conList");
+ for (String s : nameList.func_150296_c()) {
+ tempNameList.put(s, nameList.getString(s));
+ }
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setInteger("drone", droneLevel);
+ aNBT.setBoolean("useRender", useRender);
+ aNBT.setString("sort", sort);
+ NBTTagCompound conList = new NBTTagCompound();
+ for (DroneConnection con : connectionList) {
+ if (!con.customName.equals(con.machine.getLocalName()))
+ conList.setString(con.machineCoord.toString(), con.customName);
+ }
+ aNBT.setTag("conList", conList);
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ tag.setInteger("connectionCount", connectionList.size());
+ if (droneLevel != 0) tag.setInteger("droneLevel", droneLevel);
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ NBTTagCompound tag = accessor.getNBTData();
+ currenttip.add(
+ EnumChatFormatting.AQUA + StatCollector.translateToLocal("GT5U.waila.drone_downlink.droneLevel")
+ + tag.getInteger("droneLevel"));
+ currenttip.add(
+ StatCollector.translateToLocal("GT5U.waila.drone_downlink.connectionCount")
+ + tag.getInteger("connectionCount"));
+ super.getWailaBody(itemStack, currenttip, accessor, config);
+ }
+
+ @Override
+ @NotNull
+ public CheckRecipeResult checkProcessing() {
+ if (droneLevel == 0) {
+ if (!tryConsumeDrone()) return SimpleCheckRecipeResult.ofFailure("drone_noDrone");
+ }
+ if (droneLevel == 1 || droneLevel == 2) tryUpdateDrone();
+ mMaxProgresstime = 200 * droneLevel;
+ createRenderBlock();
+ return SimpleCheckRecipeResult.ofSuccess("drone_operating");
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ super.onFirstTick(aBaseMetaTileEntity);
+ if (aBaseMetaTileEntity.isServerSide()) {
+ if (droneMap.containsValue(this)) return;
+ centreCoord = new Vec3Impl(
+ getBaseMetaTileEntity().getXCoord(),
+ getBaseMetaTileEntity().getYCoord(),
+ getBaseMetaTileEntity().getZCoord());
+ droneMap.put(getBaseMetaTileEntity().getWorld().provider.dimensionId, this);
+ }
+ }
+
+ @Override
+ public void onBlockDestroyed() {
+ destroyRenderBlock();
+ connectionList.clear();
+ if (droneLevel != 0) spawnDroneItem();
+ super.onBlockDestroyed();
+ }
+
+ private void spawnDroneItem() {
+ ItemStack insideDrone = new ItemStack(switch (droneLevel) {
+ case 1:
+ yield ItemList.TierdDrone0.getItem();
+ case 2:
+ yield ItemList.TierdDrone1.getItem();
+ case 3:
+ yield ItemList.TierdDrone2.getItem();
+ default:
+ yield null;
+ }, 1);
+ final EntityItem tItemEntity = new EntityItem(
+ getBaseMetaTileEntity().getWorld(),
+ getBaseMetaTileEntity().getXCoord() + XSTR_INSTANCE.nextFloat() * 0.8F + 0.1F,
+ getBaseMetaTileEntity().getYCoord() + XSTR_INSTANCE.nextFloat() * 0.8F + 0.1F,
+ getBaseMetaTileEntity().getZCoord() + XSTR_INSTANCE.nextFloat() * 0.8F + 0.1F,
+ insideDrone);
+ tItemEntity.motionX = (XSTR_INSTANCE.nextGaussian() * 0.05D);
+ tItemEntity.motionY = (XSTR_INSTANCE.nextGaussian() * 0.25D);
+ tItemEntity.motionZ = (XSTR_INSTANCE.nextGaussian() * 0.05D);
+ getBaseMetaTileEntity().getWorld()
+ .spawnEntityInWorld(tItemEntity);
+ }
+
+ @Override
+ public void onRemoval() {
+ droneMap.remove(getBaseMetaTileEntity().getWorld().provider.dimensionId, this);
+ }
+
+ @Override
+ public void onUnload() {
+ droneMap.remove(getBaseMetaTileEntity().getWorld().provider.dimensionId, this);
+ }
+
+ public List<DroneConnection> getConnectionList() {
+ return connectionList;
+ }
+
+ public int getRange() {
+ return switch (droneLevel) {
+ case 1 -> 128;
+ case 2 -> 512;
+ case 3 -> 4096;
+ default -> 0;
+ };
+ }
+
+ public Vec3Impl getCoords() {
+ return centreCoord;
+ }
+
+ private boolean tryConsumeDrone() {
+ List<ItemStack> inputs = getStoredInputs();
+ if (inputs.isEmpty()) return false;
+ for (ItemStack item : inputs) {
+ if (item != null && item.getItem() instanceof GT_TierDrone drone) {
+ this.droneLevel = drone.getLevel();
+ item.stackSize--;
+ updateSlots();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void tryUpdateDrone() {
+ List<ItemStack> inputs = getStoredInputs();
+ if (inputs.isEmpty()) return;
+ for (ItemStack item : inputs) {
+ if (item != null && item.getItem() instanceof GT_TierDrone drone) {
+ if (drone.getLevel() <= this.droneLevel) continue;
+ this.droneLevel = drone.getLevel();
+ item.stackSize--;
+ updateSlots();
+ return;
+ }
+ }
+ }
+
+ private void createRenderBlock() {
+ if (!useRender) return;
+ int x = getBaseMetaTileEntity().getXCoord();
+ int y = getBaseMetaTileEntity().getYCoord();
+ int z = getBaseMetaTileEntity().getZCoord();
+
+ double xOffset = 2 * getExtendedFacing().getRelativeBackInWorld().offsetX;
+ double zOffset = 2 * getExtendedFacing().getRelativeBackInWorld().offsetZ;
+ double yOffset = 2 * getExtendedFacing().getRelativeBackInWorld().offsetY;
+
+ this.getBaseMetaTileEntity()
+ .getWorld()
+ .setBlock((int) (x + xOffset), (int) (y + yOffset), (int) (z + zOffset), Blocks.air);
+ this.getBaseMetaTileEntity()
+ .getWorld()
+ .setBlock((int) (x + xOffset), (int) (y + yOffset), (int) (z + zOffset), GregTech_API.sDroneRender);
+ }
+
+ private void destroyRenderBlock() {
+ int x = getBaseMetaTileEntity().getXCoord();
+ int y = getBaseMetaTileEntity().getYCoord();
+ int z = getBaseMetaTileEntity().getZCoord();
+
+ double xOffset = 2 * getExtendedFacing().getRelativeBackInWorld().offsetX;
+ double zOffset = 2 * getExtendedFacing().getRelativeBackInWorld().offsetZ;
+ double yOffset = 2 * getExtendedFacing().getRelativeBackInWorld().offsetY;
+
+ this.getBaseMetaTileEntity()
+ .getWorld()
+ .setBlock((int) (x + xOffset), (int) (y + yOffset), (int) (z + zOffset), Blocks.air);
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ super.addUIWidgets(builder, buildContext);
+ buildContext.addSyncedWindow(MACHINE_LIST_WINDOW_ID, this::createMachineListWindow);
+ buildContext.addSyncedWindow(CUSTOM_NAME_WINDOW_ID, this::createCustomNameWindow);
+ builder.widget(// Machine List
+ new ButtonWidget().setOnClick(
+ (clickData, widget) -> {
+ if (!widget.isClient()) widget.getContext()
+ .openSyncedWindow(MACHINE_LIST_WINDOW_ID);
+ })
+ .setSize(16, 16)
+ .setBackground(() -> {
+ List<UITexture> UI = new ArrayList<>();
+ UI.add(GT_UITextures.BUTTON_STANDARD);
+ UI.add(GT_UITextures.OVERLAY_BUTTON_WHITELIST);
+ return UI.toArray(new IDrawable[0]);
+ })
+ .addTooltip(StatCollector.translateToLocal("GT5U.gui.button.drone_open_list"))
+ .setPos(94, 91)
+ .setEnabled(var -> getBaseMetaTileEntity().isActive()))
+ .widget(// Turn on ALL machines
+ new ButtonWidget().setOnClick((clickData, widget) -> {
+ if (!widget.isClient()) {
+ if (!getBaseMetaTileEntity().isActive()) {
+ widget.getContext()
+ .getPlayer()
+ .addChatComponentMessage(
+ new ChatComponentTranslation("GT5U.machines.dronecentre.shutdown"));
+ return;
+ }
+ for (DroneConnection mte : connectionList) {
+ mte.machine.getBaseMetaTileEntity()
+ .enableWorking();
+ }
+ widget.getContext()
+ .getPlayer()
+ .addChatComponentMessage(new ChatComponentTranslation("GT5U.machines.dronecentre.turnon"));
+ widget.getContext()
+ .getPlayer()
+ .closeScreen();
+ }
+ })
+ .setSize(16, 16)
+ .setBackground(() -> {
+ List<UITexture> UI = new ArrayList<>();
+ UI.add(GT_UITextures.BUTTON_STANDARD);
+ UI.add(GT_UITextures.OVERLAY_BUTTON_POWER_SWITCH_ON);
+ return UI.toArray(new IDrawable[0]);
+ })
+ .addTooltip(StatCollector.translateToLocal("GT5U.gui.button.drone_poweron_all"))
+ .setPos(146, 91)
+ .setEnabled(var -> getBaseMetaTileEntity().isActive()))
+ .widget(// Turn off ALL machines
+ new ButtonWidget().setOnClick((clickData, widget) -> {
+ if (!widget.isClient()) {
+ if (!getBaseMetaTileEntity().isActive()) {
+ widget.getContext()
+ .getPlayer()
+ .addChatComponentMessage(
+ new ChatComponentTranslation("GT5U.machines.dronecentre.shutdown"));
+ return;
+ }
+ for (DroneConnection mte : connectionList) {
+ mte.machine.getBaseMetaTileEntity()
+ .disableWorking();
+ }
+ widget.getContext()
+ .getPlayer()
+ .addChatComponentMessage(new ChatComponentTranslation("GT5U.machines.dronecentre.turnoff"));
+ widget.getContext()
+ .getPlayer()
+ .closeScreen();
+ }
+ })
+ .setSize(16, 16)
+ .setBackground(() -> {
+ List<UITexture> UI = new ArrayList<>();
+ UI.add(GT_UITextures.BUTTON_STANDARD);
+ UI.add(GT_UITextures.OVERLAY_BUTTON_POWER_SWITCH_OFF);
+ return UI.toArray(new IDrawable[0]);
+ })
+ .addTooltip(StatCollector.translateToLocal("GT5U.gui.button.drone_poweroff_all"))
+ .setPos(120, 91)
+ .setEnabled(var -> getBaseMetaTileEntity().isActive()))
+ .widget(new FakeSyncWidget.ListSyncer<>(() -> connectionList, var1 -> {
+ connectionList.clear();
+ connectionList.addAll(var1);
+ }, (buffer, j) -> {
+ try {
+ buffer.writeNBTTagCompoundToBuffer(j.transConnectionToNBT());
+ } catch (IOException e) {
+ GT_Log.err.println(e.getCause());
+ }
+ }, buffer -> {
+ try {
+ return new DroneConnection(buffer.readNBTTagCompoundFromBuffer());
+ } catch (IOException e) {
+ GT_Log.err.println(e.getCause());
+ }
+ return null;
+ }));
+ }
+
+ protected ModularWindow createMachineListWindow(final EntityPlayer player) {
+ int heightCoff = getBaseMetaTileEntity().isServerSide() ? 0
+ : Minecraft.getMinecraft().currentScreen.height - 40;
+ ModularWindow.Builder builder = ModularWindow.builder(260, heightCoff);
+ builder.setBackground(GT_UITextures.BACKGROUND_SINGLEBLOCK_DEFAULT);
+ builder.setGuiTint(getGUIColorization());
+ builder.widget(
+ ButtonWidget.closeWindowButton(true)
+ .setPos(245, 3));
+ builder.widget(
+ new TextWidget(EnumChatFormatting.BOLD + StatCollector.translateToLocal("GT5U.gui.text.drone_title"))
+ .setScale(2)
+ .setTextAlignment(Alignment.Center)
+ .setPos(0, 10)
+ .setSize(260, 8));
+ // SearchBar
+ builder.widget(new TextFieldWidget() {
+
+ @Override
+ public void onRemoveFocus() {
+ super.onRemoveFocus();
+ syncToServer(2, buffer -> {});
+ }
+
+ // Refresh
+ @Override
+ public void readOnServer(int id, PacketBuffer buf) {
+ switch (id) {
+ case 1 -> super.readOnServer(id, buf);
+ case 2 -> {
+ getContext().closeWindow(MACHINE_LIST_WINDOW_ID);
+ getContext().openSyncedWindow(MACHINE_LIST_WINDOW_ID);
+ }
+ }
+ }
+ }.setGetter(() -> searchFilter)
+ .setSetter(var -> searchFilter = var)
+ .setTextAlignment(Alignment.CenterLeft)
+ .setTextColor(Color.WHITE.dark(1))
+ .setFocusOnGuiOpen(false)
+ .setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD_LIGHT_GRAY.withOffset(-1, -1, 2, 2))
+ .addTooltip(StatCollector.translateToLocal("GT5U.gui.text.drone_search"))
+ .setPos(50, 30)
+ .setSize(200, 16))
+ // Sort button
+ .widget(new ButtonWidget() {
+
+ @Override
+ public ClickResult onClick(int buttonId, boolean doubleClick) {
+ ClickResult result = super.onClick(buttonId, doubleClick);
+ syncToServer(2, buffer -> {});
+ return result;
+ }
+
+ @Override
+ public void readOnServer(int id, PacketBuffer buf) {
+ switch (id) {
+ case 1 -> super.readOnServer(id, buf);
+ case 2 -> {
+ getContext().closeWindow(MACHINE_LIST_WINDOW_ID);
+ getContext().openSyncedWindow(MACHINE_LIST_WINDOW_ID);
+ }
+ }
+ }
+ }.setOnClick((clickData, widget) -> {
+ switch (sort) {
+ case "name" -> sort = "distance";
+ case "distance" -> sort = "error";
+ case "error" -> sort = "name";
+ }
+ })
+ .addTooltip(StatCollector.translateToLocal("GT5U.gui.button.drone_" + sort))
+ .setBackground(
+ () -> new IDrawable[] { GT_UITextures.BUTTON_STANDARD, GT_UITextures.OVERLAY_BUTTON_SORTING_MODE })
+ .setPos(10, 30)
+ .setSize(16, 16))
+ .widget(new FakeSyncWidget.StringSyncer(() -> sort, var1 -> sort = var1))
+ // Localized Button
+ .widget(new ButtonWidget() {
+
+ @Override
+ public ClickResult onClick(int buttonId, boolean doubleClick) {
+ ClickResult result = super.onClick(buttonId, doubleClick);
+ syncToServer(2, buffer -> {});
+ return result;
+ }
+
+ @Override
+ public void readOnServer(int id, PacketBuffer buf) {
+ switch (id) {
+ case 1 -> super.readOnServer(id, buf);
+ case 2 -> {
+ getContext().closeWindow(MACHINE_LIST_WINDOW_ID);
+ getContext().openSyncedWindow(MACHINE_LIST_WINDOW_ID);
+ }
+ }
+ }
+ }.setOnClick((clickData, widget) -> showLocalizedName = !showLocalizedName)
+ .addTooltip(StatCollector.translateToLocal("GT5U.gui.button.drone_showLocalName"))
+ .setBackground(
+ () -> new IDrawable[] {
+ showLocalizedName ? GT_UITextures.BUTTON_STANDARD_PRESSED : GT_UITextures.BUTTON_STANDARD,
+ GT_UITextures.OVERLAY_BUTTON_CYCLIC })
+ .setPos(30, 30)
+ .setSize(16, 16));
+ // Sort first
+ switch (sort) {
+ case "name" -> connectionList = connectionList.stream()
+ .sorted(
+ (o1, o2) -> Collator.getInstance(Locale.UK)
+ .compare(o1.getCustomName(false), o2.getCustomName(false)))
+ .collect(Collectors.toList());
+ case "distance" -> connectionList = connectionList.stream()
+ .sorted(Comparator.comparing(DroneConnection::getDistanceSquared))
+ .collect(Collectors.toList());
+ case "error" -> connectionList = connectionList.stream()
+ .sorted(
+ Comparator.comparing(DroneConnection::isMachineShutdown)
+ .reversed()
+ .thenComparing(DroneConnection::getDistanceSquared))
+ .collect(Collectors.toList());
+ }
+
+ Scrollable MachineContainer = new Scrollable().setVerticalScroll();
+ int posY = 0;
+ for (int i = 0; i < connectionList.size(); i++) {
+ DroneConnection connection = connectionList.get(i);
+ if (!connection.customName.toLowerCase()
+ .contains(searchFilter.toLowerCase())) continue;
+ ItemStackHandler drawitem = new ItemStackHandler(1);
+ drawitem.setStackInSlot(0, connection.machineItem);
+ DynamicPositionedRow row = new DynamicPositionedRow().setSynced(false);
+ GT_MetaTileEntity_MultiBlockBase coreMachine = connection.machine;
+ int finalI = i;
+ row.widget(
+ SlotWidget.phantom(drawitem, 0)
+ .disableInteraction()
+ .setPos(0, 0))
+ .widget(new ButtonWidget().setOnClick((clickData, widget) -> {
+ buttonID = finalI;
+ if (!widget.isClient()) widget.getContext()
+ .openSyncedWindow(CUSTOM_NAME_WINDOW_ID);
+ })
+ .addTooltip(StatCollector.translateToLocal("GT5U.gui.button.drone_setname"))
+ .setBackground(
+ () -> new IDrawable[] { GT_UITextures.BUTTON_STANDARD, GT_UITextures.OVERLAY_BUTTON_PRINT })
+ .setSize(16, 16));
+ // Client can't handle unloaded machines
+ row.widget(
+ new ButtonWidget().setOnClick(
+ (clickData, widget) -> Optional.ofNullable(coreMachine)
+ .ifPresent(machine -> {
+ if (!getBaseMetaTileEntity().isActive()) {
+ player.addChatComponentMessage(
+ new ChatComponentTranslation("GT5U.machines.dronecentre.shutdown"));
+ return;
+ }
+ if (machine.isAllowedToWork()) {
+ machine.disableWorking();
+ } else {
+ machine.enableWorking();
+ }
+ }))
+ .setPlayClickSoundResource(
+ () -> Optional.ofNullable(coreMachine)
+ .filter(GT_MetaTileEntity_MultiBlockBase::isAllowedToWork)
+ .map(var -> SoundResource.GUI_BUTTON_UP.resourceLocation)
+ .orElse(SoundResource.GUI_BUTTON_DOWN.resourceLocation))
+ .setBackground(
+ () -> Optional.ofNullable(coreMachine)
+ .map(
+ machine -> machine.isAllowedToWork()
+ ? new IDrawable[] { GT_UITextures.BUTTON_STANDARD_PRESSED,
+ GT_UITextures.OVERLAY_BUTTON_POWER_SWITCH_ON }
+ : new IDrawable[] { GT_UITextures.BUTTON_STANDARD,
+ GT_UITextures.OVERLAY_BUTTON_POWER_SWITCH_OFF })
+ .orElse(new IDrawable[] { GT_UITextures.OVERLAY_BUTTON_CROSS }))
+ .attachSyncer(
+ new FakeSyncWidget.BooleanSyncer(
+ () -> Optional.ofNullable(coreMachine)
+ .map(GT_MetaTileEntity_MultiBlockBase::isAllowedToWork)
+ .orElse(false),
+ var -> Optional.ofNullable(coreMachine)
+ .ifPresent(machine -> {
+ if (var) machine.enableWorking();
+ else machine.disableWorking();
+ })),
+ builder)
+ .addTooltip(
+ coreMachine != null ? StatCollector.translateToLocal("GT5U.gui.button.power_switch")
+ : StatCollector.translateToLocal("GT5U.gui.button.drone_outofrange"))
+ .setSize(16, 16))
+ .widget(new ButtonWidget().setOnClick((clickData, widget) -> {
+ if (widget.isClient()) {
+ highlightMachine(player, connection.machineCoord);
+ player.closeScreen();
+ }
+ })
+ .addTooltip(StatCollector.translateToLocal("GT5U.gui.button.drone_highlight"))
+ .setBackground(
+ new IDrawable[] { GT_UITextures.BUTTON_STANDARD, GT_UITextures.OVERLAY_BUTTON_INVERT_REDSTONE })
+ .setSize(16, 16));
+ // Show the reason why the machine shutdown
+ row.widget(
+ new DrawableWidget().dynamicTooltip(
+ () -> Collections.singletonList(
+ Optional.ofNullable(coreMachine)
+ .map(
+ machine -> machine.getBaseMetaTileEntity()
+ .getLastShutDownReason()
+ .getDisplayString())
+ .orElse("")))
+ .setBackground(GT_UITextures.PICTURE_STALLED_ELECTRICITY)
+ .setSize(16, 16)
+ .setEnabled(
+ var -> coreMachine != null && coreMachine.shouldDisplayShutDownReason()
+ && !coreMachine.getBaseMetaTileEntity()
+ .isActive()
+ && GT_Utility.isStringValid(
+ coreMachine.getBaseMetaTileEntity()
+ .getLastShutDownReason()
+ .getDisplayString())
+ && coreMachine.getBaseMetaTileEntity()
+ .wasShutdown())
+ .attachSyncer(
+ new ShutDownReasonSyncer(
+ () -> Optional.ofNullable(coreMachine)
+ .map(
+ var -> coreMachine.getBaseMetaTileEntity()
+ .getLastShutDownReason())
+ .orElse(ShutDownReasonRegistry.NONE),
+ reason -> Optional.ofNullable(coreMachine)
+ .ifPresent(
+ machine -> coreMachine.getBaseMetaTileEntity()
+ .setShutDownReason(reason))),
+ builder)
+ .attachSyncer(
+ new FakeSyncWidget.BooleanSyncer(
+ () -> Optional.ofNullable(coreMachine)
+ .map(
+ var -> coreMachine.getBaseMetaTileEntity()
+ .wasShutdown())
+ .orElse(false),
+ wasShutDown -> Optional.ofNullable(coreMachine)
+ .ifPresent(
+ machine -> coreMachine.getBaseMetaTileEntity()
+ .setShutdownStatus(wasShutDown))),
+ builder));
+ row.widget(
+ new TextWidget(
+ connectionList.get(i)
+ .getCustomName(showLocalizedName)).setTextAlignment(Alignment.CenterLeft)
+ .setPos(0, 4));
+ MachineContainer.widget(
+ row.setAlignment(MainAxisAlignment.SPACE_BETWEEN)
+ .setSpace(4)
+ .setPos(0, posY));
+ posY += 20;
+ }
+ return builder.widget(
+ MachineContainer.setPos(10, 50)
+ .setSize(240, heightCoff - 60))
+ .setDraggable(false)
+ .build();
+ }
+
+ protected ModularWindow createCustomNameWindow(final EntityPlayer player) {
+ ModularWindow.Builder builder = ModularWindow.builder(150, 40);
+ builder.setBackground(GT_UITextures.BACKGROUND_SINGLEBLOCK_DEFAULT);
+ builder.setGuiTint(getGUIColorization());
+ return builder.widget(
+ ButtonWidget.closeWindowButton(true)
+ .setPos(135, 3))
+ .widget(
+ new TextWidget(StatCollector.translateToLocal("GT5U.gui.text.drone_custom_name"))
+ .setTextAlignment(Alignment.Center)
+ .setPos(0, 5)
+ .setSize(150, 8))
+ .widget(new TextFieldWidget() {
+
+ @Override
+ public void onDestroy() {
+ if (isClient()) return;
+ getContext().closeWindow(MACHINE_LIST_WINDOW_ID);
+ getContext().openSyncedWindow(MACHINE_LIST_WINDOW_ID);
+ }
+ }.setGetter(
+ () -> connectionList.get(buttonID)
+ .getCustomName(false))
+ .setSetter(
+ var -> connectionList.get(buttonID)
+ .setCustomName(var))
+ .setTextAlignment(Alignment.CenterLeft)
+ .setTextColor(Color.WHITE.dark(1))
+ .setFocusOnGuiOpen(true)
+ .setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD_LIGHT_GRAY.withOffset(-1, -1, 2, 2))
+ .setPos(10, 16)
+ .setSize(130, 16))
+ .build();
+ }
+
+ // Just like HIGHLIGHT_INTERFACE (and exactly from it)
+ private void highlightMachine(EntityPlayer player, ChunkCoordinates machineCoord) {
+ DimensionalCoord blockPos = new DimensionalCoord(
+ machineCoord.posX,
+ machineCoord.posY,
+ machineCoord.posZ,
+ player.dimension);
+ WorldCoord blockPos2 = new WorldCoord((int) player.posX, (int) player.posY, (int) player.posZ);
+ BlockPosHighlighter.highlightBlock(
+ blockPos,
+ System.currentTimeMillis() + 500 * WorldCoord.getTaxicabDistance(blockPos, blockPos2));
+ }
+
+ public static HashMultimap<Integer, GT_MetaTileEntity_DroneCentre> getCentreMap() {
+ return droneMap;
+ }
+
+ @Override
+ public boolean getDefaultHasMaintenanceChecks() {
+ return false;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/drone/GT_MetaTileEntity_Hatch_DroneDownLink.java b/src/main/java/gregtech/common/tileentities/machines/multi/drone/GT_MetaTileEntity_Hatch_DroneDownLink.java
new file mode 100644
index 0000000000..33dad93d52
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/drone/GT_MetaTileEntity_Hatch_DroneDownLink.java
@@ -0,0 +1,329 @@
+package gregtech.common.tileentities.machines.multi.drone;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import net.minecraft.block.Block;
+import net.minecraft.client.renderer.texture.IIconRegister;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.ChatComponentTranslation;
+import net.minecraft.util.ChunkCoordinates;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.FakePlayer;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizon.structurelib.util.Vec3Impl;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.math.Color;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+import com.gtnewhorizons.modularui.common.widget.textfield.TextFieldWidget;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Textures;
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.IIconContainer;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.interfaces.tileentity.IMachineBlockUpdateable;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Maintenance;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_MultiBlockBase;
+import gregtech.api.render.TextureFactory;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public class GT_MetaTileEntity_Hatch_DroneDownLink extends GT_MetaTileEntity_Hatch_Maintenance {
+
+ private Vec3Impl downlinkCoord;
+ private DroneConnection connection;
+ // This has to be existed for doing random damage.
+ private GT_MetaTileEntity_MultiBlockBase machine;
+ private static final IIconContainer moduleActive = new Textures.BlockIcons.CustomIcon(
+ "iconsets/OVERLAY_DRONE_MODULE_ACTIVE");
+
+ public GT_MetaTileEntity_Hatch_DroneDownLink(int aID, String aName, String aNameRegional, int aTier) {
+ super(aID, aName, aNameRegional, aTier);
+ }
+
+ public GT_MetaTileEntity_Hatch_DroneDownLink(String aName, int aTier, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures, false);
+ }
+
+ @Override
+ public String[] getDescription() {
+ return new String[] { "Built-in powerful navigation beacon!" };
+ }
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public void registerIcons(IIconRegister aBlockIconRegister) {
+ super.registerIcons(aBlockIconRegister);
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(moduleActive) };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(moduleActive) };
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Hatch_DroneDownLink(
+ this.mName,
+ this.mTier,
+ this.mDescriptionArray,
+ this.mTextures);
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ downlinkCoord = new Vec3Impl(
+ getBaseMetaTileEntity().getXCoord(),
+ getBaseMetaTileEntity().getYCoord(),
+ getBaseMetaTileEntity().getZCoord());
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aBaseMetaTileEntity.isServerSide()) {
+ if (hasConnection()) {
+ if (connection.centre.getBaseMetaTileEntity()
+ .isActive()) {
+ doNormalMaintain();
+ } else {
+ // Centre offline? ...do nothing.
+ // machine.causeMaintenanceIssue();
+ }
+ } else {
+ // If the connection invalid, set it to null.
+ // Find connection every 10 second
+ if (aTick % 200 == 0) {
+ connection = null;
+ tryFindConnection();
+ // Let's have some "surprise". Sorry, surprise party is over.
+ // if (this.machine != null && this.machine.isValid()) {
+ // machine.causeMaintenanceIssue();
+ }
+ }
+ }
+ }
+
+ private void doNormalMaintain() {
+ this.mWrench = this.mScrewdriver = this.mSoftHammer = this.mHardHammer = this.mCrowbar = this.mSolderingTool = true;
+ connection.machine.mWrench = connection.machine.mScrewdriver = connection.machine.mSoftHammer = connection.machine.mHardHammer = connection.machine.mCrowbar = connection.machine.mSolderingTool = true;
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer, ForgeDirection side,
+ float aX, float aY, float aZ) {
+ if (aBaseMetaTileEntity.isClientSide()) return true;
+ if (side == aBaseMetaTileEntity.getFrontFacing()) {
+ if (aPlayer instanceof FakePlayer) return false;
+ if (connection == null || !connection.isValid()) {
+ aPlayer.addChatComponentMessage(new ChatComponentTranslation("GT5U.machines.dronecentre.noconnection"));
+ return false;
+ }
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public void onRemoval() {
+ if (hasConnection()) connection.machine = null;
+ }
+
+ private boolean hasConnection() {
+ if (connection == null) return false;
+ if (connection.isValid()) return true;
+ return connection.reCheckConnection();
+ }
+
+ /**
+ * Find a drone connection. This will search for all DC in the same dimension, then find one in range.
+ */
+ private void tryFindConnection() {
+ if (GT_MetaTileEntity_DroneCentre.getCentreMap()
+ .containsKey(getBaseMetaTileEntity().getWorld().provider.dimensionId)) {
+ List<GT_MetaTileEntity_DroneCentre> target = GT_MetaTileEntity_DroneCentre.getCentreMap()
+ .get(getBaseMetaTileEntity().getWorld().provider.dimensionId)
+ .stream()
+ .collect(Collectors.toList());
+ for (GT_MetaTileEntity_DroneCentre centre : target) {
+ if (centre.getCoords()
+ .withinDistance(this.downlinkCoord, centre.getRange())
+ && centre.getBaseMetaTileEntity()
+ .isActive()) {
+ GT_MetaTileEntity_MultiBlockBase machine = tryFindCoreGTMultiBlock();
+ if (machine != null && machine.isValid()) {
+ this.machine = machine;
+ connection = new DroneConnection(machine, centre);
+ connection.centre.getConnectionList()
+ .add(connection);
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ // Find mainframe. Mainly from a method in GT_API——This will cause performance issue! Do not call it frequently.
+ private GT_MetaTileEntity_MultiBlockBase tryFindCoreGTMultiBlock() {
+ Queue<ChunkCoordinates> tQueue = new LinkedList<>();
+ Set<ChunkCoordinates> visited = new HashSet<>(80);
+ tQueue.add(
+ this.getBaseMetaTileEntity()
+ .getCoords());
+ World world = this.getBaseMetaTileEntity()
+ .getWorld();
+ while (!tQueue.isEmpty()) {
+ final ChunkCoordinates aCoords = tQueue.poll();
+ final TileEntity tTileEntity;
+ final boolean isMachineBlock;
+ tTileEntity = world.getTileEntity(aCoords.posX, aCoords.posY, aCoords.posZ);
+ Block block = world.getBlock(aCoords.posX, aCoords.posY, aCoords.posZ);
+ // Plascrete block isn't registered as machineBlock, therefore we have to check it manually so that drone
+ // can work with cleanroom.
+ // Todo: loading cleanroom's config for other blocks
+ isMachineBlock = GregTech_API
+ .isMachineBlock(block, world.getBlockMetadata(aCoords.posX, aCoords.posY, aCoords.posZ))
+ || (block == GregTech_API.sBlockReinforced
+ && world.getBlockMetadata(aCoords.posX, aCoords.posY, aCoords.posZ) == 2);
+ // See if the block itself is MultiBlock, also the one we need.
+ if (tTileEntity instanceof IGregTechTileEntity te
+ && te.getMetaTileEntity() instanceof GT_MetaTileEntity_MultiBlockBase mte)
+ if (mte.mMaintenanceHatches.contains(this)) return mte;
+
+ // Now see if we should add the nearby blocks to the queue:
+ // 1) If we've visited less than 5 blocks, then yes
+ // 2) If the tile says we should recursively update (pipes don't, machine blocks do)
+ // 3) If the block at the coordinates is marked as a machine block
+ if (visited.size() < 5
+ || (tTileEntity instanceof IMachineBlockUpdateable
+ && ((IMachineBlockUpdateable) tTileEntity).isMachineBlockUpdateRecursive())
+ || isMachineBlock) {
+ ChunkCoordinates tCoords;
+
+ if (visited.add(tCoords = new ChunkCoordinates(aCoords.posX + 1, aCoords.posY, aCoords.posZ)))
+ tQueue.add(tCoords);
+ if (visited.add(tCoords = new ChunkCoordinates(aCoords.posX - 1, aCoords.posY, aCoords.posZ)))
+ tQueue.add(tCoords);
+ if (visited.add(tCoords = new ChunkCoordinates(aCoords.posX, aCoords.posY + 1, aCoords.posZ)))
+ tQueue.add(tCoords);
+ if (visited.add(tCoords = new ChunkCoordinates(aCoords.posX, aCoords.posY - 1, aCoords.posZ)))
+ tQueue.add(tCoords);
+ if (visited.add(tCoords = new ChunkCoordinates(aCoords.posX, aCoords.posY, aCoords.posZ + 1)))
+ tQueue.add(tCoords);
+ if (visited.add(tCoords = new ChunkCoordinates(aCoords.posX, aCoords.posY, aCoords.posZ - 1)))
+ tQueue.add(tCoords);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean doesBindPlayerInventory() {
+ return false;
+ }
+
+ @Override
+ public int getGUIWidth() {
+ return 150;
+ }
+
+ @Override
+ public int getGUIHeight() {
+ return 40;
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ builder.setBackground(GT_UITextures.BACKGROUND_SINGLEBLOCK_DEFAULT);
+ builder.setGuiTint(getGUIColorization());
+ builder.widget(
+ ButtonWidget.closeWindowButton(true)
+ .setPos(135, 3))
+ .widget(
+ new TextWidget(StatCollector.translateToLocal("GT5U.gui.text.drone_custom_name"))
+ .setTextAlignment(Alignment.Center)
+ .setPos(0, 5)
+ .setSize(150, 8))
+ .widget(
+ new TextFieldWidget().setGetter(() -> connection == null ? "" : connection.getCustomName(false))
+ .setSetter(var -> { if (connection != null) connection.setCustomName(var); })
+ .setTextAlignment(Alignment.CenterLeft)
+ .setTextColor(Color.WHITE.dark(1))
+ .setFocusOnGuiOpen(true)
+ .setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD_LIGHT_GRAY.withOffset(-1, -1, 2, 2))
+ .setPos(10, 16)
+ .setSize(130, 16))
+ .build();
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ tag.setBoolean("connection", connection == null);
+ if (connection != null) {
+ tag.setInteger("x", connection.centreCoord.posX);
+ tag.setInteger("y", connection.centreCoord.posY);
+ tag.setInteger("z", connection.centreCoord.posZ);
+ tag.setString("name", connection.customName);
+ }
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ NBTTagCompound tag = accessor.getNBTData();
+ if (tag.getBoolean("connection")) {
+ currenttip
+ .add(EnumChatFormatting.RED + StatCollector.translateToLocal("GT5U.waila.drone_downlink.noConnection"));
+ } else {
+ currenttip.add(
+ EnumChatFormatting.AQUA + StatCollector.translateToLocal("GT5U.waila.drone_downlink.connection")
+ + tag.getInteger("x")
+ + " "
+ + tag.getInteger("y")
+ + " "
+ + tag.getInteger("z"));
+ currenttip.add(EnumChatFormatting.YELLOW + tag.getString("name"));
+ }
+ super.getWailaBody(itemStack, currenttip, accessor, config);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_Hatch_DegasifierControlHatch.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_Hatch_DegasifierControlHatch.java
new file mode 100644
index 0000000000..3d49f05dca
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_Hatch_DegasifierControlHatch.java
@@ -0,0 +1,122 @@
+package gregtech.common.tileentities.machines.multi.purification;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.IIconContainer;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch;
+import gregtech.api.render.TextureFactory;
+
+public class GT_MetaTileEntity_Hatch_DegasifierControlHatch extends GT_MetaTileEntity_Hatch {
+
+ private byte outputStrength = 0;
+
+ private static final IIconContainer textureFont = Textures.BlockIcons.OVERLAY_HATCH_PH_SENSOR;
+ private static final IIconContainer textureFont_Glow = Textures.BlockIcons.OVERLAY_HATCH_PH_SENSOR_GLOW;
+
+ public GT_MetaTileEntity_Hatch_DegasifierControlHatch(int aID, String aName, String aNameRegional, int aTier) {
+ super(aID, aName, aNameRegional, aTier, 0, "Outputs a control signal for the Degasser Purification Unit");
+ }
+
+ public GT_MetaTileEntity_Hatch_DegasifierControlHatch(String aName, int aTier, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, 0, aDescription, aTextures);
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return false;
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public boolean allowGeneralRedstoneOutput() {
+ return true;
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection Side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public void initDefaultModes(NBTTagCompound aNBT) {
+ getBaseMetaTileEntity().setActive(true);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Hatch_DegasifierControlHatch(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public String[] getDescription() {
+ return new String[] { "Can be installed in the Degasser Purification Unit.",
+ "Outputs Redstone Signal Strength based on the current control signal." };
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ outputStrength = aNBT.getByte("mOutputStrength");
+ super.loadNBTData(aNBT);
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ aNBT.setByte("mOutputStrength", outputStrength);
+ super.saveNBTData(aNBT);
+ }
+
+ // Pass zero signal to disable output
+ public void updateOutputSignal(byte signal) {
+ outputStrength = signal;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (outputStrength > 0) {
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ aBaseMetaTileEntity.setStrongOutputRedstoneSignal(side, outputStrength);
+ }
+ } else {
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ aBaseMetaTileEntity.setStrongOutputRedstoneSignal(side, (byte) 0);
+ }
+ }
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(textureFont), TextureFactory.builder()
+ .addIcon(textureFont_Glow)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(textureFont) };
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_LensHousing.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_LensHousing.java
new file mode 100644
index 0000000000..bd025e0e69
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_LensHousing.java
@@ -0,0 +1,52 @@
+package gregtech.common.tileentities.machines.multi.purification;
+
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_InputBus;
+import gregtech.client.GT_TooltipHandler;
+
+public class GT_MetaTileEntity_LensHousing extends GT_MetaTileEntity_Hatch_InputBus {
+
+ public GT_MetaTileEntity_LensHousing(int id, String name, String nameRegional) {
+ super(
+ id,
+ name,
+ nameRegional,
+ GT_TooltipHandler.Tier.UV.ordinal(),
+ 1,
+ new String[] { "Holds a lens for UV laser focusing." });
+ }
+
+ public GT_MetaTileEntity_LensHousing(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_LensHousing(this.mName, this.mTier, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public int getSizeInventory() {
+ return 1;
+ }
+
+ @Override
+ public int getCircuitSlot() {
+ return -1;
+ }
+
+ @Override
+ public boolean allowSelectCircuit() {
+ return false;
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ getBaseMetaTileEntity().add1by1Slot(builder);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_LensIndicator.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_LensIndicator.java
new file mode 100644
index 0000000000..c017025d42
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_LensIndicator.java
@@ -0,0 +1,117 @@
+package gregtech.common.tileentities.machines.multi.purification;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.IIconContainer;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch;
+import gregtech.api.render.TextureFactory;
+
+public class GT_MetaTileEntity_LensIndicator extends GT_MetaTileEntity_Hatch {
+
+ private boolean isOn = false;
+
+ private static final IIconContainer textureFont = Textures.BlockIcons.OVERLAY_HATCH_PH_SENSOR;
+ private static final IIconContainer textureFont_Glow = Textures.BlockIcons.OVERLAY_HATCH_PH_SENSOR_GLOW;
+
+ public GT_MetaTileEntity_LensIndicator(int aID, String aName, String aNameRegional, int aTier) {
+ super(aID, aName, aNameRegional, aTier, 0, "Indicates required lens swaps.");
+ }
+
+ public GT_MetaTileEntity_LensIndicator(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 0, aDescription, aTextures);
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return false;
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public boolean allowGeneralRedstoneOutput() {
+ return true;
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection Side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public void initDefaultModes(NBTTagCompound aNBT) {
+ getBaseMetaTileEntity().setActive(true);
+ }
+
+ @Override
+ public String[] getDescription() {
+ return new String[] { "Can be installed in the UV Treatment Purification Unit.",
+ "Outputs Redstone Signal when a lens swap is requested." };
+ }
+
+ /**
+ * Updates redstone output strength based on the pH of the multiblock.
+ */
+ public void updateRedstoneOutput(boolean enabled) {
+ isOn = enabled;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (isOn) {
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ aBaseMetaTileEntity.setInternalOutputRedstoneSignal(side, (byte) 15);
+ }
+ } else {
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ aBaseMetaTileEntity.setInternalOutputRedstoneSignal(side, (byte) 0);
+ }
+ }
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_LensIndicator(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(textureFont), TextureFactory.builder()
+ .addIcon(textureFont_Glow)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(textureFont) };
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationPlant.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationPlant.java
new file mode 100644
index 0000000000..42bfd06d7a
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationPlant.java
@@ -0,0 +1,740 @@
+package gregtech.common.tileentities.machines.multi.purification;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.lazy;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlockAnyMeta;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain;
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.ExoticEnergy;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_Values.AuthorNotAPenguin;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PROCESSING_ARRAY;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PROCESSING_ARRAY_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PROCESSING_ARRAY_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PROCESSING_ARRAY_GLOW;
+import static gregtech.api.util.GT_RecipeBuilder.SECONDS;
+import static gregtech.api.util.GT_StructureUtility.ofFrame;
+import static gregtech.common.tileentities.machines.multi.purification.GT_MetaTileEntity_PurificationUnitBase.WATER_BOOST_BONUS_CHANCE;
+import static gregtech.common.tileentities.machines.multi.purification.GT_MetaTileEntity_PurificationUnitBase.WATER_BOOST_NEEDED_FLUID;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.ChatComponentText;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.google.common.collect.ImmutableList;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+import com.gtnewhorizons.modularui.api.drawable.shapes.Rectangle;
+import com.gtnewhorizons.modularui.api.forge.ItemStackHandler;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.math.Color;
+import com.gtnewhorizons.modularui.api.math.Size;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.api.widget.Widget;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.DynamicPositionedColumn;
+import com.gtnewhorizons.modularui.common.widget.DynamicPositionedRow;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.Scrollable;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.Textures;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.IHatchElement;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Log;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_StructureUtility;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.shutdown.ShutDownReasonRegistry;
+import gregtech.common.gui.modularui.widget.ShutDownReasonSyncer;
+import gregtech.common.gui.modularui.widget.TextButtonWidget;
+
+public class GT_MetaTileEntity_PurificationPlant
+ extends GT_MetaTileEntity_ExtendedPowerMultiBlockBase<GT_MetaTileEntity_PurificationPlant>
+ implements ISurvivalConstructable {
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final String STRUCTURE_PIECE_MAIN_SURVIVAL = "main_survival";
+
+ /**
+ * Maximum distance in each axis between the purification plant main controller and the controller blocks of the
+ * purification plant units.
+ */
+ public static final int MAX_UNIT_DISTANCE = 32;
+
+ /**
+ * Time in ticks for a full processing cycle to complete.
+ */
+ public static final int CYCLE_TIME_TICKS = 120 * SECONDS;
+
+ /**
+ * Stores all purification units linked to this controller.
+ * Normally all units in this list should be valid and unique, if not then there is a bug where they are not being
+ * unlinked properly on block destruction/relinking.
+ */
+ private final List<LinkedPurificationUnit> mLinkedUnits = new ArrayList<>();
+
+ private static final IStructureDefinition<GT_MetaTileEntity_PurificationPlant> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_PurificationPlant>builder()
+ .addShape(STRUCTURE_PIECE_MAIN, PurificationPlantStructureString.STRUCTURE_STRING)
+ // Create an identical structure for survival autobuild, with water replaced with air
+ .addShape(
+ STRUCTURE_PIECE_MAIN_SURVIVAL,
+ Arrays.stream(PurificationPlantStructureString.STRUCTURE_STRING)
+ .map(
+ sa -> Arrays.stream(sa)
+ .map(s -> s.replaceAll("F", " "))
+ .toArray(String[]::new))
+ .toArray(String[][]::new))
+ // Superplasticizer-treated high strength concrete
+ .addElement('A', ofBlock(GregTech_API.sBlockCasings9, 3))
+ // Sterile Water Plant Casing
+ .addElement('B', ofBlock(GregTech_API.sBlockCasings9, 4))
+ // Reinforced Sterile Water Plant Casing
+ .addElement('C', ofBlock(GregTech_API.sBlockCasings9, 5))
+ // Tinted Industrial Glass
+ .addElement('D', ofBlockAnyMeta(GregTech_API.sBlockTintedGlass, 0))
+ .addElement('F', ofBlock(Blocks.water, 0))
+ .addElement('G', ofFrame(Materials.Tungsten))
+ // Hatch space
+ .addElement(
+ 'H',
+ ofChain(
+ lazy(
+ t -> GT_StructureUtility.<GT_MetaTileEntity_PurificationPlant>buildHatchAdder()
+ .atLeastList(t.getAllowedHatches())
+ .dot(1)
+ .casingIndex(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings9, 4))
+ .build()),
+ ofBlock(GregTech_API.sBlockCasings9, 4)))
+ .build();
+
+ public GT_MetaTileEntity_PurificationPlant(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_PurificationPlant(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 3, 6, 0);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ int built = survivialBuildPiece(STRUCTURE_PIECE_MAIN_SURVIVAL, stackSize, 3, 6, 0, elementBudget, env, true);
+ if (built == -1) {
+ GT_Utility.sendChatToPlayer(
+ env.getActor(),
+ EnumChatFormatting.GREEN + "Auto placing done ! Now go place the water yourself !");
+ return 0;
+ }
+ return built;
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_PurificationPlant> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Purification Plant")
+ .addInfo("Main controller block for the Water Purification Plant.")
+ .addInfo(
+ "Freely place " + EnumChatFormatting.YELLOW
+ + "Purification Units "
+ + EnumChatFormatting.GRAY
+ + "within "
+ + EnumChatFormatting.RED
+ + MAX_UNIT_DISTANCE
+ + EnumChatFormatting.GRAY
+ + " blocks along each axis.")
+ .addInfo("Left click this controller with a data stick, then right click a purification unit to link.")
+ .addInfo("Supplies power to linked purification units. This multiblock accepts TecTech energy hatches.")
+ .addSeparator()
+ .addInfo(
+ "Works in fixed time processing cycles of " + EnumChatFormatting.RED
+ + CYCLE_TIME_TICKS / SECONDS
+ + EnumChatFormatting.GRAY
+ + " seconds.")
+ .addInfo("All linked units follow this cycle.")
+ .addSeparator()
+ .addInfo("Every recipe has a base chance of success. Success rate can be boosted")
+ .addInfo("by using a portion of the target output as a secondary input.")
+ .addInfo(
+ EnumChatFormatting.RED + GT_Utility.formatNumbers(WATER_BOOST_NEEDED_FLUID * 100)
+ + "%"
+ + EnumChatFormatting.GRAY
+ + " of output yield will be consumed in exchange for an")
+ .addInfo(
+ "additive " + EnumChatFormatting.RED
+ + GT_Utility.formatNumbers(WATER_BOOST_BONUS_CHANCE * 100)
+ + "%"
+ + EnumChatFormatting.GRAY
+ + " increase to success.")
+ .addInfo(
+ "On recipe failure, each purification unit has a " + EnumChatFormatting.RED
+ + "50%"
+ + EnumChatFormatting.GRAY
+ + " chance")
+ .addInfo("to return water of the same quality as the input or lower.")
+ .addSeparator()
+ .addInfo("Every purification unit has a configuration window to configure maximum parallel amount.")
+ .addInfo(
+ "This will only scale purified water I/O and power usage. Other catalysts and outputs are unchanged.")
+ .addSeparator()
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "Contaminants and ionized particles in water can cause significant imperfections in delicate")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "processes related to the cutting and engraving of silicon wafers and chips. It is crucial that")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "the water is systematically purified through a series of increasingly precise and complex")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "purification processes, and this multiblock is the heart of the operation.")
+ .addInfo(AuthorNotAPenguin)
+ .beginStructureBlock(7, 9, 8, false)
+ .addCasingInfoExactlyColored(
+ "Superplasticizer-Treated High Strength Concrete",
+ EnumChatFormatting.GRAY,
+ 56,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Sterile Water Plant Casing",
+ EnumChatFormatting.GRAY,
+ 77,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoRangeColored(
+ "Reinforced Sterile Water Plant Casing",
+ EnumChatFormatting.GRAY,
+ 71,
+ 72,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Tungsten Frame Box",
+ EnumChatFormatting.GRAY,
+ 30,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Tinted Industrial Glass",
+ EnumChatFormatting.GRAY,
+ 6,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored("Reinforced Door", EnumChatFormatting.GRAY, 1, EnumChatFormatting.GOLD, false)
+ .addController("Front center")
+ .addEnergyHatch(EnumChatFormatting.GOLD + "1", 1)
+ .addMaintenanceHatch(EnumChatFormatting.GOLD + "1", 1)
+ .addStructureInfo("Requires water to be placed in the tank.")
+ .addStructureInfo("Use the StructureLib Hologram Projector to build the structure.")
+ .toolTipFinisher("GregTech");
+ return tt;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_PurificationPlant(this.mName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, ForgeDirection facing,
+ int colorIndex, boolean active, boolean redstoneLevel) {
+ if (side == facing) {
+ if (active) return new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings9, 4)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_PROCESSING_ARRAY_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_PROCESSING_ARRAY_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] {
+ Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings9, 4)),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_PROCESSING_ARRAY)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_PROCESSING_ARRAY_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { Textures.BlockIcons
+ .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings9, 4)) };
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ private List<IHatchElement<? super GT_MetaTileEntity_PurificationPlant>> getAllowedHatches() {
+ return ImmutableList.of(Maintenance, Energy, ExoticEnergy);
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ // Check self
+ if (!checkPiece(STRUCTURE_PIECE_MAIN, 3, 6, 0)) {
+ return false;
+ }
+
+ // Check hatches
+ if (!checkHatches()) {
+ return false;
+ }
+
+ // using nano forge method of detecting hatches.
+ if (!checkExoticAndNormalEnergyHatches()) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean checkHatches() {
+ // Exactly one maintenance hatch is required
+ return mMaintenanceHatches.size() == 1;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+
+ if (aBaseMetaTileEntity.isServerSide()) {
+ // Trigger structure check of linked units, but never all in the same tick, and at most once per cycle.
+ for (int i = 0; i < mLinkedUnits.size(); ++i) {
+ if (aTick % CYCLE_TIME_TICKS == i) {
+ LinkedPurificationUnit unit = mLinkedUnits.get(i);
+ boolean structure = unit.metaTileEntity()
+ .checkStructure(true);
+ // If unit was active but deformed, set as inactive
+ if (unit.isActive() && !structure) {
+ unit.setActive(false);
+ // Also remember to recalculate power usage, since otherwise the deformed unit will
+ // keep drawing power
+ this.lEUt = -calculateEffectivePowerUsage();
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void runMachine(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ updateCycleProgress();
+ // Calculate efficiency based on maintenance issues
+ if (mMaxProgresstime > 0) {
+ mEfficiency = Math.max(
+ 0,
+ Math.min(
+ mEfficiency + mEfficiencyIncrease,
+ getMaxEfficiency(mInventory[1]) - ((getIdealStatus() - getRepairStatus()) * 1000)));
+ }
+ }
+
+ private void updateCycleProgress() {
+ // Since the plant does not run recipes directly, we just continuously loop the base cycle
+ if (mMachine) {
+ // cycle is running, so simply advance it
+ if (mMaxProgresstime > 0) {
+ // onRunningTick is responsible for draining power
+ if (onRunningTick(mInventory[1])) {
+ markDirty();
+ mProgresstime += 1;
+ // Update progress time for active units
+ for (LinkedPurificationUnit unit : this.mLinkedUnits) {
+ if (unit.isActive()) {
+ GT_MetaTileEntity_PurificationUnitBase<?> metaTileEntity = unit.metaTileEntity();
+ metaTileEntity.mProgresstime = mProgresstime;
+ }
+ }
+ // Cycle finished
+ if (mProgresstime >= mMaxProgresstime) {
+ this.endCycle();
+ }
+ } else {
+ // Power drain failed, shut down all other units due to power loss.
+ // Note that we do not need to shut down self, as this is done in
+ // onRunningTick already
+ for (LinkedPurificationUnit unit : mLinkedUnits) {
+ if (unit.isActive()) {
+ unit.metaTileEntity()
+ .stopMachine(ShutDownReasonRegistry.POWER_LOSS);
+ }
+ }
+ }
+ }
+
+ // No cycle running, start a new cycle if the machine is turned on
+ if (mMaxProgresstime == 0 && isAllowedToWork()) {
+ this.startCycle();
+ }
+ }
+ }
+
+ private void startCycle() {
+ mProgresstime = 0;
+ mMaxProgresstime = CYCLE_TIME_TICKS;
+ mEfficiency = (10000 - (getIdealStatus() - getRepairStatus()) * 1000);
+
+ // Find active units and notify them that the cycle started
+ for (LinkedPurificationUnit unit : this.mLinkedUnits) {
+ GT_MetaTileEntity_PurificationUnitBase<?> metaTileEntity = unit.metaTileEntity();
+ PurificationUnitStatus status = metaTileEntity.status();
+ // Unit needs to be online to be considered active.
+ if (status == PurificationUnitStatus.ONLINE) {
+ // Perform recipe check for unit and start it if successful
+ if (metaTileEntity.doPurificationRecipeCheck()) {
+ unit.setActive(true);
+ metaTileEntity.startCycle(mMaxProgresstime, mProgresstime);
+ }
+ }
+ }
+
+ // After activating all units, calculate power usage
+ lEUt = -calculateEffectivePowerUsage();
+ }
+
+ private void endCycle() {
+ mMaxProgresstime = 0;
+
+ // Mark all units as inactive and reset their progress time
+ for (LinkedPurificationUnit unit : this.mLinkedUnits) {
+ GT_MetaTileEntity_PurificationUnitBase<?> metaTileEntity = unit.metaTileEntity();
+ // If this unit was active, end the cycle
+ if (unit.isActive()) {
+ metaTileEntity.endCycle();
+ }
+ unit.setActive(false);
+ }
+ }
+
+ /**
+ * Calculate power usage of all units
+ */
+ private long calculateEffectivePowerUsage() {
+ long euT = 0;
+ for (LinkedPurificationUnit unit : mLinkedUnits) {
+ if (unit.isActive()) {
+ euT += unit.metaTileEntity()
+ .getActualPowerUsage();
+ }
+ }
+ return euT;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ public void registerLinkedUnit(GT_MetaTileEntity_PurificationUnitBase<?> unit) {
+ this.mLinkedUnits.add(new LinkedPurificationUnit(unit));
+ }
+
+ public void unregisterLinkedUnit(GT_MetaTileEntity_PurificationUnitBase<?> unit) {
+ this.mLinkedUnits.removeIf(link -> link.metaTileEntity() == unit);
+ }
+
+ @Override
+ public void onLeftclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ if (!(aPlayer instanceof EntityPlayerMP)) return;
+
+ // Save link data to data stick, very similar to Crafting Input Buffer.
+
+ ItemStack dataStick = aPlayer.inventory.getCurrentItem();
+ if (!ItemList.Tool_DataStick.isStackEqual(dataStick, false, true)) return;
+
+ NBTTagCompound tag = new NBTTagCompound();
+ tag.setString("type", "PurificationPlant");
+ tag.setInteger("x", aBaseMetaTileEntity.getXCoord());
+ tag.setInteger("y", aBaseMetaTileEntity.getYCoord());
+ tag.setInteger("z", aBaseMetaTileEntity.getZCoord());
+
+ dataStick.stackTagCompound = tag;
+ dataStick.setStackDisplayName(
+ "Purification Plant Link Data Stick (" + aBaseMetaTileEntity
+ .getXCoord() + ", " + aBaseMetaTileEntity.getYCoord() + ", " + aBaseMetaTileEntity.getZCoord() + ")");
+ aPlayer.addChatMessage(new ChatComponentText("Saved Link Data to Data Stick"));
+ }
+
+ @Override
+ public String[] getInfoData() {
+ var ret = new ArrayList<String>();
+ // Show linked purification units and their status
+ ret.add("Linked Purification Units: ");
+ for (LinkedPurificationUnit unit : this.mLinkedUnits) {
+ String text = EnumChatFormatting.AQUA + unit.metaTileEntity()
+ .getLocalName() + ": ";
+ PurificationUnitStatus status = unit.metaTileEntity()
+ .status();
+ switch (status) {
+ case ONLINE -> {
+ text = text + EnumChatFormatting.GREEN + "Online";
+ }
+ case DISABLED -> {
+ text = text + EnumChatFormatting.YELLOW + "Disabled";
+ }
+ case INCOMPLETE_STRUCTURE -> {
+ text = text + EnumChatFormatting.RED + "Incomplete Structure";
+ }
+ }
+ ret.add(text);
+ }
+ return ret.toArray(new String[0]);
+ }
+
+ @Override
+ public void onBlockDestroyed() {
+ // When the controller is destroyed we want to notify all currently linked units
+ for (LinkedPurificationUnit unit : this.mLinkedUnits) {
+ unit.metaTileEntity()
+ .unlinkController();
+ }
+ super.onBlockDestroyed();
+ }
+
+ private void drawTopText(DynamicPositionedColumn screenElements) {
+ screenElements.setSynced(false)
+ .setSpace(0)
+ .setPos(10, 8);
+
+ screenElements
+ .widget(
+ new TextWidget(GT_Utility.trans("138", "Incomplete Structure.")).setDefaultColor(EnumChatFormatting.RED)
+ .setEnabled(widget -> !mMachine))
+ .widget(new FakeSyncWidget.BooleanSyncer(() -> mMachine, val -> mMachine = val));
+
+ screenElements.widget(
+ new TextWidget("Hit with Soft Mallet to start.").setDefaultColor(EnumChatFormatting.BLACK)
+ .setEnabled(
+ widget -> getBaseMetaTileEntity().getErrorDisplayID() == 0 && !getBaseMetaTileEntity().isActive()))
+ .widget(
+ new FakeSyncWidget.IntegerSyncer(
+ () -> getBaseMetaTileEntity().getErrorDisplayID(),
+ val -> getBaseMetaTileEntity().setErrorDisplayID(val)))
+ .widget(
+ new FakeSyncWidget.BooleanSyncer(
+ () -> getBaseMetaTileEntity().isActive(),
+ val -> getBaseMetaTileEntity().setActive(val)));
+ screenElements.widget(
+ new TextWidget(GT_Utility.trans("142", "Running perfectly.")).setDefaultColor(EnumChatFormatting.GREEN)
+ .setEnabled(
+ widget -> getBaseMetaTileEntity().getErrorDisplayID() == 0 && getBaseMetaTileEntity().isActive()));
+ screenElements.widget(
+ TextWidget.dynamicString(
+ () -> getBaseMetaTileEntity().getLastShutDownReason()
+ .getDisplayString())
+ .setSynced(false)
+ .setTextAlignment(Alignment.CenterLeft)
+ .setEnabled(
+ widget -> shouldDisplayShutDownReason() && !getBaseMetaTileEntity().isActive()
+ && GT_Utility.isStringValid(
+ getBaseMetaTileEntity().getLastShutDownReason()
+ .getDisplayString())
+ && getBaseMetaTileEntity().wasShutdown()))
+ .widget(
+ new ShutDownReasonSyncer(
+ () -> getBaseMetaTileEntity().getLastShutDownReason(),
+ reason -> getBaseMetaTileEntity().setShutDownReason(reason)))
+ .widget(
+ new FakeSyncWidget.BooleanSyncer(
+ () -> getBaseMetaTileEntity().wasShutdown(),
+ wasShutDown -> getBaseMetaTileEntity().setShutdownStatus(wasShutDown)));
+ screenElements.widget(
+ TextWidget.dynamicString(this::generateCurrentRecipeInfoString)
+ .setSynced(false)
+ .setTextAlignment(Alignment.CenterLeft)
+ .setEnabled(widget -> (mMaxProgresstime > 0)))
+ .widget(new FakeSyncWidget.IntegerSyncer(() -> mProgresstime, val -> mProgresstime = val))
+ .widget(new FakeSyncWidget.IntegerSyncer(() -> mMaxProgresstime, val -> mMaxProgresstime = val));
+ }
+
+ private final int STATUS_WINDOW_ID = 10;
+
+ private ModularWindow createStatusWindow(final EntityPlayer player) {
+ final int windowWidth = 260;
+ final int windowHeight = 200;
+ ModularWindow.Builder builder = ModularWindow.builder(windowWidth, windowHeight);
+ builder.setBackground(GT_UITextures.BACKGROUND_SINGLEBLOCK_DEFAULT);
+ builder.widget(
+ ButtonWidget.closeWindowButton(true)
+ .setPos(windowWidth - 15, 3));
+
+ // Title widget
+ builder.widget(
+ new TextWidget(EnumChatFormatting.BOLD + "Purification Unit Status").setTextAlignment(Alignment.Center)
+ .setPos(5, 10)
+ .setSize(windowWidth, 8));
+
+ int currentYPosition = 20;
+ Scrollable mainDisp = new Scrollable().setVerticalScroll();
+
+ int rowHeight = 20;
+ for (int i = 0; i < this.mLinkedUnits.size(); i++) {
+ mainDisp.widget(makeUnitStatusWidget(mLinkedUnits.get(i)).setPos(0, rowHeight * (i + 1)));
+ }
+
+ builder.widget(
+ mainDisp.setPos(5, currentYPosition)
+ .setSize(windowWidth - 10, windowHeight - currentYPosition - 5));
+ return builder.build();
+ }
+
+ private Widget makeStatusWindowButton() {
+ TextButtonWidget widget = (TextButtonWidget) new TextButtonWidget("Status").setLeftMargin(4)
+ .setSize(40, 16)
+ .setPos(10, 40);
+ widget.button()
+ .setOnClick(
+ (clickData, w) -> {
+ if (!w.isClient()) w.getContext()
+ .openSyncedWindow(STATUS_WINDOW_ID);
+ })
+ .setBackground(GT_UITextures.BUTTON_STANDARD);
+ widget.text()
+ .setTextAlignment(Alignment.CenterLeft)
+ .setDefaultColor(EnumChatFormatting.BLACK);
+ return widget;
+ }
+
+ private Widget makeUnitStatusWidget(LinkedPurificationUnit unit) {
+ // Draw small machine controller icon
+ DynamicPositionedRow row = new DynamicPositionedRow();
+ ItemStackHandler machineIcon = new ItemStackHandler(1);
+ machineIcon.setStackInSlot(
+ 0,
+ unit.metaTileEntity()
+ .getStackForm(1));
+
+ row.widget(
+ SlotWidget.phantom(machineIcon, 0)
+ .disableInteraction()
+ .setPos(0, 0))
+ .setSize(20, 20);
+
+ // Display machine name and status
+ String name = unit.metaTileEntity()
+ .getLocalName();
+
+ row.widget(
+ TextWidget.dynamicString(() -> name + " " + unit.getStatusString())
+ .setSynced(false)
+ .setTextAlignment(Alignment.CenterLeft)
+ .setPos(25, 0)
+ .setSize(0, 20))
+ .widget(new FakeSyncWidget.StringSyncer(() -> name, _name -> {}))
+ .widget(
+ unit.metaTileEntity()
+ .makeSyncerWidgets())
+ .widget(new FakeSyncWidget.BooleanSyncer(unit::isActive, unit::setActive));;
+
+ return row;
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+
+ buildContext.addSyncedWindow(STATUS_WINDOW_ID, this::createStatusWindow);
+
+ // Draw basic recipe info
+ final DynamicPositionedColumn controlTextArea = new DynamicPositionedColumn();
+ drawTopText(controlTextArea);
+ builder.widget(controlTextArea);
+
+ // Draw line separator
+ builder.widget(
+ new Rectangle().setColor(Color.rgb(114, 120, 139))
+ .asWidget()
+ .setSizeProvider((screenSize, window, parent) -> new Size(window.getSize().width - 8, 2))
+ .setPos(3, 32));
+
+ // Add status window button
+ builder.widget(makeStatusWindowButton());
+
+ // Add parallel count number input
+
+ builder.widget(createPowerSwitchButton(builder));
+
+ // Add value syncers, note that we do this here so
+ // everything is updated once the status gui opens
+ addSyncers(builder);
+ }
+
+ private void addSyncers(ModularWindow.Builder builder) {
+ // Sync connection list to client
+ builder.widget(new FakeSyncWidget.ListSyncer<>(() -> mLinkedUnits, links -> {
+ mLinkedUnits.clear();
+ mLinkedUnits.addAll(links);
+ }, (buffer, link) -> {
+ // Try to save link data to NBT, so we can reconstruct it on client
+ try {
+ buffer.writeNBTTagCompoundToBuffer(link.writeLinkDataToNBT());
+ } catch (IOException e) {
+ GT_Log.err.println(e.getCause());
+ }
+ }, buffer -> {
+ // Try to load link data from NBT compound as constructed above.
+ try {
+ return new LinkedPurificationUnit(buffer.readNBTTagCompoundFromBuffer());
+ } catch (IOException e) {
+ GT_Log.err.println(e.getCause());
+ }
+ return null;
+ }));
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitBase.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitBase.java
new file mode 100644
index 0000000000..075ddc74c2
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitBase.java
@@ -0,0 +1,805 @@
+package gregtech.common.tileentities.machines.multi.purification;
+
+import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY;
+import static net.minecraft.util.StatCollector.translateToLocal;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.stream.Stream;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.ChatComponentText;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.world.World;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.drawable.UITexture;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.math.Color;
+import com.gtnewhorizons.modularui.api.math.Size;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.api.widget.Widget;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.MultiChildWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+import com.gtnewhorizons.modularui.common.widget.textfield.NumericWidget;
+
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.VoidingMode;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Input;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.recipe.metadata.PurificationPlantBaseChanceKey;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.blocks.GT_Block_Casings_Abstract;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+/**
+ * Base class for purification units. This class handles all shared behaviour between units.
+ * When inheriting from this, make sure to call super.loadNBTData() and super.saveNBTData()
+ * if you override these methods, or linking will break.
+ */
+public abstract class GT_MetaTileEntity_PurificationUnitBase<T extends GT_MetaTileEntity_ExtendedPowerMultiBlockBase<T>>
+ extends GT_MetaTileEntity_ExtendedPowerMultiBlockBase<T> {
+
+ /**
+ * Ratio of output fluid that needs to be inserted back as input to trigger a "water boost".
+ * Must be in [0, 1].
+ */
+ public static final float WATER_BOOST_NEEDED_FLUID = 0.1f;
+ /**
+ * Additive bonus to success chance when water boost is active.
+ * Must be in [0, 1]
+ */
+ public static final float WATER_BOOST_BONUS_CHANCE = 0.15f;
+
+ /**
+ * Small internal enum to report back the various error cases when linking purification units to the
+ * purification plant.
+ */
+ private enum LinkResult {
+ /**
+ * Link target was out of range of the main controller
+ */
+ TOO_FAR,
+ /**
+ * No valid GT_MetaTileEntity_PurificationPlant was found at the link target position.
+ */
+ NO_VALID_PLANT,
+ /**
+ * Link successful
+ */
+ SUCCESS,
+ }
+
+ /**
+ * Coordinates of the main purification plant controller. These can be used to find the controller again
+ * on world load.
+ */
+ private int controllerX, controllerY, controllerZ;
+
+ /**
+ * Whether a controller was previously set.
+ */
+ private boolean controllerSet = false;
+
+ /**
+ * Pointer to the main purification plant controller.
+ */
+ private GT_MetaTileEntity_PurificationPlant controller = null;
+
+ /**
+ * The current recipe being run in the purification unit. Note that purification unit recipes are a bit special,
+ * so input and output in the recipe might not exactly match the required inputs and produced outputs.
+ * For more information, always look at the purification unit tooltip and implementation.
+ */
+ protected GT_Recipe currentRecipe = null;
+
+ /**
+ * Current chance of the recipe succeeding, always in [0, 100]. A chance above 100 will be interpreted as 100.
+ */
+ protected float currentRecipeChance = 0.0f;
+
+ /**
+ * Configured parallel amount. Only water I/O and power scale.
+ */
+ protected int maxParallel = 1;
+
+ protected int effectiveParallel = 1;
+
+ protected ArrayList<FluidStack> storedFluids = null;
+
+ protected GT_MetaTileEntity_PurificationUnitBase(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ protected GT_MetaTileEntity_PurificationUnitBase(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean doRandomMaintenanceDamage() {
+ // The individual purification unit structures cannot have maintenance issues, so do nothing.
+ return true;
+ }
+
+ @Override
+ public boolean getDefaultHasMaintenanceChecks() {
+ return false;
+ }
+
+ @Override
+ protected void setHatchRecipeMap(GT_MetaTileEntity_Hatch_Input hatch) {
+ // Do nothing, we don't want to lock hatches to recipe maps since this can cause
+ // them to reject our catalyst fluids
+ }
+
+ /**
+ * Used to more easily grab a correct texture index from a block + meta.
+ *
+ * @param block Block to use as base. Must implement GT_Block_Casings_Abstract
+ * @param meta Metadata of the block to pick the actual block
+ * @return The correct index into the global texture atlas.
+ */
+ protected static int getTextureIndex(Block block, int meta) {
+ return ((GT_Block_Casings_Abstract) block).getTextureIndex(meta);
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) {
+ super.onPostTick(aBaseMetaTileEntity, aTimer);
+ // Try to re-link to controller periodically, for example on game load.
+ if (aTimer % 100 == 5 && controllerSet && getController() == null) {
+ trySetControllerFromCoord(controllerX, controllerY, controllerZ);
+ }
+ }
+
+ @Override
+ protected void runMachine(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ // Main controller updates progress time. We can do I/O logic here.
+ // The logic for operating purification units is typically implemented by overriding this behaviour.
+ if (mMaxProgresstime > 0) {
+ this.markDirty();
+ // Do not take maintenance into consideration, because purification units do not get
+ // maintenance issues.
+ // Technically, this entire efficiency stat is a bit useless for purification units, since
+ // their power draw does not actually depend on it, but it's nice to keep around for consistency with other
+ // multiblocks. This way, you still gradually see the efficiency go down when it powers down.
+ mEfficiency = Math.max(0, Math.min(mEfficiency + mEfficiencyIncrease, getMaxEfficiency(mInventory[1])));
+ }
+ }
+
+ protected CheckRecipeResult findRecipeForInputs(FluidStack[] fluidInputs, ItemStack... itemInputs) {
+ RecipeMap<?> recipeMap = this.getRecipeMap();
+
+ // Grab a stream of recipes and find the one with the highest success chance
+ Stream<GT_Recipe> recipes = recipeMap.findRecipeQuery()
+ .fluids(fluidInputs)
+ .items(itemInputs)
+ .findAll();
+ GT_Recipe recipe = recipes
+ .max(Comparator.comparing(r -> r.getMetadataOrDefault(PurificationPlantBaseChanceKey.INSTANCE, 0.0f)))
+ .orElse(null);
+
+ if (recipe == null) {
+ return CheckRecipeResultRegistry.NO_RECIPE;
+ }
+
+ if (this.protectsExcessFluid() && !this.canOutputAll(recipe.mFluidOutputs)) {
+ return CheckRecipeResultRegistry.FLUID_OUTPUT_FULL;
+ }
+
+ if (this.protectsExcessItem() && !this.canOutputAll(recipe.mOutputs)) {
+ return CheckRecipeResultRegistry.ITEM_OUTPUT_FULL;
+ }
+
+ this.currentRecipe = recipe;
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ }
+
+ /**
+ * By default, only checks fluid input.
+ *
+ * @return
+ */
+ @NotNull
+ @Override
+ public CheckRecipeResult checkProcessing() {
+ this.storedFluids = this.getStoredFluids();
+ CheckRecipeResult result = overrideRecipeCheck();
+ if (result == null) result = findRecipeForInputs(storedFluids.toArray(new FluidStack[] {}));
+
+ // If we had a successful result, calculate effective parallel
+ if (result.wasSuccessful()) {
+ FluidStack waterInput = this.currentRecipe.mFluidInputs[0];
+ // Count total available purified water input of the previous step
+ long amountAvailable = 0;
+ for (FluidStack fluid : this.storedFluids) {
+ if (fluid.isFluidEqual(waterInput)) {
+ amountAvailable += fluid.amount;
+ }
+ }
+ // Determine effective parallel
+ effectiveParallel = (int) Math.min(maxParallel, Math.floorDiv(amountAvailable, waterInput.amount));
+ // This should not happen, throw an error
+ if (effectiveParallel == 0) return CheckRecipeResultRegistry.INTERNAL_ERROR;
+ }
+
+ return result;
+ }
+
+ public CheckRecipeResult overrideRecipeCheck() {
+ return null;
+ }
+
+ /**
+ * Equivalent to checkRecipe(), but public because the purification plant needs to access it and checkRecipe()
+ * is protected.
+ *
+ * @return True if successfully found a recipe and/or started processing/
+ */
+ public boolean doPurificationRecipeCheck() {
+ effectiveParallel = 1;
+ return this.checkRecipe();
+ }
+
+ /**
+ * Get the success chance of the recipe, from 0 to 100. Never call this while a recipe is running, because items
+ * or modifiers used to boost might disappear by the time recipe check comes around,
+ * which would invalidate this result.
+ */
+ public float calculateBoostedSuccessChance() {
+ // If this.currentRecipe is null, there is a bug, so throwing a NPE is fine.
+ float recipeChance = this.currentRecipe.getMetadataOrDefault(PurificationPlantBaseChanceKey.INSTANCE, 0.0f);
+ // Apply water boost if available.
+ if (isWaterBoosted(this.currentRecipe)) {
+ recipeChance = Math.min(recipeChance + WATER_BOOST_BONUS_CHANCE * 100.0f, 100.0f);
+ }
+ return recipeChance;
+ }
+
+ /**
+ * By default, the final recipe success chance is simply the success chance calculated on recipe check.
+ * This applies water boosts when needed to the base chance. Purification units can override this to perform
+ * more complex success chance calculations, that even take into account what happened during the runtime of the
+ * recipe.
+ *
+ * @return The success chance of the recipe, at the point in time the outputs are to be produced.
+ */
+ public float calculateFinalSuccessChance() {
+ return this.currentRecipeChance;
+ }
+
+ /**
+ * Get the tier of water this unit makes. Starts at 1.
+ */
+ public abstract int getWaterTier();
+
+ /**
+ * Get the amount of water needed to execute a water boost, in mb.
+ */
+ public FluidStack getWaterBoostAmount(GT_Recipe recipe) {
+ // Recipes should always be constructed so that output water is always the first fluid output
+ FluidStack outputWater = recipe.mFluidOutputs[0];
+ int amount = Math.round(outputWater.amount * WATER_BOOST_NEEDED_FLUID);
+ return new FluidStack(outputWater.getFluid(), amount);
+ }
+
+ /**
+ * Returns true if this purification unit contains enough water to apply a water boost for the selected recipe.
+ * This should only be called during recipe check! Never call this while a recipe is running, because water used to
+ * boost might disappear by the time recipe check comes around, which would invalidate this result.
+ *
+ * @param recipe The recipe to check the water boost of
+ */
+ public boolean isWaterBoosted(GT_Recipe recipe) {
+ FluidStack inputWater = getWaterBoostAmount(recipe);
+ // Simulate input drain to see if we can water boost
+ return depleteInput(inputWater, true);
+ }
+
+ /**
+ * Consumes all <b>fluid</b> inputs of the current recipe.
+ */
+ public void depleteRecipeInputs() {
+ for (FluidStack input : this.currentRecipe.mFluidInputs) {
+ FluidStack copyWithParallel = input.copy();
+ copyWithParallel.amount = input.amount * effectiveParallel;
+ this.depleteInput(copyWithParallel);
+ }
+ }
+
+ /**
+ * Called after a recipe is found and accepted.
+ *
+ * @param cycleTime Time for a full cycle to complete
+ * @param progressTime Current progress time
+ */
+ public void startCycle(int cycleTime, int progressTime) {
+ // Important to calculate this before depleting inputs, otherwise we may get issues with boost items
+ // disappearing.
+ this.currentRecipeChance = this.calculateBoostedSuccessChance();
+
+ // Deplete inputs from water boost if enabled.
+ if (isWaterBoosted(this.currentRecipe)) {
+ FluidStack inputWater = this.getWaterBoostAmount(this.currentRecipe);
+ this.depleteInput(inputWater);
+ }
+
+ // Consume inputs
+ this.depleteRecipeInputs();
+ // Initialize recipe and progress information.
+ this.mMaxProgresstime = cycleTime;
+ this.mProgresstime = progressTime;
+ this.mEfficiency = 10000;
+ // These need to be set so the GUI code can display the produced outputs
+
+ // Make sure to scale purified water output with parallel amount.
+ // Make sure to make a full copy of the array, so we don't go modifying recipes
+ FluidStack[] fluidOutputs = new FluidStack[this.currentRecipe.mFluidOutputs.length];
+ for (int i = 0; i < this.currentRecipe.mFluidOutputs.length; ++i) {
+ fluidOutputs[i] = this.currentRecipe.mFluidOutputs[i].copy();
+ }
+ fluidOutputs[0].amount *= effectiveParallel;
+ this.mOutputFluids = fluidOutputs;
+ this.mOutputItems = this.currentRecipe.mOutputs;
+ // Set this value, so it can be displayed in Waila. Note that the logic for the units is
+ // specifically overridden so setting this value does not actually drain power.
+ // Instead, power is drained by the main purification plant controller.
+ this.lEUt = -this.getActualPowerUsage();
+ }
+
+ public void addRecipeOutputs() {
+ ThreadLocalRandom random = ThreadLocalRandom.current();
+ this.addFluidOutputs(mOutputFluids);
+ // If this recipe has random item outputs, roll on it and add outputs
+ if (this.currentRecipe.mChances != null) {
+ // Roll on each output individually
+ for (int i = 0; i < this.currentRecipe.mOutputs.length; ++i) {
+ // Recipes store probabilities as a value ranging from 1-10000
+ int roll = random.nextInt(10000);
+ if (roll <= this.currentRecipe.mChances[i]) {
+ this.addOutput(this.currentRecipe.mOutputs[i]);
+ }
+ }
+ } else {
+ // Guaranteed item output
+ for (int i = 0; i < this.currentRecipe.mOutputs.length; ++i) {
+ this.addOutput(this.currentRecipe.mOutputs[i]);
+ }
+ }
+ }
+
+ public void endCycle() {
+ ThreadLocalRandom random = ThreadLocalRandom.current();
+
+ // First see if the recipe succeeded. For some reason random.nextFloat does not compile, so we use this
+ // hack instead.
+ float successRoll = random.nextInt(0, 10000) / 100.0f;
+ if (successRoll <= calculateFinalSuccessChance()) {
+ addRecipeOutputs();
+ } else {
+ onRecipeFail();
+ }
+
+ // Reset recipe values for next iteration
+ this.mMaxProgresstime = 0;
+ this.mProgresstime = 0;
+ this.lEUt = 0;
+ this.mEfficiency = 0;
+ this.currentRecipe = null;
+ this.currentRecipeChance = 0.0f;
+ this.mOutputItems = null;
+ this.mOutputFluids = null;
+ this.effectiveParallel = 1;
+ }
+
+ /**
+ * Outputs fluid when recipe fails.
+ */
+ private void onRecipeFail() {
+ // Possibly output lower quality water.
+ // Note that if there is no space for this, it will be voided regardless of fluid void setting!
+ FluidStack outputWater = getDegradedOutputWater();
+ this.addOutput(outputWater);
+ }
+
+ /**
+ * On recipe fail, water quality may degrade to the same or lower tier. This function returns the water to output
+ * in this case, or null if no water is produced at all.
+ */
+ private FluidStack getDegradedOutputWater() {
+ ThreadLocalRandom random = ThreadLocalRandom.current();
+ int roll = random.nextInt(0, 2);
+ // 50% chance to not output anything at all
+ if (roll == 0) return null;
+
+ for (int waterTier = getWaterTier(); waterTier > 0; --waterTier) {
+ // 50% chance every time of degrading into the previous tier
+ roll = random.nextInt(0, 2);
+ if (roll == 1) {
+ // Rolled good, stop the loop and output water below current tier
+ int amount = mOutputFluids[0].amount;
+ // For tier 1, this is distilled water, so we cannot use the helper function!
+ if (waterTier == 1) {
+ return GT_ModHandler.getDistilledWater(amount);
+ }
+ Materials water = PurifiedWaterHelpers.getPurifiedWaterTier(waterTier - 1);
+ return water.getFluid(amount);
+ }
+ // Bad roll, keep looping and degrade quality even further
+ }
+ // Rolled bad on every iteration, no output for you
+ return null;
+ }
+
+ /**
+ * Get the EU/t usage of this unit while it is running.
+ */
+ public abstract long getBasePowerUsage();
+
+ public long getActualPowerUsage() {
+ return getBasePowerUsage() * effectiveParallel;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ // The individual purification unit structures cannot have maintenance issues, so fix them all.
+ this.mCrowbar = true;
+ this.mWrench = true;
+ this.mHardHammer = true;
+ this.mSoftHammer = true;
+ this.mSolderingTool = true;
+ this.mScrewdriver = true;
+ return true;
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ // If a linked controller was found, load its coordinates.
+ // The unit will try to link to the real controller block periodically in onPostTick()
+ // We cannot do this linking here yet because the controller block might not be loaded yet.
+ // TODO: We could try though?
+ if (aNBT.hasKey("controller")) {
+ NBTTagCompound controllerNBT = aNBT.getCompoundTag("controller");
+ controllerX = controllerNBT.getInteger("x");
+ controllerY = controllerNBT.getInteger("y");
+ controllerZ = controllerNBT.getInteger("z");
+ controllerSet = true;
+ }
+ currentRecipeChance = aNBT.getFloat("currentRecipeChance");
+ if (aNBT.hasKey("configuredParallel")) {
+ maxParallel = aNBT.getInteger("configuredParallel");
+ }
+ if (aNBT.hasKey("effectiveParallel")) {
+ effectiveParallel = aNBT.getInteger("effectiveParallel");
+ }
+ }
+
+ public NBTTagCompound saveLinkDataToNBT() {
+ NBTTagCompound controllerNBT = new NBTTagCompound();
+ controllerNBT.setInteger("x", controllerX);
+ controllerNBT.setInteger("y", controllerY);
+ controllerNBT.setInteger("z", controllerZ);
+ return controllerNBT;
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ if (controllerSet) {
+ NBTTagCompound controllerNBT = saveLinkDataToNBT();
+ aNBT.setTag("controller", controllerNBT);
+ }
+ aNBT.setFloat("currentRecipeChance", currentRecipeChance);
+ aNBT.setInteger("configuredParallel", maxParallel);
+ aNBT.setInteger("effectiveParallel", effectiveParallel);
+ }
+
+ private LinkResult trySetControllerFromCoord(int x, int y, int z) {
+ IGregTechTileEntity ourBaseMetaTileEntity = this.getBaseMetaTileEntity();
+ // First check whether the controller we try to link to is within range. The range is defined
+ // as a max distance in each axis.
+ if (Math.abs(ourBaseMetaTileEntity.getXCoord() - x) > GT_MetaTileEntity_PurificationPlant.MAX_UNIT_DISTANCE)
+ return LinkResult.TOO_FAR;
+ if (Math.abs(ourBaseMetaTileEntity.getYCoord() - y) > GT_MetaTileEntity_PurificationPlant.MAX_UNIT_DISTANCE)
+ return LinkResult.TOO_FAR;
+ if (Math.abs(ourBaseMetaTileEntity.getZCoord() - z) > GT_MetaTileEntity_PurificationPlant.MAX_UNIT_DISTANCE)
+ return LinkResult.TOO_FAR;
+
+ // Find the block at the requested coordinated and check if it is a purification plant controller.
+ var tileEntity = getBaseMetaTileEntity().getWorld()
+ .getTileEntity(x, y, z);
+ if (tileEntity == null) return LinkResult.NO_VALID_PLANT;
+ if (!(tileEntity instanceof IGregTechTileEntity gtTileEntity)) return LinkResult.NO_VALID_PLANT;
+ var metaTileEntity = gtTileEntity.getMetaTileEntity();
+ if (!(metaTileEntity instanceof GT_MetaTileEntity_PurificationPlant)) return LinkResult.NO_VALID_PLANT;
+
+ // Before linking, unlink from current controller, so we don't end up with units linked to multiple
+ // controllers.
+ GT_MetaTileEntity_PurificationPlant oldController = getController();
+ if (oldController != null) {
+ oldController.unregisterLinkedUnit(this);
+ this.unlinkController();
+ }
+
+ // Now link to new controller
+ controllerX = x;
+ controllerY = y;
+ controllerZ = z;
+ controllerSet = true;
+ controller = (GT_MetaTileEntity_PurificationPlant) metaTileEntity;
+ controller.registerLinkedUnit(this);
+ return LinkResult.SUCCESS;
+ }
+
+ private boolean tryLinkDataStick(EntityPlayer aPlayer) {
+ // Make sure the held item is a data stick
+ ItemStack dataStick = aPlayer.inventory.getCurrentItem();
+ if (!ItemList.Tool_DataStick.isStackEqual(dataStick, false, true)) {
+ return false;
+ }
+
+ // Make sure this data stick is a proper purification plant link data stick.
+ if (!dataStick.hasTagCompound() || !dataStick.stackTagCompound.getString("type")
+ .equals("PurificationPlant")) {
+ return false;
+ }
+
+ // Now read link coordinates from the data stick.
+ NBTTagCompound nbt = dataStick.stackTagCompound;
+ int x = nbt.getInteger("x");
+ int y = nbt.getInteger("y");
+ int z = nbt.getInteger("z");
+
+ // Try to link, and report the result back to the player.
+ LinkResult result = trySetControllerFromCoord(x, y, z);
+ if (result == LinkResult.SUCCESS) {
+ aPlayer.addChatMessage(new ChatComponentText("Link successful"));
+ } else if (result == LinkResult.TOO_FAR) {
+ aPlayer.addChatMessage(new ChatComponentText("Link failed: Out of range."));
+ } else if (result == LinkResult.NO_VALID_PLANT) {
+ aPlayer.addChatMessage(new ChatComponentText("Link failed: No Purification Plant found at link location"));
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ if (!(aPlayer instanceof EntityPlayerMP)) {
+ return false;
+ }
+
+ // Right-clicking could be a data stick linking action, so try this first.
+ if (tryLinkDataStick(aPlayer)) {
+ return true;
+ }
+
+ return super.onRightclick(aBaseMetaTileEntity, aPlayer);
+ }
+
+ public GT_MetaTileEntity_PurificationPlant getController() {
+ if (controller == null) return null;
+ // Controller disappeared
+ if (controller.getBaseMetaTileEntity() == null) return null;
+ return controller;
+ }
+
+ // If the controller is broken this can be called to explicitly unlink the controller, so we don't have any
+ // references lingering around
+ public void unlinkController() {
+ this.controllerSet = false;
+ this.controller = null;
+ this.controllerX = 0;
+ this.controllerY = 0;
+ this.controllerZ = 0;
+ }
+
+ @Override
+ public void onBlockDestroyed() {
+ // When this block is destroyed, explicitly unlink it from the controller if there is any.
+ GT_MetaTileEntity_PurificationPlant controller = getController();
+ if (controller != null) {
+ controller.unregisterLinkedUnit(this);
+ }
+ super.onBlockDestroyed();
+ }
+
+ @Override
+ public String[] getInfoData() {
+ var ret = new ArrayList<String>();
+ // If this purification unit is linked to a controller, add this info to the scanner output.
+ if (getController() != null) {
+ ret.add(
+ "This Purification Unit is linked to the Water Purification Plant at " + controllerX
+ + ", "
+ + controllerY
+ + ", "
+ + controllerZ
+ + ".");
+
+ // If recipe is running, display success chance
+ if (this.mMaxProgresstime != 0) {
+ ret.add(
+ "Success chance: " + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(this.calculateFinalSuccessChance())
+ + "%"
+ + EnumChatFormatting.RESET);
+ }
+
+ } else ret.add("This Purification Unit is not linked to any Water Purification Plant.");
+ ret.add("Current parallel: " + EnumChatFormatting.YELLOW + this.effectiveParallel);
+ return ret.toArray(new String[0]);
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ NBTTagCompound tag = accessor.getNBTData();
+
+ // Display linked controller in Waila.
+ if (tag.getBoolean("linked")) {
+ currenttip.add(
+ EnumChatFormatting.AQUA + "Linked to Purification Plant at "
+ + EnumChatFormatting.WHITE
+ + tag.getInteger("controllerX")
+ + ", "
+ + tag.getInteger("controllerY")
+ + ", "
+ + tag.getInteger("controllerZ")
+ + EnumChatFormatting.RESET);
+ } else {
+ currenttip.add(EnumChatFormatting.AQUA + "Unlinked");
+ }
+
+ super.getWailaBody(itemStack, currenttip, accessor, config);
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+
+ tag.setBoolean("linked", getController() != null);
+ if (getController() != null) {
+ tag.setInteger("controllerX", controllerX);
+ tag.setInteger("controllerY", controllerY);
+ tag.setInteger("controllerZ", controllerZ);
+ }
+
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ }
+
+ public PurificationUnitStatus status() {
+ if (!this.mMachine) {
+ return PurificationUnitStatus.INCOMPLETE_STRUCTURE;
+ } else if (!this.isAllowedToWork()) {
+ return PurificationUnitStatus.DISABLED;
+ } else {
+ return PurificationUnitStatus.ONLINE;
+ }
+ }
+
+ /**
+ * Creates all widgets needed to sync this unit's status with the client
+ */
+ public Widget makeSyncerWidgets() {
+ return new MultiChildWidget()
+ .addChild(new FakeSyncWidget.BooleanSyncer(() -> this.mMachine, machine -> this.mMachine = machine))
+ .addChild(new FakeSyncWidget.BooleanSyncer(this::isAllowedToWork, _work -> {}));
+ }
+
+ private static final int PARALLEL_WINDOW_ID = 10;
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ buildContext.addSyncedWindow(PARALLEL_WINDOW_ID, this::createParallelWindow);
+ builder.widget(new ButtonWidget().setOnClick((clickData, widget) -> {
+ if (!widget.isClient()) {
+ widget.getContext()
+ .openSyncedWindow(PARALLEL_WINDOW_ID);
+ }
+ })
+ .setPlayClickSound(true)
+ .setBackground(() -> {
+ List<UITexture> ret = new ArrayList<>();
+ ret.add(GT_UITextures.BUTTON_STANDARD);
+ ret.add(GT_UITextures.OVERLAY_BUTTON_BATCH_MODE_ON);
+ return ret.toArray(new IDrawable[0]);
+ })
+ .addTooltip(translateToLocal("GT5U.tpm.parallelwindow"))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setPos(174, 112)
+ .setSize(16, 16));
+ super.addUIWidgets(builder, buildContext);
+ }
+
+ protected ModularWindow createParallelWindow(final EntityPlayer player) {
+ final int WIDTH = 158;
+ final int HEIGHT = 52;
+ final int PARENT_WIDTH = getGUIWidth();
+ final int PARENT_HEIGHT = getGUIHeight();
+ ModularWindow.Builder builder = ModularWindow.builder(WIDTH, HEIGHT);
+ builder.setBackground(GT_UITextures.BACKGROUND_SINGLEBLOCK_DEFAULT);
+ builder.setGuiTint(getGUIColorization());
+ builder.setDraggable(true);
+ builder.setPos(
+ (size, window) -> Alignment.Center.getAlignedPos(size, new Size(PARENT_WIDTH, PARENT_HEIGHT))
+ .add(
+ Alignment.BottomRight.getAlignedPos(new Size(PARENT_WIDTH, PARENT_HEIGHT), new Size(WIDTH, HEIGHT))
+ .add(WIDTH - 3, 0)
+ .subtract(0, 10)));
+ builder.widget(
+ TextWidget.localised("GTPP.CC.parallel")
+ .setPos(3, 4)
+ .setSize(150, 20))
+ .widget(
+ new NumericWidget().setSetter(val -> maxParallel = (int) val)
+ .setGetter(() -> maxParallel)
+ .setBounds(1, Integer.MAX_VALUE)
+ .setDefaultValue(1)
+ .setScrollValues(1, 4, 64)
+ .setTextAlignment(Alignment.Center)
+ .setTextColor(Color.WHITE.normal)
+ .setSize(150, 18)
+ .setPos(4, 25)
+ .setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD)
+ .attachSyncer(
+ new FakeSyncWidget.IntegerSyncer(() -> maxParallel, (val) -> maxParallel = val),
+ builder));
+ return builder.build();
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return false;
+ }
+
+ @Override
+ public Set<VoidingMode> getAllowedVoidingModes() {
+ return EnumSet.of(VoidingMode.VOID_NONE);
+ }
+
+ @Override
+ protected boolean supportsCraftingMEBuffer() {
+ return false;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitClarifier.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitClarifier.java
new file mode 100644
index 0000000000..7823fa0ba7
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitClarifier.java
@@ -0,0 +1,333 @@
+package gregtech.common.tileentities.machines.multi.purification;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.lazy;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.GT_Values.AuthorNotAPenguin;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_DISTILLATION_TOWER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_DISTILLATION_TOWER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_DISTILLATION_TOWER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_DISTILLATION_TOWER_GLOW;
+import static gregtech.api.util.GT_StructureUtility.ofFrame;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ThreadLocalRandom;
+
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.google.common.collect.ImmutableList;
+import com.gtnewhorizon.structurelib.alignment.IAlignmentLimits;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.Textures;
+import gregtech.api.enums.TierEU;
+import gregtech.api.interfaces.IHatchElement;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_StructureUtility;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_PurificationUnitClarifier
+ extends GT_MetaTileEntity_PurificationUnitBase<GT_MetaTileEntity_PurificationUnitClarifier>
+ implements ISurvivalConstructable {
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final String STRUCTURE_PIECE_MAIN_SURVIVAL = "main_survival";
+
+ private static final int STRUCTURE_X_OFFSET = 5;
+ private static final int STRUCTURE_Y_OFFSET = 2;
+ private static final int STRUCTURE_Z_OFFSET = 1;
+
+ // Chance that the filter is damaged every cycle.
+ public static final float FILTER_DAMAGE_RATE = 20.0f;
+
+ private static final int CASING_TEXTURE_INDEX = getTextureIndex(GregTech_API.sBlockCasings9, 5);
+
+ private static final String[][] structure =
+ // spotless:off
+ new String[][] {
+ { " ", " ", " ", " " },
+ { " ", " AAAAA ", " AH~HA ", " AAAAA " },
+ { " ", " A A ", " AWWWWWA ", " AAAAAAA " },
+ { " ", " A A ", " AWWWWWWWA ", " AAAAAAAAA " },
+ { " ", "A A", "AWWWCCCWWWA", "AAAAFFFAAAA" },
+ { " DDD ", "A A", "HWWCWWWCWWH", "AAAFFFFFAAA" },
+ { "DDDDDBD ", "A B A", "AWWCWBWCWWA", "AAAFFFFFAAA" },
+ { " DDD ", "A A", "HWWCWWWCWWH", "AAAFFFFFAAA" },
+ { " ", "A A", "AWWWCCCWWWA", "AAAAFFFAAAA" },
+ { " ", " A A ", " AWWWWWWWA ", " AAAAAAAAA " },
+ { " ", " A A ", " AWWWWWA ", " AAAAAAA " },
+ { " ", " AAAAA ", " AHAHA ", " AAAAA " } };
+ // spotless:on
+
+ private static final IStructureDefinition<GT_MetaTileEntity_PurificationUnitClarifier> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_PurificationUnitClarifier>builder()
+ .addShape(STRUCTURE_PIECE_MAIN, structure)
+ .addShape(
+ STRUCTURE_PIECE_MAIN_SURVIVAL,
+ Arrays.stream(structure)
+ .map(
+ sa -> Arrays.stream(sa)
+ .map(s -> s.replaceAll("W", " "))
+ .toArray(String[]::new))
+ .toArray(String[][]::new))
+ // Hatches
+ .addElement(
+ 'H',
+ ofChain(
+ lazy(
+ t -> GT_StructureUtility.<GT_MetaTileEntity_PurificationUnitClarifier>buildHatchAdder()
+ .atLeastList(t.getAllowedHatches())
+ .casingIndex(CASING_TEXTURE_INDEX)
+ .dot(1)
+ .build()),
+ // Reinforced Sterile Water Plant Casing
+ ofBlock(GregTech_API.sBlockCasings9, 5)))
+ // Reinforced Sterile Water Plant Casing
+ .addElement('A', ofBlock(GregTech_API.sBlockCasings9, 5))
+ // PTFE pipe casing
+ .addElement('B', ofBlock(GregTech_API.sBlockCasings8, 1))
+ .addElement('C', ofFrame(Materials.Iridium))
+ .addElement('D', ofFrame(Materials.DamascusSteel))
+ .addElement('W', ofChain(ofBlock(Blocks.water, 0)))
+ // Filter machine casing
+ .addElement('F', ofBlock(GregTech_API.sBlockCasings3, 11))
+ .build();
+
+ public GT_MetaTileEntity_PurificationUnitClarifier(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_PurificationUnitClarifier(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public int getWaterTier() {
+ return 1;
+ }
+
+ @Override
+ public long getBasePowerUsage() {
+ return TierEU.RECIPE_LuV;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.purificationClarifierRecipes;
+ }
+
+ @NotNull
+ @Override
+ public CheckRecipeResult overrideRecipeCheck() {
+ // Clarifier needs to check item inputs from recipe as well to find filter item
+ return findRecipeForInputs(
+ this.storedFluids.toArray(new FluidStack[] {}),
+ this.getStoredInputs()
+ .toArray(new ItemStack[] {}));
+ }
+
+ @Override
+ public void depleteRecipeInputs() {
+ super.depleteRecipeInputs();
+
+ // Now do random roll to determine if the filter should be destroyed
+ ThreadLocalRandom random = ThreadLocalRandom.current();
+ int roll = random.nextInt(1, 101);
+ if (roll < FILTER_DAMAGE_RATE) {
+ this.depleteInput(this.currentRecipe.mInputs[0]);
+ }
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_PurificationUnitClarifier> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ protected IAlignmentLimits getInitialAlignmentLimits() {
+ // Rotated sifter not allowed, water will flow out.
+ return (d, r, f) -> d.offsetY == 0 && r.isNotRotated() && !f.isVerticallyFliped();
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Purification Unit")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.BOLD
+ + "Water Tier: "
+ + EnumChatFormatting.WHITE
+ + GT_Utility.formatNumbers(getWaterTier())
+ + EnumChatFormatting.RESET)
+ .addInfo("Controller block for the Clarifier Purification Unit.")
+ .addInfo("Must be linked to a Purification Plant using a data stick to work.")
+ .addSeparator()
+ .addInfo("Requires a filter made of Activated Carbon to work.")
+ .addInfo(
+ "Every cycle, has a " + EnumChatFormatting.RED
+ + GT_Utility.formatNumbers(FILTER_DAMAGE_RATE)
+ + "%"
+ + EnumChatFormatting.GRAY
+ + " chance to destroy the filter.")
+ .addSeparator()
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "The first step to acquiring purified water is to filter out macroscopic contaminants through the")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "use of large physical filters. As more contaminants are captured, the efficacy of the filter")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "decreases so continual replacements must be supplied to maintain full function of the Clarifier.")
+ .addInfo(AuthorNotAPenguin)
+ .beginStructureBlock(11, 4, 11, false)
+ .addSeparator()
+ .addCasingInfoRangeColored(
+ "Reinforced Sterile Water Plant Casing",
+ EnumChatFormatting.GRAY,
+ 123,
+ 131,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Filter Machine Casing",
+ EnumChatFormatting.GRAY,
+ 21,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Iridium Frame Box",
+ EnumChatFormatting.GRAY,
+ 21,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Damascus Steel Frame Box",
+ EnumChatFormatting.GRAY,
+ 12,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored("PTFE Pipe Casing", EnumChatFormatting.GRAY, 3, EnumChatFormatting.GOLD, false)
+ .addController("Front center")
+ .addInputBus(EnumChatFormatting.GOLD + "1" + EnumChatFormatting.GRAY + "+", 1)
+ .addOutputBus(EnumChatFormatting.GOLD + "1" + EnumChatFormatting.GRAY + "+", 1)
+ .addInputHatch(EnumChatFormatting.GOLD + "1" + EnumChatFormatting.GRAY + "+", 1)
+ .addOutputHatch(EnumChatFormatting.GOLD + "1" + EnumChatFormatting.GRAY + "+", 1)
+ .addStructureInfo("Requires water to be placed in the structure.")
+ .addStructureInfo("Use the StructureLib Hologram Projector to build the structure.")
+ .toolTipFinisher("GregTech");
+ return tt;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(
+ STRUCTURE_PIECE_MAIN,
+ stackSize,
+ hintsOnly,
+ STRUCTURE_X_OFFSET,
+ STRUCTURE_Y_OFFSET,
+ STRUCTURE_Z_OFFSET);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ int built = survivialBuildPiece(
+ STRUCTURE_PIECE_MAIN_SURVIVAL,
+ stackSize,
+ STRUCTURE_X_OFFSET,
+ STRUCTURE_Y_OFFSET,
+ STRUCTURE_Z_OFFSET,
+ elementBudget,
+ env,
+ true);
+ if (built == -1) {
+ GT_Utility.sendChatToPlayer(
+ env.getActor(),
+ EnumChatFormatting.GREEN + "Auto placing done ! Now go place the water yourself !");
+ return 0;
+ }
+ return built;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_PurificationUnitClarifier(this.mName);
+ }
+
+ private List<IHatchElement<? super GT_MetaTileEntity_PurificationUnitClarifier>> getAllowedHatches() {
+ return ImmutableList.of(InputBus, InputHatch, OutputBus, OutputHatch);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ if (sideDirection == facingDirection) {
+ if (active) return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_TEXTURE_INDEX),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_DISTILLATION_TOWER_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_DISTILLATION_TOWER_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_TEXTURE_INDEX),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_DISTILLATION_TOWER)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_DISTILLATION_TOWER_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_TEXTURE_INDEX) };
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ if (!checkPiece(STRUCTURE_PIECE_MAIN, STRUCTURE_X_OFFSET, STRUCTURE_Y_OFFSET, STRUCTURE_Z_OFFSET)) return false;
+ return super.checkMachine(aBaseMetaTileEntity, aStack);
+ }
+
+ @Override
+ protected ResourceLocation getActivitySoundLoop() {
+ return SoundResource.GT_MACHINES_PURIFICATIONPLANT_LOOP.resourceLocation;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitDegasifier.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitDegasifier.java
new file mode 100644
index 0000000000..5a8e96ea48
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitDegasifier.java
@@ -0,0 +1,832 @@
+package gregtech.common.tileentities.machines.multi.purification;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.lazy;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.onElementPass;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.GT_Values.AuthorNotAPenguin;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_GLOW;
+import static gregtech.api.util.GT_StructureUtility.ofFrame;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.function.Supplier;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidStack;
+
+import com.github.bartimaeusnek.bartworks.system.material.WerkstoffLoader;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.Textures;
+import gregtech.api.enums.TierEU;
+import gregtech.api.interfaces.IHatchElement;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Input;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_MultiInput;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_StructureUtility;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.IGT_HatchAdder;
+
+public class GT_MetaTileEntity_PurificationUnitDegasifier
+ extends GT_MetaTileEntity_PurificationUnitBase<GT_MetaTileEntity_PurificationUnitDegasifier>
+ implements ISurvivalConstructable {
+
+ private static final int CASING_INDEX_MAIN = getTextureIndex(GregTech_API.sBlockCasings9, 11);
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+
+ // spotless:off
+ private static final String[][] structure = new String[][] {
+ { " AAAAA ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " AAAAA " },
+ { " AAAAAAAAA ", " AAAAA ", " ", " C ", " CC ", " C ", " CC ", " C ", " ", " ", " ", " C ", " CC ", " C ", " CC ", " C ", " ", " ", " ", " C ", " CC ", " C ", " CC ", " CCAA~AA ", " AAAAAAAAA " },
+ { " AAAAAAAAAAAAA ", " CAAAAAAAAA ", " BBBBB CC ", " BBBBB ", " BBBBB ", " BBB ", " B ", " ", " C ", " ", " C ", " ", " ", " ", " ", " ", " C ", " ", " B C ", " BBB ", " BBBBB ", " BBBBB ", " BBBBB ", " AAAAAAAAA ", " AAAAAAAAAAAAA " },
+ { " AAAAAAAAAAAAA ", " AAAAAAAAAAAC ", " C BBB BB ", " BB BB ", " B B ", " BB BB ", " BBB BBB ", " BBBBB ", " BBB ", " C C ", " ", " ", " ", " ", " ", " ", " BBB ", " C BBBBB C ", " BBB BBB ", " BB BB ", " B B ", " BB BB ", " BB BB ", " AAAAAAAAAAA ", " AAAAAAAAAAAAA " },
+ { " AAAAAAAAAAAAAAA ", " AAAAAAAAAAAAA ", " CB B ", " B B ", " B B ", " B B ", " B B ", " BB BB ", " BB BB C ", " BBBBB ", " C BBB ", " ", " ", " ", " BBB ", " BBBBB ", " BB BB C ", " BB BB ", " C B B ", " B B ", " B B ", " B B ", " B B ", " AAAAAAAAAAAAAC ", " AAAAAAAAAAAAAAA " },
+ { " AAAAAAAAAAAAAAA ", " AAAAAAAAAAAAA ", " B B ", " C B B ", " B B ", " B B ", " B B ", " B B C ", " B B ", " B B ", " B B ", " C BBB ", " ", " BBB ", " B B ", " B B C ", " B B ", " B B ", " B B ", " C B B ", " B B ", " B B ", " B B ", " AAAAAAAAAAAAAC ", " AAAAAAAAAAAAAAA " },
+ { "AAAAAAAAAAAAAAAAA", " AAAAAAAAAAAAAAA ", " B B ", " B B ", " CB B ", " B B ", " B B C ", " B B ", " B B ", " B B ", " B B ", " BBBBB ", " C BBB ", " BBBBB ", " B B C ", " B B ", " B B ", " B B ", " B B ", " B B ", " CB B ", " B B ", " B BC ", " AAAAAAAAAAAAAAA ", "AAAAAAAAAAAAAAAAA" },
+ { "AAAAAAAAAAAAAAAAA", " AAAAAAAAAAAAAAA ", " B B ", " B B ", " CB B ", " B B ", " B B C ", " B B ", " B B ", " B B ", " B B ", " BB BB ", " C BBBBB ", " BB BB ", " B B C ", " B B ", " B B ", " B B ", " B B ", " B B ", " CB B ", " B B ", " B BC ", " AAAAAAAAAAAAAAA ", "AAAAAAAAAAAAAAAAA" },
+ { "AAAAAAAAAAAAAAAAA", " AAAAAAAAAAAAAAA ", " B B ", " B B ", " B B ", " CB BC ", " B B ", " B B ", " B B ", " B B ", " B B ", " BB BB ", " BBBBB ", " C BB BB C ", " B B ", " B B ", " B B ", " B B ", " B B ", " B B ", " B B ", " CB BC ", " B B ", " AAAAAAAAAAAAAAA ", "AAAAAAAAAAAAAAAAA" },
+ { "AAAAAAAAAAAAAAAAA", " AAAAAAAAAAAAAAA ", " B B ", " B B ", " B BC ", " B B ", " C B B ", " B B ", " B B ", " B B ", " B B ", " BB BB ", " BBBBB C ", " BB BB ", " C B B ", " B B ", " B B ", " B B ", " B B ", " B B ", " B BC ", " B B ", " CB B ", " AAAAAAAAAAAAAAA ", "AAAAAAAAAAAAAAAAA" },
+ { "AAAAAAAAAAAAAAAAA", " AAAAAAAAAAAAAAA ", " B B ", " B B ", " B BC ", " B B ", " C B B ", " B B ", " B B ", " B B ", " B B ", " BBBBB ", " BBB C ", " BBBBB ", " C B B ", " B B ", " B B ", " B B ", " B B ", " B B ", " B BC ", " B B ", " CB B ", " AAAAAAAAAAAAAAA ", "AAAAAAAAAAAAAAAAA" },
+ { " AAAAAAAAAAAAAAA ", " AAAAAAAAAAAAA ", " B B ", " B B C ", " B B ", " B B ", " B B ", " C B B ", " B B ", " B B ", " B B ", " BBB C ", " ", " BBB ", " B B ", " C B B ", " B B ", " B B ", " B B ", " B B C ", " B B ", " B B ", " B B ", " CAAAAAAAAAAAAA ", " AAAAAAAAAAAAAAA " },
+ { " AAAAAAAAAAAAAAA ", " AAAAAAAAAAAAA ", " B BC ", " B B ", " B B ", " B B ", " B B ", " BB BB ", " C BB BB ", " BBBBB ", " BBB C ", " ", " ", " ", " BBB ", " BBBBB ", " C BB BB ", " BB BB ", " B B C ", " B B ", " B B ", " B B ", " B B ", " CAAAAAAAAAAAAA ", " AAAAAAAAAAAAAAA " },
+ { " AAAAAAAAAAAAA ", " CAAAAAAAAAAA ", " BB BB C ", " BB BB ", " B B ", " BB BB ", " BBB BBB ", " BBBBB ", " BBB ", " C C ", " ", " ", " ", " ", " ", " ", " BBB ", " C BBBBB C ", " BBB BBB ", " BB BB ", " B B ", " BB BB ", " BB BB ", " AAAAAAAAAAA ", " AAAAAAAAAAAAA " },
+ { " AAAAAAAAAAAAA ", " AAAAAAAAAC ", " CC BBBBB ", " BBBBB ", " BBBBB ", " BBB ", " B ", " ", " C ", " ", " C ", " ", " ", " ", " ", " ", " C ", " ", " C B ", " BBB ", " BBBBB ", " BBBBB ", " BBBBB ", " AAAAAAAAA ", " AAAAAAAAAAAAA " },
+ { " AAAAAAAAA ", " AAAAA ", " ", " C ", " CC ", " C ", " CC ", " C ", " ", " ", " ", " C ", " CC ", " C ", " CC ", " C ", " ", " ", " ", " C ", " CC ", " C ", " CC ", " AAAAACC ", " AAAAAAAAA " },
+ { " AAAAA ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " AAAAA " } };
+ // spotless:on
+
+ private int casingCount = 0;
+ private static final int MIN_CASING = 780;
+
+ private static final IStructureDefinition<GT_MetaTileEntity_PurificationUnitDegasifier> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_PurificationUnitDegasifier>builder()
+ .addShape(STRUCTURE_PIECE_MAIN, structure)
+ .addElement(
+ 'A',
+ ofChain(
+ lazy(
+ t -> GT_StructureUtility.<GT_MetaTileEntity_PurificationUnitDegasifier>buildHatchAdder()
+ .atLeastList(Arrays.asList(InputHatch, OutputHatch, SpecialHatchElement.ControlHatch))
+ .casingIndex(CASING_INDEX_MAIN)
+ .dot(1)
+ .cacheHint(() -> "Input Hatch, Output Hatch, Control Hatch")
+ .build()),
+ onElementPass(t -> t.casingCount++, ofBlock(GregTech_API.sBlockCasings9, 11))))
+ // Omni-purpose infinity fused glass
+ .addElement('B', ofBlock(GregTech_API.sBlockGlass1, 2))
+ .addElement('C', ofFrame(Materials.Bedrockium))
+ .build();
+
+ private static final int STRUCTURE_X_OFFSET = 8;
+ private static final int STRUCTURE_Y_OFFSET = 23;
+ private static final int STRUCTURE_Z_OFFSET = 1;
+
+ // Supplier because werkstoff loads later than multiblock controllers... fml
+ private static final Supplier<FluidStack[]> INERT_GASES = () -> new FluidStack[] { Materials.Helium.getGas(10000L),
+ WerkstoffLoader.Neon.getFluidOrGas(7500), WerkstoffLoader.Krypton.getFluidOrGas(5000),
+ WerkstoffLoader.Xenon.getFluidOrGas(2500) };
+
+ private static final class SuperconductorMaterial {
+
+ public FluidStack fluid;
+ public float multiplier;
+
+ SuperconductorMaterial(FluidStack fluid, float multiplier) {
+ this.fluid = fluid;
+ this.multiplier = multiplier;
+ }
+ }
+
+ private static final long SUPERCON_FLUID_AMOUNT = 1440L;
+
+ private static final Supplier<SuperconductorMaterial[]> SUPERCONDUCTOR_MATERIALS = () -> new SuperconductorMaterial[] {
+ new SuperconductorMaterial(Materials.Longasssuperconductornameforuvwire.getMolten(SUPERCON_FLUID_AMOUNT), 1.0f),
+ new SuperconductorMaterial(
+ Materials.Longasssuperconductornameforuhvwire.getMolten(SUPERCON_FLUID_AMOUNT),
+ 1.25f),
+ new SuperconductorMaterial(Materials.SuperconductorUEVBase.getMolten(SUPERCON_FLUID_AMOUNT), 1.5f),
+ new SuperconductorMaterial(Materials.SuperconductorUIVBase.getMolten(SUPERCON_FLUID_AMOUNT), 1.75f),
+ new SuperconductorMaterial(Materials.SuperconductorUMVBase.getMolten(SUPERCON_FLUID_AMOUNT), 2.0f), };
+
+ private static final FluidStack CATALYST_FLUID = Materials.Neutronium.getMolten(4608L);
+ private static final FluidStack COOLANT_FLUID = Materials.SuperCoolant.getFluid(10000L);
+
+ private static final long CONSUME_INTERVAL = 20;
+
+ private static class ControlSignal {
+
+ private byte signal;
+
+ public ControlSignal(byte sig) {
+ signal = sig;
+ }
+
+ public void randomize() {
+ ThreadLocalRandom random = ThreadLocalRandom.current();
+ // We want to give the final bit a lower chance at being on, since this bypasses most of the automation.
+ // If this bit has a 50% chance of being on, you could opt to not automate the degasser at all and never
+ // insert anything. This way you still get 50% output which might just be good enough.
+
+ // To do this weighting, we simply only generate the lower 3 bits, and then with a smaller chance we add
+ // 8 to the result
+ signal = (byte) random.nextInt(0, 8);
+ if (random.nextInt(0, 5) == 0) {
+ signal += 8;
+ }
+ }
+
+ public boolean getBit(int bit) {
+ if (bit < 0 || bit > 3) {
+ throw new IllegalArgumentException("Invalid bit index for degasser control signal");
+ }
+
+ // Shift signal so the requested bit is in the lowermost bit
+ // Then only keep the lowermost bit
+ // Then test if this bit is on.
+ return ((signal >> bit) & 1) == 1;
+ }
+
+ public byte getSignal() {
+ return signal;
+ }
+
+ // Get integer value representing control bits 1 and 2
+ public int getControlBit12() {
+ return (signal >> 1) & 0b11;
+ }
+
+ public boolean isZero() {
+ return signal == (byte) 0;
+ }
+
+ @Override
+ public String toString() {
+ return Integer.toBinaryString(((int) signal) & 0b1111);
+ }
+ }
+
+ private static class ControlBitStatus {
+
+ public FluidStack stack;
+ public boolean satisfied;
+
+ public ControlBitStatus(FluidStack stack, boolean satisfied) {
+ this.stack = stack;
+ this.satisfied = satisfied;
+ }
+ }
+
+ private ControlSignal controlSignal = new ControlSignal((byte) 0);
+
+ private final HashMap<Fluid, FluidStack> insertedStuffThisCycle = new HashMap<>();
+
+ private float outputMultiplier = 1.0f;
+
+ private GT_MetaTileEntity_Hatch_DegasifierControlHatch controlHatch = null;
+
+ public GT_MetaTileEntity_PurificationUnitDegasifier(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ protected GT_MetaTileEntity_PurificationUnitDegasifier(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_PurificationUnitDegasifier(mName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, ForgeDirection facing,
+ int colorIndex, boolean active, boolean redstoneLevel) {
+ if (side == facing) {
+ if (active) return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX_MAIN),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX_MAIN),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX_MAIN) };
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(
+ STRUCTURE_PIECE_MAIN,
+ stackSize,
+ hintsOnly,
+ STRUCTURE_X_OFFSET,
+ STRUCTURE_Y_OFFSET,
+ STRUCTURE_Z_OFFSET);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ return survivialBuildPiece(
+ STRUCTURE_PIECE_MAIN,
+ stackSize,
+ STRUCTURE_X_OFFSET,
+ STRUCTURE_Y_OFFSET,
+ STRUCTURE_Z_OFFSET,
+ elementBudget,
+ env,
+ true);
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_PurificationUnitDegasifier> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Purification Unit")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.BOLD
+ + "Water Tier: "
+ + EnumChatFormatting.WHITE
+ + GT_Utility.formatNumbers(getWaterTier())
+ + EnumChatFormatting.RESET)
+ .addInfo("Controller block for the Residual Decontaminant Degasser Purification Unit.")
+ .addInfo("Must be linked to a Purification Plant using a data stick to work.")
+ .addSeparator()
+ .addInfo(
+ "At the start of the operation, the " + EnumChatFormatting.WHITE
+ + "Degasser Control Hatch"
+ + EnumChatFormatting.GRAY
+ + " will output a redstone signal.")
+ .addInfo("To succeed the recipe, you will need to successfully decode the instructions in the signal.")
+ .addInfo("To decode the signal, interpret the signal strength as a 4-bit number from 0-15.")
+ .addInfo("Denote the lowest bit as bit 1, and the highest as bit 4.")
+ .addSeparator()
+ .addInfo(
+ EnumChatFormatting.WHITE + ""
+ + EnumChatFormatting.BOLD
+ + "Bit 1: "
+ + EnumChatFormatting.BLUE
+ + ""
+ + EnumChatFormatting.BOLD
+ + "Ozone Sparging by Inert Gas")
+ .addInfo(
+ "If this bit is on, you must insert an " + EnumChatFormatting.WHITE
+ + "inert gas"
+ + EnumChatFormatting.GRAY
+ + " into the machine.")
+ .addInfo(
+ "To determine which gas to insert, interpret bits " + EnumChatFormatting.WHITE
+ + "2-3"
+ + EnumChatFormatting.GRAY
+ + " as a 2-bit number.")
+ .addInfo(
+ EnumChatFormatting.GRAY + "0: "
+ + EnumChatFormatting.RED
+ + "10000L "
+ + EnumChatFormatting.WHITE
+ + "Helium"
+ + EnumChatFormatting.GRAY
+ + " / "
+ + "1: "
+ + EnumChatFormatting.RED
+ + "7500L "
+ + EnumChatFormatting.WHITE
+ + "Neon"
+ + EnumChatFormatting.GRAY
+ + " / "
+ + "2: "
+ + EnumChatFormatting.RED
+ + "5000L "
+ + EnumChatFormatting.WHITE
+ + "Krypton"
+ + EnumChatFormatting.GRAY
+ + " / "
+ + "3: "
+ + EnumChatFormatting.RED
+ + "2500L "
+ + EnumChatFormatting.WHITE
+ + "Xenon")
+ .addSeparator()
+ .addInfo(
+ EnumChatFormatting.WHITE + ""
+ + EnumChatFormatting.BOLD
+ + "Bit 2: "
+ + EnumChatFormatting.BLUE
+ + ""
+ + EnumChatFormatting.BOLD
+ + "Superconductive Deionization")
+ .addInfo(
+ "If this bit is on, you must insert " + EnumChatFormatting.RED
+ + "1440L "
+ + EnumChatFormatting.WHITE
+ + "Molten Superconductor Base.")
+ .addInfo("Using higher tier superconductor provides bonus output.")
+ .addInfo(
+ "Output multiplier: " + EnumChatFormatting.DARK_GREEN
+ + "UV"
+ + EnumChatFormatting.GRAY
+ + ": "
+ + EnumChatFormatting.WHITE
+ + "1x"
+ + EnumChatFormatting.GRAY
+ + " / "
+ + EnumChatFormatting.DARK_RED
+ + "UHV"
+ + EnumChatFormatting.GRAY
+ + ": "
+ + EnumChatFormatting.WHITE
+ + "1.25x"
+ + EnumChatFormatting.GRAY
+ + " / "
+ + EnumChatFormatting.DARK_PURPLE
+ + "UEV"
+ + EnumChatFormatting.GRAY
+ + ": "
+ + EnumChatFormatting.WHITE
+ + "1.5x"
+ + EnumChatFormatting.GRAY
+ + " / "
+ + EnumChatFormatting.DARK_BLUE
+ + ""
+ + EnumChatFormatting.BOLD
+ + "UIV"
+ + EnumChatFormatting.GRAY
+ + ": "
+ + EnumChatFormatting.WHITE
+ + "1.75x"
+ + EnumChatFormatting.GRAY
+ + " / "
+ + EnumChatFormatting.RED
+ + ""
+ + EnumChatFormatting.BOLD
+ + "UMV"
+ + EnumChatFormatting.GRAY
+ + ": "
+ + EnumChatFormatting.WHITE
+ + "2x")
+ .addSeparator()
+ .addInfo(
+ EnumChatFormatting.WHITE + ""
+ + EnumChatFormatting.BOLD
+ + "Bit 3: "
+ + EnumChatFormatting.BLUE
+ + ""
+ + EnumChatFormatting.BOLD
+ + "Gravitationally-Generated Differential Vacuum Extraction")
+ .addInfo(
+ "If this bit is on, you must insert " + EnumChatFormatting.RED
+ + "4608L "
+ + EnumChatFormatting.WHITE
+ + "Molten Neutronium")
+ .addSeparator()
+ .addInfo(
+ EnumChatFormatting.WHITE + ""
+ + EnumChatFormatting.BOLD
+ + "Bit 4: "
+ + EnumChatFormatting.BLUE
+ + ""
+ + EnumChatFormatting.BOLD
+ + "Seldonian Settlement Process")
+ .addInfo(
+ "If this bit is on," + EnumChatFormatting.RED
+ + " DISREGARD "
+ + EnumChatFormatting.GRAY
+ + "all other bits and do not insert anything.")
+ .addSeparator()
+ .addInfo(
+ EnumChatFormatting.WHITE + ""
+ + EnumChatFormatting.BOLD
+ + "No bits: "
+ + EnumChatFormatting.BLUE
+ + ""
+ + EnumChatFormatting.BOLD
+ + "Machine Overload")
+ .addInfo("In rare cases, the machine may overload and output no control signal at all.")
+ .addInfo(
+ "To prevent machine damage, insert " + EnumChatFormatting.RED
+ + "10000L "
+ + EnumChatFormatting.WHITE
+ + "Super Coolant.")
+ .addSeparator()
+ .addInfo("The recipe can only succeed if the entire signal is decoded correctly.")
+ .addInfo("Inserting any fluid not requested by the signal will always void the recipe.")
+ .addSeparator()
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "The penultimate stage of water purification, step seven, is an irregular series of complex")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "processes designed to remove any residual materials left by the decontaminants from the previous")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "steps such as any energetic ions, acids, clarifiers, or gasses. Depending on what the Degasser")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "detects in the water, it will request various materials to complete the processes listed above.")
+ .addInfo(AuthorNotAPenguin)
+ .beginStructureBlock(17, 25, 17, false)
+ .addCasingInfoRangeColored(
+ "Heat-Resistant Trinium Plated Casing",
+ EnumChatFormatting.GRAY,
+ MIN_CASING,
+ 803,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Omni-Purpose Infinity Fused Glass",
+ EnumChatFormatting.GRAY,
+ 622,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Bedrockium Frame Box",
+ EnumChatFormatting.GRAY,
+ 126,
+ EnumChatFormatting.GOLD,
+ false)
+ .addController("Front center")
+ .addOutputHatch(EnumChatFormatting.GOLD + "1" + EnumChatFormatting.GRAY + "+, Any Trinium Casing", 1)
+ .addInputHatch(EnumChatFormatting.GOLD + "1" + EnumChatFormatting.GRAY + "+, Any Trinium Casing", 1)
+ .addOtherStructurePart(
+ "Degasser Control Hatch",
+ EnumChatFormatting.GOLD + "1" + EnumChatFormatting.GRAY + ", Any Trinium Casing",
+ 1)
+ .toolTipFinisher("GregTech");
+ return tt;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.purificationDegasifierRecipes;
+ }
+
+ // Whether this fluid stack is accepted as a fluid in the inputs that the unit requires at runtime.
+ public static boolean isValidFluid(FluidStack stack) {
+ return stack.isFluidEqual(CATALYST_FLUID) || stack.isFluidEqual(COOLANT_FLUID)
+ || Arrays.stream(INERT_GASES.get())
+ .anyMatch(stack::isFluidEqual)
+ || Arrays.stream(SUPERCONDUCTOR_MATERIALS.get())
+ .anyMatch(mat -> stack.isFluidEqual(mat.fluid));
+ }
+
+ // Check if an exact match for this FluidStack was found in the map of inserted fluids
+ private boolean wasFluidInsertedExact(FluidStack toFind) {
+ FluidStack candidate = insertedStuffThisCycle.get(toFind.getFluid());
+ // Fluid was inserted if found and the amount matches
+ return candidate != null && candidate.amount == toFind.amount;
+ }
+
+ private ControlBitStatus isBit0Satisfied() {
+ // Check if instructions matching the first bit are satisfied.
+ // Instructions:
+ // If bit 0 is on, insert an inert gas. Bits 1-2 of the control signal determine which inert
+ // gas to insert.
+
+ if (controlSignal.getBit(0)) {
+ // Grab the gas to insert from the control bits
+ int gasToInsert = controlSignal.getControlBit12();
+ FluidStack gasStack = INERT_GASES.get()[gasToInsert];
+ // Check if it was inserted
+ if (wasFluidInsertedExact(gasStack)) return new ControlBitStatus(gasStack, true);
+ else return new ControlBitStatus(null, false);
+ }
+
+ // Bit 0 is not on, so this is always satisfied
+ return new ControlBitStatus(null, true);
+ }
+
+ private ControlBitStatus isBit1Satisfied() {
+ // Check if instructions matching the second bit (bit 1) are satisfied.
+ // Instructions:
+ // If bit 1 is on, insert molten superconductor.
+ // Higher tier superconductor gives a better bonus.
+ // Only one type of superconductor may be inserted or the operation fails,
+ // so we don't care about the order in which we find it.
+ if (controlSignal.getBit(1)) {
+ // Find the first superconductor material in the list that was inserted with an exact match
+ Optional<SuperconductorMaterial> material = Arrays.stream(SUPERCONDUCTOR_MATERIALS.get())
+ .filter(candidate -> wasFluidInsertedExact(candidate.fluid))
+ .findFirst();
+ if (material.isPresent()) {
+ // Get the material and set the output multiplier, then
+ // report success with the matching fluid.
+ SuperconductorMaterial scMaterial = material.get();
+ this.outputMultiplier = scMaterial.multiplier;
+ return new ControlBitStatus(scMaterial.fluid, true);
+ }
+ // No superconductor was inserted but bit is on fail.
+ return new ControlBitStatus(null, false);
+ }
+
+ return new ControlBitStatus(null, true);
+ }
+
+ private ControlBitStatus isBit2Satisfied() {
+ // Check if instructions matching the third bit (bit 2) are satisfied.
+ // Instructions:
+ // If bit 2 is on, insert molten neutronium.
+ if (controlSignal.getBit(2)) {
+ // If steel was inserted, return it and report success.
+ if (wasFluidInsertedExact(CATALYST_FLUID)) return new ControlBitStatus(CATALYST_FLUID, true);
+ // Otherwise report failure.
+ return new ControlBitStatus(null, false);
+ }
+
+ return new ControlBitStatus(null, true);
+ }
+
+ private ControlBitStatus isBit3Satisfied() {
+ // Check if instructions matching the fourth bit (bit 3) are satisfied.
+ // Instructions:
+ // If bit 3 is on, do not insert anything.
+ if (controlSignal.getBit(3)) {
+ // Simply check if the map of inserted fluids is empty
+ if (insertedStuffThisCycle.isEmpty()) return new ControlBitStatus(null, true);
+ return new ControlBitStatus(null, false);
+ }
+ return new ControlBitStatus(null, true);
+ }
+
+ private boolean areAllBitsSatisfied() {
+ // Check if each individual bit is satisfied.
+ // Additional instructions: If no bits are on, insert super coolant
+
+ if (controlSignal.isZero()) {
+ return wasFluidInsertedExact(COOLANT_FLUID);
+ }
+
+ ControlBitStatus bit0 = isBit0Satisfied();
+ ControlBitStatus bit1 = isBit1Satisfied();
+ ControlBitStatus bit2 = isBit2Satisfied();
+ ControlBitStatus bit3 = isBit3Satisfied();
+
+ // If bit 3 is satisfied and on, all other bits are automatically satisfied,
+ // with no fluids being allowed to be inserted.
+ if (controlSignal.getBit(3) && bit3.satisfied) {
+ bit0 = bit1 = bit2 = new ControlBitStatus(null, true);
+ }
+
+ if (bit0.satisfied && bit1.satisfied && bit2.satisfied && bit3.satisfied) {
+ // Check if the map contains any other stacks than the ones in the control bit statuses
+ for (FluidStack inserted : insertedStuffThisCycle.values()) {
+ // If the inserted stack is null, or any of the fluids required, this stack is fine.
+ if (inserted == null) continue;
+ if (bit0.stack != null && inserted.isFluidEqual(bit0.stack)) continue;
+ if (bit1.stack != null && inserted.isFluidEqual(bit1.stack)) continue;
+ if (bit2.stack != null && inserted.isFluidEqual(bit2.stack)) continue;
+ if (bit3.stack != null && inserted.isFluidEqual(bit3.stack)) continue;
+ // Otherwise it's a nonrequested stack and the recipe should fail.
+ return false;
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void startCycle(int cycleTime, int progressTime) {
+ super.startCycle(cycleTime, progressTime);
+ this.controlSignal.randomize();
+ this.insertedStuffThisCycle.clear();
+ this.outputMultiplier = 1.0f;
+ // Make sure to output the hatch control signal.
+ this.controlHatch.updateOutputSignal(this.controlSignal.getSignal());
+ }
+
+ private static ArrayList<FluidStack> getDrainableFluidsFromHatch(GT_MetaTileEntity_Hatch_Input hatch) {
+ // Need special handling for quad input hatches, otherwise it only returns the first fluid in the hatch
+ if (hatch instanceof GT_MetaTileEntity_Hatch_MultiInput) {
+ return new ArrayList<>(Arrays.asList(((GT_MetaTileEntity_Hatch_MultiInput) hatch).getStoredFluid()));
+ }
+ return new ArrayList<>(Collections.singletonList(hatch.getFluid()));
+ }
+
+ @Override
+ protected void runMachine(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.runMachine(aBaseMetaTileEntity, aTick);
+
+ // If machine is running, continuously consume all valid inputs
+ if (mMaxProgresstime > 0 && aTick % CONSUME_INTERVAL == 0) {
+ // For each hatch, check if each fluid inside is one of the valid fluids. If so, consume it all.
+ for (GT_MetaTileEntity_Hatch_Input hatch : mInputHatches) {
+ ArrayList<FluidStack> drainableFluids = getDrainableFluidsFromHatch(hatch);
+ for (FluidStack fluid : drainableFluids) {
+ if (fluid != null && isValidFluid(fluid)) {
+ // Apparently this parameter is mostly ignored, but might as well get it right.
+ ForgeDirection front = hatch.getBaseMetaTileEntity()
+ .getFrontFacing();
+ // Drain the fluid and save it
+ FluidStack drainedFluid = hatch.drain(front, fluid, true);
+ // If the fluid does not yet exist in the map, insert it.
+ // Otherwise, merge the amounts
+ insertedStuffThisCycle.merge(
+ fluid.getFluid(),
+ drainedFluid,
+ (a, b) -> new FluidStack(a.getFluid(), a.amount + b.amount));
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void addRecipeOutputs() {
+ super.addRecipeOutputs();
+ if (outputMultiplier > 1.01f) {
+ FluidStack waterOutput = currentRecipe.mFluidOutputs[0];
+ FluidStack bonusOutput = new FluidStack(
+ waterOutput.getFluid(),
+ (int) (waterOutput.amount * (outputMultiplier - 1.0f)));
+ this.addOutput(bonusOutput);
+ }
+ }
+
+ @Override
+ public float calculateFinalSuccessChance() {
+ // Success chance is 100% when all bits are satisfied, 0% otherwise.
+ if (areAllBitsSatisfied()) {
+ return 100.0f;
+ } else {
+ return 0.0f;
+ }
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public int getWaterTier() {
+ return 7;
+ }
+
+ @Override
+ public long getBasePowerUsage() {
+ return TierEU.RECIPE_UHV;
+ }
+
+ public boolean addControlHatchToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_DegasifierControlHatch) {
+ // Only allow a single control hatch, so fail structure check if there is already one
+ if (this.controlHatch == null) {
+ ((GT_MetaTileEntity_Hatch) aMetaTileEntity).updateTexture(aBaseCasingIndex);
+ this.controlHatch = (GT_MetaTileEntity_Hatch_DegasifierControlHatch) aMetaTileEntity;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ this.casingCount = 0;
+ this.controlHatch = null;
+ if (!checkPiece(STRUCTURE_PIECE_MAIN, STRUCTURE_X_OFFSET, STRUCTURE_Y_OFFSET, STRUCTURE_Z_OFFSET)) return false;
+ if (casingCount < MIN_CASING) return false;
+ return super.checkMachine(aBaseMetaTileEntity, aStack);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ controlSignal = new ControlSignal(aNBT.getByte("controlSignal"));
+ outputMultiplier = aNBT.getFloat("outputMultiplier");
+ NBTTagCompound fluidMap = aNBT.getCompoundTag("insertedFluidMap");
+ for (String key : fluidMap.func_150296_c()) {
+ FluidStack fluid = FluidStack.loadFluidStackFromNBT(fluidMap.getCompoundTag(key));
+ // Ignore if fluid failed to load, for example if the fluid ID was changed between versions
+ if (fluid == null) {
+ continue;
+ }
+ insertedStuffThisCycle.put(fluid.getFluid(), fluid);
+ }
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setByte("controlSignal", controlSignal.getSignal());
+ aNBT.setFloat("outputMultiplier", outputMultiplier);
+ NBTTagCompound fluidMap = new NBTTagCompound();
+ for (FluidStack stack : insertedStuffThisCycle.values()) {
+ NBTTagCompound compound = new NBTTagCompound();
+ stack.writeToNBT(compound);
+ fluidMap.setTag(
+ stack.getFluid()
+ .getName(),
+ compound);
+ }
+ aNBT.setTag("insertedFluidMap", fluidMap);
+ }
+
+ private static String generateInfoStringForBit(int i, ControlBitStatus status) {
+ String base = "Bit " + (i + 1) + " status: ";
+ if (status.satisfied) {
+ return base + EnumChatFormatting.GREEN + "OK";
+ } else {
+ return base + EnumChatFormatting.RED + "NOT OK";
+ }
+ }
+
+ @Override
+ public String[] getInfoData() {
+ ArrayList<String> info = new ArrayList<>(Arrays.asList(super.getInfoData()));
+ info.add("Current control signal: " + EnumChatFormatting.YELLOW + controlSignal.toString());
+ info.add("Current output multiplier: " + EnumChatFormatting.YELLOW + outputMultiplier);
+ for (FluidStack stack : insertedStuffThisCycle.values()) {
+ info.add(
+ "Fluid inserted this cycle: " + EnumChatFormatting.YELLOW
+ + stack.amount
+ + "L "
+ + stack.getLocalizedName());
+ }
+ info.add(generateInfoStringForBit(0, isBit0Satisfied()));
+ info.add(generateInfoStringForBit(1, isBit1Satisfied()));
+ info.add(generateInfoStringForBit(2, isBit2Satisfied()));
+ info.add(generateInfoStringForBit(3, isBit3Satisfied()));
+ return info.toArray(new String[] {});
+ }
+
+ private enum SpecialHatchElement implements IHatchElement<GT_MetaTileEntity_PurificationUnitDegasifier> {
+
+ ControlHatch(GT_MetaTileEntity_PurificationUnitDegasifier::addControlHatchToMachineList,
+ GT_MetaTileEntity_Hatch_DegasifierControlHatch.class) {
+
+ @Override
+ public long count(GT_MetaTileEntity_PurificationUnitDegasifier mte) {
+ return mte.controlHatch == null ? 0 : 1;
+ }
+ };
+
+ private final List<Class<? extends IMetaTileEntity>> mteClasses;
+ private final IGT_HatchAdder<GT_MetaTileEntity_PurificationUnitDegasifier> adder;
+
+ @SafeVarargs
+ SpecialHatchElement(IGT_HatchAdder<GT_MetaTileEntity_PurificationUnitDegasifier> adder,
+ Class<? extends IMetaTileEntity>... mteClasses) {
+ this.mteClasses = Collections.unmodifiableList(Arrays.asList(mteClasses));
+ this.adder = adder;
+ }
+
+ @Override
+ public List<? extends Class<? extends IMetaTileEntity>> mteClasses() {
+ return mteClasses;
+ }
+
+ public IGT_HatchAdder<? super GT_MetaTileEntity_PurificationUnitDegasifier> adder() {
+ return adder;
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitFlocculation.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitFlocculation.java
new file mode 100644
index 0000000000..3f414d0eca
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitFlocculation.java
@@ -0,0 +1,489 @@
+package gregtech.common.tileentities.machines.multi.purification;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.lazy;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlockAnyMeta;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.onElementPass;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.GT_Values.AuthorNotAPenguin;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_GLOW;
+import static gregtech.api.util.GT_RecipeBuilder.SECONDS;
+import static gregtech.api.util.GT_StructureUtility.ofFrame;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import com.google.common.collect.ImmutableList;
+import com.gtnewhorizon.structurelib.alignment.IAlignmentLimits;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.Textures;
+import gregtech.api.enums.TierEU;
+import gregtech.api.interfaces.IHatchElement;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_StructureUtility;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.shutdown.ShutDownReasonRegistry;
+
+public class GT_MetaTileEntity_PurificationUnitFlocculation
+ extends GT_MetaTileEntity_PurificationUnitBase<GT_MetaTileEntity_PurificationUnitFlocculation>
+ implements ISurvivalConstructable {
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final String STRUCTURE_PIECE_MAIN_SURVIVAL = "main_survival";
+
+ private static final int STRUCTURE_X_OFFSET = 4;
+ private static final int STRUCTURE_Y_OFFSET = 3;
+ private static final int STRUCTURE_Z_OFFSET = 0;
+
+ /**
+ * Amount of input fluid needed to boost the success chance by another level
+ */
+ public static final long INPUT_CHEMICAL_PER_LEVEL = 100000;
+ /**
+ * Amount of waste water produced for each success chance level. This matches the amount of input fluid
+ * so it can be perfectly recycled into each other.
+ */
+ private static final long WASTE_WATER_PER_LEVEL = INPUT_CHEMICAL_PER_LEVEL;
+ /**
+ * Additive boost to success chance for each level of input fluid supplied
+ */
+ public static final float SUCCESS_PER_LEVEL = 10.0f;
+ /**
+ * Amount of ticks between each tick the unit will try to consume input fluid
+ */
+ private static final int CONSUME_INTERVAL = 1 * SECONDS;
+
+ /**
+ * Fluid that needs to be supplied to boost success chance
+ */
+ private static final Materials INPUT_CHEMICAL = Materials.PolyAluminiumChloride;
+ /**
+ * Output fluid to be produced as waste. The intended behaviour is that this output fluid can be cycled
+ * semi-perfectly into the input fluid.
+ */
+ private static final Materials OUTPUT_WASTE = Materials.FlocculationWasteLiquid;
+
+ /**
+ * Total amount of input fluid consumed during this recipe cycle.
+ */
+ private long inputFluidConsumed = 0;
+
+ private static final String[][] structure = new String[][]
+ // spotless:off
+ {
+ { " ", " ", " BBBBBBB ", " BBB~BBB ", " BBBBBBB " },
+ { " ", " ", " B B ", " BWWWWWB ", " BCCCCCB " },
+ { " ", " ", " B B ", " GWWWWWG ", " BCAAACB " },
+ { " ", " ", " B B ", " GWWWWWG ", " BCAAACB " },
+ { " ", " ", " B B ", " GWWWWWG ", " BCAAACB " },
+ { " ", " EE EE ", " BE EB ", " BEWWWEB ", " BCCCCCB " },
+ { "D D", "DEE EED", "DBBBBBBBD", "DBBBBBBBD", "DBBBBBBBD" },
+ { "DD DD", "DD DD", "DD DD", "DD DD", "DD DD" }
+ };
+ // spotless:on
+
+ private static final int MAIN_CASING_INDEX = getTextureIndex(GregTech_API.sBlockCasings9, 6);
+
+ private int casingCount = 0;
+ private static final int MIN_CASING = 56;
+
+ private static final IStructureDefinition<GT_MetaTileEntity_PurificationUnitFlocculation> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_PurificationUnitFlocculation>builder()
+ .addShape(STRUCTURE_PIECE_MAIN, structure)
+ .addShape(
+ STRUCTURE_PIECE_MAIN_SURVIVAL,
+ Arrays.stream(structure)
+ .map(
+ sa -> Arrays.stream(sa)
+ .map(s -> s.replaceAll("W", " "))
+ .toArray(String[]::new))
+ .toArray(String[][]::new))
+ // Filter machine casing
+ .addElement('A', ofBlock(GregTech_API.sBlockCasings3, 11))
+ .addElement(
+ 'B',
+ ofChain(
+ lazy(
+ t -> GT_StructureUtility.<GT_MetaTileEntity_PurificationUnitFlocculation>buildHatchAdder()
+ .atLeastList(t.getAllowedHatches())
+ .casingIndex(MAIN_CASING_INDEX)
+ .dot(1)
+ .build()),
+ // Clean Flocculation Casing
+ onElementPass(t -> t.casingCount++, ofBlock(GregTech_API.sBlockCasings9, 6))))
+ // Reinforced Sterile Water Plant Casing
+ .addElement('C', ofBlock(GregTech_API.sBlockCasings9, 5))
+ // Sterile Water Plant Casing
+ .addElement('D', ofBlock(GregTech_API.sBlockCasings9, 4))
+ .addElement('E', ofFrame(Materials.Adamantium))
+ .addElement('W', ofBlock(Blocks.water, 0))
+ // Tinted industrial glass
+ .addElement('G', ofBlockAnyMeta(GregTech_API.sBlockTintedGlass))
+ .build();
+
+ List<IHatchElement<? super GT_MetaTileEntity_PurificationUnitFlocculation>> getAllowedHatches() {
+ return ImmutableList.of(InputBus, InputHatch, OutputBus, OutputHatch);
+ }
+
+ public GT_MetaTileEntity_PurificationUnitFlocculation(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_PurificationUnitFlocculation(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_PurificationUnitFlocculation(this.mName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side == aFacing) {
+ if (aActive) return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(MAIN_CASING_INDEX),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(MAIN_CASING_INDEX),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(MAIN_CASING_INDEX) };
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(
+ STRUCTURE_PIECE_MAIN,
+ stackSize,
+ hintsOnly,
+ STRUCTURE_X_OFFSET,
+ STRUCTURE_Y_OFFSET,
+ STRUCTURE_Z_OFFSET);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ int built = survivialBuildPiece(
+ STRUCTURE_PIECE_MAIN_SURVIVAL,
+ stackSize,
+ STRUCTURE_X_OFFSET,
+ STRUCTURE_Y_OFFSET,
+ STRUCTURE_Z_OFFSET,
+ elementBudget,
+ env,
+ true);
+ if (built == -1) {
+ GT_Utility.sendChatToPlayer(
+ env.getActor(),
+ EnumChatFormatting.GREEN + "Auto placing done ! Now go place the water yourself !");
+ return 0;
+ }
+ return built;
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_PurificationUnitFlocculation> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ protected IAlignmentLimits getInitialAlignmentLimits() {
+ // Do not allow rotation when water would flow out
+ return (d, r, f) -> d.offsetY == 0 && r.isNotRotated() && !f.isVerticallyFliped();
+ }
+
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ casingCount = 0;
+ if (!checkPiece(STRUCTURE_PIECE_MAIN, STRUCTURE_X_OFFSET, STRUCTURE_Y_OFFSET, STRUCTURE_Z_OFFSET)) return false;
+
+ // At most two input hatches allowed
+ if (mInputHatches.size() > 2) {
+ return false;
+ }
+
+ // At most two output hatches allowed
+ if (mOutputHatches.size() > 2) {
+ return false;
+ }
+
+ if (casingCount < MIN_CASING) return false;
+
+ return super.checkMachine(aBaseMetaTileEntity, aStack);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Purification Unit")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.BOLD
+ + "Water Tier: "
+ + EnumChatFormatting.WHITE
+ + GT_Utility.formatNumbers(getWaterTier())
+ + EnumChatFormatting.RESET)
+ .addInfo("Controller block for the Flocculation Purification Unit.")
+ .addInfo("Must be linked to a Purification Plant using a data stick to work.")
+ .addSeparator()
+ .addInfo(
+ "Supply with " + EnumChatFormatting.WHITE
+ + INPUT_CHEMICAL.mLocalizedName
+ + EnumChatFormatting.GRAY
+ + " to operate.")
+ .addInfo(
+ "Outputs " + EnumChatFormatting.WHITE
+ + OUTPUT_WASTE.mLocalizedName
+ + EnumChatFormatting.GRAY
+ + " that can be recycled.")
+ .addSeparator()
+ .addInfo(
+ "During operation, will consume ALL " + EnumChatFormatting.WHITE
+ + INPUT_CHEMICAL.mLocalizedName
+ + EnumChatFormatting.GRAY
+ + " in the input hatch.")
+ .addInfo(
+ "At the end of the recipe, for every " + EnumChatFormatting.RED
+ + INPUT_CHEMICAL_PER_LEVEL
+ + "L "
+ + EnumChatFormatting.GRAY
+ + "of "
+ + EnumChatFormatting.WHITE
+ + INPUT_CHEMICAL.mLocalizedName
+ + EnumChatFormatting.GRAY
+ + " consumed")
+ .addInfo(
+ "gain an additive " + EnumChatFormatting.RED
+ + SUCCESS_PER_LEVEL
+ + "%"
+ + EnumChatFormatting.GRAY
+ + " increase to success. If total fluid supplied is not")
+ .addInfo(
+ "a multiple of " + EnumChatFormatting.RED
+ + INPUT_CHEMICAL_PER_LEVEL
+ + "L"
+ + EnumChatFormatting.GRAY
+ + ", a penalty to success is applied using the following formula:")
+ .addInfo(EnumChatFormatting.GREEN + "Success = Success * 2^(-10 * Overflow ratio)")
+ .addSeparator()
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "Step three in purifying water is to remove microscopic contaminants such as dusts, microplastics and other")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "pollutants using a clarifying agent (In this case, polyaluminium chloride) to cause flocculation - the process")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "of aggregating dispersed suspended particles from a solution into larger clumps for further filtration.")
+ .addInfo(AuthorNotAPenguin)
+ .beginStructureBlock(7, 4, 7, false)
+ .addCasingInfoRangeColored(
+ "Slick Sterile Flocculation Casing",
+ EnumChatFormatting.GRAY,
+ MIN_CASING,
+ 65,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Sterile Water Plant Casing",
+ EnumChatFormatting.GRAY,
+ 16,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Reinforced Sterile Water Plant Casing",
+ EnumChatFormatting.GRAY,
+ 30,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Tinted Industrial Glass",
+ EnumChatFormatting.GRAY,
+ 6,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Adamantium Frame Box",
+ EnumChatFormatting.GRAY,
+ 12,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Filter Machine Casing",
+ EnumChatFormatting.GRAY,
+ 9,
+ EnumChatFormatting.GOLD,
+ false)
+ .addController("Front center")
+ .addOutputBus(EnumChatFormatting.GOLD + "1" + EnumChatFormatting.GRAY + "+", 1)
+ .addInputHatch(
+ EnumChatFormatting.GOLD + "1" + EnumChatFormatting.GRAY + "-" + EnumChatFormatting.GOLD + "2",
+ 1)
+ .addOutputHatch(
+ EnumChatFormatting.GOLD + "1" + EnumChatFormatting.GRAY + "-" + EnumChatFormatting.GOLD + "2",
+ 1)
+ .addStructureInfo("Use the StructureLib Hologram Projector to build the structure.")
+ .toolTipFinisher("GregTech");
+ return tt;
+ }
+
+ @Override
+ public void startCycle(int cycleTime, int progressTime) {
+ super.startCycle(cycleTime, progressTime);
+ // Reset amount of fluid consumed in this cycle.
+ this.inputFluidConsumed = 0;
+ }
+
+ @Override
+ public void endCycle() {
+ super.endCycle();
+ // Output waste water proportional to amount of boost levels. We do this even when the recipe fails, so you can
+ // always fully recycle.
+ // NOTE: If this process ever PRODUCES excess chlorine, there is a recipe bug.
+ int levels = calculateBoostLevels();
+ long amount = levels * WASTE_WATER_PER_LEVEL;
+ this.addFluidOutputs(new FluidStack[] { OUTPUT_WASTE.getFluid(amount) });
+ // Make sure to reset consumed fluid (again)
+ this.inputFluidConsumed = 0;
+ }
+
+ @Override
+ protected void runMachine(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.runMachine(aBaseMetaTileEntity, aTick);
+
+ // Consume all input fluid periodically, only when running
+ if (aTick % CONSUME_INTERVAL == 0 && mMaxProgresstime > 0) {
+ // Iterate over all fluids stored
+ List<FluidStack> fluids = this.getStoredFluids();
+ for (FluidStack fluid : fluids) {
+ // If this FluidStack is the input chemical, consume it all
+ if (fluid.getFluid()
+ .equals(INPUT_CHEMICAL.mFluid)) {
+ this.inputFluidConsumed += fluid.amount;
+ if (!this.depleteInput(fluid)) {
+ stopMachine(ShutDownReasonRegistry.outOfFluid(fluid));
+ }
+ }
+ }
+ }
+ }
+
+ private int calculateBoostLevels() {
+ return (int) Math.floor((float) this.inputFluidConsumed / (float) INPUT_CHEMICAL_PER_LEVEL);
+ }
+
+ @Override
+ public float calculateFinalSuccessChance() {
+ // Amount of times the required amount of input fluid was fully inserted
+ int levels = calculateBoostLevels();
+ // Target amount of fluid needed to reach this amount of boost levels
+ long targetAmount = levels * INPUT_CHEMICAL_PER_LEVEL;
+ // Amount of excess fluid inserted.
+ long overflow = inputFluidConsumed - targetAmount;
+ // Base boost chance, before applying overflow penalty
+ float boost = SUCCESS_PER_LEVEL * levels;
+ // If there was any overflow, apply an exponential penalty multiplier based on percentage overflow
+ if (overflow > 0) {
+ float overflowPct = (float) overflow / INPUT_CHEMICAL_PER_LEVEL;
+ float penaltyMultiplier = (float) Math.pow(2.0f, overflowPct * -10.0);
+ return Math.max(0.0f, (this.currentRecipeChance + boost) * penaltyMultiplier);
+ } else {
+ return Math.min(100.0f, this.currentRecipeChance + boost);
+ }
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public int getWaterTier() {
+ return 3;
+ }
+
+ @Override
+ public long getBasePowerUsage() {
+ return TierEU.RECIPE_ZPM;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.purificationFlocculationRecipes;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ ArrayList<String> infoData = new ArrayList<>(Arrays.asList(super.getInfoData()));
+ infoData.add(
+ INPUT_CHEMICAL.mLocalizedName + " consumed this cycle: "
+ + EnumChatFormatting.RED
+ + inputFluidConsumed
+ + "L");
+ return infoData.toArray(new String[] {});
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setLong("mInputFluidConsumed", inputFluidConsumed);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ this.inputFluidConsumed = aNBT.getLong("mInputFluidConsumed");
+ }
+
+ @Override
+ protected ResourceLocation getActivitySoundLoop() {
+ return SoundResource.GT_MACHINES_COAGULATION_LOOP.resourceLocation;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitOzonation.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitOzonation.java
new file mode 100644
index 0000000000..a539cd157e
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitOzonation.java
@@ -0,0 +1,299 @@
+package gregtech.common.tileentities.machines.multi.purification;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.lazy;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlockAnyMeta;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.onElementPass;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.GT_Values.AuthorNotAPenguin;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_GLOW;
+import static gregtech.api.util.GT_StructureUtility.ofFrame;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.google.common.collect.ImmutableList;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.Textures;
+import gregtech.api.enums.TierEU;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_StructureUtility;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_PurificationUnitOzonation
+ extends GT_MetaTileEntity_PurificationUnitBase<GT_MetaTileEntity_PurificationUnitOzonation>
+ implements ISurvivalConstructable {
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final String STRUCTURE_PIECE_MAIN_SURVIVAL = "main_survival";
+
+ private static final String[][] structure = new String[][] {
+ // spotless:off
+ { " ", " ", " A ", " A ", " AAA ", " AAA ", " A A ", " A A ", " A A ", " A~A " },
+ { " A ", " A ", " A A ", " A A ", "BBBBA A", "BDDBA A", "BBBBA D A", "E A D A", "E A D A", "E AAAAA" },
+ { " AAA ", " A A ", " A A", " A A", "BDDBA A", "O BA A", "BBBBA A", " C A A", " CCA A", " AAAAA" },
+ { " A ", " A ", " A A ", " A A ", "BBBBA A", "BDDBA A", "BBBBA A", "E A A", "E A A", "E AAAAA" },
+ { " ", " ", " A ", " A ", " AAA ", " AAA ", " AAA ", " AAA ", " AAA ", " AAA " } };
+ // spotless:on
+
+ private static final int MAIN_CASING_INDEX = getTextureIndex(GregTech_API.sBlockCasings9, 10);
+
+ private static final int OFFSET_X = 6;
+ private static final int OFFSET_Y = 9;
+ private static final int OFFSET_Z = 0;
+
+ /**
+ * If the player inserts more ozone gas than this amount, the multi will explode.
+ */
+ public static final int MAX_OZONE_GAS_FOR_EXPLOSION = 1000 * (int) Math.pow(2, 10);
+
+ private int casingCount = 0;
+ private static final int MIN_CASING = 96;
+
+ private static final IStructureDefinition<GT_MetaTileEntity_PurificationUnitOzonation> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_PurificationUnitOzonation>builder()
+ .addShape(STRUCTURE_PIECE_MAIN, structure)
+ // Inert Filtration Casing
+ .addElement(
+ 'A',
+ ofChain(
+ lazy(
+ t -> GT_StructureUtility.<GT_MetaTileEntity_PurificationUnitOzonation>buildHatchAdder()
+ .atLeastList(ImmutableList.of(InputHatch, OutputHatch, OutputBus))
+ .casingIndex(getTextureIndex(GregTech_API.sBlockCasings9, 10))
+ .dot(1)
+ .build()),
+ onElementPass(t -> t.casingCount++, ofBlock(GregTech_API.sBlockCasings9, 10))))
+ // High Pressure Resistant Casing (possibly placeholder name)
+ .addElement('B', ofBlock(GregTech_API.sBlockCasings9, 9))
+ // PTFE pipe casing
+ .addElement('C', ofBlock(GregTech_API.sBlockCasings8, 1))
+ // Any tinted industrial glass
+ .addElement('D', ofBlockAnyMeta(GregTech_API.sBlockTintedGlass))
+ .addElement('E', ofFrame(Materials.TungstenSteel))
+ // Ozone input hatch
+ .addElement(
+ 'O',
+ lazy(
+ t -> GT_StructureUtility.<GT_MetaTileEntity_PurificationUnitOzonation>buildHatchAdder()
+ .atLeast(InputHatch)
+ .casingIndex(getTextureIndex(GregTech_API.sBlockCasings9, 9))
+ .dot(2)
+ .buildAndChain(ofBlock(GregTech_API.sBlockCasings9, 9))))
+ .build();
+
+ public GT_MetaTileEntity_PurificationUnitOzonation(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ protected GT_MetaTileEntity_PurificationUnitOzonation(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_PurificationUnitOzonation(this.mName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side == aFacing) {
+ if (aActive) return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(MAIN_CASING_INDEX),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(MAIN_CASING_INDEX),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(MAIN_CASING_INDEX) };
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, OFFSET_X, OFFSET_Y, OFFSET_Z);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ return survivialBuildPiece(
+ STRUCTURE_PIECE_MAIN,
+ stackSize,
+ OFFSET_X,
+ OFFSET_Y,
+ OFFSET_Z,
+ elementBudget,
+ env,
+ true);
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_PurificationUnitOzonation> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Purification Unit")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.BOLD
+ + "Water Tier: "
+ + EnumChatFormatting.WHITE
+ + GT_Utility.formatNumbers(getWaterTier())
+ + EnumChatFormatting.RESET)
+ .addInfo("Controller block for the Ozonation Purification Unit.")
+ .addInfo("Must be linked to a Purification Plant using a data stick to work.")
+ .addSeparator()
+ .addInfo(
+ "Will explode if the input hatch contains more than " + EnumChatFormatting.RED
+ + MAX_OZONE_GAS_FOR_EXPLOSION
+ + "L "
+ + EnumChatFormatting.WHITE
+ + "Ozone Gas.")
+ .addInfo(
+ "Receives a " + EnumChatFormatting.RED
+ + "20%"
+ + EnumChatFormatting.GRAY
+ + " bonus to success chance for every doubling of "
+ + EnumChatFormatting.WHITE
+ + "Ozone Gas.")
+ .addSeparator()
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "The second step in water purification is ozonation, which involves injecting large quantities of small")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "bubbles of highly reactive ozone gas into the water. This removes trace element contaminants like")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "sulfur, iron and manganese, creating insoluble oxide compounds which are then filtered out.")
+ .addInfo(AuthorNotAPenguin)
+ .beginStructureBlock(9, 10, 5, false)
+ .addCasingInfoRangeColored(
+ "Inert Filtration Casing",
+ EnumChatFormatting.GRAY,
+ MIN_CASING,
+ 102,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Reactive Gas Containment Casing",
+ EnumChatFormatting.GRAY,
+ 27,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Any Tinted Industrial Glass",
+ EnumChatFormatting.GRAY,
+ 9,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Tungstensteel Frame Box",
+ EnumChatFormatting.GRAY,
+ 6,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored("PTFE Pipe Casing", EnumChatFormatting.GRAY, 3, EnumChatFormatting.GOLD, false)
+ .addOutputBus(EnumChatFormatting.GOLD + "1" + EnumChatFormatting.GRAY + "+", 1)
+ .addInputHatch(EnumChatFormatting.GOLD + "1" + EnumChatFormatting.GRAY + "+", 1)
+ .addOutputHatch(EnumChatFormatting.GOLD + "1" + EnumChatFormatting.GRAY + "+", 1)
+ .addOtherStructurePart("Input Hatch (Ozone)", EnumChatFormatting.GOLD + "1", 2)
+ .toolTipFinisher("GregTech");
+ return tt;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.purificationOzonationRecipes;
+ }
+
+ @NotNull
+ @Override
+ public CheckRecipeResult checkProcessing() {
+ // First do recipe checking logic
+ CheckRecipeResult result = super.checkProcessing();
+ if (!result.wasSuccessful()) return result;
+ // Look for ozone, blow up if more than max allowed
+ for (FluidStack fluid : this.storedFluids) {
+ if (fluid.isFluidEqual(Materials.Ozone.getGas(1L))) {
+ if (fluid.amount > MAX_OZONE_GAS_FOR_EXPLOSION) {
+ // TODO: Fix crash in hatch
+ // this.explodeMultiblock();
+ }
+ }
+ }
+ return result;
+ }
+
+ @Override
+ protected ResourceLocation getActivitySoundLoop() {
+ return SoundResource.GT_MACHINES_OZONATION_LOOP.resourceLocation;
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public int getWaterTier() {
+ return 2;
+ }
+
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ casingCount = 0;
+ if (!checkPiece(STRUCTURE_PIECE_MAIN, OFFSET_X, OFFSET_Y, OFFSET_Z)) return false;
+ if (casingCount < MIN_CASING) return false;
+ return super.checkMachine(aBaseMetaTileEntity, aStack);
+ }
+
+ @Override
+ public long getBasePowerUsage() {
+ return TierEU.RECIPE_LuV;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitParticleExtractor.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitParticleExtractor.java
new file mode 100644
index 0000000000..17945f7cc9
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitParticleExtractor.java
@@ -0,0 +1,566 @@
+package gregtech.common.tileentities.machines.multi.purification;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.lazy;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.onElementPass;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.GT_Values.AuthorNotAPenguin;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PROCESSING_ARRAY;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PROCESSING_ARRAY_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PROCESSING_ARRAY_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PROCESSING_ARRAY_GLOW;
+import static gregtech.api.util.GT_StructureUtility.ofFrame;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.concurrent.ThreadLocalRandom;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.Textures;
+import gregtech.api.enums.TierEU;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Input;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_StructureUtility;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.shutdown.ShutDownReasonRegistry;
+import gregtech.common.items.GT_MetaGenerated_Item_03;
+import gregtech.common.items.ID_MetaItem_03;
+
+public class GT_MetaTileEntity_PurificationUnitParticleExtractor
+ extends GT_MetaTileEntity_PurificationUnitBase<GT_MetaTileEntity_PurificationUnitParticleExtractor>
+ implements ISurvivalConstructable {
+
+ public static long BARYONIC_MATTER_OUTPUT = 2000L;
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final int STRUCTURE_X_OFFSET = 8;
+ private static final int STRUCTURE_Y_OFFSET = 8;
+ private static final int STRUCTURE_Z_OFFSET = 0;
+
+ static final String[][] structure = new String[][] {
+ // spotless:off
+ { " ", " ", " ", " ", " ", " ", " AAAAA ", " AAAAA ", " AA~AA ", " AAAAA ", " AAAAA ", " ", " ", " ", " ", " ", " " },
+ { " ", " E ", " E ", " E ", " E ", " E ", " AAAAA ", " AAAAA ", " EEEEEAAAAAEEEEE ", " AAAAA ", " AAAAA ", " E ", " E ", " E ", " E ", " E ", " " },
+ { " ", " E ", " ", " ", " ", " ", " CCCCC ", " CDDDC ", " E CDBDC E ", " CDDDC ", " CCCCC ", " ", " ", " ", " ", " E ", " " },
+ { " ", " E ", " ", " ", " ", " ", " ", " DDD ", " E DBD E ", " DDD ", " ", " ", " ", " ", " ", " E ", " " },
+ { " ", " E ", " ", " ", " ", " ", " ", " DDD ", " E DBD E ", " DDD ", " ", " ", " ", " ", " "," E ", " " },
+ { " ", " E ", " ", " ", " ", " ", " ", " DDD ", " E DBD E ", " DDD ", " ", " ", " ", " ", " ", " E ", " " },
+ { " AAAAA ", " AAAAA ", " CCCCC ", " ", " ", " ", "AAC AAAAA CAA", "AAC ADDDA CAA", "AAC ADBDA CAA", "AAC ADDDA CAA", "AAC AAAAA CAA", " ", " ", " ", " CCCCC ", " AAAAA ", " AAAAA " },
+ { " AAAAA ", " AAAAA ", " CDDDC ", " DDD ", " DDD ", " DDD ", "AAC ADDDA CAA", "AADDDDD DDDDDAA", "AADDDDD B DDDDDAA", "AADDDDD DDDDDAA", "AAC ADDDA CAA", " DDD ", " DDD ", " DDD ", " CDDDC ", " AAAAA ", " AAAAA " },
+ { " AAAAA ", " EEEEEAAAAAEEEEE ", " E CDBDC E ", " E DBD E ", " E DBD E ", " E DBD E ", "AAC ADBDA CAA", "AADDDDD B DDDDDAA", "AABBBBBBBBBBBBBAA", "AADDDDD B DDDDDAA", "AAC ADBDA CAA", " E DBD E ", " E DBD E ", " E DBD E ", " E CDBDC E ", " EEEEEAAAAAEEEEE ", " AAAAA " },
+ { " AAAAA ", " AAAAA ", " CDDDC ", " DDD ", " DDD ", " DDD ", "AAC ADDDA CAA", "AADDDDD DDDDDAA", "AADDDDD B DDDDDAA", "AADDDDD DDDDDAA", "AAC ADDDA CAA", " DDD ", " DDD ", " DDD ", " CDDDC ", " AAAAA ", " AAAAA " },
+ { " AAAAA ", " AAAAA ", " CCCCC ", " ", " ", " ", "AAC AAAAA CAA", "AAC ADDDA CAA", "AAC ADBDA CAA", "AAC ADDDA CAA", "AAC AAAAA CAA", " ", " ", " ", " CCCCC ", " AAAAA ", " AAAAA " },
+ { " ", " E ", " ", " ", " ", " ", " ", " DDD ", " E DBD E ", " DDD ", " ", " ", " ", " ", " ", " E ", " " },
+ { " ", " E ", " ", " ", " ", " ", " ", " DDD ", " E DBD E ", " DDD ", " ", " ", " ", " ", " ", " E ", " " },
+ { " ", " E ", " ", " ", " ", " ", " ", " DDD ", " E DBD E ", " DDD ", " ", " ", " ", " ", " ", " E ", " " },
+ { " ", " E ", " ", " ", " ", " ", " CCCCC ", " CDDDC ", " E CDBDC E ", " CDDDC ", " CCCCC ", " ", " ", " ", " ", " E ", " " },
+ { " ", " E ", " E ", " E ", " E ", " E ", " AAAAA ", " AAAAA ", " EEEEEAAAAAEEEEE ", " AAAAA ", " AAAAA ", " E ", " E ", " E ", " E ", " E ", " " },
+ { " ", " ", " ", " ", " ", " ", " AAAAA ", " AAAAA ", " AAAAA ", " AAAAA ", " AAAAA ", " ", " ", " ", " ", " ", " " } };
+ // spotless:on
+
+ // Dimensionally transcendent casing (placeholder)
+ private static final int CASING_INDEX_MAIN = getTextureIndex(GregTech_API.sBlockCasings10, 2);
+
+ private static final IStructureDefinition<GT_MetaTileEntity_PurificationUnitParticleExtractor> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_PurificationUnitParticleExtractor>builder()
+ .addShape(STRUCTURE_PIECE_MAIN, structure)
+ // Quark Exclusion Casing
+ .addElement(
+ 'A',
+ ofChain(
+ lazy(
+ t -> GT_StructureUtility.<GT_MetaTileEntity_PurificationUnitParticleExtractor>buildHatchAdder()
+ .atLeastList(Arrays.asList(InputBus, OutputBus, InputHatch, OutputHatch))
+ .dot(1)
+ .casingIndex(CASING_INDEX_MAIN)
+ .build()),
+ onElementPass(t -> t.numCasings++, ofBlock(GregTech_API.sBlockCasings10, 2))))
+ // Particle Beam Guidance Pipe Casing
+ .addElement('B', ofBlock(GregTech_API.sBlockCasings9, 14))
+ // Femtometer-Calibrated Particle Beam Casing
+ .addElement('C', ofBlock(GregTech_API.sBlockCasings9, 15))
+ // Non-Photonic Matter Exclusion Glass
+ .addElement('D', ofBlock(GregTech_API.sBlockGlass1, 3))
+ .addElement('E', ofFrame(Materials.Bedrockium))
+ .build();
+
+ private static class CatalystCombination {
+
+ public ItemStack firstCatalyst;
+ public ItemStack secondCatalyst;
+
+ public static ItemList[] CATALYST_ITEMS = new ItemList[] { ItemList.Quark_Creation_Catalyst_Up,
+ ItemList.Quark_Creation_Catalyst_Down, ItemList.Quark_Creation_Catalyst_Bottom,
+ ItemList.Quark_Creation_Catalyst_Top, ItemList.Quark_Creation_Catalyst_Strange,
+ ItemList.Quark_Creation_Catalyst_Charm };
+
+ public CatalystCombination(ItemStack first, ItemStack second) {
+ firstCatalyst = first;
+ secondCatalyst = second;
+ }
+
+ public boolean matches(ItemStack a, ItemStack b) {
+ return (a.isItemEqual(firstCatalyst) && b.isItemEqual(secondCatalyst))
+ || (b.isItemEqual(firstCatalyst) && a.isItemEqual(secondCatalyst));
+ }
+
+ public NBTTagCompound saveToNBT() {
+ NBTTagCompound nbt = new NBTTagCompound();
+ NBTTagCompound first = new NBTTagCompound();
+ NBTTagCompound second = new NBTTagCompound();
+ firstCatalyst.writeToNBT(first);
+ secondCatalyst.writeToNBT(second);
+ nbt.setTag("first", first);
+ nbt.setTag("second", second);
+ return nbt;
+ }
+
+ public static CatalystCombination readFromNBT(NBTTagCompound nbt) {
+ NBTTagCompound first = nbt.getCompoundTag("first");
+ NBTTagCompound second = nbt.getCompoundTag("second");
+ return new CatalystCombination(
+ ItemStack.loadItemStackFromNBT(first),
+ ItemStack.loadItemStackFromNBT(second));
+ }
+ }
+
+ private static CatalystCombination generateNewCombination() {
+ ThreadLocalRandom random = ThreadLocalRandom.current();
+ // Generate two unique indices into the list
+ int firstIndex = random.nextInt(0, CatalystCombination.CATALYST_ITEMS.length);
+ int secondIndex = random.nextInt(0, CatalystCombination.CATALYST_ITEMS.length);
+ while (secondIndex == firstIndex) {
+ secondIndex = random.nextInt(0, CatalystCombination.CATALYST_ITEMS.length);
+ }
+
+ return new CatalystCombination(
+ CatalystCombination.CATALYST_ITEMS[firstIndex].get(1),
+ CatalystCombination.CATALYST_ITEMS[secondIndex].get(1));
+ }
+
+ private CatalystCombination currentCombination = null;
+
+ private ArrayList<ItemStack> insertedCatalysts = new ArrayList<>();
+
+ private static final long CATALYST_BASE_COST = 144L;
+
+ private int correctStartIndex = -1;
+ private int numCasings = 0;
+ private static final int MIN_CASINGS = 300;
+
+ public GT_MetaTileEntity_PurificationUnitParticleExtractor(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_PurificationUnitParticleExtractor(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_PurificationUnitParticleExtractor(mName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, ForgeDirection facing,
+ int colorIndex, boolean active, boolean redstoneLevel) {
+ if (side == facing) {
+ if (active) return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX_MAIN),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_PROCESSING_ARRAY_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_PROCESSING_ARRAY_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX_MAIN),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_PROCESSING_ARRAY)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_PROCESSING_ARRAY_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX_MAIN) };
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(
+ STRUCTURE_PIECE_MAIN,
+ stackSize,
+ hintsOnly,
+ STRUCTURE_X_OFFSET,
+ STRUCTURE_Y_OFFSET,
+ STRUCTURE_Z_OFFSET);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ return survivialBuildPiece(
+ STRUCTURE_PIECE_MAIN,
+ stackSize,
+ STRUCTURE_X_OFFSET,
+ STRUCTURE_Y_OFFSET,
+ STRUCTURE_Z_OFFSET,
+ elementBudget,
+ env,
+ true);
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_PurificationUnitParticleExtractor> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ numCasings = 0;
+ if (!checkPiece(STRUCTURE_PIECE_MAIN, STRUCTURE_X_OFFSET, STRUCTURE_Y_OFFSET, STRUCTURE_Z_OFFSET)) return false;
+ if (numCasings < MIN_CASINGS) return false;
+ return super.checkMachine(aBaseMetaTileEntity, aStack);
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Purification Unit")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.BOLD
+ + "Water Tier: "
+ + EnumChatFormatting.WHITE
+ + GT_Utility.formatNumbers(getWaterTier())
+ + EnumChatFormatting.RESET)
+ .addInfo("Controller block for the Absolute Baryonic Perfection Purification Unit.")
+ .addInfo("Must be linked to a Purification Plant using a data stick to work.")
+ .addSeparator()
+ .addInfo(
+ "Insert " + EnumChatFormatting.WHITE
+ + "Quark Releasing Catalysts "
+ + EnumChatFormatting.GRAY
+ + "into the input bus while running.")
+ .addInfo(
+ "Every recipe cycle, a different combination of " + EnumChatFormatting.RED
+ + "2"
+ + EnumChatFormatting.GRAY
+ + " different "
+ + EnumChatFormatting.WHITE
+ + "Quark Releasing Catalysts")
+ .addInfo("will correctly identify the lone quark and succeed the recipe.")
+ .addSeparator()
+ .addInfo(
+ "Every " + EnumChatFormatting.RED
+ + "20"
+ + EnumChatFormatting.GRAY
+ + " ticks, consumes ALL catalysts in the input bus.")
+ .addInfo(
+ "The base cost of inserting a catalyst is " + EnumChatFormatting.RED
+ + CATALYST_BASE_COST
+ + "L"
+ + EnumChatFormatting.WHITE
+ + " Molten Infinity")
+ .addInfo("For every duplicate occurrence of an inserted catalyst in the sequence, this cost is doubled.")
+ .addSeparator()
+ .addInfo("Keeps track of the entire sequence of catalysts inserted this recipe.")
+ .addInfo(
+ "If the correct catalyst combination is in the sequence of inserted catalysts, immediately outputs "
+ + EnumChatFormatting.WHITE
+ + "Stabilised Baryonic Matter")
+ .addInfo("At the end of the recipe, all incorrectly inserted catalysts are returned in the output bus.")
+ .addSeparator()
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "The final stage of water purification goes beyond subatomic particles and identifies the smallest")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "possible imperfections within the baryons themselves. By correctly identifying which pairs of quark")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "flavors are required, the unit will activate the catalysts, stabilizing the errant particles.")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "This ultimately creates both Stabilised Baryonic Matter and, most importantly, absolutely perfectly purified water.")
+ .addInfo(AuthorNotAPenguin)
+ .beginStructureBlock(17, 17, 17, false)
+ .addCasingInfoMinColored(
+ "Quark Exclusion Casing",
+ EnumChatFormatting.GRAY,
+ MIN_CASINGS,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Femtometer-Calibrated Particle Beam Casing",
+ EnumChatFormatting.GRAY,
+ 96,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Particle Beam Guidance Pipe Casing",
+ EnumChatFormatting.GRAY,
+ 37,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Non-Photonic Matter Exclusion Glass",
+ EnumChatFormatting.GRAY,
+ 240,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Bedrockium Frame Box",
+ EnumChatFormatting.GRAY,
+ 108,
+ EnumChatFormatting.GOLD,
+ false)
+ .addController("Front Center")
+ .addInputBus("Any Quark Exclusion Casing", 1)
+ .addInputHatch("Any Quark Exclusion Casing", 1)
+ .addOutputBus("Any Quark Exclusion Casing", 1)
+ .addOutputHatch("Any Quark Exclusion Casing", 1)
+ .toolTipFinisher("GregTech");
+ return tt;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.purificationParticleExtractionRecipes;
+ }
+
+ @Override
+ public void startCycle(int cycleTime, int progressTime) {
+ super.startCycle(cycleTime, progressTime);
+ this.insertedCatalysts.clear();
+ this.currentCombination = generateNewCombination();
+ correctStartIndex = -1;
+ }
+
+ private boolean isCatalyst(ItemStack stack) {
+ if (stack.getItem() instanceof GT_MetaGenerated_Item_03) {
+ int meta = stack.getItemDamage() - 32000; // why, greg.
+ return meta >= ID_MetaItem_03.Quark_Creation_Catalyst_Up.ID
+ && meta <= ID_MetaItem_03.Quark_Creation_Catalyst_Top.ID;
+ }
+ return false;
+ }
+
+ @Override
+ public void endCycle() {
+ super.endCycle();
+ // Output incorrect indices unchanged, the spent ones will follow if recipe was successful from the actual
+ // recipe outputs
+ for (int i = 0; i < insertedCatalysts.size(); ++i) {
+ if (i == correctStartIndex || i == correctStartIndex + 1) continue;
+
+ addOutput(insertedCatalysts.get(i));
+ }
+ }
+
+ @Override
+ public float calculateFinalSuccessChance() {
+ // Only succeed if correct combination was inserted
+ if (correctStartIndex != -1) return 100.0f;
+ else return 0.0f;
+ }
+
+ private int calculateCatalystCost(ItemStack newCatalyst) {
+ // Count number of previously inserted catalysts
+ int count = 0;
+ for (ItemStack cat : this.insertedCatalysts) {
+ // We already assume that newCatalyst is a valid catalyst item
+ if (cat.getItemDamage() == newCatalyst.getItemDamage()) {
+ ++count;
+ }
+ }
+ // Cost is exponential in function of amount of duplicate catalysts
+ return (int) (Math.pow(2, count) * CATALYST_BASE_COST);
+ }
+
+ // Returns the first index of a valid combination, or -1 if there is no valid combination in the sequence
+ public int checkSequence() {
+ // Loop over the entire sequence and check if any pair contains a valid combination
+ for (int i = 0; i < insertedCatalysts.size() - 1; ++i) {
+ ItemStack first = insertedCatalysts.get(i);
+ ItemStack second = insertedCatalysts.get(i + 1);
+ // Found a match, return its starting index
+ if (currentCombination.matches(first, second)) {
+ return i;
+ }
+ }
+ // No match found, return -1
+ return -1;
+ }
+
+ @Override
+ protected void runMachine(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.runMachine(aBaseMetaTileEntity, aTick);
+ // Every 20 ticks, add all catalysts from the input bus to the internal inventory.
+ if (mMaxProgresstime > 0 && aTick % 20 == 0) {
+ ArrayList<ItemStack> storedInputs = getStoredInputs();
+ // For each stack in the input, check if it is a valid catalyst item and if so consume it
+ for (ItemStack stack : storedInputs) {
+ if (isCatalyst(stack)) {
+ // Try to deplete catalyst cost first
+ int cost = calculateCatalystCost(stack);
+ FluidStack inputCost = Materials.Infinity.getMolten(cost);
+ // Drain the input cost directly from a hatch since we are not inside
+ // recipe processing
+ boolean drained = false;
+ for (GT_MetaTileEntity_Hatch_Input hatch : this.mInputHatches) {
+ FluidStack drainedStack = hatch.drain(ForgeDirection.UNKNOWN, inputCost, true);
+ if (drainedStack != null && drainedStack.amount == inputCost.amount) {
+ drained = true;
+ break;
+ }
+ }
+ // If we could not drain, stop the machine
+ if (!drained) {
+ stopMachine(ShutDownReasonRegistry.outOfFluid(inputCost));
+ return;
+ }
+ // Now add the catalysts to the list, one by one since there may be multiples and we want to
+ // keep them as single entries in the list
+ for (int i = 0; i < stack.stackSize; ++i) {
+ ItemStack singleStack = new ItemStack(stack.getItem(), 1, stack.getItemDamage());
+ this.insertedCatalysts.add(singleStack);
+ }
+ // Then deplete the entire stack
+ this.depleteInput(stack);
+ }
+ }
+
+ // Only do this check if we didn't find a correct combination yet
+ if (correctStartIndex != -1) return;
+
+ // Now check the sequence for a correct combination
+ correctStartIndex = checkSequence();
+ // If we found something, immediately output stable baryonic matter
+ if (correctStartIndex != -1) addOutput(Materials.StableBaryonicMatter.getFluid(BARYONIC_MATTER_OUTPUT));
+ }
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ NBTTagCompound insertedNBT = new NBTTagCompound();
+ for (int i = 0; i < insertedCatalysts.size(); ++i) {
+ ItemStack inserted = insertedCatalysts.get(i);
+ NBTTagCompound itemNBT = new NBTTagCompound();
+ inserted.writeToNBT(itemNBT);
+ insertedNBT.setTag(Integer.toString(i), itemNBT);
+ }
+ aNBT.setTag("insertedItems", insertedNBT);
+ aNBT.setInteger("correctStartIndex", correctStartIndex);
+ super.saveNBTData(aNBT);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ // Generate a random combination on load
+ currentCombination = generateNewCombination();
+ if (aNBT.hasKey("insertedItems")) {
+ NBTTagCompound insertedList = aNBT.getCompoundTag("insertedItems");
+ // Initialize empty list with correct size
+ this.insertedCatalysts = new ArrayList<>(
+ Collections.nCopies(
+ insertedList.func_150296_c()
+ .size(),
+ null));
+ for (String key : insertedList.func_150296_c()) {
+ NBTTagCompound itemCompound = insertedList.getCompoundTag(key);
+ int index = Integer.parseInt(key);
+ this.insertedCatalysts.set(index, ItemStack.loadItemStackFromNBT(itemCompound));
+ }
+ }
+ if (aNBT.hasKey("correctStartIndex")) {
+ correctStartIndex = aNBT.getInteger("correctStartIndex");
+ }
+ super.loadNBTData(aNBT);
+ }
+
+ private String getCorrectlyDecodedString() {
+ if (correctStartIndex != -1) {
+ return EnumChatFormatting.GREEN + "Yes";
+ }
+ return EnumChatFormatting.RED + "No";
+ }
+
+ public EnumChatFormatting getQuarkColor(ItemStack stack) {
+ int meta = stack.getItemDamage() - 32000;
+ if (meta == ID_MetaItem_03.Quark_Creation_Catalyst_Up.ID) return EnumChatFormatting.BLUE;
+ if (meta == ID_MetaItem_03.Quark_Creation_Catalyst_Down.ID) return EnumChatFormatting.LIGHT_PURPLE;
+ if (meta == ID_MetaItem_03.Quark_Creation_Catalyst_Strange.ID) return EnumChatFormatting.YELLOW;
+ if (meta == ID_MetaItem_03.Quark_Creation_Catalyst_Charm.ID) return EnumChatFormatting.GREEN;
+ if (meta == ID_MetaItem_03.Quark_Creation_Catalyst_Bottom.ID) return EnumChatFormatting.AQUA;
+ if (meta == ID_MetaItem_03.Quark_Creation_Catalyst_Top.ID) return EnumChatFormatting.RED;
+ return EnumChatFormatting.GRAY;
+ }
+
+ public String[] getInfoData() {
+ ArrayList<String> info = new ArrayList<>(Arrays.asList(super.getInfoData()));
+ info.add("Catalyst insertion history for this recipe cycle");
+ for (int i = 0; i < insertedCatalysts.size(); ++i) {
+ ItemStack stack = insertedCatalysts.get(i);
+ String name = stack.getDisplayName();
+ String[] split = name.split("-");
+ info.add(
+ EnumChatFormatting.YELLOW + ""
+ + (i + 1)
+ + ": "
+ + getQuarkColor(stack)
+ + split[0]
+ + EnumChatFormatting.GRAY
+ + "-"
+ + split[1]);
+ }
+ info.add("Quark Combination correctly identified: " + getCorrectlyDecodedString());
+ return info.toArray(new String[] {});
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public int getWaterTier() {
+ return 8;
+ }
+
+ @Override
+ public long getBasePowerUsage() {
+ return TierEU.RECIPE_UEV;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitPhAdjustment.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitPhAdjustment.java
new file mode 100644
index 0000000000..99495dc484
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitPhAdjustment.java
@@ -0,0 +1,612 @@
+package gregtech.common.tileentities.machines.multi.purification;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.lazy;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.GT_Values.AuthorNotAPenguin;
+import static gregtech.api.enums.Mods.GoodGenerator;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_GLOW;
+import static gregtech.api.util.GT_RecipeBuilder.SECONDS;
+import static gregtech.api.util.GT_StructureUtility.ofFrame;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ThreadLocalRandom;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidRegistry;
+import net.minecraftforge.fluids.FluidStack;
+
+import com.google.common.collect.ImmutableList;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.Textures;
+import gregtech.api.enums.TierEU;
+import gregtech.api.interfaces.IHatchElement;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Input;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_InputBus;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_StructureUtility;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.IGT_HatchAdder;
+import gregtech.api.util.shutdown.SimpleShutDownReason;
+
+public class GT_MetaTileEntity_PurificationUnitPhAdjustment
+ extends GT_MetaTileEntity_PurificationUnitBase<GT_MetaTileEntity_PurificationUnitPhAdjustment>
+ implements ISurvivalConstructable {
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final int STRUCTURE_X_OFFSET = 7;
+ private static final int STRUCTURE_Y_OFFSET = 4;
+ private static final int STRUCTURE_Z_OFFSET = 1;
+
+ private static final String[][] structure = new String[][] {
+ // spotless:off
+ { "E E E E", "EAAAE EAAAE", "EAGAE EAHAE", "EAGAE EAHAE", "EAGAE EAHAE", "EAAAE EAAAE" },
+ { " AAA AAA ", "A A A A", "A A A A", "A A A A", "A ABB~BBA A", "AAAAA AAAAA" },
+ { " AXA AYA ", "A A A A", "G A A H", "G ABBBBBA H", "G H", "AAAAABRBRBAAAAA" },
+ { " AAA AAA ", "A A A A", "A A A A", "A A A A", "A AIIIIIA A", "AAAAA AAAAA" },
+ { "E E E E", "EAAAE EAAAE", "EAGAE EAHAE", "EAGAE EAHAE", "EAGAE EAHAE", "EAAAE EAAAE" } };
+ // spotless:on
+
+ private static final int CASING_INDEX_MIDDLE = getTextureIndex(GregTech_API.sBlockCasings9, 7);
+ private static final int CASING_INDEX_TOWER = getTextureIndex(GregTech_API.sBlockCasings9, 8);
+
+ /**
+ * The current pH value of the water inside the multiblock
+ */
+ private float currentpHValue = 0.0f;
+
+ /**
+ * The multiblock will try to consume catalyst every CONSUME_INTERVAL ticks.
+ */
+ private static final int CONSUME_INTERVAL = 1 * SECONDS;
+
+ /**
+ * Maximum deviation the initial pH value can have away from the neutral value.
+ */
+ private static final float INITIAL_PH_DEVIATION = 2.5f;
+
+ /**
+ * pH value of entirely pH neutral water.
+ */
+ private static final float PH_NEUTRAL_VALUE = 7.0f;
+
+ /**
+ * Maximum deviation from the neutral value that is allowed for the recipe to succeed.
+ */
+ private static final float PH_MAX_DEVIATION = 0.05f;
+
+ /**
+ * Change in pH value for each piece of alkaline dust supplied.
+ */
+ public static final float PH_PER_ALKALINE_DUST = 0.01f;
+
+ /**
+ * Change in pH value for every 10L of acid supplied.
+ */
+ public static final float PH_PER_10_ACID_LITER = -0.01f;
+
+ /**
+ * Alkaline catalyst material
+ */
+ public static final Materials ALKALINE_MATERIAL = Materials.SodiumHydroxide;
+
+ /**
+ * Acidic catalyst material
+ */
+ public static final Materials ACIDIC_MATERIAL = Materials.HydrochloricAcid;
+
+ /**
+ * The input hatch for the acidic material
+ */
+ private GT_MetaTileEntity_Hatch_Input acidInputHatch;
+ /**
+ * The input bus for the alkaline material
+ */
+ private GT_MetaTileEntity_Hatch_InputBus alkalineInputBus;
+
+ /**
+ * List of all placed sensor hatches in the multi, so we can update them with the proper pH value when it changes.
+ */
+ private final ArrayList<GT_MetaTileEntity_pHSensor> sensorHatches = new ArrayList<>();
+
+ private static final IStructureDefinition<GT_MetaTileEntity_PurificationUnitPhAdjustment> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_PurificationUnitPhAdjustment>builder()
+ .addShape(STRUCTURE_PIECE_MAIN, structure)
+ // Extreme Corrosion Resistant Casing
+ .addElement('A', ofBlock(GregTech_API.sBlockCasings9, 8))
+ // Naquadah Reinforced Water Plant Casing
+ .addElement('B', ofBlock(GregTech_API.sBlockCasings9, 7))
+ .addElement('E', ofFrame(Materials.NaquadahAlloy))
+ // pH Resistant Glass
+ .addElement('G', ofBlock(GregTech_API.sBlockGlass1, 0))
+ .addElement('H', ofBlock(GregTech_API.sBlockGlass1, 0))
+ // Regular I/O hatches
+ .addElement(
+ 'I',
+ ofChain(
+ lazy(
+ t -> GT_StructureUtility.<GT_MetaTileEntity_PurificationUnitPhAdjustment>buildHatchAdder()
+ .atLeastList(t.getAllowedHatches())
+ .dot(1)
+ .casingIndex(CASING_INDEX_MIDDLE)
+ .build()),
+ // Naquadah Reinforced Water Plant Casing
+ ofBlock(GregTech_API.sBlockCasings9, 7)))
+ .addElement(
+ 'R',
+ ofChain(
+ lazy(
+ t -> GT_StructureUtility.<GT_MetaTileEntity_PurificationUnitPhAdjustment>buildHatchAdder()
+ .atLeast(SpecialHatchElement.PhSensor)
+ .dot(2)
+ .cacheHint(() -> "pH Sensor Hatch")
+ .casingIndex(CASING_INDEX_MIDDLE)
+ .build()),
+ // Naquadah Reinforced Water Plant Casing
+ ofBlock(GregTech_API.sBlockCasings9, 7)))
+ // Special I/O hatches
+ .addElement(
+ 'X',
+ lazy(
+ t -> GT_StructureUtility.<GT_MetaTileEntity_PurificationUnitPhAdjustment>buildHatchAdder()
+ .atLeast(InputBus)
+ .dot(3)
+ .adder(GT_MetaTileEntity_PurificationUnitPhAdjustment::addAlkalineBusToMachineList)
+ .cacheHint(() -> "Input Bus (" + ALKALINE_MATERIAL.mLocalizedName + ")")
+ .casingIndex(CASING_INDEX_TOWER)
+ .allowOnly(ForgeDirection.UP)
+ .build()))
+ .addElement(
+ 'Y',
+ lazy(
+ t -> GT_StructureUtility.<GT_MetaTileEntity_PurificationUnitPhAdjustment>buildHatchAdder()
+ .atLeast(InputHatch)
+ .dot(4)
+ .adder(GT_MetaTileEntity_PurificationUnitPhAdjustment::addAcidHatchToMachineList)
+ .cacheHint(() -> "Input Hatch (" + ACIDIC_MATERIAL.mLocalizedName + ")")
+ .casingIndex(CASING_INDEX_TOWER)
+ .allowOnly(ForgeDirection.UP)
+ .build()))
+ .build();
+
+ private List<IHatchElement<? super GT_MetaTileEntity_PurificationUnitPhAdjustment>> getAllowedHatches() {
+ return ImmutableList.of(InputHatch, OutputHatch);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side == aFacing) {
+ if (aActive) return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX_MIDDLE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX_MIDDLE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX_MIDDLE) };
+ }
+
+ public GT_MetaTileEntity_PurificationUnitPhAdjustment(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_PurificationUnitPhAdjustment(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_PurificationUnitPhAdjustment(this.mName);
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(
+ STRUCTURE_PIECE_MAIN,
+ stackSize,
+ hintsOnly,
+ STRUCTURE_X_OFFSET,
+ STRUCTURE_Y_OFFSET,
+ STRUCTURE_Z_OFFSET);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ return survivialBuildPiece(
+ STRUCTURE_PIECE_MAIN,
+ stackSize,
+ STRUCTURE_X_OFFSET,
+ STRUCTURE_Y_OFFSET,
+ STRUCTURE_Z_OFFSET,
+ elementBudget,
+ env,
+ true);
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_PurificationUnitPhAdjustment> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.purificationPhAdjustmentRecipes;
+ }
+
+ public boolean addAcidHatchToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Input) {
+ ((GT_MetaTileEntity_Hatch) aMetaTileEntity).updateTexture(aBaseCasingIndex);
+ ((GT_MetaTileEntity_Hatch_Input) aMetaTileEntity).mRecipeMap = null;
+ acidInputHatch = (GT_MetaTileEntity_Hatch_Input) aMetaTileEntity;
+ return true;
+ }
+ return false;
+ }
+
+ public boolean addAlkalineBusToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_InputBus) {
+ ((GT_MetaTileEntity_Hatch) aMetaTileEntity).updateTexture(aBaseCasingIndex);
+ ((GT_MetaTileEntity_Hatch_InputBus) aMetaTileEntity).mRecipeMap = null;
+ alkalineInputBus = (GT_MetaTileEntity_Hatch_InputBus) aMetaTileEntity;
+ return true;
+ }
+ return false;
+ }
+
+ public boolean addSensorHatchToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_pHSensor) {
+ ((GT_MetaTileEntity_Hatch) aMetaTileEntity).updateTexture(aBaseCasingIndex);
+ return this.sensorHatches.add((GT_MetaTileEntity_pHSensor) aMetaTileEntity);
+ }
+ return false;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Purification Unit")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.BOLD
+ + "Water Tier: "
+ + EnumChatFormatting.WHITE
+ + GT_Utility.formatNumbers(getWaterTier())
+ + EnumChatFormatting.RESET)
+ .addInfo("Controller block for the pH Neutralization Purification Unit.")
+ .addInfo("Must be linked to a Purification Plant using a data stick to work.")
+ .addSeparator()
+ .addInfo(
+ "Initial pH value every cycle varies from " + EnumChatFormatting.RED
+ + (PH_NEUTRAL_VALUE - INITIAL_PH_DEVIATION)
+ + EnumChatFormatting.GRAY
+ + " - "
+ + EnumChatFormatting.RED
+ + (PH_NEUTRAL_VALUE + INITIAL_PH_DEVIATION)
+ + " pH"
+ + EnumChatFormatting.GRAY
+ + ".")
+ .addInfo(
+ "If the pH value is within " + EnumChatFormatting.RED
+ + PH_MAX_DEVIATION
+ + " pH "
+ + EnumChatFormatting.GRAY
+ + "of 7.0 pH at the end of the cycle, the recipe always succeeds.")
+ .addInfo("Otherwise, the recipe always fails.")
+ .addInfo("Use a pH Sensor Hatch to read the current pH value.")
+ .addInfo("For safety, the machine will shut down if the pH goes below 0 or exceeds 14.")
+ .addSeparator()
+ .addInfo(
+ "Every " + EnumChatFormatting.RED
+ + CONSUME_INTERVAL
+ + EnumChatFormatting.GRAY
+ + " ticks, consumes ALL "
+ + EnumChatFormatting.WHITE
+ + ALKALINE_MATERIAL.mLocalizedName
+ + EnumChatFormatting.GRAY
+ + " and "
+ + EnumChatFormatting.WHITE
+ + ACIDIC_MATERIAL.mLocalizedName
+ + EnumChatFormatting.GRAY
+ + " in the special hatches.")
+ .addInfo(
+ EnumChatFormatting.RED + "Raises "
+ + EnumChatFormatting.GRAY
+ + "the pH value by "
+ + EnumChatFormatting.RED
+ + PH_PER_ALKALINE_DUST
+ + " pH "
+ + EnumChatFormatting.GRAY
+ + "per piece of "
+ + EnumChatFormatting.WHITE
+ + ALKALINE_MATERIAL.getDust(1)
+ .getDisplayName()
+ + EnumChatFormatting.GRAY
+ + ".")
+ .addInfo(
+ EnumChatFormatting.RED + "Lowers "
+ + EnumChatFormatting.GRAY
+ + "the pH value by "
+ + EnumChatFormatting.RED
+ + -PH_PER_10_ACID_LITER
+ + " pH "
+ + EnumChatFormatting.GRAY
+ + "per "
+ + EnumChatFormatting.RED
+ + "10L "
+ + EnumChatFormatting.GRAY
+ + "of "
+ + EnumChatFormatting.WHITE
+ + ACIDIC_MATERIAL.getFluid(1L)
+ .getLocalizedName()
+ + EnumChatFormatting.GRAY
+ + ".")
+ .addSeparator()
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "The fourth step of water purification is to neutralize the solution and bring its pH to exactly 7, rendering")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "the solution inert with no hydrogen ion activity beyond water’s natural amphiproticity. Acids and bases from soils")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "and geology cause natural alkalinity variations in water which can cause corrosive reactions with sensitive")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "materials. This necessitates the use of the corresponding neutralizing agents to pH balance the water.")
+ .addInfo(AuthorNotAPenguin)
+ .beginStructureBlock(7, 4, 7, false)
+ .addCasingInfoExactlyColored(
+ "Stabilized Naquadah Water Plant Casing",
+ EnumChatFormatting.GRAY,
+ 15,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Chemical Grade Glass",
+ EnumChatFormatting.GRAY,
+ 18,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Naquadah Alloy Frame Box",
+ EnumChatFormatting.GRAY,
+ 48,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Inert Neutralization Water Plant Casing",
+ EnumChatFormatting.GRAY,
+ 67 * 2,
+ EnumChatFormatting.GOLD,
+ false)
+ .addController("Front center")
+ .addOtherStructurePart("Input Hatch (Water)", EnumChatFormatting.GOLD + "1+", 1)
+ .addOtherStructurePart("Output Hatch", EnumChatFormatting.GOLD + "1", 1)
+ .addOtherStructurePart("pH Sensor Hatch", EnumChatFormatting.GOLD + "2", 2)
+ .addOtherStructurePart("Input Bus (Sodium Hydroxide)", EnumChatFormatting.GOLD + "1", 3)
+ .addOtherStructurePart("Input Hatch (Hydrochloric Acid)", EnumChatFormatting.GOLD + "1", 4)
+ .addStructureInfo("Use the StructureLib Hologram Projector to build the structure.")
+ .toolTipFinisher("GregTech");
+ return tt;
+ }
+
+ @Override
+ public void startCycle(int cycleTime, int progressTime) {
+ super.startCycle(cycleTime, progressTime);
+ // Randomize initial pH value
+ ThreadLocalRandom random = ThreadLocalRandom.current();
+ // Generate random integer in [-RNG_PRECISION, RNG_PRECISION]
+ final int RNG_PRECISION = 1000;
+ int rng = random.nextInt(-RNG_PRECISION, RNG_PRECISION);
+ // Remap to [-1.0, 1.0] and then to [-INITIAL_PH_DEVIATION, INITIAL_PH_DEVIATION]
+ float deviation = ((float) rng / RNG_PRECISION) * INITIAL_PH_DEVIATION;
+ // Round to 2 digits
+ this.currentpHValue = Math.round((PH_NEUTRAL_VALUE + deviation) * 100.0f) / 100.0f;
+ }
+
+ @Override
+ protected void runMachine(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.runMachine(aBaseMetaTileEntity, aTick);
+ // Eat all acid and alkaline material every second
+ if (mMaxProgresstime > 0 && aTick % CONSUME_INTERVAL == 0) {
+ // Important that we drain backwards, since draining stacks can auto-sort the bus
+ long totalAlkalineDrained = 0;
+ for (int i = alkalineInputBus.getSizeInventory() - 1; i >= 0; --i) {
+ ItemStack stack = alkalineInputBus.getStackInSlot(i);
+ // If this ItemStack is the alkaline material, drain it entirely and record the amount drained
+ if (stack != null && stack.isItemEqual(ALKALINE_MATERIAL.getDust(1))) {
+ totalAlkalineDrained += stack.stackSize;
+ alkalineInputBus.decrStackSize(i, stack.stackSize);
+ }
+ }
+
+ // Now do fluid, this is simpler since we only need to bother with one slot
+ FluidStack stack = acidInputHatch.getDrainableStack();
+ int numMultiples = 0;
+ if (stack != null && stack.isFluidEqual(ACIDIC_MATERIAL.getFluid(1))) {
+ int acidAvailable = stack.amount;
+ // We only care about multiples of 10, but we still drain all.
+ numMultiples = Math.floorDiv(acidAvailable, 10);
+ acidInputHatch.drain(acidAvailable, true);
+ } else {
+ // Little easier egg: Fluoroantimonic acid has a pH value of -31, it's an acid so strong it will
+ // instantly shatter the glass in the structure.
+ if (GoodGenerator.isModLoaded()) {
+ Fluid acid = FluidRegistry.getFluid("fluoroantimonic acid");
+ if (stack != null && stack.getFluid()
+ .equals(acid)) {
+ // TODO: Actually break the glass and trigger achievement lol
+ }
+ }
+ }
+
+ // Adjust pH with to new value
+ this.currentpHValue = this.currentpHValue + totalAlkalineDrained * PH_PER_ALKALINE_DUST
+ + numMultiples * PH_PER_10_ACID_LITER;
+
+ // Clamp pH to sensible values
+ this.currentpHValue = Math.min(Math.max(this.currentpHValue, 0.0f), 14.0f);
+
+ // Round to 2 decimals
+ this.currentpHValue = Math.round(this.currentpHValue * 100.0f) / 100.0f;
+
+ // If pH is 0 or 14, stop the machine
+ if (Math.abs(this.currentpHValue) < 0.001 || Math.abs(this.currentpHValue - 14.0f) < 0.001) {
+ stopMachine(SimpleShutDownReason.ofNormal("critical_ph_value"));
+ }
+ }
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) {
+ super.onPostTick(aBaseMetaTileEntity, aTimer);
+ // Update sensor hatch
+ for (GT_MetaTileEntity_pHSensor hatch : sensorHatches) {
+ hatch.updateRedstoneOutput(this.currentpHValue);
+ }
+ }
+
+ @Override
+ public float calculateFinalSuccessChance() {
+ // Success chance is 100% when inside target range, 0% otherwise
+ float distance = Math.abs(this.currentpHValue - PH_NEUTRAL_VALUE);
+ if (distance <= PH_MAX_DEVIATION) {
+ return 100.0f;
+ } else {
+ return 0.0f;
+ }
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public int getWaterTier() {
+ return 4;
+ }
+
+ @Override
+ public long getBasePowerUsage() {
+ return TierEU.RECIPE_ZPM;
+ }
+
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ if (!checkPiece(STRUCTURE_PIECE_MAIN, STRUCTURE_X_OFFSET, STRUCTURE_Y_OFFSET, STRUCTURE_Z_OFFSET)) return false;
+ // Do not form without positioned hatches
+ if (acidInputHatch == null || alkalineInputBus == null) return false;
+ return super.checkMachine(aBaseMetaTileEntity, aStack);
+ }
+
+ @Override
+ public String[] getInfoData() {
+ ArrayList<String> infoData = new ArrayList<>(Arrays.asList(super.getInfoData()));
+ infoData.add("Current pH Value: " + EnumChatFormatting.YELLOW + currentpHValue + " pH");
+ return infoData.toArray(new String[] {});
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setFloat("mCurrentpH", this.currentpHValue);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ this.currentpHValue = aNBT.getFloat("mCurrentpH");
+ }
+
+ @Override
+ protected ResourceLocation getActivitySoundLoop() {
+ return SoundResource.GT_MACHINES_PURIFICATION_PH_LOOP.resourceLocation;
+ }
+
+ private enum SpecialHatchElement implements IHatchElement<GT_MetaTileEntity_PurificationUnitPhAdjustment> {
+
+ PhSensor(GT_MetaTileEntity_PurificationUnitPhAdjustment::addSensorHatchToMachineList,
+ GT_MetaTileEntity_pHSensor.class) {
+
+ @Override
+ public long count(
+ GT_MetaTileEntity_PurificationUnitPhAdjustment gtMetaTileEntityPurificationUnitPhAdjustment) {
+ return gtMetaTileEntityPurificationUnitPhAdjustment.sensorHatches.size();
+ }
+ };
+
+ private final List<Class<? extends IMetaTileEntity>> mteClasses;
+ private final IGT_HatchAdder<GT_MetaTileEntity_PurificationUnitPhAdjustment> adder;
+
+ @SafeVarargs
+ SpecialHatchElement(IGT_HatchAdder<GT_MetaTileEntity_PurificationUnitPhAdjustment> adder,
+ Class<? extends IMetaTileEntity>... mteClasses) {
+ this.mteClasses = Collections.unmodifiableList(Arrays.asList(mteClasses));
+ this.adder = adder;
+ }
+
+ @Override
+ public List<? extends Class<? extends IMetaTileEntity>> mteClasses() {
+ return mteClasses;
+ }
+
+ public IGT_HatchAdder<? super GT_MetaTileEntity_PurificationUnitPhAdjustment> adder() {
+ return adder;
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitPlasmaHeater.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitPlasmaHeater.java
new file mode 100644
index 0000000000..8b10cae9b7
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitPlasmaHeater.java
@@ -0,0 +1,566 @@
+package gregtech.common.tileentities.machines.multi.purification;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.lazy;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlockAnyMeta;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.onElementPass;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.GT_Values.AuthorNotAPenguin;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_GLOW;
+import static gregtech.api.recipe.RecipeMaps.purificationPlasmaHeatingRecipes;
+import static gregtech.api.util.GT_RecipeBuilder.SECONDS;
+import static gregtech.api.util.GT_StructureUtility.ofFrame;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import net.minecraft.block.Block;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import com.google.common.collect.ImmutableList;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import cpw.mods.fml.common.registry.GameRegistry;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.Mods;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.Textures;
+import gregtech.api.enums.TierEU;
+import gregtech.api.interfaces.IHatchElement;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Input;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_StructureUtility;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_PurificationUnitPlasmaHeater
+ extends GT_MetaTileEntity_PurificationUnitBase<GT_MetaTileEntity_PurificationUnitPlasmaHeater>
+ implements ISurvivalConstructable {
+
+ private static final int CASING_INDEX_HEATER = getTextureIndex(GregTech_API.sBlockCasings9, 11);
+ private static final int CASING_INDEX_TOWER = getTextureIndex(GregTech_API.sBlockCasings9, 5);
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final int STRUCTURE_X_OFFSET = 2;
+ private static final int STRUCTURE_Y_OFFSET = 14;
+ private static final int STRUCTURE_Z_OFFSET = 5;
+
+ /**
+ * Fluid is consumed every CONSUME_INTERVAL ticks
+ */
+ private static final long CONSUME_INTERVAL = 1 * SECONDS;
+
+ /**
+ * Current internal temperature of the multiblock
+ */
+ private long currentTemperature = 0;
+ /**
+ * Amount of successful heating cycles completed
+ */
+ private int cyclesCompleted = 0;
+ /**
+ * Whether this recipe is ruined due to high temperature
+ */
+ private boolean ruinedCycle = false;
+
+ private enum CycleState {
+ // Was previously at 0K, currently waiting to heat to 10000K
+ Heating,
+ // Was previously at 10000K, currently waiting to cool down to 0K
+ Cooling
+ }
+
+ private CycleState state = CycleState.Heating;
+
+ // A cycle is 30s at shortest, a purification plant cycle is 120s. 33% chance per heating cycle
+ // will give you plenty of room for delay and still get to 99% chance.
+ public static final long SUCCESS_PER_CYCLE = 33;
+
+ // Consumption rates in liters/second
+ public static final long MAX_PLASMA_PER_SEC = 10;
+ public static final long MAX_COOLANT_PER_SEC = 100;
+ // Change in temperature per consumed liter of plasma
+ public static final long PLASMA_TEMP_PER_LITER = 100;
+ // Change in temperature per consumed liter of coolant
+ public static final long COOLANT_TEMP_PER_LITER = -5;
+ // Temperature at which the batch is ruined
+ public static final long MAX_TEMP = 12500;
+ // Point at which the heating point of the cycle is reached
+ public static final long HEATING_POINT = 10000;
+
+ private static final Materials plasmaMaterial = Materials.Helium;
+ private static final Materials coolantMaterial = Materials.SuperCoolant;
+
+ private GT_MetaTileEntity_Hatch_Input plasmaInputHatch;
+ private GT_MetaTileEntity_Hatch_Input coolantInputHatch;
+
+ private static final String[][] structure = new String[][] {
+ // spotless:off
+ { " DDDDDDD ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " DDDDD ", " DDDDD ", " DDKDD " },
+ { " DD DD ", " DDDDDDD ", " DDDDD ", " ", " ", " ", " ", " ", " DDDDD ", " DDDDD ", " DDDDD ", " DDDDD ", " DD DD ", " DD DD ", " DDDDDDDDD " },
+ { " D D ", " DDD DDD ", " DDDDDDD ", " DDDDD ", " DDDDD ", " DDDDD ", " DDDDD ", " DDDDD ", " DD DD ", " DD DD ", " DD DD ", " DD DD ", " D D ", " D D ", " DDDDDDDDDDD " },
+ { " D D ", " D DD ", " DD D ", " DD DD ", " DD DD ", " DD DD ", " DD DD ", " DD DD ", " D D ", " D D ", " D D ", " D D ", " D D ", " D D ", " DDDDDDDDDDDDD " },
+ { " D D", " DD DD ", " DD D ", " D D ", " D D ", " D D ", " D D ", " D D ", " D D ", " D D ", " D D ", " D D ", " D D ", " D D ", " DDDDDDDDDDDDD " },
+ { " D D", " D D ", " DD D ", " D D ", " D D ", "GBBBG D D ", "G G D D ", "G G D D ", "G G D D ", "G G D D ", "G G D D ", "G G D D ", "G G D D", "G G D D", "GB~BG DDDDDDDDDDDDDDD" },
+ { " D D", " D D ", " DD D ", " D D ", " BBB D D ", "BBBBB D D ", " EEE D D ", " EEE D D ", " EEE D D ", " EEE D D ", " EEE D D ", " EEE D D ", " EEE D D", " EEEBBBBD D", "BAAAB DDDDDDDDDDDDDDD" },
+ { " D D", " D D ", " DD D ", " D D ", " BBB D D ", "BBBBB D D ", " EFE D D ", " EFE D D ", " EFE D D ", " EFE D D ", " EFE D D ", " EFE D D ", " EFEBBBBD D", " EFE D D", "PAAABBBBDDDDDDDDDDDDDDD" },
+ { " D D", " D D ", " DD D ", " D D ", " BBB D D ", "BBBBB D D ", " EEE D D ", " EEE D D ", " EEE D D ", " EEE D D ", " EEE D D ", " EEE D D ", " EEE D D", " EEEBBBBD D", "BAAAB DDDDDDDDDDDDDDD" },
+ { " D D", " D D ", " DD D ", " D D ", " D D ", "GBBBG D D ", "G G D D ", "G G D D ", "G G D D ", "G G D D ", "G G D D ", "G G D D ", "G G D D", "G G D D", "GBBBG DDDDDDDDDDDDDDD" },
+ { " D D", " D D ", " D D ", " D DD ", " D DD ", " D DD ", " D DD ", " D DD ", " D D ", " D D ", " D D ", " D D ", " D D ", " D D ", " DDDDDDDDDDDDD " },
+ { " D DD", " D D ", " D D ", " DD DD ", " DD DD ", " DD DD ", " DD DD ", " DD DD ", " D D ", " D D ", " D D ", " D D ", " D D ", " D D ", " DDDDDDDDDDDDD " },
+ { " D D ", " DD DD ", " D D ", " DDDDD ", " DDDDD ", " DDDDD ", " DDDDD ", " DDDDD ", " DD DD ", " DD DD ", " DD DD ", " DD DD ", " D D ", " D D ", " DDDDDDDDDDD " },
+ { " DD DD ", " DDDDDDD ", " DDDDD ", " ", " ", " ", " ", " ", " DDDDD ", " DDDDD ", " DDDDD ", " DDDDD ", " DD DD ", " DD DD ", " DDDDDDDDD " },
+ { " DDDDDDD ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " DDDDD ", " DDDDD ", " DDDDD " } };
+ // spotless:on
+
+ private int casingCount = 0;
+ private static final int MIN_CASING = 50;
+
+ private static final IStructureDefinition<GT_MetaTileEntity_PurificationUnitPlasmaHeater> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_PurificationUnitPlasmaHeater>builder()
+ .addShape(STRUCTURE_PIECE_MAIN, structure)
+ // Superconducting coil block
+ .addElement('A', ofBlock(GregTech_API.sBlockCasings1, 15))
+ // Plasma Heating Casing
+ .addElement(
+ 'B',
+ ofChain(
+ lazy(
+ t -> GT_StructureUtility.<GT_MetaTileEntity_PurificationUnitPlasmaHeater>buildHatchAdder()
+ .atLeastList(t.getAllowedHatches())
+ .dot(1)
+ .casingIndex(CASING_INDEX_HEATER)
+ .build()),
+ onElementPass(t -> t.casingCount++, ofBlock(GregTech_API.sBlockCasings9, 11))))
+ // Reinforced Sterile Water Plant Casing
+ .addElement('D', ofBlock(GregTech_API.sBlockCasings9, 5))
+ // Any Tinted Glass
+ .addElement('E', ofBlockAnyMeta(GregTech_API.sBlockTintedGlass, 0))
+ // Neonite, with fallback to air
+ .addElement('F', lazy(t -> {
+ if (Mods.Chisel.isModLoaded()) {
+ Block neonite = GameRegistry.findBlock(Mods.Chisel.ID, "neonite");
+ return ofBlockAnyMeta(neonite, 7);
+ } else {
+ return ofBlockAnyMeta(Blocks.air);
+ }
+ }))
+ // Superconductor Base ZPM frame box
+ .addElement('G', ofFrame(Materials.Tetranaquadahdiindiumhexaplatiumosminid))
+ // Coolant input hatch
+ .addElement(
+ 'K',
+ lazy(
+ t -> GT_StructureUtility.<GT_MetaTileEntity_PurificationUnitPlasmaHeater>buildHatchAdder()
+ .atLeast(InputHatch)
+ .dot(2)
+ .adder(GT_MetaTileEntity_PurificationUnitPlasmaHeater::addCoolantHatchToMachineList)
+ .cacheHint(() -> "Input Hatch (Coolant)")
+ .casingIndex(CASING_INDEX_TOWER)
+ .buildAndChain(ofBlock(GregTech_API.sBlockCasings9, 5))))
+ // Plasma input hatch
+ .addElement(
+ 'P',
+ lazy(
+ t -> GT_StructureUtility.<GT_MetaTileEntity_PurificationUnitPlasmaHeater>buildHatchAdder()
+ .atLeast(InputHatch)
+ .dot(3)
+ .adder(GT_MetaTileEntity_PurificationUnitPlasmaHeater::addPlasmaHatchToMachineList)
+ .cacheHint(() -> "Input Hatch (Plasma)")
+ .casingIndex(CASING_INDEX_HEATER)
+ .buildAndChain(ofBlock(GregTech_API.sBlockCasings9, 11))))
+ .build();
+
+ private List<IHatchElement<? super GT_MetaTileEntity_PurificationUnitPlasmaHeater>> getAllowedHatches() {
+ return ImmutableList.of(InputHatch, OutputHatch);
+ }
+
+ public GT_MetaTileEntity_PurificationUnitPlasmaHeater(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_PurificationUnitPlasmaHeater(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_PurificationUnitPlasmaHeater(this.mName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, ForgeDirection facing,
+ int colorIndex, boolean active, boolean redstoneLevel) {
+ if (side == facing) {
+ if (active) return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX_HEATER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX_HEATER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX_HEATER) };
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(
+ STRUCTURE_PIECE_MAIN,
+ stackSize,
+ hintsOnly,
+ STRUCTURE_X_OFFSET,
+ STRUCTURE_Y_OFFSET,
+ STRUCTURE_Z_OFFSET);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ return survivialBuildPiece(
+ STRUCTURE_PIECE_MAIN,
+ stackSize,
+ STRUCTURE_X_OFFSET,
+ STRUCTURE_Y_OFFSET,
+ STRUCTURE_Z_OFFSET,
+ elementBudget,
+ env,
+ true);
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_PurificationUnitPlasmaHeater> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return purificationPlasmaHeatingRecipes;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Purification Unit")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.BOLD
+ + "Water Tier: "
+ + EnumChatFormatting.WHITE
+ + GT_Utility.formatNumbers(getWaterTier())
+ + EnumChatFormatting.RESET)
+ .addInfo("Controller block for the Extreme Temperature Fluctuation Purification Unit.")
+ .addInfo("Must be linked to a Purification Plant using a data stick to work.")
+ .addSeparator()
+ .addInfo(
+ "Complete heating cycles by first heating the water to " + EnumChatFormatting.RED
+ + HEATING_POINT
+ + "K"
+ + EnumChatFormatting.GRAY
+ + ",")
+ .addInfo(
+ "and then cooling it back down to " + EnumChatFormatting.RED + "0K" + EnumChatFormatting.GRAY + ".")
+ .addInfo(
+ "Initial temperature is reset to " + EnumChatFormatting.RED
+ + "0K"
+ + EnumChatFormatting.GRAY
+ + " on recipe start.")
+ .addInfo(
+ // TODO: Refer to heating cycles in another way to avoid confusion
+ "Each completed heating cycle boosts success chance by " + EnumChatFormatting.RED
+ + SUCCESS_PER_CYCLE
+ + "%.")
+ .addInfo(
+ "If the temperature ever reaches " + EnumChatFormatting.RED
+ + MAX_TEMP
+ + "K"
+ + EnumChatFormatting.GRAY
+ + " the recipe will fail and output steam.")
+ .addSeparator()
+ .addInfo(
+ "Consumes up to " + EnumChatFormatting.RED
+ + MAX_PLASMA_PER_SEC
+ + "L/s "
+ + EnumChatFormatting.WHITE
+ + plasmaMaterial.getPlasma(1)
+ .getLocalizedName()
+ + EnumChatFormatting.GRAY
+ + " and up to "
+ + EnumChatFormatting.RED
+ + MAX_COOLANT_PER_SEC
+ + "L/s "
+ + EnumChatFormatting.WHITE
+ + coolantMaterial.getFluid(1)
+ .getLocalizedName())
+ .addInfo(
+ EnumChatFormatting.RED + "Raises "
+ + EnumChatFormatting.GRAY
+ + "the temperature by "
+ + EnumChatFormatting.RED
+ + PLASMA_TEMP_PER_LITER
+ + "K"
+ + EnumChatFormatting.GRAY
+ + " per liter of plasma consumed.")
+ .addInfo(
+ EnumChatFormatting.RED + "Lowers "
+ + EnumChatFormatting.GRAY
+ + "the temperature by "
+ + EnumChatFormatting.RED
+ + -COOLANT_TEMP_PER_LITER
+ + "K"
+ + EnumChatFormatting.GRAY
+ + " per liter of coolant consumed.")
+ .addSeparator()
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "Step five of water purification is to evaporate complex organic polymers and extremophile organisms")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "that might be resistant to simple acids, clarifying agents, and filters. Using an ultra high")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "pressure chamber in combination with extreme temperature fluctuations allows the water to remain")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "supercritical while evaporating any remaining contaminants, ready for filtration.")
+ .addInfo(AuthorNotAPenguin)
+ .beginStructureBlock(23, 15, 15, false)
+ .addCasingInfoExactlyColored(
+ "Reinforced Sterile Water Plant Casing",
+ EnumChatFormatting.GRAY,
+ 1091,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Heat-Resistant Trinium Plated Casing",
+ EnumChatFormatting.GRAY,
+ 54,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Any Tinted Industrial Glass",
+ EnumChatFormatting.GRAY,
+ 64,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Superconductor Base ZPM Frame Box",
+ EnumChatFormatting.GRAY,
+ 40,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored("Any Neonite", EnumChatFormatting.GRAY, 8, EnumChatFormatting.GOLD, false)
+ .addCasingInfoExactlyColored(
+ "Superconducting Coil Block",
+ EnumChatFormatting.GRAY,
+ 8,
+ EnumChatFormatting.GOLD,
+ false)
+ .addController("Front center")
+ .addOtherStructurePart("Input Hatch (Water)", EnumChatFormatting.GOLD + "1+", 1)
+ .addOtherStructurePart("Output Hatch", EnumChatFormatting.GOLD + "1", 1)
+ .addOtherStructurePart("Input Hatch (Coolant)", EnumChatFormatting.GOLD + "1", 2)
+ .addOtherStructurePart("Input Hatch (Plasma)", EnumChatFormatting.GOLD + "1", 3)
+ .addStructureInfo("Use the StructureLib Hologram Projector to build the structure.")
+ .toolTipFinisher("GregTech");
+ return tt;
+ }
+
+ @Override
+ public void startCycle(int cycleTime, int progressTime) {
+ super.startCycle(cycleTime, progressTime);
+ // Reset internal state
+ this.cyclesCompleted = 0;
+ this.currentTemperature = 0;
+ this.ruinedCycle = false;
+ this.state = CycleState.Heating;
+ }
+
+ // Drains up to maxAmount of a fluid if it is the same fluid as given, returns the amount drained
+ private long drainFluidLimited(GT_MetaTileEntity_Hatch_Input inputHatch, FluidStack fluid, long maxAmount) {
+ FluidStack hatchStack = inputHatch.getDrainableStack();
+ if (hatchStack == null) return 0;
+ if (hatchStack.isFluidEqual(fluid)) {
+ long amountToDrain = Math.min(maxAmount, hatchStack.amount);
+ if (amountToDrain > 0) {
+ inputHatch.drain((int) amountToDrain, true);
+ }
+ return amountToDrain;
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public void addRecipeOutputs() {
+ super.addRecipeOutputs();
+ // If the cycle was ruined, output steam
+ if (this.ruinedCycle) {
+ FluidStack insertedWater = currentRecipe.mFluidInputs[0];
+ // Multiply by 60 since that's the water:steam ratio in GTNH
+ long steamAmount = insertedWater.amount * 60L;
+ addOutput(GT_ModHandler.getSteam(steamAmount));
+ }
+ }
+
+ @Override
+ protected void runMachine(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.runMachine(aBaseMetaTileEntity, aTick);
+ if (mMaxProgresstime > 0 && aTick % CONSUME_INTERVAL == 0) {
+ // Drain plasma and coolant up to limited amount per second
+ long plasmaDrained = drainFluidLimited(plasmaInputHatch, plasmaMaterial.getPlasma(1L), MAX_PLASMA_PER_SEC);
+ long coolantDrained = drainFluidLimited(
+ coolantInputHatch,
+ coolantMaterial.getFluid(1L),
+ MAX_COOLANT_PER_SEC);
+ // Calculate temperature change
+ long tempChance = plasmaDrained * PLASMA_TEMP_PER_LITER + coolantDrained * COOLANT_TEMP_PER_LITER;
+ currentTemperature = Math.max(0, currentTemperature + tempChance);
+ // Check if batch was ruined
+ if (currentTemperature > MAX_TEMP) {
+ ruinedCycle = true;
+ }
+ // Update cycle state.
+ switch (state) {
+ case Heating -> {
+ // Heating state can change to cooling when temperature exceeds 10000K
+ if (currentTemperature >= HEATING_POINT) {
+ state = CycleState.Cooling;
+ }
+ }
+ case Cooling -> {
+ if (currentTemperature == 0) {
+ state = CycleState.Heating;
+ cyclesCompleted += 1;
+ }
+ }
+ }
+ }
+ }
+
+ public boolean addCoolantHatchToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Input) {
+ ((GT_MetaTileEntity_Hatch) aMetaTileEntity).updateTexture(aBaseCasingIndex);
+ ((GT_MetaTileEntity_Hatch_Input) aMetaTileEntity).mRecipeMap = null;
+ coolantInputHatch = (GT_MetaTileEntity_Hatch_Input) aMetaTileEntity;
+ return true;
+ }
+ return false;
+ }
+
+ public boolean addPlasmaHatchToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Input) {
+ ((GT_MetaTileEntity_Hatch) aMetaTileEntity).updateTexture(aBaseCasingIndex);
+ ((GT_MetaTileEntity_Hatch_Input) aMetaTileEntity).mRecipeMap = null;
+ plasmaInputHatch = (GT_MetaTileEntity_Hatch_Input) aMetaTileEntity;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public float calculateFinalSuccessChance() {
+ if (ruinedCycle) return 0.0f;
+ // Success chance directly depends on number of cycles completed.
+ return cyclesCompleted * SUCCESS_PER_CYCLE + currentRecipeChance;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ ArrayList<String> infoData = new ArrayList<>(Arrays.asList(super.getInfoData()));
+ infoData.add("Current temperature: " + EnumChatFormatting.YELLOW + currentTemperature + "K");
+ infoData.add("Heating cycles completed this run: " + EnumChatFormatting.YELLOW + cyclesCompleted);
+ return infoData.toArray(new String[] {});
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ aNBT.setLong("mCurrentTemperature", currentTemperature);
+ aNBT.setInteger("mCyclesCompleted", cyclesCompleted);
+ aNBT.setBoolean("mRuinedCycle", ruinedCycle);
+ aNBT.setString("mCycleState", state.toString());
+ super.saveNBTData(aNBT);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ currentTemperature = aNBT.getLong("mCurrentTemperature");
+ cyclesCompleted = aNBT.getInteger("mCyclesCompleted");
+ ruinedCycle = aNBT.getBoolean("mRuinedCycle");
+ state = CycleState.valueOf(aNBT.getString("mCycleState"));
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public int getWaterTier() {
+ return 5;
+ }
+
+ @Override
+ public long getBasePowerUsage() {
+ return TierEU.RECIPE_UV;
+ }
+
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ casingCount = 0;
+ if (!checkPiece(STRUCTURE_PIECE_MAIN, STRUCTURE_X_OFFSET, STRUCTURE_Y_OFFSET, STRUCTURE_Z_OFFSET)) return false;
+ if (casingCount < MIN_CASING) return false;
+ // Do not form without positioned hatches
+ if (plasmaInputHatch == null || coolantInputHatch == null) return false;
+ return super.checkMachine(aBaseMetaTileEntity, aStack);
+ }
+
+ @Override
+ protected ResourceLocation getActivitySoundLoop() {
+ return SoundResource.GT_MACHINES_PURIFICATION_PLASMA_LOOP.resourceLocation;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitUVTreatment.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitUVTreatment.java
new file mode 100644
index 0000000000..f542216617
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitUVTreatment.java
@@ -0,0 +1,526 @@
+package gregtech.common.tileentities.machines.multi.purification;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.lazy;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+import static gregtech.api.enums.GT_Values.AuthorNotAPenguin;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_GLOW;
+import static gregtech.api.util.GT_RecipeBuilder.SECONDS;
+import static gregtech.api.util.GT_StructureUtility.ofFrame;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ThreadLocalRandom;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.Textures;
+import gregtech.api.enums.TierEU;
+import gregtech.api.interfaces.IHatchElement;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_StructureUtility;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.IGT_HatchAdder;
+
+public class GT_MetaTileEntity_PurificationUnitUVTreatment
+ extends GT_MetaTileEntity_PurificationUnitBase<GT_MetaTileEntity_PurificationUnitUVTreatment>
+ implements ISurvivalConstructable {
+
+ private static final int CASING_INDEX_MAIN = getTextureIndex(GregTech_API.sBlockCasings9, 12);
+
+ private static final String STRUCTURE_PIECE_MAIN = "main";
+ private static final int STRUCTURE_X_OFFSET = 6;
+ private static final int STRUCTURE_Y_OFFSET = 8;
+ private static final int STRUCTURE_Z_OFFSET = 0;
+
+ private GT_MetaTileEntity_LensHousing lensInputBus;
+ private GT_MetaTileEntity_LensIndicator lensIndicator;
+
+ private UVTreatmentLensCycle lensCycle = null;
+
+ /**
+ * Bonus chance to success for each lens swap
+ */
+ public static final float SUCCESS_PER_LENS = 10.0f;
+
+ /**
+ * Maximum amount of ticks between two lens swaps
+ */
+ public static final int MAX_TIME_BETWEEN_SWAPS = GT_MetaTileEntity_PurificationPlant.CYCLE_TIME_TICKS / 8;
+ /**
+ * Minimum amount of time between two lens swaps
+ */
+ public static final int MIN_TIME_BETWEEN_SWAPS = MAX_TIME_BETWEEN_SWAPS / 4;
+
+ public static final ArrayList<ItemStack> LENS_ITEMS = new ArrayList<>();
+
+ private int numSwapsPerformed = 0;
+ private int timeUntilNextSwap = 0;
+
+ private boolean removedTooEarly = false;
+
+ private static final String[][] structure = new String[][] {
+ // spotless:off
+ { " ", " DDD ", " ", " ", " ", " ", " ", " DDD ", " H~H " },
+ { " AAA ", " DDAAADD ", " BBB ", " BBB ", " BBB ", " BBB ", " BBB ", " DDBBBDD ", " AAAAAAA " },
+ { " AAAAAAA ", " DDAACCCAADD ", " BB BB ", " BB BB ", " BB BB ", " BB BB ", " BB BB ", " DDBB BBDD ", " AAAAAAAAAAA " },
+ { " AAAAAAAAAAA ", "DAACCCCCCCAAD", " BB BB ", " BB BB ", " BB BB ", " BB BB ", " BB BB ", "DBB BBD", "HAAAAAAAAAAAH" },
+ { " AAAAALAAAAA ", "DACCCCCCCCCAD", " B B ", " B B ", " B B ", " B B ", " B B ", "DB BD", "HAAAAAAAAAAAH" },
+ { " AAAAAAAAAAA ", "DAACCCCCCCAAD", " BB BB ", " BB BB ", " BB BB ", " BB BB ", " BB BB ", "DBB BBD", "HAAAAAAAAAAAH" },
+ { " AAAAAAA ", " DDAACCCAADD ", " BB BB ", " BB BB ", " BB BB ", " BB BB ", " BB BB ", " DDBB BBDD ", " AAAAAAAAAAA " },
+ { " AIA ", " DDAAADD ", " BBB ", " BBB ", " BBB ", " BBB ", " BBB ", " DDBBBDD ", " AAAAAAA " },
+ { " ", " DDD ", " ", " ", " ", " ", " ", " DDD ", " HHH " } };
+ // spotless:on
+
+ private static final IStructureDefinition<GT_MetaTileEntity_PurificationUnitUVTreatment> STRUCTURE_DEFINITION = StructureDefinition
+ .<GT_MetaTileEntity_PurificationUnitUVTreatment>builder()
+ .addShape(STRUCTURE_PIECE_MAIN, structure)
+ // Naquadria-Reinforced Water Plant Casing
+ .addElement('A', ofBlock(GregTech_API.sBlockCasings9, 12))
+ // Neutronium-Coated UV-Resistant Glass
+ .addElement('B', ofBlock(GregTech_API.sBlockGlass1, 1))
+ // UV Backlight sterilizer casing
+ .addElement('C', ofBlock(GregTech_API.sBlockCasings9, 13))
+ .addElement('D', ofFrame(Materials.StellarAlloy))
+ // Lens housing bus
+ .addElement(
+ 'L',
+ lazy(
+ t -> GT_StructureUtility.<GT_MetaTileEntity_PurificationUnitUVTreatment>buildHatchAdder()
+ .atLeast(SpecialHatchElement.LensHousing)
+ .dot(2)
+ .cacheHint(() -> "Lens Housing")
+ .casingIndex(CASING_INDEX_MAIN)
+ .build()))
+ // Lens indicator hatch
+ .addElement(
+ 'I',
+ lazy(
+ t -> GT_StructureUtility.<GT_MetaTileEntity_PurificationUnitUVTreatment>buildHatchAdder()
+ .atLeast(SpecialHatchElement.LensIndicator)
+ .dot(3)
+ .cacheHint(() -> "Lens Indicator")
+ .casingIndex(CASING_INDEX_MAIN)
+ .build()))
+ // Input or output hatch
+ .addElement(
+ 'H',
+ ofChain(
+ lazy(
+ t -> GT_StructureUtility.<GT_MetaTileEntity_PurificationUnitUVTreatment>buildHatchAdder()
+ .atLeastList(Arrays.asList(InputHatch, OutputHatch))
+ .dot(1)
+ .cacheHint(() -> "Input Hatch, Output Hatch")
+ .casingIndex(CASING_INDEX_MAIN)
+ .build()),
+ // Naquadria-reinforced Water Plant Casing
+ ofBlock(GregTech_API.sBlockCasings9, 12)))
+ .build();
+
+ public GT_MetaTileEntity_PurificationUnitUVTreatment(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_PurificationUnitUVTreatment(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_PurificationUnitUVTreatment(this.mName);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, ForgeDirection facing,
+ int colorIndex, boolean active, boolean redstoneLevel) {
+ if (side == facing) {
+ if (active) return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX_MAIN),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX_MAIN),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+ return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX_MAIN) };
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(
+ STRUCTURE_PIECE_MAIN,
+ stackSize,
+ hintsOnly,
+ STRUCTURE_X_OFFSET,
+ STRUCTURE_Y_OFFSET,
+ STRUCTURE_Z_OFFSET);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ return survivialBuildPiece(
+ STRUCTURE_PIECE_MAIN,
+ stackSize,
+ STRUCTURE_X_OFFSET,
+ STRUCTURE_Y_OFFSET,
+ STRUCTURE_Z_OFFSET,
+ elementBudget,
+ env,
+ true);
+ }
+
+ @Override
+ public IStructureDefinition<GT_MetaTileEntity_PurificationUnitUVTreatment> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Purification Unit");
+ tt.addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.BOLD
+ + "Water Tier: "
+ + EnumChatFormatting.WHITE
+ + GT_Utility.formatNumbers(getWaterTier())
+ + EnumChatFormatting.RESET)
+ .addInfo("Controller block for the High Energy Laser Purification Unit.")
+ .addInfo("Must be linked to a Purification Plant using a data stick to work.")
+ .addSeparator()
+ .addInfo(
+ "During operation, swap the lens in the " + EnumChatFormatting.WHITE
+ + "Lens Housing"
+ + EnumChatFormatting.GRAY
+ + ".")
+ .addInfo(
+ "The multiblock will output a signal through the " + EnumChatFormatting.WHITE + "Lens Indicator Hatch")
+ .addInfo("when the current lens must be swapped.")
+ .addInfo(
+ "Lens swaps will be requested in random intervals of " + EnumChatFormatting.RED
+ + (MIN_TIME_BETWEEN_SWAPS / SECONDS)
+ + " to "
+ + (MAX_TIME_BETWEEN_SWAPS / SECONDS)
+ + "s"
+ + EnumChatFormatting.GRAY
+ + ".")
+ .addSeparator()
+ .addInfo(
+ "Success chance is boosted by " + EnumChatFormatting.RED
+ + SUCCESS_PER_LENS
+ + "% "
+ + EnumChatFormatting.GRAY
+ + "for each successful swap performed.")
+ .addInfo("Removing a lens too early will fail the recipe.")
+ .addInfo("Find the order of lenses in the recipe in NEI,")
+ .addInfo("or use a portable scanner to view the currently requested lens.")
+ .addSeparator()
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "The sixth step of water purification involves identifying any remaining negatively charged ions within")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "the water which may cause electrical faults in future wafer manufacturing. Bombarding the water with varying")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "wavelengths of photon beams will impart energy into outer-shell electrons, causing them to detach from the")
+ .addInfo(
+ EnumChatFormatting.AQUA + ""
+ + EnumChatFormatting.ITALIC
+ + "atoms themselves and pass through the walls of the tank, ensuring the water is perfectly electrically polar.")
+ .addInfo(AuthorNotAPenguin)
+ .beginStructureBlock(13, 9, 9, true)
+ .addCasingInfoRangeColored(
+ "Naquadria-Reinforced Water Plant Casing",
+ EnumChatFormatting.GRAY,
+ 147,
+ 155,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Electron-Permeable Neutronium Coated Glass",
+ EnumChatFormatting.GRAY,
+ 144,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "High Energy Ultraviolet Emitter Casing",
+ EnumChatFormatting.GRAY,
+ 24,
+ EnumChatFormatting.GOLD,
+ false)
+ .addCasingInfoExactlyColored(
+ "Stellar Alloy Frame Box",
+ EnumChatFormatting.GRAY,
+ 56,
+ EnumChatFormatting.GOLD,
+ false)
+ .addController("Front center")
+ .addOtherStructurePart("Input Hatch, Output Hatch", EnumChatFormatting.GOLD + "1+", 1)
+ .addOtherStructurePart("Lens Housing", EnumChatFormatting.GOLD + "1", 2)
+ .addOtherStructurePart("Lens Indicator", EnumChatFormatting.GOLD + "1", 3)
+ .addStructureInfo("Use the StructureLib Hologram Projector to build the structure.")
+ .toolTipFinisher("GregTech");
+ return tt;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.purificationUVTreatmentRecipes;
+ }
+
+ @NotNull
+ @Override
+ public CheckRecipeResult checkProcessing() {
+ CheckRecipeResult result = super.checkProcessing();
+ if (result.wasSuccessful()) {
+ this.lensCycle = new UVTreatmentLensCycle(LENS_ITEMS);
+ }
+ return result;
+ }
+
+ private int generateNextSwapTime() {
+ ThreadLocalRandom random = ThreadLocalRandom.current();
+ return random.nextInt(MIN_TIME_BETWEEN_SWAPS, MAX_TIME_BETWEEN_SWAPS);
+ }
+
+ @Override
+ public void startCycle(int cycleTime, int progressTime) {
+ super.startCycle(cycleTime, progressTime);
+ // Reset internal state
+ this.timeUntilNextSwap = 0;
+ this.numSwapsPerformed = 0;
+ this.lensCycle.reset();
+ this.removedTooEarly = false;
+ }
+
+ private ItemStack getCurrentlyInsertedLens() {
+ return this.lensInputBus.getStackInSlot(0);
+ }
+
+ @Override
+ protected void runMachine(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.runMachine(aBaseMetaTileEntity, aTick);
+
+ // Do no processing if no recipe is running
+ if (mMaxProgresstime <= 0) return;
+
+ // This can happen because the lens cycle isn't saved to NBT correctly yet, FIXME
+ if (this.lensCycle == null) {
+ // FIXME: Properly save current recipe in NBT instead of exiting early
+ return;
+ }
+
+ ItemStack currentLens = getCurrentlyInsertedLens();
+
+ // If we are currently counting down to a next swap, do so
+ if (timeUntilNextSwap > 0) {
+ timeUntilNextSwap -= 1;
+ // Set the indicator to not output a signal for now
+ lensIndicator.updateRedstoneOutput(false);
+
+ // If we are counting down to the next swap, and there is no correct lens in the bus, we removed a lens
+ // too early
+ if (currentLens == null || !currentLens.isItemEqual(lensCycle.current())) {
+ removedTooEarly = true;
+ }
+
+ // If the time until the next swap became zero, move on to the next requested lens
+ if (timeUntilNextSwap == 0) {
+ boolean advanced = lensCycle.advance();
+ if (!advanced) {
+ // cycle didn't advance, we arrived at the end. This mainly means we want to stop the cycle
+ // The easiest way to do this is by setting the time until next swap larger than the recipe time
+ timeUntilNextSwap = mMaxProgresstime + 1;
+ }
+ }
+ }
+
+ // Time until next swap is zero, this means we are waiting for the user to output a lens.
+ else if (timeUntilNextSwap == 0) {
+ // Set the indicator to output a signal
+ lensIndicator.updateRedstoneOutput(true);
+
+ // If we now have a matching lens, we can accept it and move on to the next swap
+ if (currentLens != null && currentLens.isItemEqual(lensCycle.current())) {
+ numSwapsPerformed += 1;
+ timeUntilNextSwap = generateNextSwapTime();
+ }
+ }
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public int getWaterTier() {
+ return 6;
+ }
+
+ @Override
+ public long getBasePowerUsage() {
+ return TierEU.RECIPE_UV;
+ }
+
+ @Override
+ public float calculateFinalSuccessChance() {
+ if (removedTooEarly) return 0.0f;
+ return numSwapsPerformed * SUCCESS_PER_LENS + currentRecipeChance;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ ArrayList<String> infoData = new ArrayList<>(Arrays.asList(super.getInfoData()));
+ if (this.lensCycle != null) {
+ infoData.add("Lens swaps performed this run: " + EnumChatFormatting.YELLOW + numSwapsPerformed);
+ infoData.add(
+ "Current lens requested: " + EnumChatFormatting.GREEN
+ + lensCycle.current()
+ .getDisplayName());
+ if (removedTooEarly) {
+ infoData.add("Removed lens too early. Failing this recipe.");
+ }
+ }
+ return infoData.toArray(new String[] {});
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setInteger("numSwapsPerformed", numSwapsPerformed);
+ aNBT.setInteger("timeUntilNextSwap", timeUntilNextSwap);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ numSwapsPerformed = aNBT.getInteger("numSwapsPerformed");
+ timeUntilNextSwap = aNBT.getInteger("timeUntilNextSwap");
+ }
+
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ if (!checkPiece(STRUCTURE_PIECE_MAIN, STRUCTURE_X_OFFSET, STRUCTURE_Y_OFFSET, STRUCTURE_Z_OFFSET)) return false;
+ // Do not form without lens bus
+ if (lensInputBus == null) return false;
+ return super.checkMachine(aBaseMetaTileEntity, aStack);
+ }
+
+ public boolean addLensHousingToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_LensHousing) {
+ ((GT_MetaTileEntity_Hatch) aMetaTileEntity).updateTexture(aBaseCasingIndex);
+ this.lensInputBus = (GT_MetaTileEntity_LensHousing) aMetaTileEntity;
+ return true;
+ }
+ return false;
+ }
+
+ public boolean addLensIndicatorToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_LensIndicator) {
+ ((GT_MetaTileEntity_Hatch) aMetaTileEntity).updateTexture(aBaseCasingIndex);
+ this.lensIndicator = (GT_MetaTileEntity_LensIndicator) aMetaTileEntity;
+ lensIndicator.updateRedstoneOutput(false);
+ return true;
+ }
+ return false;
+ }
+
+ private enum SpecialHatchElement implements IHatchElement<GT_MetaTileEntity_PurificationUnitUVTreatment> {
+
+ LensHousing(GT_MetaTileEntity_PurificationUnitUVTreatment::addLensHousingToMachineList,
+ GT_MetaTileEntity_LensHousing.class) {
+
+ @Override
+ public long count(
+ GT_MetaTileEntity_PurificationUnitUVTreatment gtMetaTileEntityPurificationUnitUVTreatment) {
+ if (gtMetaTileEntityPurificationUnitUVTreatment.lensInputBus == null) return 0;
+ else return 1;
+ }
+ },
+
+ LensIndicator(GT_MetaTileEntity_PurificationUnitUVTreatment::addLensIndicatorToMachineList,
+ GT_MetaTileEntity_LensHousing.class) {
+
+ @Override
+ public long count(
+ GT_MetaTileEntity_PurificationUnitUVTreatment gtMetaTileEntityPurificationUnitUVTreatment) {
+ if (gtMetaTileEntityPurificationUnitUVTreatment.lensIndicator == null) return 0;
+ else return 1;
+ }
+ };
+
+ private final List<Class<? extends IMetaTileEntity>> mteClasses;
+ private final IGT_HatchAdder<GT_MetaTileEntity_PurificationUnitUVTreatment> adder;
+
+ @SafeVarargs
+ SpecialHatchElement(IGT_HatchAdder<GT_MetaTileEntity_PurificationUnitUVTreatment> adder,
+ Class<? extends IMetaTileEntity>... mteClasses) {
+ this.mteClasses = Collections.unmodifiableList(Arrays.asList(mteClasses));
+ this.adder = adder;
+ }
+
+ @Override
+ public List<? extends Class<? extends IMetaTileEntity>> mteClasses() {
+ return mteClasses;
+ }
+
+ public IGT_HatchAdder<? super GT_MetaTileEntity_PurificationUnitUVTreatment> adder() {
+ return adder;
+ }
+ }
+
+ @Override
+ protected ResourceLocation getActivitySoundLoop() {
+ return SoundResource.IC2_MACHINES_MAGNETIZER_LOOP.resourceLocation;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_pHSensor.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_pHSensor.java
new file mode 100644
index 0000000000..e8e393cb3c
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_pHSensor.java
@@ -0,0 +1,192 @@
+package gregtech.common.tileentities.machines.multi.purification;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.StatCollector;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.math.Color;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+import com.gtnewhorizons.modularui.common.widget.textfield.NumericWidget;
+
+import gregtech.api.enums.Textures;
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.IIconContainer;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.gui.modularui.widget.CoverCycleButtonWidget;
+
+public class GT_MetaTileEntity_pHSensor extends GT_MetaTileEntity_Hatch {
+
+ // This implementation was largely copied from the neutron sensor hatch
+
+ protected float threshold = 0;
+ protected boolean inverted = false;
+ private boolean isOn = false;
+
+ private static final IIconContainer textureFont = Textures.BlockIcons.OVERLAY_HATCH_PH_SENSOR;
+ private static final IIconContainer textureFont_Glow = Textures.BlockIcons.OVERLAY_HATCH_PH_SENSOR_GLOW;
+
+ public GT_MetaTileEntity_pHSensor(int aID, String aName, String aNameRegional, int aTier) {
+ super(aID, aName, aNameRegional, aTier, 0, "Detects pH value.");
+ }
+
+ public GT_MetaTileEntity_pHSensor(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 0, aDescription, aTextures);
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return false;
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public boolean allowGeneralRedstoneOutput() {
+ return true;
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection Side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public void initDefaultModes(NBTTagCompound aNBT) {
+ getBaseMetaTileEntity().setActive(true);
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer, ForgeDirection side,
+ float aX, float aY, float aZ) {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+
+ @Override
+ public String[] getDescription() {
+ return new String[] { "Can be installed in the pH Neutralization Purification Unit.",
+ "Outputs Redstone Signal according to the current pH value.",
+ "Right click to open the GUI and change settings." };
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ threshold = aNBT.getFloat("mThreshold");
+ inverted = aNBT.getBoolean("mInverted");
+ super.loadNBTData(aNBT);
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ aNBT.setFloat("mThreshold", threshold);
+ aNBT.setBoolean("mInverted", inverted);
+ super.saveNBTData(aNBT);
+ }
+
+ /**
+ * Updates redstone output strength based on the pH of the multiblock.
+ */
+ public void updateRedstoneOutput(float pH) {
+ isOn = (pH > threshold) ^ inverted;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (isOn) {
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ aBaseMetaTileEntity.setStrongOutputRedstoneSignal(side, (byte) 15);
+ }
+ } else {
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ aBaseMetaTileEntity.setStrongOutputRedstoneSignal(side, (byte) 0);
+ }
+ }
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_pHSensor(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(textureFont), TextureFactory.builder()
+ .addIcon(textureFont_Glow)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(textureFont) };
+ }
+
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ final String INVERTED = GT_Utility.trans("INVERTED", "Inverted");
+ final String NORMAL = GT_Utility.trans("NORMAL", "Normal");
+
+ builder.widget(
+ new CoverCycleButtonWidget().setToggle(() -> inverted, (val) -> inverted = val)
+ .setTextureGetter(
+ (state) -> state == 1 ? GT_UITextures.OVERLAY_BUTTON_REDSTONE_ON
+ : GT_UITextures.OVERLAY_BUTTON_REDSTONE_OFF)
+ .addTooltip(0, NORMAL)
+ .addTooltip(1, INVERTED)
+ .setPos(10, 8))
+ .widget(
+ new TextWidget().setStringSupplier(() -> inverted ? INVERTED : NORMAL)
+ .setDefaultColor(COLOR_TEXT_GRAY.get())
+ .setTextAlignment(Alignment.CenterLeft)
+ .setPos(28, 12))
+ .widget(
+ new NumericWidget().setBounds(0, 14.0)
+ .setIntegerOnly(false)
+ .setGetter(() -> (double) threshold)
+ .setSetter((value) -> threshold = (float) Math.round(value * 100.0) / 100.0f)
+ .setScrollValues(0.1, 0.01, 1.0)
+ .setMaximumFractionDigits(2)
+ .setTextColor(Color.WHITE.dark(1))
+ .setTextAlignment(Alignment.CenterLeft)
+ .setFocusOnGuiOpen(true)
+ .setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD.withOffset(-1, -1, 2, 2))
+ .setPos(10, 28)
+ .setSize(77, 12))
+ .widget(
+ new TextWidget(StatCollector.translateToLocal("GT5U.gui.text.ph_sensor"))
+ .setDefaultColor(COLOR_TEXT_GRAY.get())
+ .setTextAlignment(Alignment.CenterLeft)
+ .setPos(90, 30));
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/LinkedPurificationUnit.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/LinkedPurificationUnit.java
new file mode 100644
index 0000000000..104cbe300f
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/LinkedPurificationUnit.java
@@ -0,0 +1,125 @@
+package gregtech.common.tileentities.machines.multi.purification;
+
+import static gregtech.GT_Mod.gregtechproxy;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.world.World;
+import net.minecraftforge.common.DimensionManager;
+
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.util.GT_Util;
+
+/**
+ * Small wrapper around a GT_MetaTileEntity_PurificationUnitBase, to be stored in the main purification plant
+ * controller. May be useful for storing additional data in the controller that the individual units do not need
+ * to know about.
+ */
+public class LinkedPurificationUnit {
+
+ /**
+ * Whether this unit is active in the current cycle. We need to keep track of this so units cannot come online
+ * in the middle of a cycle and suddenly start processing.
+ */
+ private boolean mIsActive = false;
+
+ private final GT_MetaTileEntity_PurificationUnitBase<?> mMetaTileEntity;
+
+ public LinkedPurificationUnit(GT_MetaTileEntity_PurificationUnitBase<?> unit) {
+ this.mMetaTileEntity = unit;
+ }
+
+ /**
+ * Construct new link data from a NBT compound. This is intended to sync the linked units to the client.
+ *
+ * @param nbtData NBT data obtained from writeLinkDataToNBT()
+ */
+ public LinkedPurificationUnit(NBTTagCompound nbtData) {
+ this.mIsActive = nbtData.getBoolean("active");
+ NBTTagCompound linkData = nbtData.getCompoundTag("linkData");
+ World world = null;
+ if (!gregtechproxy.isClientSide()) {
+ world = DimensionManager.getWorld(nbtData.getInteger("worldID"));
+ } else {
+ world = Minecraft.getMinecraft().thePlayer.worldObj;
+ }
+
+ // Load coordinates from link data
+ int x = linkData.getInteger("x");
+ int y = linkData.getInteger("y");
+ int z = linkData.getInteger("z");
+
+ // Find a TileEntity at this location
+ TileEntity te = GT_Util.getTileEntity(world, x, y, z, true);
+ if (te == null) {
+ // This is a bug, throw a fatal error.
+ throw new NullPointerException("Unit disappeared during server sync. This is a bug.");
+ }
+
+ // Cast TileEntity to proper GT TileEntity
+ this.mMetaTileEntity = (GT_MetaTileEntity_PurificationUnitBase<?>) ((IGregTechTileEntity) te)
+ .getMetaTileEntity();
+ }
+
+ public GT_MetaTileEntity_PurificationUnitBase<?> metaTileEntity() {
+ return mMetaTileEntity;
+ }
+
+ /**
+ * Whether this unit is considered as active in the current cycle
+ *
+ * @return true if this unit is active in the current cycle
+ */
+ public boolean isActive() {
+ return mIsActive;
+ }
+
+ public void setActive(boolean active) {
+ this.mIsActive = active;
+ }
+
+ public String getStatusString() {
+ if (this.isActive()) {
+ return EnumChatFormatting.GREEN + "Active";
+ }
+
+ PurificationUnitStatus status = this.mMetaTileEntity.status();
+ switch (status) {
+ case ONLINE -> {
+ return EnumChatFormatting.GREEN + "Online";
+ }
+ case DISABLED -> {
+ return EnumChatFormatting.YELLOW + "Disabled";
+ }
+ case INCOMPLETE_STRUCTURE -> {
+ return EnumChatFormatting.RED + "Incomplete Structure";
+ }
+ }
+
+ // If this happens, this is a bug and there is a switch case missing.
+ return null;
+ }
+
+ /**
+ * Save link data to a NBT tag, so it can be reconstructed at the client side
+ */
+ public NBTTagCompound writeLinkDataToNBT() {
+ NBTTagCompound tag = new NBTTagCompound();
+ tag.setBoolean("active,", this.mIsActive);
+
+ NBTTagCompound linkData = new NBTTagCompound();
+ IGregTechTileEntity mte = this.mMetaTileEntity.getBaseMetaTileEntity();
+ linkData.setInteger("x", mte.getXCoord());
+ linkData.setInteger("y", mte.getYCoord());
+ linkData.setInteger("z", mte.getZCoord());
+ tag.setTag("linkData", linkData);
+
+ tag.setInteger(
+ "worldID",
+ this.mMetaTileEntity.getBaseMetaTileEntity()
+ .getWorld().provider.dimensionId);
+ return tag;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/PurificationPlantStructureString.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/PurificationPlantStructureString.java
new file mode 100644
index 0000000000..7c40f9be53
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/PurificationPlantStructureString.java
@@ -0,0 +1,15 @@
+package gregtech.common.tileentities.machines.multi.purification;
+
+// Extracted to another class to not fill the main source file with bloat
+public class PurificationPlantStructureString {
+
+ public static final String[][] STRUCTURE_STRING = new String[][] {
+ { " ", " ", " ", " GBG ", " GHG ", " GHG ", " G~G ", " GHG ", "AAAAAAA" },
+ { " ", " ", " ", " GBG ", "BBBBBBB", "BCCCCCB", "BCCCCCB", "BCCCCCB", "AAAAAAA" },
+ { " G G ", " G G ", " G G ", " GGBGG ", "BCCCCCB", "C C", "C C", "C C", "AAAAAAA" },
+ { " BBB ", " BBB ", " BBB ", " BBB ", "BCCCCCB", "C C", "D C", "C C", "AAAAAAA" },
+ { " BFB ", " BFB ", " BFB ", " BFB ", "BCCCCCB", "C C", "D C", "C C", "AAAAAAA" },
+ { " BBB ", " BBB ", " BBB ", " BBB ", "BCCCCCB", "C C", "D ", "C ", "AAAAAAA" },
+ { " G G ", " G G ", " G G ", " G G ", "BCCCCCB", "C C", "C C", "C C", "AAAAAAA" },
+ { " ", " ", " ", " ", "BBBBBBB", "BCCCCCB", "BCDDDCB", "BCCCCCB", "AAAAAAA" } };
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/PurificationUnitStatus.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/PurificationUnitStatus.java
new file mode 100644
index 0000000000..403961e28d
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/PurificationUnitStatus.java
@@ -0,0 +1,14 @@
+package gregtech.common.tileentities.machines.multi.purification;
+
+/**
+ * Represents the status of a linked purification unit. This is updated by the main purification plant
+ * controller by checking the linked machines.
+ */
+public enum PurificationUnitStatus {
+ // The purification unit is online and ready to work
+ ONLINE,
+ // The purification unit is correctly formed, but switched off.
+ DISABLED,
+ // The purification unit has failed its structure check
+ INCOMPLETE_STRUCTURE,
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/PurifiedWaterHelpers.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/PurifiedWaterHelpers.java
new file mode 100644
index 0000000000..ab397aac94
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/PurifiedWaterHelpers.java
@@ -0,0 +1,42 @@
+package gregtech.common.tileentities.machines.multi.purification;
+
+import net.minecraftforge.fluids.FluidStack;
+
+import gregtech.api.enums.Materials;
+import gregtech.api.util.GT_Recipe;
+
+public class PurifiedWaterHelpers {
+
+ public static Materials getPurifiedWaterTier(int tier) {
+ return switch (tier) {
+ case 1 -> Materials.Grade1PurifiedWater;
+ case 2 -> Materials.Grade2PurifiedWater;
+ case 3 -> Materials.Grade3PurifiedWater;
+ case 4 -> Materials.Grade4PurifiedWater;
+ case 5 -> Materials.Grade5PurifiedWater;
+ case 6 -> Materials.Grade6PurifiedWater;
+ case 7 -> Materials.Grade7PurifiedWater;
+ case 8 -> Materials.Grade8PurifiedWater;
+ default -> throw new IllegalStateException("Unexpected value: " + tier);
+ };
+ }
+
+ public static int getWaterTier(FluidStack fluid) {
+ if (fluid == null) return 0;
+ else if (fluid.isFluidEqual(Materials.Grade1PurifiedWater.getFluid(1000L))) return 1;
+ else if (fluid.isFluidEqual(Materials.Grade2PurifiedWater.getFluid(1000L))) return 2;
+ else if (fluid.isFluidEqual(Materials.Grade3PurifiedWater.getFluid(1000L))) return 3;
+ else if (fluid.isFluidEqual(Materials.Grade4PurifiedWater.getFluid(1000L))) return 4;
+ else if (fluid.isFluidEqual(Materials.Grade5PurifiedWater.getFluid(1000L))) return 5;
+ else if (fluid.isFluidEqual(Materials.Grade6PurifiedWater.getFluid(1000L))) return 6;
+ else if (fluid.isFluidEqual(Materials.Grade7PurifiedWater.getFluid(1000L))) return 7;
+ else if (fluid.isFluidEqual(Materials.Grade8PurifiedWater.getFluid(1000L))) return 8;
+ else return 0;
+ }
+
+ // Used to construct NEI comparator for water tier. Returns 0 if no water is used in this recipe
+ public static int getWaterTierFromRecipe(GT_Recipe recipe) {
+ if (recipe.mFluidInputs.length == 0) return 0;
+ else return getWaterTier(recipe.mFluidInputs[0]);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/UVTreatmentLensCycle.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/UVTreatmentLensCycle.java
new file mode 100644
index 0000000000..b834cdd0fe
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/UVTreatmentLensCycle.java
@@ -0,0 +1,39 @@
+package gregtech.common.tileentities.machines.multi.purification;
+
+import java.util.List;
+
+import net.minecraft.item.ItemStack;
+
+public class UVTreatmentLensCycle {
+
+ private final List<ItemStack> lenses;
+
+ private int currentLens = 0;
+
+ public UVTreatmentLensCycle(List<ItemStack> lenses) {
+ this.lenses = lenses;
+ if (lenses.isEmpty()) {
+ throw new IllegalArgumentException("Supplied lens array may not be empty");
+ }
+ }
+
+ public ItemStack current() {
+ return lenses.get(currentLens);
+ }
+
+ public boolean advance() {
+ if (currentLens < lenses.size() - 1) {
+ currentLens = currentLens + 1;
+ return true;
+ }
+ return false;
+ }
+
+ public void reset() {
+ currentLens = 0;
+ }
+
+ public ItemStack first() {
+ return lenses.get(0);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multiblock/AdvChemicalProcessor.java b/src/main/java/gregtech/common/tileentities/machines/multiblock/AdvChemicalProcessor.java
new file mode 100644
index 0000000000..87e986f941
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multiblock/AdvChemicalProcessor.java
@@ -0,0 +1,508 @@
+package gregtech.common.tileentities.machines.multiblock;
+
+import static com.google.common.primitives.Ints.saturatedCast;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.ENERGY_IN;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.FLUID_IN;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.FLUID_OUT;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.ITEM_IN;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.ITEM_OUT;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.NOTHING;
+import static gregtech.api.util.GT_StructureUtilityMuTE.MOTOR_CASINGS;
+import static gregtech.api.util.GT_StructureUtilityMuTE.ofMuTECasings;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraftforge.common.util.Constants;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.IFluidTank;
+
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+import com.gtnewhorizon.structurelib.util.Vec3Impl;
+import com.gtnewhorizons.modularui.api.forge.ItemStackHandler;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.math.Color;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.api.widget.IWidgetBuilder;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.MultiChildWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotGroup;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+import com.gtnewhorizons.modularui.common.widget.textfield.NumericWidget;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.HeatingCoilLevel;
+import gregtech.api.enums.Materials;
+import gregtech.api.fluid.FluidTankGT;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.multitileentity.enums.GT_MultiTileCasing;
+import gregtech.api.multitileentity.multiblock.base.ComplexParallelController;
+import gregtech.api.multitileentity.multiblock.casing.Glasses;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_StructureUtility;
+import gregtech.api.util.GT_StructureUtilityMuTE;
+import gregtech.common.tileentities.machines.multiblock.logic.AdvChemicalProcessorProcessingLogic;
+
+public class AdvChemicalProcessor
+ extends ComplexParallelController<AdvChemicalProcessor, AdvChemicalProcessorProcessingLogic> {
+
+ private static IStructureDefinition<AdvChemicalProcessor> STRUCTURE_DEFINITION = null;
+ protected static final String STRUCTURE_PIECE_T1 = "T1";
+ protected static final String STRUCTURE_PIECE_T2 = "T2";
+ protected static final String STRUCTURE_PIECE_T3 = "T3";
+ protected static final String STRUCTURE_PIECE_T4 = "T4";
+ protected static final String STRUCTURE_PIECE_T5_6 = "T5_6";
+ protected static final String STRUCTURE_PIECE_T7_8 = "T7_8";
+ protected static final Vec3Impl STRUCTURE_OFFSET_T1 = new Vec3Impl(3, 1, 0);
+ protected static final Vec3Impl STRUCTURE_OFFSET_T2 = new Vec3Impl(1, 4, -3);
+ protected static final Vec3Impl STRUCTURE_OFFSET_T3 = new Vec3Impl(8, 0, 5);
+ protected static final Vec3Impl STRUCTURE_OFFSET_T4 = new Vec3Impl(-14, 0, 0);
+ protected static final Vec3Impl STRUCTURE_OFFSET_T5 = new Vec3Impl(14, 0, -6);
+ protected static final Vec3Impl STRUCTURE_OFFSET_T6 = new Vec3Impl(-16, 0, 0);
+ protected static final Vec3Impl STRUCTURE_OFFSET_T7 = new Vec3Impl(16, 0, 15);
+ protected static final Vec3Impl STRUCTURE_OFFSET_T8 = new Vec3Impl(-16, 0, 0);
+ protected static final int PROCESS_WINDOW_BASE_ID = 100;
+ protected static final int ITEM_WHITELIST_SLOTS = 8;
+ protected static final int FLUID_WHITELIST_SLOTS = 8;
+ protected static final int MAX_PROCESSES = 8;
+ protected HeatingCoilLevel coilTier;
+ protected final ArrayList<HashSet<String>> processWhitelists = new ArrayList<>(MAX_PROCESSES);
+ protected final ArrayList<ItemStackHandler> processWhitelistInventoryHandlers = new ArrayList<>(MAX_PROCESSES);
+ protected final ArrayList<ArrayList<IFluidTank>> processFluidWhiteLists = new ArrayList<>(MAX_PROCESSES);
+ protected boolean wasWhitelistOpened = false;
+
+ public AdvChemicalProcessor() {
+ super();
+ for (int i = 0; i < MAX_PROCESSES; i++) {
+ processWhitelists.add(null);
+ processWhitelistInventoryHandlers.add(new ItemStackHandler(ITEM_WHITELIST_SLOTS));
+ ArrayList<IFluidTank> processFluidTanks = new ArrayList<>(FLUID_WHITELIST_SLOTS);
+ for (int j = 0; j < FLUID_WHITELIST_SLOTS; j++) {
+ processFluidTanks.add(new FluidTankGT());
+ }
+ processFluidWhiteLists.add(processFluidTanks);
+ }
+ setMaxComplexParallels(1, false);
+ }
+
+ @Override
+ public void readMultiTileNBT(NBTTagCompound nbt) {
+ super.readMultiTileNBT(nbt);
+ setMaxComplexParallels(nbt.getInteger("processors"), false);
+ final NBTTagCompound processWhiteLists = nbt.getCompoundTag("whiteLists");
+ long capacity = 1000;
+ if (nbt.hasKey(GT_Values.NBT.TANK_CAPACITY)) {
+ capacity = saturatedCast(nbt.getLong(GT_Values.NBT.TANK_CAPACITY));
+ }
+ for (int i = 0; i < MAX_PROCESSES; i++) {
+
+ if (processWhiteLists == null) {
+ continue;
+ }
+
+ final NBTTagCompound itemList = processWhiteLists.getCompoundTag("items" + i);
+ if (itemList != null) {
+ processWhitelistInventoryHandlers.get(i)
+ .deserializeNBT(itemList);
+ }
+ final NBTTagList fluidList = processWhiteLists.getTagList("fluids" + i, Constants.NBT.TAG_COMPOUND);
+
+ if (fluidList == null) {
+ continue;
+ }
+
+ for (int j = 0; j < fluidList.tagCount(); j++) {
+ final NBTTagCompound fluid = fluidList.getCompoundTagAt(j);
+
+ if (fluid == null) {
+ continue;
+ }
+
+ short index = fluid.getShort("s");
+ FluidStack fluidStack = FluidStack.loadFluidStackFromNBT(fluid);
+ if (fluidStack != null) {
+ processFluidWhiteLists.get(i)
+ .get(index)
+ .fill(fluidStack, true);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void writeMultiTileNBT(NBTTagCompound nbt) {
+ super.writeMultiTileNBT(nbt);
+ nbt.setInteger("processors", maxComplexParallels);
+ final NBTTagCompound processWhiteLists = new NBTTagCompound();
+ for (int i = 0; i < MAX_PROCESSES; i++) {
+ processWhiteLists.setTag(
+ "items" + i,
+ processWhitelistInventoryHandlers.get(i)
+ .serializeNBT());
+ final NBTTagList fluidList = new NBTTagList();
+ for (int j = 0; j < FLUID_WHITELIST_SLOTS; j++) {
+ final FluidStack fluidStack = processFluidWhiteLists.get(i)
+ .get(j)
+ .getFluid();
+ if (fluidStack != null) {
+ final NBTTagCompound tag = new NBTTagCompound();
+ tag.setByte("s", (byte) j);
+ fluidStack.writeToNBT(tag);
+ fluidList.appendTag(tag);
+ }
+ }
+ processWhiteLists.setTag("fluids" + i, fluidList);
+ }
+ nbt.setTag("whiteLists", processWhiteLists);
+ }
+
+ @Override
+ public short getCasingRegistryID() {
+ return GT_MultiTileCasing.Chemical.getRegistryId();
+ }
+
+ @Override
+ public int getCasingMeta() {
+ return GT_MultiTileCasing.Chemical.getId();
+ }
+
+ @Override
+ public GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Chemical Reactor")
+ .addInfo("Controller block for the Advanced Chemical Processor")
+ .addInfo("Does not lose efficiency when overclocked")
+ .addInfo("Accepts fluids instead of fluid cells")
+ .addInfo("Can do multiple different recipes at once")
+ .addInfo("By using the whitelist filter a recipe can push its output")
+ .addInfo("to a different recipes input to chain them")
+ .addInfo("Disclaimer: Still WIP - Use at your own risk")
+ .addInfo(GT_Values.Authorminecraft7771)
+ .addSeparator()
+ .beginStructureBlock(5, 3, 3, false)
+ .addController("Front center")
+ .addCasingInfoExactly("PTFE Pipe Machine Casing", 8, false)
+ .addCasingInfoExactly("Heating Coils", 3, true)
+ .addCasingInfoExactly("EV+ Glass", 3, true)
+ .addCasingInfoExactly("Motor Casing", 3, true)
+ .addCasingInfoExactly("Chemical Casing", 27, false)
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public Vec3Impl getStartingStructureOffset() {
+ return STRUCTURE_OFFSET_T1;
+ }
+
+ @Override
+ public boolean checkMachine() {
+ setCoilTier(HeatingCoilLevel.None);
+ buildState.startBuilding(getStartingStructureOffset());
+ if (!checkPiece(STRUCTURE_PIECE_T1, buildState.getCurrentOffset())) return buildState.failBuilding();
+ if (maxComplexParallels > 1) {
+ buildState.addOffset(STRUCTURE_OFFSET_T2);
+ if (!checkPiece(STRUCTURE_PIECE_T2, buildState.getCurrentOffset())) return buildState.failBuilding();
+ }
+ if (maxComplexParallels > 2) {
+ buildState.addOffset(STRUCTURE_OFFSET_T3);
+ if (!checkPiece(STRUCTURE_PIECE_T3, buildState.getCurrentOffset())) return buildState.failBuilding();
+ }
+ if (maxComplexParallels > 3) {
+ buildState.addOffset(STRUCTURE_OFFSET_T4);
+ if (!checkPiece(STRUCTURE_PIECE_T4, buildState.getCurrentOffset())) return buildState.failBuilding();
+ }
+ if (maxComplexParallels > 4) {
+ buildState.addOffset(STRUCTURE_OFFSET_T5);
+ if (!checkPiece(STRUCTURE_PIECE_T5_6, buildState.getCurrentOffset())) return buildState.failBuilding();
+ }
+ if (maxComplexParallels > 5) {
+ buildState.addOffset(STRUCTURE_OFFSET_T6);
+ if (!checkPiece(STRUCTURE_PIECE_T5_6, buildState.getCurrentOffset())) return buildState.failBuilding();
+ }
+ if (maxComplexParallels > 6) {
+ buildState.addOffset(STRUCTURE_OFFSET_T7);
+ if (!checkPiece(STRUCTURE_PIECE_T7_8, buildState.getCurrentOffset())) return buildState.failBuilding();
+ }
+ if (maxComplexParallels > 7) {
+ buildState.addOffset(STRUCTURE_OFFSET_T8);
+ if (!checkPiece(STRUCTURE_PIECE_T7_8, buildState.getCurrentOffset())) return buildState.failBuilding();
+ }
+ buildState.stopBuilding();
+ return super.checkMachine();
+ }
+
+ @Override
+ public void construct(ItemStack trigger, boolean hintsOnly) {
+ buildState.startBuilding(getStartingStructureOffset());
+ buildPiece(STRUCTURE_PIECE_T1, trigger, hintsOnly, buildState.getCurrentOffset());
+ if (maxComplexParallels > 1) {
+ buildState.addOffset(STRUCTURE_OFFSET_T2);
+ buildPiece(STRUCTURE_PIECE_T2, trigger, hintsOnly, buildState.getCurrentOffset());
+ }
+ if (maxComplexParallels > 2) {
+ buildState.addOffset(STRUCTURE_OFFSET_T3);
+ buildPiece(STRUCTURE_PIECE_T3, trigger, hintsOnly, buildState.getCurrentOffset());
+ }
+ if (maxComplexParallels > 3) {
+ buildState.addOffset(STRUCTURE_OFFSET_T4);
+ buildPiece(STRUCTURE_PIECE_T4, trigger, hintsOnly, buildState.getCurrentOffset());
+ }
+ if (maxComplexParallels > 4) {
+ buildState.addOffset(STRUCTURE_OFFSET_T5);
+ buildPiece(STRUCTURE_PIECE_T5_6, trigger, hintsOnly, buildState.getCurrentOffset());
+ }
+ if (maxComplexParallels > 5) {
+ buildState.addOffset(STRUCTURE_OFFSET_T6);
+ buildPiece(STRUCTURE_PIECE_T5_6, trigger, hintsOnly, buildState.getCurrentOffset());
+ }
+ if (maxComplexParallels > 6) {
+ buildState.addOffset(STRUCTURE_OFFSET_T7);
+ buildPiece(STRUCTURE_PIECE_T7_8, trigger, hintsOnly, buildState.getCurrentOffset());
+ }
+ if (maxComplexParallels > 7) {
+ buildState.addOffset(STRUCTURE_OFFSET_T8);
+ buildPiece(STRUCTURE_PIECE_T7_8, trigger, hintsOnly, buildState.getCurrentOffset());
+ }
+
+ buildState.stopBuilding();
+ }
+
+ @Override
+ public IStructureDefinition<AdvChemicalProcessor> getStructureDefinition() {
+ if (STRUCTURE_DEFINITION == null) {
+ STRUCTURE_DEFINITION = StructureDefinition.<AdvChemicalProcessor>builder()
+ .addShape(
+ STRUCTURE_PIECE_T1,
+ transpose(
+ new String[][] { { "CPCPC", "CCCCC", "CPCPC" }, { "CGC~C", "GWWWU", "CGCCC" },
+ { "CPCPC", "CTTTC", "CPCPC" } }))
+ .addShape(
+ STRUCTURE_PIECE_T2,
+ new String[][] { { " ", " ", " ", " ", " ", " F F ", " B B " },
+ { " ", " ", " ", " ", " ", " F F ", " B B " },
+ { " ", " ", " ", " ", " ", " F F ", " BBB " },
+ { " C ", " CGC ", " CGC ", " CGC ", " CGC ", " CGC ", " BCCCB " },
+ { " BBB ", " C C ", " C C ", " C C ", " C C ", " C C ", "BCPPPCB" },
+ { " CBBBC ", " G W G ", " G W G ", " G W G ", " G W G ", " G W G ", "BCPCPCB" },
+ { " BBB ", " C C ", " C C ", " C C ", " C C ", " C C ", "BCPPPCB" },
+ { " C ", " CGC ", " CGC ", " CGC ", " CGC ", " CGC ", " BCCCB " },
+ { " ", " ", " ", " ", " ", " ", " BBB " } })
+ .addShape(
+ STRUCTURE_PIECE_T3,
+ new String[][] {
+ { " ", " ", " ", " ", " ", " ", " BBB " },
+ { " C ", " CGC ", " CGC ", " CGC ", " CGC ", " CGC ", " BCCCB " },
+ { " BBB ", " C C ", " C C ", " C C ", " C C ", " C CFFF", "BCPPPCBBB" },
+ { " CBBBC ", " G W G ", " G W G ", " G W G ", " G W G ", " G W G ", "BCPCPCB " },
+ { " BBB ", " C C ", " C C ", " C C ", " C C ", " C CFFF", "BCPPPCBBB" },
+ { " C ", " CGC ", " CGC ", " CGC ", " CGC ", " CGC ", " BCCCB " },
+ { " ", " ", " ", " ", " ", " ", " BBB " } })
+ .addShape(
+ STRUCTURE_PIECE_T4,
+ new String[][] {
+ { " ", " ", " ", " ", " ", " ", " BBB " },
+ { " C ", " CGC ", " CGC ", " CGC ", " CGC ", " CGC ", " BCCCB " },
+ { " BBB ", " C C ", " C C ", " C C ", " C C ", "FFFC C ", "BBBCPPPCB" },
+ { " CBBBC ", " G W G ", " G W G ", " G W G ", " G W G ", " G W G ", " BCPCPCB" },
+ { " BBB ", " C C ", " C C ", " C C ", " C C ", "FFFC C ", "BBBCPPPCB" },
+ { " C ", " CGC ", " CGC ", " CGC ", " CGC ", " CGC ", " BCCCB " },
+ { " ", " ", " ", " ", " ", " ", " BBB " } })
+ .addShape(
+ STRUCTURE_PIECE_T5_6,
+ new String[][] { { " ", " ", " ", " ", " ", " F F ", " " },
+ { " ", " ", " ", " ", " ", " F F ", " B B " },
+ { " ", " ", " ", " ", " ", " F F ", " B B " },
+ { " ", " ", " ", " ", " ", " F F ", " BBB " },
+ { " C ", " CGC ", " CGC ", " CGC ", " CGC ", " CGC ", " BCCCB " },
+ { " BBB ", " C C ", " C C ", " C C ", " C C ", " C C ", "BCPPPCB" },
+ { " CBBBC ", " G W G ", " G W G ", " G W G ", " G W G ", " G W G ", "BCPCPCB" },
+ { " BBB ", " C C ", " C C ", " C C ", " C C ", " C C ", "BCPPPCB" },
+ { " C ", " CGC ", " CGC ", " CGC ", " CGC ", " CGC ", " BCCCB " },
+ { " ", " ", " ", " ", " ", " ", " BBB " } })
+ .addShape(
+ STRUCTURE_PIECE_T7_8,
+ new String[][] { { " ", " ", " ", " ", " ", " ", " BBB " },
+ { " C ", " CGC ", " CGC ", " CGC ", " CGC ", " CGC ", " BCCCB " },
+ { " BBB ", " C C ", " C C ", " C C ", " C C ", " C C ", "BCPPPCB" },
+ { " CBBBC ", " G W G ", " G W G ", " G W G ", " G W G ", " G W G ", "BCPCPCB" },
+ { " BBB ", " C C ", " C C ", " C C ", " C C ", " C C ", "BCPPPCB" },
+ { " C ", " CGC ", " CGC ", " CGC ", " CGC ", " CGC ", " BCCCB " },
+ { " ", " ", " ", " ", " ", " F F ", " BBB " },
+ { " ", " ", " ", " ", " ", " F F ", " B B " },
+ { " ", " ", " ", " ", " ", " F F ", " B B " },
+ { " ", " ", " ", " ", " ", " F F ", " " } })
+ .addElement(
+ 'C',
+ ofMuTECasings(
+ FLUID_IN | ITEM_IN | FLUID_OUT | ITEM_OUT | ENERGY_IN,
+ GT_MultiTileCasing.Chemical.getCasing()))
+ .addElement('P', ofBlock(GregTech_API.sBlockCasings8, 1))
+ .addElement('T', ofMuTECasings(NOTHING, MOTOR_CASINGS))
+ .addElement(
+ 'W',
+ GT_StructureUtility.ofCoil(AdvChemicalProcessor::setCoilTier, AdvChemicalProcessor::getCoilTier))
+ .addElement('G', Glasses.chainAllGlasses())
+ .addElement('B', ofBlock(GregTech_API.sBlockCasings4, 1))
+ .addElement('F', GT_StructureUtility.ofFrame(Materials.Steel))
+ .addElement(
+ 'U',
+ ofMuTECasings(
+ FLUID_IN | ITEM_IN | FLUID_OUT | ITEM_OUT | ENERGY_IN,
+ GT_MultiTileCasing.Chemical.getCasing(),
+ GT_StructureUtilityMuTE.INVENTORY_CASINGS))
+ .build();
+ }
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ protected void setMaxComplexParallels(int parallel, boolean stopMachine) {
+ super.setMaxComplexParallels(parallel, stopMachine);
+ onStructureChange();
+ }
+
+ protected MultiChildWidget createMainPage(IWidgetBuilder<?> builder) {
+ MultiChildWidget child = new MultiChildWidget();
+ for (int i = 0; i < MAX_PROCESSES; i++) {
+ final int processIndex = i;
+ child.addChild(
+ new ButtonWidget().setPlayClickSound(true)
+ .setOnClick(
+ (clickData, widget) -> {
+ if (!widget.isClient()) widget.getContext()
+ .openSyncedWindow(PROCESS_WINDOW_BASE_ID + processIndex);
+ })
+ .setBackground(GT_UITextures.BUTTON_STANDARD, GT_UITextures.OVERLAY_BUTTON_WHITELIST)
+ .setSize(18, 18)
+ .setEnabled((widget -> processIndex < maxComplexParallels))
+ .setPos(20 * (i % 4) + 18, 18 + (i / 4) * 20));
+ }
+ child.addChild(
+ new NumericWidget().setGetter(() -> maxComplexParallels)
+ .setSetter(parallel -> setMaxComplexParallels((int) parallel, true))
+ .setBounds(1, MAX_PROCESSES)
+ .setTextColor(Color.WHITE.normal)
+ .setTextAlignment(Alignment.Center)
+ .addTooltip("Tier")
+ .setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD)
+ .setSize(18, 18)
+ .setPos(130, 85));
+ return child;
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ super.addUIWidgets(builder, buildContext);
+ for (int i = 0; i < MAX_PROCESSES; i++) {
+ final int processIndex = i;
+ buildContext.addSyncedWindow(
+ PROCESS_WINDOW_BASE_ID + i,
+ (player) -> createProcessConfigWindow(player, processIndex));
+ }
+ buildContext.addCloseListener(() -> {
+ // Reset HashSet, we will let it re-generate on next item output
+ if (wasWhitelistOpened) {
+ for (int i = 0; i < MAX_PROCESSES; i++) {
+ processWhitelists.set(i, null);
+ }
+ wasWhitelistOpened = false;
+ }
+ });
+ }
+
+ protected ModularWindow createProcessConfigWindow(final EntityPlayer player, final int processIndex) {
+ wasWhitelistOpened = true;
+ ModularWindow.Builder builder = ModularWindow.builder(86, 100);
+ builder.widget(
+ new TextWidget("Process " + processIndex).setTextAlignment(Alignment.Center)
+ .setPos(13, 7));
+ builder.setBackground(GT_UITextures.BACKGROUND_SINGLEBLOCK_DEFAULT);
+ builder.widget(
+ SlotGroup.ofItemHandler(processWhitelistInventoryHandlers.get(processIndex), 4)
+ .startFromSlot(0)
+ .endAtSlot(ITEM_WHITELIST_SLOTS - 1)
+ .phantom(true)
+ .background(getGUITextureSet().getItemSlot())
+ .build()
+ .setPos(7, 19));
+ builder.widget(
+ SlotGroup.ofFluidTanks(processFluidWhiteLists.get(processIndex), 4)
+ .startFromSlot(0)
+ .endAtSlot(FLUID_WHITELIST_SLOTS - 1)
+ .phantom(true)
+ .build()
+ .setPos(7, 55));
+ return builder.build();
+ }
+
+ @Override
+ public String getTileEntityName() {
+ return "gt.multitileentity.multiblock.advchemicalprocessor";
+ }
+
+ @Override
+ public String getLocalName() {
+ return "Advanced Chemical Processor";
+ }
+
+ public void setCoilTier(HeatingCoilLevel coilTier) {
+ this.coilTier = coilTier;
+ }
+
+ public HeatingCoilLevel getCoilTier() {
+ return coilTier;
+ }
+
+ @Override
+ protected boolean hasPerfectOverclock() {
+ return true;
+ }
+
+ protected void generateWhitelist(int processIndex) {
+ HashSet<String> whitelist = new HashSet<>();
+ for (ItemStack itemStack : processWhitelistInventoryHandlers.get(processIndex)
+ .getStacks()) {
+ if (itemStack != null) {
+ whitelist.add(getWhitelistString(itemStack));
+ }
+ }
+ for (IFluidTank tank : processFluidWhiteLists.get(processIndex)) {
+ if (tank.getFluid() != null) {
+ whitelist.add(getWhitelistString(tank.getFluid()));
+ }
+ }
+ processWhitelists.set(processIndex, whitelist);
+ }
+
+ protected String getWhitelistString(ItemStack itemStack) {
+ if (itemStack != null) {
+ return itemStack.getUnlocalizedName();
+ }
+ return null;
+ }
+
+ protected String getWhitelistString(FluidStack fluidStack) {
+ if (fluidStack != null) {
+ return fluidStack.getUnlocalizedName();
+ }
+ return null;
+ }
+
+ @Override
+ @Nonnull
+ protected AdvChemicalProcessorProcessingLogic createProcessingLogic() {
+ return new AdvChemicalProcessorProcessingLogic();
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multiblock/CokeOven.java b/src/main/java/gregtech/common/tileentities/machines/multiblock/CokeOven.java
new file mode 100644
index 0000000000..e8e31ba32c
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multiblock/CokeOven.java
@@ -0,0 +1,157 @@
+package gregtech.common.tileentities.machines.multiblock;
+
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.ITEM_IN;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.ITEM_OUT;
+import static gregtech.api.util.GT_StructureUtilityMuTE.ofMuTECasings;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.StatCollector;
+
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+import com.gtnewhorizon.structurelib.util.Vec3Impl;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.common.internal.network.NetworkUtils;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+
+import gregtech.GT_Mod;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.multitileentity.enums.GT_MultiTileCasing;
+import gregtech.api.multitileentity.multiblock.base.Controller;
+import gregtech.api.task.tasks.PollutionTask;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.common.tileentities.machines.multiblock.logic.CokeOvenProcessingLogic;
+
+public class CokeOven extends Controller<CokeOven, CokeOvenProcessingLogic> {
+
+ private static IStructureDefinition<CokeOven> STRUCTURE_DEFINITION = null;
+ private static final Vec3Impl OFFSET = new Vec3Impl(1, 1, 0);
+ private static final String MAIN = "Main";
+ private static final int POLLUTION_AMOUNT = 10;
+
+ public CokeOven() {
+ super();
+ setElectric(false);
+ new PollutionTask<>(this).setPollutionPerSecond(POLLUTION_AMOUNT);
+ }
+
+ @Override
+ public void construct(ItemStack trigger, boolean hintsOnly) {
+ buildState.startBuilding(getStartingStructureOffset());
+ buildPiece(MAIN, trigger, hintsOnly, buildState.stopBuilding());
+ }
+
+ @Override
+ public boolean checkMachine() {
+ buildState.startBuilding(getStartingStructureOffset());
+ return checkPiece(MAIN, buildState.stopBuilding());
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack trigger, int elementBudget, ISurvivalBuildEnvironment env) {
+ buildState.startBuilding(getStartingStructureOffset());
+ return survivalBuildPiece(MAIN, trigger, buildState.stopBuilding(), elementBudget, env, false);
+ }
+
+ @Override
+ public short getCasingRegistryID() {
+ return 0;
+ }
+
+ @Override
+ public int getCasingMeta() {
+ return GT_MultiTileCasing.CokeOven.getId();
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Coke Oven")
+ .addInfo("Used for charcoal")
+ .beginStructureBlock(3, 3, 3, true)
+ .addCasingInfoExactly("Coke Oven Bricks", 25, false)
+ .addPollutionAmount(POLLUTION_AMOUNT)
+ .toolTipFinisher(GT_Values.AuthorBlueWeabo);
+ return tt;
+ }
+
+ @Override
+ public Vec3Impl getStartingStructureOffset() {
+ return OFFSET;
+ }
+
+ @Override
+ public IStructureDefinition<CokeOven> getStructureDefinition() {
+ if (STRUCTURE_DEFINITION == null) {
+ STRUCTURE_DEFINITION = StructureDefinition.<CokeOven>builder()
+ .addShape(
+ MAIN,
+ new String[][] { { "AAA", "A~A", "AAA" }, { "AAA", "A-A", "AAA" }, { "AAA", "AAA", "AAA" } })
+ .addElement('A', ofMuTECasings(ITEM_IN | ITEM_OUT, GT_MultiTileCasing.CokeOven.getCasing()))
+ .build();
+ }
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public boolean hasFluidInput() {
+ return false;
+ }
+
+ @Override
+ protected void addTitleTextStyle(ModularWindow.Builder builder, String title) {
+ final int TAB_PADDING = 3;
+ final int TITLE_PADDING = 2;
+ int titleWidth = 0, titleHeight = 0;
+ if (NetworkUtils.isClient()) {
+ final FontRenderer fontRenderer = Minecraft.getMinecraft().fontRenderer;
+ final List<String> titleLines = fontRenderer
+ .listFormattedStringToWidth(title, getGUIWidth() - (TAB_PADDING + TITLE_PADDING) * 2);
+ titleWidth = titleLines.size() > 1 ? getGUIWidth() - (TAB_PADDING + TITLE_PADDING) * 2
+ : fontRenderer.getStringWidth(title);
+ titleHeight = titleLines.size() * fontRenderer.FONT_HEIGHT + (titleLines.size() - 1);
+ }
+
+ final DrawableWidget tab = new DrawableWidget();
+ final TextWidget text = new TextWidget(title).setDefaultColor(getTitleColor())
+ .setTextAlignment(Alignment.CenterLeft)
+ .setMaxWidth(titleWidth);
+ if (GT_Mod.gregtechproxy.mTitleTabStyle == 1) {
+ tab.setDrawable(getGUITextureSet().getTitleTabAngular())
+ .setPos(0, -(titleHeight + TAB_PADDING) + 1)
+ .setSize(getGUIWidth(), titleHeight + TAB_PADDING * 2);
+ text.setPos(TAB_PADDING + TITLE_PADDING, -titleHeight + TAB_PADDING);
+ } else {
+ tab.setDrawable(getGUITextureSet().getTitleTabDark())
+ .setPos(0, -(titleHeight + TAB_PADDING * 2) + 1)
+ .setSize(titleWidth + (TAB_PADDING + TITLE_PADDING) * 2, titleHeight + TAB_PADDING * 2 - 1);
+ text.setPos(TAB_PADDING + TITLE_PADDING, -titleHeight);
+ }
+ builder.widget(tab)
+ .widget(text);
+ }
+
+ @Override
+ public String getLocalName() {
+ return StatCollector.translateToLocal(getTileEntityName());
+ }
+
+ public String getTileEntityName() {
+ return "gt.multitileentity.multiblock.cokeOven";
+ }
+
+ @Override
+ @Nonnull
+ protected CokeOvenProcessingLogic createProcessingLogic() {
+ return new CokeOvenProcessingLogic();
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multiblock/DistillationTower.java b/src/main/java/gregtech/common/tileentities/machines/multiblock/DistillationTower.java
new file mode 100644
index 0000000000..59efd46c94
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multiblock/DistillationTower.java
@@ -0,0 +1,176 @@
+package gregtech.common.tileentities.machines.multiblock;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.*;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.*;
+import static gregtech.api.util.GT_StructureUtilityMuTE.MOTOR_CASINGS;
+import static gregtech.api.util.GT_StructureUtilityMuTE.ofMuTECasings;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.util.StatCollector;
+
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+import com.gtnewhorizon.structurelib.util.Vec3Impl;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.HeatingCoilLevel;
+import gregtech.api.enums.Materials;
+import gregtech.api.multitileentity.enums.GT_MultiTileCasing;
+import gregtech.api.multitileentity.multiblock.base.StackableController;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_StructureUtility;
+import gregtech.common.tileentities.machines.multiblock.logic.DistillationTowerProcessingLogic;
+
+public class DistillationTower extends StackableController<DistillationTower, DistillationTowerProcessingLogic> {
+
+ private static IStructureDefinition<DistillationTower> STRUCTURE_DEFINITION_MEGA = null;
+ private static final Vec3Impl STRUCTURE_OFFSET_MEGA = new Vec3Impl(8, 3, 0);
+ private static final Vec3Impl STRUCTURE_OFFSET_MEGA_START = new Vec3Impl(0, 3, 0);
+ private static final Vec3Impl STRUCTURE_OFFSET_MEGA_STOP = new Vec3Impl(0, 5, 0);
+ private static final Vec3Impl STRUCTURE_OFFSET_MEGA_STACK = new Vec3Impl(0, 3, 0);
+ private static final String STACKABLE_MIDDLE_1 = "STACKABLE_MIDDLE_1";
+ private static final String STACKABLE_MIDDLE_2 = "STACKABLE_MIDDLE_2";
+ private boolean isMega = true;
+
+ @Override
+ public short getCasingRegistryID() {
+ return 0;
+ }
+
+ @Override
+ public int getCasingMeta() {
+ return GT_MultiTileCasing.Distillation.getId();
+ }
+
+ @Override
+ public GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Distillation Tower")
+ .addInfo("Controller block for the Distillation Tower")
+ .addInfo("Can be specialised to be a mega structure")
+ .addInfo(GT_Values.Authorminecraft7771)
+ .addSeparator()
+ .beginStructureBlock(5, 3, 3, false)
+ .addController("Front center")
+ .toolTipFinisher("Gregtech");
+ return tt;
+ }
+
+ @Override
+ public Vec3Impl getStartingStructureOffset() {
+ return STRUCTURE_OFFSET_MEGA;
+ }
+
+ @Override
+ public IStructureDefinition<DistillationTower> getStructureDefinition() {
+ if (STRUCTURE_DEFINITION_MEGA == null) {
+ STRUCTURE_DEFINITION_MEGA = StructureDefinition.<DistillationTower>builder()
+ .addShape(
+ STACKABLE_START,
+ transpose(
+ // spotless:off
+ new String[][]{
+ {" "," "," "," "," "," ECCCCCE "," CCCCCCC "," C C "," EC CE "," CC CC "," CC CC ABA "," CC CC B B "," CC CC ABA "," CC CC "," EC CE "," C C "," CCCCCCC "," ECCCCCE "," "},
+ {" "," EEE "," E "," E "," E "," E E E "," CCCCCCC "," C C "," EC CE "," C C "," C C ABA "," C C B B "," C C ABA "," C C "," EC CE "," C C "," CCCCCCC "," E E "," "},
+ {" CCCCC "," CCCCC "," CCCCC "," "," "," E E "," CCCCCCC "," C C "," EC CE "," C C "," C C ABA "," C C B B "," C C ABA "," C C "," EC CE "," C C "," CCCCCCC "," E E "," "},
+ {" CXC~C "," BDDDB "," CBCBC "," B B "," B B "," E B B E "," CCCCCCC "," C C "," EC CE "," C C "," C C AAA "," C C A A "," C C AAA "," C C "," EC CE "," C C "," CCCCCCC "," E E "," "},
+ {" CCCCC "," CCCCC "," CCCCC "," "," CCCCCCCCCCC "," CAAAAAAAAAC ","CCCACCCCCCCACCC ","CAACAAAAAAACAAC ","CACAADDDDDAACAC ","CACADDDDDDDACACCCCC","CACADDAAADDACACCCCC","CACADDAAADDACACCCCC","CACADDAAADDACACCCCC","CACADDDDDDDACACCCCC","CACAADDDDDAACAC ","CAACAAAAAAACAAC ","CCCACCCCCCCACCC "," CAAAAAAAAAC "," CCCCCCCCCCC "}
+ }))
+ .addShape(
+ STACKABLE_STOP,
+ transpose(
+ new String[][]{
+ {" "," "," "," "," "," "," "," "," "," "," CCC "," CCC "," CCC "," "," "," "," "," "," "},
+ {" "," "," "," "," "," E E "," E E "," E E "," EEEEAAAAAEEEE "," AAAAAAA "," AA AA "," AA AA "," AA AA "," AAAAAAA "," EEEEAAAAAEEEE "," E E "," E E "," E E "," "},
+ {" "," "," "," "," "," ECCCCCE "," CCCCCCCCC "," CCCCCCCCCCC "," ECCC CCCE "," CCC CCC "," CCC CCC "," CCC CCC "," CCC CCC "," CCC CCC "," ECCC CCCE "," CCCCCCCCCCC "," CCCCCCCCC "," ECCCCCE "," "},
+ {" "," "," "," "," "," E E "," CCCCCCC "," C C "," EC CE "," C C "," C C "," C C "," C C "," C C "," EC CE "," C C "," CCCCCCC "," E E "," "},
+ {" "," "," "," "," "," E E "," CCCCCCC "," C C "," EC CE "," C C "," C CAAAAA "," C CABBBA "," C CAAAAA "," C C "," EC CE "," C C "," CCCCCCC "," E E "," "},
+ {" "," "," "," "," "," ECCCCCE "," CCCCCCC "," C C "," EC CE "," CC CC "," CC CABBBA "," CC C B "," CC CABBBA "," CC CC "," EC CE "," C C "," CCCCCCC "," ECCCCCE "," "},
+ {" "," "," "," "," "," E E "," CCCCCCC "," C C "," EC CE "," C C "," C CAAABA "," C CAAA B "," C CAAABA "," C C "," EC CE "," C C "," CCCCCCC "," E E "," "},
+ {" "," "," "," "," "," E E "," CCCCCCC "," C C "," EC CE "," C C "," C C ABA "," C C A B "," C C ABA "," C C "," EC CE "," C C "," CCCCCCC "," E E "," "},
+ }))
+ .addShape(
+ STACKABLE_MIDDLE_1,
+ transpose(
+ new String[][]{
+ {" "," "," "," "," "," E E "," CCCCCCC "," C C "," EC CE "," C C "," C C ABA "," C C B B "," C C ABA "," C C "," EC CE "," C C "," CCCCCCC "," E E "," "},
+ {" "," "," "," "," "," E E "," CCCCCCC "," C C "," EC CE "," C C "," C C ABA "," C C B B "," C C ABA "," C C "," EC CE "," C C "," CCCCCCC "," E E "," "},
+ {" "," "," "," "," "," E E "," CCCCCCC "," C C "," EC CE "," C C "," C C ABA "," C C B B "," C C ABA "," C C "," EC CE "," C C "," CCCCCCC "," E E "," "},
+ }))
+ .addShape(
+ STACKABLE_MIDDLE_2,
+ transpose(
+ new String[][]{
+ {" "," "," "," "," "," ECCCCCE "," CCCCCCC "," C C "," EC CE "," CC CC "," CC CC ABA "," CC CC B B "," CC CC ABA "," CC CC "," EC CE "," C C "," CCCCCCC "," ECCCCCE "," "},
+ {" "," "," "," "," "," E E "," CCCCCCC "," C C "," EC CE "," C C "," C C ABA "," C C B B "," C C ABA "," C C "," EC CE "," C C "," CCCCCCC "," E E "," "},
+ {" "," "," "," "," "," E E "," CCCCCCC "," C C "," EC CE "," C C "," C C ABA "," C C B B "," C C ABA "," C C "," EC CE "," C C "," CCCCCCC "," E E "," "},
+ }))
+ // spotless:on
+ .addElement(
+ 'C',
+ ofMuTECasings(
+ FLUID_IN | ITEM_IN | FLUID_OUT | ITEM_OUT | ENERGY_IN,
+ GT_MultiTileCasing.Distillation.getCasing()))
+ .addElement('E', GT_StructureUtility.ofFrame(Materials.StainlessSteel))
+ .addElement('A', ofBlock(GregTech_API.sBlockCasings2, 0))
+ .addElement('B', ofBlock(GregTech_API.sBlockCasings2, 13))
+ .addElement('X', ofMuTECasings(NOTHING, MOTOR_CASINGS))
+ .addElement('D', GT_StructureUtility.ofCoil((tile, meta) -> {}, (tile) -> HeatingCoilLevel.None))
+ .build();
+ }
+ return STRUCTURE_DEFINITION_MEGA;
+ }
+
+ @Override
+ public int getFluidOutputCount() {
+ return 12;
+ }
+
+ @Override
+ public int getMinStacks() {
+ return 0;
+ }
+
+ @Override
+ public int getMaxStacks() {
+ return 9;
+ }
+
+ @Override
+ public Vec3Impl getStartingStackOffset() {
+ return STRUCTURE_OFFSET_MEGA_START;
+ }
+
+ @Override
+ public Vec3Impl getPerStackOffset() {
+ return STRUCTURE_OFFSET_MEGA_STACK;
+ }
+
+ @Override
+ public Vec3Impl getAfterLastStackOffset() {
+ return STRUCTURE_OFFSET_MEGA_STOP;
+ }
+
+ @Override
+ public String getTileEntityName() {
+ return "gt.multitileentity.multiblock.distillationtower";
+ }
+
+ @Override
+ public String getLocalName() {
+ return StatCollector.translateToLocal(getTileEntityName());
+ }
+
+ @Override
+ protected String getStackableMiddle(int stackIndex) {
+ return stackIndex % 2 == 0 ? STACKABLE_MIDDLE_1 : STACKABLE_MIDDLE_2;
+ }
+
+ @Override
+ @Nonnull
+ protected DistillationTowerProcessingLogic createProcessingLogic() {
+ return new DistillationTowerProcessingLogic();
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multiblock/LaserEngraver.java b/src/main/java/gregtech/common/tileentities/machines/multiblock/LaserEngraver.java
new file mode 100644
index 0000000000..d4a7283f3e
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multiblock/LaserEngraver.java
@@ -0,0 +1,305 @@
+package gregtech.common.tileentities.machines.multiblock;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.*;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlockUnlocalizedName;
+import static gregtech.api.enums.Mods.*;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.*;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.ENERGY_IN;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.FLUID_OUT;
+import static gregtech.api.util.GT_StructureUtilityMuTE.*;
+
+import java.util.UUID;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+import com.gtnewhorizon.structurelib.util.Vec3Impl;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.math.Color;
+import com.gtnewhorizons.modularui.api.widget.IWidgetBuilder;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.MultiChildWidget;
+import com.gtnewhorizons.modularui.common.widget.textfield.NumericWidget;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.Materials;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.multitileentity.enums.GT_MultiTileCasing;
+import gregtech.api.multitileentity.multiblock.base.ComplexParallelController;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_StructureUtility;
+import gregtech.common.tileentities.machines.multiblock.logic.LaserEngraverProcessingLogic;
+
+public class LaserEngraver extends ComplexParallelController<LaserEngraver, LaserEngraverProcessingLogic> {
+
+ private static IStructureDefinition<LaserEngraver> STRUCTURE_DEFINITION = null;
+ protected static final String STRUCTURE_MAIN = "Main";
+ protected static final String STRUCTURE_PIECE_T1 = "T1";
+ protected static final String STRUCTURE_PIECE_T2 = "T2";
+ protected static final String STRUCTURE_PIECE_T3 = "T3";
+ protected static final String STRUCTURE_PIECE_T4 = "T4";
+ protected static final String STRUCTURE_PIECE_T5 = "T5";
+ protected static final String STRUCTURE_PIECE_T6 = "T6";
+ protected static final int PROCESS_WINDOW_BASE_ID = 100;
+ protected static final Vec3Impl STRUCTURE_OFFSET_T1 = new Vec3Impl(3, 1, 0);
+ protected static final Vec3Impl STRUCTURE_OFFSET_T2 = new Vec3Impl(1, 3, 0);
+ protected static final Vec3Impl STRUCTURE_OFFSET_T3 = new Vec3Impl(-6, 0, -5);
+ protected static final Vec3Impl STRUCTURE_OFFSET_T4 = new Vec3Impl(18, 0, 0);
+ protected static final Vec3Impl STRUCTURE_OFFSET_T5 = new Vec3Impl(-18, 0, 9);
+ protected static final Vec3Impl STRUCTURE_OFFSET_T6 = new Vec3Impl(18, 0, 0);
+ protected static final int MAX_PROCESSES = 6;
+ protected RecipeMap<?> recipeMap;
+ private UUID LaserEngraver;
+
+ @Override
+ public String getTileEntityName() {
+ return "gt.multitileentity.multiblock.laserengraver";
+ }
+
+ @Override
+ public Vec3Impl getStartingStructureOffset() {
+ return STRUCTURE_OFFSET_T1;
+ }
+
+ @Override
+ public boolean checkMachine() {
+ buildState.startBuilding(getStartingStructureOffset());
+ if (!checkPiece(STRUCTURE_PIECE_T1, buildState.getCurrentOffset())) return buildState.failBuilding();
+ if (maxComplexParallels > 1) {
+ buildState.addOffset(STRUCTURE_OFFSET_T2);
+ if (!checkPiece(STRUCTURE_PIECE_T2, buildState.getCurrentOffset())) return buildState.failBuilding();
+ }
+ if (maxComplexParallels > 2) {
+ buildState.addOffset(STRUCTURE_OFFSET_T3);
+ if (!checkPiece(STRUCTURE_PIECE_T3, buildState.getCurrentOffset())) return buildState.failBuilding();
+ }
+ if (maxComplexParallels > 3) {
+ buildState.addOffset(STRUCTURE_OFFSET_T4);
+ if (!checkPiece(STRUCTURE_PIECE_T4, buildState.getCurrentOffset())) return buildState.failBuilding();
+ }
+ if (maxComplexParallels > 4) {
+ buildState.addOffset(STRUCTURE_OFFSET_T5);
+ if (!checkPiece(STRUCTURE_PIECE_T5, buildState.getCurrentOffset())) return buildState.failBuilding();
+ }
+ if (maxComplexParallels > 5) {
+ buildState.addOffset(STRUCTURE_OFFSET_T6);
+ if (!checkPiece(STRUCTURE_PIECE_T6, buildState.getCurrentOffset())) return buildState.failBuilding();
+ }
+ buildState.stopBuilding();
+ return super.checkMachine();
+ }
+
+ @Override
+ public void construct(ItemStack trigger, boolean hintsOnly) {
+ buildState.startBuilding(getStartingStructureOffset());
+ buildPiece(STRUCTURE_PIECE_T1, trigger, hintsOnly, buildState.getCurrentOffset());
+ if (maxComplexParallels > 1) {
+ buildState.addOffset(STRUCTURE_OFFSET_T2);
+ buildPiece(STRUCTURE_PIECE_T2, trigger, hintsOnly, buildState.getCurrentOffset());
+ }
+ if (maxComplexParallels > 2) {
+ buildState.addOffset(STRUCTURE_OFFSET_T3);
+ buildPiece(STRUCTURE_PIECE_T3, trigger, hintsOnly, buildState.getCurrentOffset());
+ }
+ if (maxComplexParallels > 3) {
+ buildState.addOffset(STRUCTURE_OFFSET_T4);
+ buildPiece(STRUCTURE_PIECE_T4, trigger, hintsOnly, buildState.getCurrentOffset());
+ }
+ if (maxComplexParallels > 4) {
+ buildState.addOffset(STRUCTURE_OFFSET_T5);
+ buildPiece(STRUCTURE_PIECE_T5, trigger, hintsOnly, buildState.getCurrentOffset());
+ }
+ if (maxComplexParallels > 5) {
+ buildState.addOffset(STRUCTURE_OFFSET_T6);
+ buildPiece(STRUCTURE_PIECE_T6, trigger, hintsOnly, buildState.getCurrentOffset());
+ }
+ buildState.stopBuilding();
+ }
+
+ @Override
+ public IStructureDefinition<LaserEngraver> getStructureDefinition() {
+ if (STRUCTURE_DEFINITION == null) {
+ STRUCTURE_DEFINITION = StructureDefinition.<LaserEngraver>builder()
+ .addShape(
+ STRUCTURE_PIECE_T1,
+ transpose(
+ // spotless:off
+ new String[][]{{"ACADA", "AAAAA", "AAAAA"}, {"GGA~A", "H I", "GGAAA"},
+ {"AAAAA", "ABBBA", "AAAAA"}}))
+ .addShape(
+ STRUCTURE_PIECE_T2,
+ new String[][]{{" ", " ", " ", " ", " ", " "},
+ {" K ", " K ", " K ", " ", " ", " "},
+ {" K ", " ", " ", " ", " ", " "},
+ {" K ", " ", " ", " ", " ", " "},
+ {" K ", " ", " ", " ", " ", " "},
+ {" K ", "FBF FBF", " G G ", " G G ", " G G ", "FBF FBF"},
+ {" KKKKK ", "BIB BIB", "G G G G", "G G G G", "G G G G", "BHB BHB"},
+ {" K K ", "FBF FBF", " G G ", " G G ", " G G ", "FBF FBF"},
+ {" K K ", " ", " ", " ", " ", " "},
+ {" K K ", "FBF FBF", " G G ", " G G ", " G G ", "FBF FBF"},
+ {" KKKKK ", "BIB BIB", "G G G G", "G G G G", "G G G G", "BHB BHB"},
+ {" ", "FBF FBF", " G G ", " G G ", " G G ", "FBF FBF",}})
+ .addShape(
+ STRUCTURE_PIECE_T3,
+ new String[][]{
+ {" ", " BBBBB FBF ", " BGGGB G ", " BGGGB G ", " BMLMB G ", " BBBBB FBF "},
+ {" KKKKKKK ", " BBAAABB BIB ", " B B G G ", " B B G G ", " B B G G ", " BBBBBBB BHB "},
+ {" K ", " BABBBAB FBF ", " G G G ", " G G G ", " M HHH M G ", " BBBBBBB FBF "},
+ {"KKKKKK ", " BABIBAB ", " G G ", " G G ", " L HHH L ", " BBBBBBB "},
+ {" K ", " BABBBAB FBF ", " G G G ", " G G G ", " M HHH M G ", " BBBBBBB FBF "},
+ {" KKKKKKK ", " BBAAABB BIB ", " B B G G ", " B B G G ", " B B G G ", " BBBBBBB BHB "},
+ {" ", " BBBBB FBF ", " BGGGB G ", " BGGGB G ", " BMLMB G ", " BBBBB FBF "}})
+ .addShape(
+ STRUCTURE_PIECE_T4,
+ new String[][]{
+ {" ", "FBF BBBBB ", " G BGGGB ", " G BGGGB ", " G BNLNB ", "FBF BBBBB "},
+ {" KKKKKKK ", "BIB BBAAABB ", "G G B B ", "G G B B ", "G G B B ", "BHB BBBBBBB "},
+ {" K ", "FBF BABBBAB ", " G G G ", " G G G ", " G N HHH N ", "FBF BBBBBBB "},
+ {" KKKKKK", " BABIBAB ", " G G ", " G G ", " L HHH L ", " BBBBBBB "},
+ {" K ", "FBF BABBBAB ", " G G G ", " G G G ", " G N HHH N ", "FBF BBBBBBB "},
+ {" KKKKKKK ", "BIB BBAAABB ", "G G B B ", "G G B B ", "G G B B ", "BHB BBBBBBB "},
+ {" ", "FBF BBBBB ", " G BGGGB ", " G BGGGB ", " G BNLNB ", "FBF BBBBB "}})
+ .addShape(
+ STRUCTURE_PIECE_T5,
+ new String[][]{
+ {" ", " BBBBB FBF ", " BGGGB G ", " BGGGB G ", " BOLOB G ", " BBBBB FBF "},
+ {" KKKKKKK ", " BBAAABB BIB ", " B B G G ", " B B G G ", " B B G G ", " BBBBBBB BHB "},
+ {" K ", " BABBBAB FBF ", " G G G ", " G G G ", " O HHH O G ", " BBBBBBB FBF "},
+ {" K ", " BABIBAB ", " G G ", " G G ", " L HHH L ", " BBBBBBB "},
+ {" K ", " BABBBAB FBF ", " G G G ", " G G G ", " O HHH O G ", " BBBBBBB FBF "},
+ {" KKKKKKK ", " BBAAABB BIB ", " B B G G ", " B B G G ", " B B G G ", " BBBBBBB BHB "},
+ {" K ", " BBBBB FBF ", " BGGGB G ", " BGGGB G ", " BOLOB G ", " BBBBB FBF "},
+ {" K ", " ", " ", " ", " "},
+ {" K ", " ", " ", " ", " "},
+ {" K ", " ", " ", " ", " "}})
+ .addShape(
+ STRUCTURE_PIECE_T6,
+ new String[][]{
+ {" ", "FBF BBBBB ", " G BGGGB ", " G BGGGB ", " G BPLPB ", "FBF BBBBB "},
+ {" KKKKKKK ", "BIB BBAAABB ", "G G B B ", "G G B B ", "G G B B ", "BHB BBBBBBB "},
+ {" K ", "FBF BABBBAB ", " G G G ", " G G G ", " G P HHH P ", "FBF BBBBBBB "},
+ {" K ", " BABIBAB ", " G G ", " G G ", " L HHH L ", " BBBBBBB "},
+ {" K ", "FBF BABBBAB ", " G G G ", " G G G ", " G P HHH P ", "FBF BBBBBBB "},
+ {" KKKKKKK ", "BIB BBAAABB ", "G G B B ", "G G B B ", "G G B B ", "BHB BBBBBBB "},
+ {" K ", "FBF BBBBB ", " G BGGGB ", " G BGGGB ", " G BPLPB ", "FBF BBBBB "},
+ {" K ", " ", " ", " ", " "},
+ {" K ", " ", " ", " ", " "},
+ {" K ", " ", " ", " ", " "}})
+ // spotless:on
+ .addElement(
+ 'A',
+ ofMuTECasings(
+ FLUID_IN | ITEM_IN | FLUID_OUT | ITEM_OUT | ENERGY_IN,
+ GT_MultiTileCasing.LaserEngraver.getCasing()))
+ .addElement(
+ 'B',
+ ofMuTECasings(
+ FLUID_IN | ITEM_IN | FLUID_OUT | ITEM_OUT | ENERGY_IN,
+ GT_MultiTileCasing.BlackLaserEngraverCasing.getCasing()))
+ .addElement(
+ 'C',
+ ofMuTECasings(NOTHING, CLEANROOM_CASINGS, GT_MultiTileCasing.LaserEngraver.getCasing()))
+ .addElement('D', ofMuTECasings(NOTHING, WIRELESS_CASINGS, GT_MultiTileCasing.LaserEngraver.getCasing()))
+ .addElement('E', ofMuTECasings(NOTHING, MOTOR_CASINGS))
+ .addElement('F', GT_StructureUtility.ofFrame(Materials.Naquadah)
+
+ )
+ .addElement('H', ofMuTECasings(NOTHING, GT_MultiTileCasing.Mirror.getCasing()))
+
+ .addElement(
+ 'G',
+ ofChain(
+ ofBlockUnlocalizedName(IndustrialCraft2.ID, "blockAlloyGlass", 0, true),
+ ofBlockUnlocalizedName(BartWorks.ID, "BW_GlasBlocks", 0, true),
+ ofBlockUnlocalizedName(BartWorks.ID, "BW_GlasBlocks2", 0, true),
+ ofBlockUnlocalizedName(Thaumcraft.ID, "blockCosmeticOpaque", 2, false)))
+ .addElement('I', ofMuTECasings(NOTHING, EMITTER_CASINGS))
+ .addElement('K', ofBlock(GregTech_API.sBlockCasings3, 11))
+ .addElement('L', ofMuTECasings(NOTHING, ROBOT_ARM_CASINGS))
+ .addElement('M', ofMuTECasings(NOTHING, GT_MultiTileCasing.LaserEngraverUpgrade1.getCasing()))
+ .addElement('N', ofMuTECasings(NOTHING, GT_MultiTileCasing.LaserEngraverUpgrade2.getCasing()))
+ .addElement('O', ofMuTECasings(NOTHING, GT_MultiTileCasing.LaserEngraverUpgrade3.getCasing()))
+ .addElement('P', ofMuTECasings(NOTHING, GT_MultiTileCasing.LaserEngraverUpgrade4.getCasing()))
+ .build();
+ buildState.stopBuilding();
+ }
+ return STRUCTURE_DEFINITION;
+ }
+
+ protected MultiChildWidget createMainPage(IWidgetBuilder<?> builder) {
+ MultiChildWidget child = new MultiChildWidget();
+ for (int i = 0; i < MAX_PROCESSES; i++) {
+ final int processIndex = i;
+ child.addChild(
+ new ButtonWidget().setPlayClickSound(true)
+ .setOnClick(
+ (clickData, widget) -> {
+ if (!widget.isClient()) widget.getContext()
+ .openSyncedWindow(PROCESS_WINDOW_BASE_ID + processIndex);
+ })
+ .setBackground(GT_UITextures.BUTTON_STANDARD, GT_UITextures.OVERLAY_BUTTON_WHITELIST)
+ .setSize(18, 18)
+ .setEnabled((widget -> processIndex < maxComplexParallels))
+ .setPos(20 * (i % 4) + 18, 18 + (i / 4) * 20));
+ }
+ child.addChild(
+ new NumericWidget().setGetter(() -> maxComplexParallels)
+ .setSetter(parallel -> setMaxComplexParallels((int) parallel, true))
+ .setBounds(1, MAX_PROCESSES)
+ .setTextColor(Color.WHITE.normal)
+ .setTextAlignment(Alignment.Center)
+ .addTooltip("Tier")
+ .setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD)
+ .setSize(18, 18)
+ .setPos(130, 85));
+ return child;
+ }
+
+ @Override
+ public short getCasingRegistryID() {
+ return 0;
+ }
+
+ @Override
+ public void readMultiTileNBT(NBTTagCompound nbt) {
+ super.readMultiTileNBT(nbt);
+ setMaxComplexParallels(nbt.getInteger("processors"), false);
+ }
+
+ @Override
+ public void writeMultiTileNBT(NBTTagCompound nbt) {
+ super.writeMultiTileNBT(nbt);
+ nbt.setInteger("processors", maxComplexParallels);
+ }
+
+ @Override
+ public int getCasingMeta() {
+ return GT_MultiTileCasing.LaserEngraver.getId();
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Laser Engraver")
+ .addInfo("Used for Engraving")
+ .addSeparator()
+ .beginStructureBlock(3, 3, 5, true)
+ .addController("Front right center")
+ .toolTipFinisher(GT_Values.AuthorTheEpicGamer274);
+ return tt;
+ }
+
+ @Override
+ @Nonnull
+ protected LaserEngraverProcessingLogic createProcessingLogic() {
+ return new LaserEngraverProcessingLogic();
+ }
+
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multiblock/LayeredCokeBattery.java b/src/main/java/gregtech/common/tileentities/machines/multiblock/LayeredCokeBattery.java
new file mode 100644
index 0000000000..e2f8dc9b6e
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multiblock/LayeredCokeBattery.java
@@ -0,0 +1,300 @@
+package gregtech.common.tileentities.machines.multiblock;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlockUnlocalizedName;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.enums.Mods.BartWorks;
+import static gregtech.api.enums.Mods.IndustrialCraft2;
+import static gregtech.api.enums.Mods.Thaumcraft;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.ENERGY_IN;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.FLUID_IN;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.FLUID_OUT;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.ITEM_IN;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.ITEM_OUT;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.NOTHING;
+import static gregtech.api.util.GT_StructureUtilityMuTE.AMPERAGE_CASINGS;
+import static gregtech.api.util.GT_StructureUtilityMuTE.HEATER_CASINGS;
+import static gregtech.api.util.GT_StructureUtilityMuTE.INSULATOR_CASINGS;
+import static gregtech.api.util.GT_StructureUtilityMuTE.MOTOR_CASINGS;
+import static gregtech.api.util.GT_StructureUtilityMuTE.ofMuTECasings;
+
+import java.util.Arrays;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.ResourceLocation;
+
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+import com.gtnewhorizon.structurelib.util.Vec3Impl;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.multitileentity.enums.GT_MultiTileCasing;
+import gregtech.api.multitileentity.multiblock.base.StackableModularController;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_StructureUtility;
+import gregtech.api.util.GT_StructureUtilityMuTE.UpgradeCasings;
+import gregtech.common.tileentities.machines.multiblock.logic.LayeredCokeBatteryProcessingLogic;
+
+public class LayeredCokeBattery
+ extends StackableModularController<LayeredCokeBattery, LayeredCokeBatteryProcessingLogic> {
+
+ private static IStructureDefinition<LayeredCokeBattery> STRUCTURE_DEFINITION_MEGA = null;
+ protected static final String STRUCTURE_PIECE_BASE = "T1";
+ private static final Vec3Impl STRUCTURE_OFFSET_BASE = new Vec3Impl(2, 2, 0);
+ private static final Vec3Impl STRUCTURE_OFFSET_MEGA_POSITION = new Vec3Impl(4, 7, -4);
+ private static final Vec3Impl STRUCTURE_OFFSET_MEGA_START = new Vec3Impl(0, 0, -3);
+ private static final Vec3Impl STRUCTURE_OFFSET_MEGA_STACK = new Vec3Impl(0, 0, -2);
+ private static final Vec3Impl STRUCTURE_OFFSET_MEGA_STOP = new Vec3Impl(0, 0, -1);
+
+ @Override
+ public String getTileEntityName() {
+ return "gt.multitileentity.multiblock.layeredcokebattery";
+ }
+
+ @Override
+ public String getLocalName() {
+ return "Layered Coke Battery";
+ }
+
+ @Override
+ public Vec3Impl getStartingStructureOffset() {
+ return STRUCTURE_OFFSET_BASE;
+ }
+
+ public UpgradeCasings getBaseMucType() {
+ return UpgradeCasings.Heater;
+ }
+
+ public int getParallelFactor() {
+ return 2;
+ }
+
+ @Override
+ public void construct(ItemStack trigger, boolean hintsOnly) {
+ final int blueprintCount = (trigger.stackSize - 1) + getMinStacks();
+ final int stackCount = Math.min(blueprintCount, getMaxStacks());
+
+ buildState.startBuilding(getStartingStructureOffset());
+ buildPiece(STRUCTURE_PIECE_BASE, trigger, hintsOnly, buildState.getCurrentOffset());
+ buildState.addOffset(getMegaPositionOffset());
+
+ if (stackCount >= 1) {
+ buildPiece(STACKABLE_START, trigger, hintsOnly, buildState.getCurrentOffset());
+ buildState.addOffset(getStartingStackOffset());
+
+ for (int i = 0; i < stackCount; i++) {
+ buildPiece(STACKABLE_MIDDLE, trigger, hintsOnly, buildState.getCurrentOffset());
+ buildState.addOffset(getPerStackOffset());
+ }
+ if (hasTop()) {
+ buildPiece(STACKABLE_STOP, trigger, hintsOnly, buildState.getCurrentOffset());
+ }
+ }
+
+ buildState.stopBuilding();
+ }
+
+ @Override
+ public IStructureDefinition<LayeredCokeBattery> getStructureDefinition() {
+ if (STRUCTURE_DEFINITION_MEGA == null) {
+ STRUCTURE_DEFINITION_MEGA = StructureDefinition.<LayeredCokeBattery>builder()
+ .addShape(
+ STRUCTURE_PIECE_BASE,
+ transpose(
+ new String[][] { { " AAA ", "AAAAA", "AEEEP", "AAAAA" }, { " AAA ", "A A", "A A", "AAAAA" },
+ { " A~A ", "A A", "A A", "AAAAA" }, { " AAA ", "A A", "A A", "AAAAA" },
+ { " AAA ", "AAAAA", "AAAAA", "AAAAA" } }))
+ .addShape(
+ STACKABLE_STOP,
+ transpose(
+ new String[][] { { "AHFFFFAFFFFHA", "AAAAAAAAAAAAA" }, { " B B B B B B ", "AFAFAFAFAFAFA" },
+ { "HB B B B B BH", "AFAFAFAFAFAFA" }, { " B B B B B B ", "AFAFAFAFAFAFA" },
+ { " B B B B B B ", "AFAFAFAFAFAFA" }, { " B B B B B B ", "AFAFAFAFAFAFA" },
+ { " B B B B B B ", "AFAFAFAFAFAFA" }, { " B B B B B B ", "AFAFAFAFAFAFA" },
+ { " B B B B B B ", "AFAFAFAFAFAFA" }, { "HB B B B B BH", "AFAFAFAFAFAFA" },
+ { " B B B B B B ", "AFAFAFAFAFAFA" }, { "AAAAAAAAAAAAA", "AAAAAAAAAAAAA" } }))
+ .addShape(
+ STACKABLE_MIDDLE,
+ transpose(
+ new String[][] { { "AHFFFFAFFFFHA", "AAAAAAAAAAAAA" }, { " B A B ", "AAAAAAAAAAAAA" },
+ { "HB A BH", "AAAAAAAAAAAAA" }, { " B A B ", "AAAAAAAAAAAAA" },
+ { " B A B ", "AAAAAAAAAAAAA" }, { " B A B ", "AAAAAAAAAAAAA" },
+ { " B A B ", "AAAAAAAAAAAAA" }, { " B A B ", "AAAAAAAAAAAAA" },
+ { " B A B ", "AAAAAAAAAAAAA" }, { "HB A BH", "AAAAAAAAAAAAA" },
+ { " B A B ", "AAAAAAAAAAAAA" }, { "AAAAAAAAAAAAA", "AAAAAAAAAAAAA" } }))
+ .addShape(
+ STACKABLE_START,
+ transpose(
+ new String[][] { { "AAAAAAAAAAAAA", "AHFFFFAFFFFHA", "AAAAAAAAAAAAA" },
+ { "AFAFAFAFAFAFA", " B B B B B B ", "AAAAAAAAAAAAA" },
+ { "AFAFAFAFAFAFA", "HB B B B B BH", "AAAAAAAAAAAAA" },
+ { "AFAFAFAFAFAFA", " B B B B B B ", "AAAAAAAAAAAAA" },
+ { "AFAFAFAFAFAFA", " B B B B B B ", "AAAAAAAAAAAAA" },
+ { "AFAFAFAFAFAFA", " B B B B B B ", "AAAAAAAAAAAAA" },
+ { "AFAFAFAFAFAFA", " B B B B B B ", "AAAAAAAAAAAAA" },
+ { "AFAFAFAFAFAFA", " B B B B B B ", "AAAAAAAAAAAAA" },
+ { "AFAFAFAFAFAFA", " B B B B B B ", "AAAAAAAAAAAAA" },
+ { "AFAFAFAFAFAFA", "HB B B B B BH", "AAAAAAAAAAAAA" },
+ { "AFAFAFAFAFAFA", " B B B B B B ", "AAAAAAAAAAAAA" },
+ { "AAAAAAAAAAAAA", "AAAAAAAAAAAAA", "AAAAAAAAAAAAA" } }))
+ .addElement(
+ 'A',
+ ofMuTECasings(
+ FLUID_IN | ITEM_IN | FLUID_OUT | ITEM_OUT | ENERGY_IN,
+ GT_MultiTileCasing.Chemical.getCasing()))
+ .addElement(
+ 'B',
+ ofMuTECasings(
+ FLUID_IN | ITEM_IN | FLUID_OUT | ITEM_OUT | ENERGY_IN,
+ GT_MultiTileCasing.Distillation.getCasing()))
+ .addElement('C', ofBlock(GregTech_API.sBlockCasings4, 1))
+ .addElement('D', GT_StructureUtility.ofFrame(Materials.Steel))
+ .addElement('E', ofMuTECasings(NOTHING, MOTOR_CASINGS))
+ .addElement(
+ 'F',
+ ofChain(
+ ofBlockUnlocalizedName(IndustrialCraft2.ID, "blockAlloyGlass", 0, true),
+ ofBlockUnlocalizedName(BartWorks.ID, "BW_GlasBlocks", 0, true),
+ ofBlockUnlocalizedName(BartWorks.ID, "BW_GlasBlocks2", 0, true),
+ ofBlockUnlocalizedName(Thaumcraft.ID, "blockCosmeticOpaque", 2, false)))
+ .addElement('H', ofMuTECasings(NOTHING, HEATER_CASINGS, INSULATOR_CASINGS))
+ .addElement('P', ofMuTECasings(NOTHING, AMPERAGE_CASINGS))
+ .build();
+ }
+ return STRUCTURE_DEFINITION_MEGA;
+ }
+
+ public boolean checkMachine() {
+ stackCount = 0;
+ resetMucCount();
+
+ buildState.startBuilding(getStartingStructureOffset());
+ if (!checkPiece(STRUCTURE_PIECE_BASE, buildState.getCurrentOffset())) return buildState.failBuilding();
+
+ buildState.addOffset(getMegaPositionOffset());
+ if (!checkPiece(STACKABLE_START, buildState.getCurrentOffset())) {
+ return buildState.failBuilding();
+ }
+
+ buildState.addOffset(getStartingStackOffset());
+ for (int i = 0; i < getMaxStacks(); i++) {
+ if (!checkPiece(getStackableMiddle(i), buildState.getCurrentOffset())) {
+ break;
+ }
+
+ buildState.addOffset(getPerStackOffset());
+ stackCount++;
+ }
+ if (stackCount < getMinStacks()) return buildState.failBuilding();
+
+ if (!checkPiece(getStackableStop(), buildState.stopBuilding())) {
+ return buildState.failBuilding();
+ }
+
+ calculateTier();
+ if (!calculateMucMultipliers()) {
+ return false;
+ }
+ calculateParallels();
+ updatePowerLogic();
+ return tier > 0;
+ }
+
+ protected boolean calculateMucMultipliers() {
+ Map<UpgradeCasings, int[]> mucMap = getMucMap();
+ int[] heaterList = mucMap.get(UpgradeCasings.Heater);
+ int[] insulatorList = mucMap.get(UpgradeCasings.Insulator);
+ int totalHeaterCount = Arrays.stream(heaterList)
+ .sum();
+ int totalInsulatorCount = Arrays.stream(insulatorList)
+ .sum();
+ if (totalHeaterCount + totalInsulatorCount < stackCount || totalInsulatorCount > totalHeaterCount) {
+ return false;
+ }
+ if (totalInsulatorCount > 0) {
+ // To be improved later, when more MUCs are added
+ // durationMultiplier = 1.0 / totalHeaterCount;
+ euTickMultiplier = 1.0 / totalInsulatorCount;
+ }
+ return true;
+ }
+
+ @Override
+ public short getCasingRegistryID() {
+ return 0;
+ }
+
+ @Override
+ public int getCasingMeta() {
+ return GT_MultiTileCasing.Distillation.getId();
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Coke Oven")
+ .addInfo("Controller for the Layered Coke Battery")
+ .addSeparator()
+ .beginVariableStructureBlock(7, 9, 2 + getMinStacks(), 2 + getMaxStacks(), 7, 9, true)
+ .addController("Bottom Front Center")
+ .addCasingInfoExactly("Test Casing", 60, false)
+ .addEnergyHatch("Any bottom layer casing")
+ .addInputHatch("Any non-optional external facing casing on the stacks")
+ .addInputBus("Any non-optional external facing casing on the stacks")
+ .addOutputHatch("Any non-optional external facing casing on the stacks")
+ .addOutputBus("Any non-optional external facing casing on the stacks")
+ .addStructureInfo(
+ String.format("Stackable middle stacks between %d-%d time(s).", getMinStacks(), getMaxStacks()))
+ .toolTipFinisher("Wildcard");
+ return tt;
+ }
+
+ @Override
+ public int getMinStacks() {
+ return 0;
+ }
+
+ @Override
+ public int getMaxStacks() {
+ return 20;
+ }
+
+ public Vec3Impl getMegaPositionOffset() {
+ return STRUCTURE_OFFSET_MEGA_POSITION;
+ }
+
+ @Override
+ public Vec3Impl getStartingStackOffset() {
+ return STRUCTURE_OFFSET_MEGA_START;
+ }
+
+ @Override
+ public Vec3Impl getPerStackOffset() {
+ return STRUCTURE_OFFSET_MEGA_STACK;
+ }
+
+ @Override
+ public Vec3Impl getAfterLastStackOffset() {
+ return STRUCTURE_OFFSET_MEGA_STOP;
+ }
+
+ @SideOnly(Side.CLIENT)
+ @Override
+ protected ResourceLocation getActivitySoundLoop() {
+ return SoundResource.IC2_MACHINES_MACERATOR_OP.resourceLocation;
+ }
+
+ @Override
+ @Nonnull
+ protected LayeredCokeBatteryProcessingLogic createProcessingLogic() {
+ return new LayeredCokeBatteryProcessingLogic();
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multiblock/Macerator.java b/src/main/java/gregtech/common/tileentities/machines/multiblock/Macerator.java
new file mode 100644
index 0000000000..dfdacfe7f2
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multiblock/Macerator.java
@@ -0,0 +1,132 @@
+package gregtech.common.tileentities.machines.multiblock;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.ENERGY_IN;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.FLUID_IN;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.FLUID_OUT;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.ITEM_IN;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.ITEM_OUT;
+import static gregtech.api.multitileentity.multiblock.base.MultiBlockPart.NOTHING;
+import static gregtech.api.util.GT_StructureUtilityMuTE.*;
+
+import javax.annotation.Nonnull;
+
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+import com.gtnewhorizon.structurelib.util.Vec3Impl;
+
+import gregtech.api.multitileentity.enums.GT_MultiTileCasing;
+import gregtech.api.multitileentity.multiblock.base.StackableController;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.common.tileentities.machines.multiblock.logic.MaceratorProcessingLogic;
+
+public class Macerator extends StackableController<Macerator, MaceratorProcessingLogic> {
+
+ private static IStructureDefinition<Macerator> STRUCTURE_DEFINITION = null;
+
+ public Macerator() {
+ super();
+ }
+
+ @Override
+ public String getTileEntityName() {
+ return "gt.multitileentity.multiblock.macerator";
+ }
+
+ @Override
+ public IStructureDefinition<Macerator> getStructureDefinition() {
+ if (STRUCTURE_DEFINITION == null) {
+ STRUCTURE_DEFINITION = StructureDefinition.<Macerator>builder()
+ .addShape(
+ STACKABLE_STOP,
+ transpose(new String[][] { { " CCC ", "CCCCC", "CCCCC", "CCCCC", " CCC " }, }))
+ .addShape(
+ STACKABLE_MIDDLE,
+ transpose(new String[][] { { " BBB ", " B---B ", "DC---CD", " B---B ", " BBB " }, }))
+ .addShape(
+ STACKABLE_START,
+ transpose(new String[][] { { " G~F ", "AAAAA", "AAAAA", "AAAAA", " AAA " }, }))
+ .addElement('A', ofMuTECasings(ENERGY_IN, GT_MultiTileCasing.Macerator.getCasing()))
+ .addElement(
+ 'B',
+ ofMuTECasings(FLUID_IN | ITEM_IN | FLUID_OUT | ITEM_OUT, GT_MultiTileCasing.Macerator.getCasing()))
+ .addElement('C', ofMuTECasings(NOTHING, GT_MultiTileCasing.Macerator.getCasing()))
+ .addElement('D', ofMuTECasings(NOTHING, GT_MultiTileCasing.Macerator.getCasing()))
+ .addElement('F', ofMuTECasings(NOTHING, MOTOR_CASINGS))
+ .addElement('G', ofMuTECasings(NOTHING, INVENTORY_CASINGS))
+ .build();
+ }
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public short getCasingRegistryID() {
+ return 0;
+ }
+
+ @Override
+ public int getCasingMeta() {
+ return 18000;
+ }
+
+ @Override
+ public boolean hasTop() {
+ return true;
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Macerator")
+ .addInfo("Controller for the Macerator")
+ .addSeparator()
+ .beginVariableStructureBlock(7, 9, 2 + getMinStacks(), 2 + getMaxStacks(), 7, 9, true)
+ .addController("Bottom Front Center")
+ .addCasingInfoExactly("Test Casing", 60, false)
+ .addEnergyHatch("Any bottom layer casing")
+ .addInputHatch("Any non-optional external facing casing on the stacks")
+ .addInputBus("Any non-optional external facing casing on the stacks")
+ .addOutputHatch("Any non-optional external facing casing on the stacks")
+ .addOutputBus("Any non-optional external facing casing on the stacks")
+ .addStructureInfo(
+ String.format("Stackable middle stacks between %d-%d time(s).", getMinStacks(), getMaxStacks()))
+ .toolTipFinisher("Wildcard");
+ return tt;
+ }
+
+ @Override
+ public int getMinStacks() {
+ return 1;
+ }
+
+ @Override
+ public int getMaxStacks() {
+ return 10;
+ }
+
+ @Override
+ public Vec3Impl getStartingStructureOffset() {
+ return new Vec3Impl(2, 0, 0);
+ }
+
+ @Override
+ public Vec3Impl getStartingStackOffset() {
+ return new Vec3Impl(1, 1, 0);
+ }
+
+ @Override
+ public Vec3Impl getPerStackOffset() {
+ return new Vec3Impl(0, 1, 0);
+ }
+
+ @Override
+ public Vec3Impl getAfterLastStackOffset() {
+ return new Vec3Impl(-1, 0, 0);
+ }
+
+ @Override
+ @Nonnull
+ protected MaceratorProcessingLogic createProcessingLogic() {
+ return new MaceratorProcessingLogic();
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multiblock/logic/AdvChemicalProcessorProcessingLogic.java b/src/main/java/gregtech/common/tileentities/machines/multiblock/logic/AdvChemicalProcessorProcessingLogic.java
new file mode 100644
index 0000000000..59879e30c7
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multiblock/logic/AdvChemicalProcessorProcessingLogic.java
@@ -0,0 +1,12 @@
+package gregtech.common.tileentities.machines.multiblock.logic;
+
+import gregtech.api.logic.ComplexParallelProcessingLogic;
+import gregtech.api.recipe.RecipeMaps;
+
+public class AdvChemicalProcessorProcessingLogic
+ extends ComplexParallelProcessingLogic<AdvChemicalProcessorProcessingLogic> {
+
+ public AdvChemicalProcessorProcessingLogic() {
+ setRecipeMap(RecipeMaps.multiblockChemicalReactorRecipes);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multiblock/logic/CokeOvenProcessingLogic.java b/src/main/java/gregtech/common/tileentities/machines/multiblock/logic/CokeOvenProcessingLogic.java
new file mode 100644
index 0000000000..4e6c80f561
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multiblock/logic/CokeOvenProcessingLogic.java
@@ -0,0 +1,69 @@
+package gregtech.common.tileentities.machines.multiblock.logic;
+
+import static gregtech.api.enums.Mods.Railcraft;
+import static net.minecraftforge.oredict.OreDictionary.getOreID;
+import static net.minecraftforge.oredict.OreDictionary.getOreIDs;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.minecraft.init.Items;
+import net.minecraft.item.ItemStack;
+
+import gregtech.api.logic.FluidInventoryLogic;
+import gregtech.api.logic.ItemInventoryLogic;
+import gregtech.api.logic.MuTEProcessingLogic;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_OreDictUnificator;
+
+public class CokeOvenProcessingLogic extends MuTEProcessingLogic<CokeOvenProcessingLogic> {
+
+ private static final int NORMAL_RECIPE_TIME = 1800;
+ private static final int WOOD_ORE_ID = getOreID("logWood");
+ private static final int COAL_ORE_ID = getOreID("coal");
+ private static final int COAL_BLOCK_ORE_ID = getOreID("blockCoal");
+ private static final int SUGARCANE_ORE_ID = getOreID("sugarcane");
+ private static final int CACTUS_ORE_ID = getOreID("blockCactus");
+ private static final int CACTUS_CHARCOAL_ORE_ID = getOreID("itemCharcoalCactus");
+ private static final int SUGAR_CHARCOAL_ORE_ID = getOreID("itemCharcoalSugar");
+ private int timeMultiplier = 1;
+
+ @Nonnull
+ @Override
+ protected Object findRecipe(@Nullable RecipeMap<?> map, @Nonnull ItemInventoryLogic itemInput,
+ @Nonnull FluidInventoryLogic fluidInput) {
+ for (ItemStack item : itemInput.getStoredItems()) {
+ ItemStack output = findRecipe(item);
+ if (output != null) {
+ ItemStack input = item.copy();
+ input.stackSize = 1;
+ return null;
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ private ItemStack findRecipe(@Nonnull ItemStack input) {
+ for (int oreId : getOreIDs(input)) {
+ if (oreId == COAL_ORE_ID) {
+ return GT_OreDictUnificator.get("fuelCoke", null, 1);
+ } else if (oreId == COAL_BLOCK_ORE_ID) {
+ timeMultiplier = 9;
+ return GT_ModHandler.getModItem(Railcraft.ID, "cube", 1, 0);
+ } else if (oreId == WOOD_ORE_ID) {
+ return new ItemStack(Items.coal, 1, 1);
+ } else if (oreId == SUGARCANE_ORE_ID) {
+ return GT_OreDictUnificator.get("itemCharcoalSugar", null, 1);
+ } else if (oreId == SUGAR_CHARCOAL_ORE_ID) {
+ return GT_OreDictUnificator.get("itemCokeSugar", null, 1);
+ } else if (oreId == CACTUS_ORE_ID) {
+ return GT_OreDictUnificator.get("itemCharcoalCactus", null, 1);
+ } else if (oreId == CACTUS_CHARCOAL_ORE_ID) {
+ return GT_OreDictUnificator.get("itemCokeCactus", null, 1);
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multiblock/logic/DistillationTowerProcessingLogic.java b/src/main/java/gregtech/common/tileentities/machines/multiblock/logic/DistillationTowerProcessingLogic.java
new file mode 100644
index 0000000000..6f27c9a0e6
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multiblock/logic/DistillationTowerProcessingLogic.java
@@ -0,0 +1,7 @@
+package gregtech.common.tileentities.machines.multiblock.logic;
+
+import gregtech.api.logic.ComplexParallelProcessingLogic;
+
+public class DistillationTowerProcessingLogic extends ComplexParallelProcessingLogic<DistillationTowerProcessingLogic> {
+
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multiblock/logic/LaserEngraverProcessingLogic.java b/src/main/java/gregtech/common/tileentities/machines/multiblock/logic/LaserEngraverProcessingLogic.java
new file mode 100644
index 0000000000..3f4a6bfe70
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multiblock/logic/LaserEngraverProcessingLogic.java
@@ -0,0 +1,7 @@
+package gregtech.common.tileentities.machines.multiblock.logic;
+
+import gregtech.api.logic.ComplexParallelProcessingLogic;
+
+public class LaserEngraverProcessingLogic extends ComplexParallelProcessingLogic<LaserEngraverProcessingLogic> {
+
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multiblock/logic/LayeredCokeBatteryProcessingLogic.java b/src/main/java/gregtech/common/tileentities/machines/multiblock/logic/LayeredCokeBatteryProcessingLogic.java
new file mode 100644
index 0000000000..b2e43b6fa9
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multiblock/logic/LayeredCokeBatteryProcessingLogic.java
@@ -0,0 +1,7 @@
+package gregtech.common.tileentities.machines.multiblock.logic;
+
+import gregtech.api.logic.MuTEProcessingLogic;
+
+public class LayeredCokeBatteryProcessingLogic extends MuTEProcessingLogic<LayeredCokeBatteryProcessingLogic> {
+
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multiblock/logic/MaceratorProcessingLogic.java b/src/main/java/gregtech/common/tileentities/machines/multiblock/logic/MaceratorProcessingLogic.java
new file mode 100644
index 0000000000..3f7d8a25f7
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/multiblock/logic/MaceratorProcessingLogic.java
@@ -0,0 +1,7 @@
+package gregtech.common.tileentities.machines.multiblock.logic;
+
+import gregtech.api.logic.MuTEProcessingLogic;
+
+public class MaceratorProcessingLogic extends MuTEProcessingLogic<MaceratorProcessingLogic> {
+
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_AlloySmelter_Bronze.java b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_AlloySmelter_Bronze.java
new file mode 100644
index 0000000000..9b2c6b3ada
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_AlloySmelter_Bronze.java
@@ -0,0 +1,139 @@
+package gregtech.common.tileentities.machines.steam;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_ALLOY_SMELTER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_ALLOY_SMELTER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_ALLOY_SMELTER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_ALLOY_SMELTER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_ALLOY_SMELTER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_ALLOY_SMELTER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_ALLOY_SMELTER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_ALLOY_SMELTER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_ALLOY_SMELTER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_ALLOY_SMELTER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_ALLOY_SMELTER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_ALLOY_SMELTER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_ALLOY_SMELTER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_ALLOY_SMELTER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_ALLOY_SMELTER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_ALLOY_SMELTER_GLOW;
+
+import gregtech.api.enums.SoundResource;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine_Bronze;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_AlloySmelter_Bronze extends GT_MetaTileEntity_BasicMachine_Bronze {
+
+ public GT_MetaTileEntity_AlloySmelter_Bronze(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional, "Combination Smelter", 2, 1, false);
+ }
+
+ public GT_MetaTileEntity_AlloySmelter_Bronze(String aName, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aDescription, aTextures, 2, 1, false);
+ }
+
+ @Override
+ protected boolean isBricked() {
+ return true;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_AlloySmelter_Bronze(mName, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.alloySmelterRecipes;
+ }
+
+ @Override
+ public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) {
+ super.startSoundLoop(aIndex, aX, aY, aZ);
+ if (aIndex == 1) {
+ GT_Utility.doSoundAtClient(SoundResource.IC2_MACHINES_INDUCTION_LOOP, 10, 1.0F, aX, aY, aZ);
+ }
+ }
+
+ @Override
+ public void startProcess() {
+ sendLoopStart((byte) 1);
+ }
+
+ @Override
+ public ITexture[] getSideFacingActive(byte aColor) {
+ return new ITexture[] { super.getSideFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_SIDE_STEAM_ALLOY_SMELTER_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_ALLOY_SMELTER_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getSideFacingInactive(byte aColor) {
+ return new ITexture[] { super.getSideFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_SIDE_STEAM_ALLOY_SMELTER), TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_ALLOY_SMELTER_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingActive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_FRONT_STEAM_ALLOY_SMELTER_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_ALLOY_SMELTER_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingInactive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_FRONT_STEAM_ALLOY_SMELTER), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_ALLOY_SMELTER_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingActive(byte aColor) {
+ return new ITexture[] { super.getTopFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_TOP_STEAM_ALLOY_SMELTER_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_STEAM_ALLOY_SMELTER_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingInactive(byte aColor) {
+ return new ITexture[] { super.getTopFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_TOP_STEAM_ALLOY_SMELTER), TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_STEAM_ALLOY_SMELTER_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingActive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_ALLOY_SMELTER_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_ALLOY_SMELTER_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingInactive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_ALLOY_SMELTER), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_ALLOY_SMELTER_GLOW)
+ .glow()
+ .build() };
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_AlloySmelter_Steel.java b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_AlloySmelter_Steel.java
new file mode 100644
index 0000000000..8b96906da1
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_AlloySmelter_Steel.java
@@ -0,0 +1,139 @@
+package gregtech.common.tileentities.machines.steam;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_ALLOY_SMELTER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_ALLOY_SMELTER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_ALLOY_SMELTER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_ALLOY_SMELTER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_ALLOY_SMELTER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_ALLOY_SMELTER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_ALLOY_SMELTER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_ALLOY_SMELTER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_ALLOY_SMELTER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_ALLOY_SMELTER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_ALLOY_SMELTER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_ALLOY_SMELTER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_ALLOY_SMELTER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_ALLOY_SMELTER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_ALLOY_SMELTER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_ALLOY_SMELTER_GLOW;
+
+import gregtech.api.enums.SoundResource;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine_Steel;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_AlloySmelter_Steel extends GT_MetaTileEntity_BasicMachine_Steel {
+
+ public GT_MetaTileEntity_AlloySmelter_Steel(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional, "Combination Smelter", 2, 1, true);
+ }
+
+ public GT_MetaTileEntity_AlloySmelter_Steel(String aName, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aDescription, aTextures, 2, 1, true);
+ }
+
+ @Override
+ protected boolean isBricked() {
+ return true;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_AlloySmelter_Steel(mName, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.alloySmelterRecipes;
+ }
+
+ @Override
+ public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) {
+ super.startSoundLoop(aIndex, aX, aY, aZ);
+ if (aIndex == 1) {
+ GT_Utility.doSoundAtClient(SoundResource.IC2_MACHINES_INDUCTION_LOOP, 10, 1.0F, aX, aY, aZ);
+ }
+ }
+
+ @Override
+ public void startProcess() {
+ sendLoopStart((byte) 1);
+ }
+
+ @Override
+ public ITexture[] getSideFacingActive(byte aColor) {
+ return new ITexture[] { super.getSideFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_SIDE_STEAM_ALLOY_SMELTER_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_ALLOY_SMELTER_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getSideFacingInactive(byte aColor) {
+ return new ITexture[] { super.getSideFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_SIDE_STEAM_ALLOY_SMELTER), TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_ALLOY_SMELTER_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingActive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_FRONT_STEAM_ALLOY_SMELTER_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_ALLOY_SMELTER_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingInactive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_FRONT_STEAM_ALLOY_SMELTER), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_ALLOY_SMELTER_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingActive(byte aColor) {
+ return new ITexture[] { super.getTopFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_TOP_STEAM_ALLOY_SMELTER_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_STEAM_ALLOY_SMELTER_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingInactive(byte aColor) {
+ return new ITexture[] { super.getTopFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_TOP_STEAM_ALLOY_SMELTER), TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_STEAM_ALLOY_SMELTER_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingActive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_ALLOY_SMELTER_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_ALLOY_SMELTER_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingInactive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_ALLOY_SMELTER), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_ALLOY_SMELTER_GLOW)
+ .glow()
+ .build() };
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Compressor_Bronze.java b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Compressor_Bronze.java
new file mode 100644
index 0000000000..ef91ed5baf
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Compressor_Bronze.java
@@ -0,0 +1,134 @@
+package gregtech.common.tileentities.machines.steam;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_COMPRESSOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_COMPRESSOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_COMPRESSOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_COMPRESSOR_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_COMPRESSOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_COMPRESSOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_COMPRESSOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_COMPRESSOR_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_COMPRESSOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_COMPRESSOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_COMPRESSOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_COMPRESSOR_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_COMPRESSOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_COMPRESSOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_COMPRESSOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_COMPRESSOR_GLOW;
+
+import gregtech.api.enums.SoundResource;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine_Bronze;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_Compressor_Bronze extends GT_MetaTileEntity_BasicMachine_Bronze {
+
+ public GT_MetaTileEntity_Compressor_Bronze(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional, "Compressing Items", 1, 1, false);
+ }
+
+ public GT_MetaTileEntity_Compressor_Bronze(String aName, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aDescription, aTextures, 1, 1, false);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Compressor_Bronze(this.mName, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.compressorRecipes;
+ }
+
+ @Override
+ public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) {
+ super.startSoundLoop(aIndex, aX, aY, aZ);
+ if (aIndex == 1) {
+ GT_Utility.doSoundAtClient(SoundResource.IC2_MACHINES_COMPRESSOR_OP, 10, 1.0F, aX, aY, aZ);
+ }
+ }
+
+ @Override
+ public void startProcess() {
+ sendLoopStart((byte) 1);
+ }
+
+ @Override
+ public ITexture[] getSideFacingActive(byte aColor) {
+ return new ITexture[] { super.getSideFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_SIDE_STEAM_COMPRESSOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_COMPRESSOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getSideFacingInactive(byte aColor) {
+ return new ITexture[] { super.getSideFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_SIDE_STEAM_COMPRESSOR), TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_COMPRESSOR_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingActive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_FRONT_STEAM_COMPRESSOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_COMPRESSOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingInactive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_FRONT_STEAM_COMPRESSOR), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_COMPRESSOR_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingActive(byte aColor) {
+ return new ITexture[] { super.getTopFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_TOP_STEAM_COMPRESSOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_STEAM_COMPRESSOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingInactive(byte aColor) {
+ return new ITexture[] { super.getTopFacingInactive(aColor)[0], TextureFactory.of(OVERLAY_TOP_STEAM_COMPRESSOR),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_STEAM_COMPRESSOR_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingActive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_COMPRESSOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_COMPRESSOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingInactive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_COMPRESSOR), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_COMPRESSOR_GLOW)
+ .glow()
+ .build() };
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Compressor_Steel.java b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Compressor_Steel.java
new file mode 100644
index 0000000000..c213cf04f5
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Compressor_Steel.java
@@ -0,0 +1,134 @@
+package gregtech.common.tileentities.machines.steam;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_COMPRESSOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_COMPRESSOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_COMPRESSOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_COMPRESSOR_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_COMPRESSOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_COMPRESSOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_COMPRESSOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_COMPRESSOR_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_COMPRESSOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_COMPRESSOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_COMPRESSOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_COMPRESSOR_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_COMPRESSOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_COMPRESSOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_COMPRESSOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_COMPRESSOR_GLOW;
+
+import gregtech.api.enums.SoundResource;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine_Steel;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_Compressor_Steel extends GT_MetaTileEntity_BasicMachine_Steel {
+
+ public GT_MetaTileEntity_Compressor_Steel(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional, "Compressing Items", 1, 1, true);
+ }
+
+ public GT_MetaTileEntity_Compressor_Steel(String aName, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aDescription, aTextures, 1, 1, true);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Compressor_Steel(this.mName, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.compressorRecipes;
+ }
+
+ @Override
+ public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) {
+ super.startSoundLoop(aIndex, aX, aY, aZ);
+ if (aIndex == 1) {
+ GT_Utility.doSoundAtClient(SoundResource.IC2_MACHINES_COMPRESSOR_OP, 10, 1.0F, aX, aY, aZ);
+ }
+ }
+
+ @Override
+ public void startProcess() {
+ sendLoopStart((byte) 1);
+ }
+
+ @Override
+ public ITexture[] getSideFacingActive(byte aColor) {
+ return new ITexture[] { super.getSideFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_SIDE_STEAM_COMPRESSOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_COMPRESSOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getSideFacingInactive(byte aColor) {
+ return new ITexture[] { super.getSideFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_SIDE_STEAM_COMPRESSOR), TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_COMPRESSOR_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingActive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_FRONT_STEAM_COMPRESSOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_COMPRESSOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingInactive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_FRONT_STEAM_COMPRESSOR), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_COMPRESSOR_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingActive(byte aColor) {
+ return new ITexture[] { super.getTopFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_TOP_STEAM_COMPRESSOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_STEAM_COMPRESSOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingInactive(byte aColor) {
+ return new ITexture[] { super.getTopFacingInactive(aColor)[0], TextureFactory.of(OVERLAY_TOP_STEAM_COMPRESSOR),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_STEAM_COMPRESSOR_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingActive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_COMPRESSOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_COMPRESSOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingInactive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_COMPRESSOR), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_COMPRESSOR_GLOW)
+ .glow()
+ .build() };
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Extractor_Bronze.java b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Extractor_Bronze.java
new file mode 100644
index 0000000000..6e986992f3
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Extractor_Bronze.java
@@ -0,0 +1,134 @@
+package gregtech.common.tileentities.machines.steam;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_EXTRACTOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_EXTRACTOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_EXTRACTOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_EXTRACTOR_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_EXTRACTOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_EXTRACTOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_EXTRACTOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_EXTRACTOR_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_EXTRACTOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_EXTRACTOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_EXTRACTOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_EXTRACTOR_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_EXTRACTOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_EXTRACTOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_EXTRACTOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_EXTRACTOR_GLOW;
+
+import gregtech.api.enums.SoundResource;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine_Bronze;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_Extractor_Bronze extends GT_MetaTileEntity_BasicMachine_Bronze {
+
+ public GT_MetaTileEntity_Extractor_Bronze(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional, "Extracting your first Rubber", 1, 1, false);
+ }
+
+ public GT_MetaTileEntity_Extractor_Bronze(String aName, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aDescription, aTextures, 1, 1, false);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Extractor_Bronze(this.mName, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.extractorRecipes;
+ }
+
+ @Override
+ public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) {
+ super.startSoundLoop(aIndex, aX, aY, aZ);
+ if (aIndex == 1) {
+ GT_Utility.doSoundAtClient(SoundResource.IC2_MACHINES_EXTRACTOR_OP, 10, 1.0F, aX, aY, aZ);
+ }
+ }
+
+ @Override
+ public void startProcess() {
+ sendLoopStart((byte) 1);
+ }
+
+ @Override
+ public ITexture[] getSideFacingActive(byte aColor) {
+ return new ITexture[] { super.getSideFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_SIDE_STEAM_EXTRACTOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_EXTRACTOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getSideFacingInactive(byte aColor) {
+ return new ITexture[] { super.getSideFacingInactive(aColor)[0], TextureFactory.of(OVERLAY_SIDE_STEAM_EXTRACTOR),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_EXTRACTOR_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingActive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_FRONT_STEAM_EXTRACTOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_EXTRACTOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingInactive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_FRONT_STEAM_EXTRACTOR), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_EXTRACTOR_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingActive(byte aColor) {
+ return new ITexture[] { super.getTopFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_TOP_STEAM_EXTRACTOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_STEAM_EXTRACTOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingInactive(byte aColor) {
+ return new ITexture[] { super.getTopFacingInactive(aColor)[0], TextureFactory.of(OVERLAY_TOP_STEAM_EXTRACTOR),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_STEAM_EXTRACTOR_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingActive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_EXTRACTOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_EXTRACTOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingInactive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_EXTRACTOR), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_EXTRACTOR_GLOW)
+ .glow()
+ .build() };
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Extractor_Steel.java b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Extractor_Steel.java
new file mode 100644
index 0000000000..6a3d024e25
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Extractor_Steel.java
@@ -0,0 +1,134 @@
+package gregtech.common.tileentities.machines.steam;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_EXTRACTOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_EXTRACTOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_EXTRACTOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_EXTRACTOR_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_EXTRACTOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_EXTRACTOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_EXTRACTOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_EXTRACTOR_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_EXTRACTOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_EXTRACTOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_EXTRACTOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_EXTRACTOR_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_EXTRACTOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_EXTRACTOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_EXTRACTOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_EXTRACTOR_GLOW;
+
+import gregtech.api.enums.SoundResource;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine_Steel;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_Extractor_Steel extends GT_MetaTileEntity_BasicMachine_Steel {
+
+ public GT_MetaTileEntity_Extractor_Steel(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional, "Extracting your first Rubber", 1, 1, true);
+ }
+
+ public GT_MetaTileEntity_Extractor_Steel(String aName, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aDescription, aTextures, 1, 1, true);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Extractor_Steel(this.mName, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.extractorRecipes;
+ }
+
+ @Override
+ public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) {
+ super.startSoundLoop(aIndex, aX, aY, aZ);
+ if (aIndex == 1) {
+ GT_Utility.doSoundAtClient(SoundResource.IC2_MACHINES_EXTRACTOR_OP, 10, 1.0F, aX, aY, aZ);
+ }
+ }
+
+ @Override
+ public void startProcess() {
+ sendLoopStart((byte) 1);
+ }
+
+ @Override
+ public ITexture[] getSideFacingActive(byte aColor) {
+ return new ITexture[] { super.getSideFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_SIDE_STEAM_EXTRACTOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_EXTRACTOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getSideFacingInactive(byte aColor) {
+ return new ITexture[] { super.getSideFacingInactive(aColor)[0], TextureFactory.of(OVERLAY_SIDE_STEAM_EXTRACTOR),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_EXTRACTOR_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingActive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_FRONT_STEAM_EXTRACTOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_EXTRACTOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingInactive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_FRONT_STEAM_EXTRACTOR), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_EXTRACTOR_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingActive(byte aColor) {
+ return new ITexture[] { super.getTopFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_TOP_STEAM_EXTRACTOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_STEAM_EXTRACTOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingInactive(byte aColor) {
+ return new ITexture[] { super.getTopFacingInactive(aColor)[0], TextureFactory.of(OVERLAY_TOP_STEAM_EXTRACTOR),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_STEAM_EXTRACTOR_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingActive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_EXTRACTOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_EXTRACTOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingInactive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_EXTRACTOR), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_EXTRACTOR_GLOW)
+ .glow()
+ .build() };
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_ForgeHammer_Bronze.java b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_ForgeHammer_Bronze.java
new file mode 100644
index 0000000000..b7d25ac172
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_ForgeHammer_Bronze.java
@@ -0,0 +1,202 @@
+package gregtech.common.tileentities.machines.steam;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_HAMMER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_HAMMER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_HAMMER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_HAMMER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_HAMMER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_HAMMER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_HAMMER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_HAMMER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_HAMMER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_HAMMER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_HAMMER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_HAMMER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_HAMMER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_HAMMER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_HAMMER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_HAMMER_GLOW;
+import static gregtech.api.objects.XSTR.XSTR_INSTANCE;
+
+import net.minecraft.block.Block;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import gregtech.api.enums.ParticleFX;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine_Bronze;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.WorldSpawnedEventBuilder.ParticleEventBuilder;
+
+public class GT_MetaTileEntity_ForgeHammer_Bronze extends GT_MetaTileEntity_BasicMachine_Bronze {
+
+ public GT_MetaTileEntity_ForgeHammer_Bronze(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional, "Forge Hammer", 1, 1, false);
+ }
+
+ public GT_MetaTileEntity_ForgeHammer_Bronze(String aName, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aDescription, aTextures, 1, 1, false);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_ForgeHammer_Bronze(this.mName, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.hammerRecipes;
+ }
+
+ @Override
+ public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) {
+ super.startSoundLoop(aIndex, aX, aY, aZ);
+ if (aIndex == 1) {
+ GT_Utility.doSoundAtClient(SoundResource.RANDOM_ANVIL_USE, 10, 1.0F, aX, aY, aZ);
+ }
+ }
+
+ @Override
+ public void startProcess() {
+ sendLoopStart((byte) 1);
+ }
+
+ @Override
+ public ITexture[] getSideFacingActive(byte aColor) {
+ return new ITexture[] { super.getSideFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_SIDE_STEAM_HAMMER_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_HAMMER_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getSideFacingInactive(byte aColor) {
+ return new ITexture[] { super.getSideFacingInactive(aColor)[0], TextureFactory.of(OVERLAY_SIDE_STEAM_HAMMER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_HAMMER_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingActive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_FRONT_STEAM_HAMMER_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_HAMMER_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingInactive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingInactive(aColor)[0], TextureFactory.of(OVERLAY_FRONT_STEAM_HAMMER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_HAMMER_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingActive(byte aColor) {
+ return new ITexture[] { super.getTopFacingActive(aColor)[0], TextureFactory.of(OVERLAY_TOP_STEAM_HAMMER_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_STEAM_HAMMER_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingInactive(byte aColor) {
+ return new ITexture[] { super.getTopFacingInactive(aColor)[0], TextureFactory.of(OVERLAY_TOP_STEAM_HAMMER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_STEAM_HAMMER_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingActive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_HAMMER_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_HAMMER_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingInactive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_HAMMER), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_HAMMER_GLOW)
+ .glow()
+ .build() };
+ }
+
+ /**
+ * Handles {@link Block#randomDisplayTick}. Draws Random Sparkles at main face.
+ *
+ * @param aBaseMetaTileEntity The entity that will handle the {@see Block#randomDisplayTick}
+ */
+ @SideOnly(Side.CLIENT)
+ @Override
+ public void onRandomDisplayTick(IGregTechTileEntity aBaseMetaTileEntity) {
+
+ // Random Sparkles at main face
+ if (aBaseMetaTileEntity.isActive() && XSTR_INSTANCE.nextInt(3) == 0) {
+
+ final ForgeDirection mainFacing = this.mMainFacing;
+
+ if ((mainFacing.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0
+ && aBaseMetaTileEntity.getCoverIDAtSide(mainFacing) == 0
+ && !aBaseMetaTileEntity.getOpacityAtSide(mainFacing)) {
+
+ final double oX = aBaseMetaTileEntity.getXCoord();
+ final double oY = aBaseMetaTileEntity.getYCoord();
+ final double oZ = aBaseMetaTileEntity.getZCoord();
+ final double offset = 0.02D;
+ final double horizontal = 0.5D + XSTR_INSTANCE.nextFloat() * 8D / 16D - 4D / 16D;
+
+ final double x, y, z, mX, mZ;
+
+ y = oY + XSTR_INSTANCE.nextFloat() * 10D / 16D + 5D / 16D;
+
+ if (mainFacing == ForgeDirection.WEST) {
+ x = oX - offset;
+ mX = -.05D;
+ z = oZ + horizontal;
+ mZ = 0D;
+ } else if (mainFacing == ForgeDirection.EAST) {
+ x = oX + offset;
+ mX = .05D;
+ z = oZ + horizontal;
+ mZ = 0D;
+ } else if (mainFacing == ForgeDirection.NORTH) {
+ x = oX + horizontal;
+ mX = 0D;
+ z = oZ - offset;
+ mZ = -.05D;
+ } else // if (frontFacing == ForgeDirection.SOUTH.ordinal())
+ {
+ x = oX + horizontal;
+ mX = 0D;
+ z = oZ + offset;
+ mZ = .05D;
+ }
+
+ ParticleEventBuilder particleEventBuilder = (new ParticleEventBuilder()).setMotion(mX, 0, mZ)
+ .setPosition(x, y, z)
+ .setWorld(getBaseMetaTileEntity().getWorld());
+ particleEventBuilder.setIdentifier(ParticleFX.LAVA)
+ .run();
+ }
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_ForgeHammer_Steel.java b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_ForgeHammer_Steel.java
new file mode 100644
index 0000000000..25785f3fa3
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_ForgeHammer_Steel.java
@@ -0,0 +1,202 @@
+package gregtech.common.tileentities.machines.steam;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_HAMMER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_HAMMER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_HAMMER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_HAMMER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_HAMMER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_HAMMER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_HAMMER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_HAMMER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_HAMMER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_HAMMER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_HAMMER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_HAMMER_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_HAMMER;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_HAMMER_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_HAMMER_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_HAMMER_GLOW;
+import static gregtech.api.objects.XSTR.XSTR_INSTANCE;
+
+import net.minecraft.block.Block;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import gregtech.api.enums.ParticleFX;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine_Steel;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.WorldSpawnedEventBuilder.ParticleEventBuilder;
+
+public class GT_MetaTileEntity_ForgeHammer_Steel extends GT_MetaTileEntity_BasicMachine_Steel {
+
+ public GT_MetaTileEntity_ForgeHammer_Steel(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional, "Forge Hammer", 1, 1, true);
+ }
+
+ public GT_MetaTileEntity_ForgeHammer_Steel(String aName, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aDescription, aTextures, 1, 1, true);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_ForgeHammer_Steel(this.mName, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.hammerRecipes;
+ }
+
+ @Override
+ public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) {
+ super.startSoundLoop(aIndex, aX, aY, aZ);
+ if (aIndex == 1) {
+ GT_Utility.doSoundAtClient(SoundResource.RANDOM_ANVIL_USE, 10, 1.0F, aX, aY, aZ);
+ }
+ }
+
+ @Override
+ public void startProcess() {
+ sendLoopStart((byte) 1);
+ }
+
+ @Override
+ public ITexture[] getSideFacingActive(byte aColor) {
+ return new ITexture[] { super.getSideFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_SIDE_STEAM_HAMMER_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_HAMMER_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getSideFacingInactive(byte aColor) {
+ return new ITexture[] { super.getSideFacingInactive(aColor)[0], TextureFactory.of(OVERLAY_SIDE_STEAM_HAMMER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_HAMMER_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingActive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_FRONT_STEAM_HAMMER_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_HAMMER_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingInactive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingInactive(aColor)[0], TextureFactory.of(OVERLAY_FRONT_STEAM_HAMMER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_HAMMER_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingActive(byte aColor) {
+ return new ITexture[] { super.getTopFacingActive(aColor)[0], TextureFactory.of(OVERLAY_TOP_STEAM_HAMMER_ACTIVE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_STEAM_HAMMER_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingInactive(byte aColor) {
+ return new ITexture[] { super.getTopFacingInactive(aColor)[0], TextureFactory.of(OVERLAY_TOP_STEAM_HAMMER),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_STEAM_HAMMER_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingActive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_HAMMER_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_HAMMER_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingInactive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_HAMMER), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_HAMMER_GLOW)
+ .glow()
+ .build() };
+ }
+
+ /**
+ * Handles {@link Block#randomDisplayTick}. Draws Random Sparkles at main face.
+ *
+ * @param aBaseMetaTileEntity The entity that will handle the {@see Block#randomDisplayTick}
+ */
+ @SideOnly(Side.CLIENT)
+ @Override
+ public void onRandomDisplayTick(IGregTechTileEntity aBaseMetaTileEntity) {
+
+ // Random Sparkles at main face
+ if (aBaseMetaTileEntity.isActive() && XSTR_INSTANCE.nextInt(3) == 0) {
+
+ final ForgeDirection mainFacing = this.mMainFacing;
+
+ if ((mainFacing.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0
+ && aBaseMetaTileEntity.getCoverIDAtSide(mainFacing) == 0
+ && !aBaseMetaTileEntity.getOpacityAtSide(mainFacing)) {
+
+ final double oX = aBaseMetaTileEntity.getXCoord();
+ final double oY = aBaseMetaTileEntity.getYCoord();
+ final double oZ = aBaseMetaTileEntity.getZCoord();
+ final double offset = 0.02D;
+ final double horizontal = 0.5D + XSTR_INSTANCE.nextFloat() * 8D / 16D - 4D / 16D;
+
+ final double x, y, z, mX, mZ;
+
+ y = oY + XSTR_INSTANCE.nextFloat() * 10D / 16D + 5D / 16D;
+
+ if (mainFacing == ForgeDirection.WEST) {
+ x = oX - offset;
+ mX = -.05D;
+ z = oZ + horizontal;
+ mZ = 0D;
+ } else if (mainFacing == ForgeDirection.EAST) {
+ x = oX + offset;
+ mX = .05D;
+ z = oZ + horizontal;
+ mZ = 0D;
+ } else if (mainFacing == ForgeDirection.NORTH) {
+ x = oX + horizontal;
+ mX = 0D;
+ z = oZ - offset;
+ mZ = -.05D;
+ } else // if (frontFacing == ForgeDirection.SOUTH.ordinal())
+ {
+ x = oX + horizontal;
+ mX = 0D;
+ z = oZ + offset;
+ mZ = .05D;
+ }
+
+ ParticleEventBuilder particleEventBuilder = (new ParticleEventBuilder()).setMotion(mX, 0, mZ)
+ .setPosition(x, y, z)
+ .setWorld(getBaseMetaTileEntity().getWorld());
+ particleEventBuilder.setIdentifier(ParticleFX.LAVA)
+ .run();
+ }
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Furnace_Bronze.java b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Furnace_Bronze.java
new file mode 100644
index 0000000000..51a91874a0
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Furnace_Bronze.java
@@ -0,0 +1,161 @@
+package gregtech.common.tileentities.machines.steam;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_FURNACE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_FURNACE_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_FURNACE_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_FURNACE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_FURNACE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_FURNACE_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_FURNACE_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_FURNACE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_FURNACE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_FURNACE_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_FURNACE_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_FURNACE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_FURNACE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_FURNACE_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_FURNACE_GLOW;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.enums.SoundResource;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine_Bronze;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_Furnace_Bronze extends GT_MetaTileEntity_BasicMachine_Bronze {
+
+ public GT_MetaTileEntity_Furnace_Bronze(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional, "Smelting things with compressed Steam", 1, 1, false);
+ }
+
+ public GT_MetaTileEntity_Furnace_Bronze(String aName, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aDescription, aTextures, 1, 1, false);
+ }
+
+ @Override
+ protected boolean isBricked() {
+ return true;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Furnace_Bronze(this.mName, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.furnaceRecipes;
+ }
+
+ @Override
+ public int checkRecipe() {
+ if (null != (this.mOutputItems[0] = GT_ModHandler.getSmeltingOutput(getInputAt(0), true, getOutputAt(0)))) {
+ this.mEUt = 4;
+ this.mMaxProgresstime = 256;
+ return FOUND_AND_SUCCESSFULLY_USED_RECIPE;
+ }
+ return DID_NOT_FIND_RECIPE;
+ }
+
+ @Override
+ protected boolean allowPutStackValidated(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return super.allowPutStackValidated(aBaseMetaTileEntity, aIndex, side, aStack)
+ && GT_ModHandler.getSmeltingOutput(GT_Utility.copyAmount(64, aStack), false, null) != null;
+ }
+
+ @Override
+ public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) {
+ super.startSoundLoop(aIndex, aX, aY, aZ);
+ if (aIndex == 1) {
+ GT_Utility.doSoundAtClient(SoundResource.IC2_MACHINES_ELECTROFURNACE_LOOP, 10, 1.0F, aX, aY, aZ);
+ }
+ }
+
+ @Override
+ public void startProcess() {
+ sendLoopStart((byte) 1);
+ }
+
+ @Override
+ public ITexture[] getSideFacingActive(byte aColor) {
+ return new ITexture[] { super.getSideFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_SIDE_STEAM_FURNACE_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_FURNACE_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getSideFacingInactive(byte aColor) {
+ return new ITexture[] { super.getSideFacingInactive(aColor)[0], TextureFactory.of(OVERLAY_SIDE_STEAM_FURNACE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_FURNACE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingActive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_FRONT_STEAM_FURNACE_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_FURNACE_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingInactive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingInactive(aColor)[0], TextureFactory.of(OVERLAY_FRONT_STEAM_FURNACE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_FURNACE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingActive(byte aColor) {
+ return new ITexture[] { super.getTopFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_TOP_STEAM_FURNACE_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_FURNACE_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingInactive(byte aColor) {
+ return new ITexture[] { super.getTopFacingInactive(aColor)[0], TextureFactory.of(OVERLAY_TOP_STEAM_FURNACE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_STEAM_FURNACE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingActive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_FURNACE_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_FURNACE_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingInactive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingInactive(aColor)[0],
+ TextureFactory.of(
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_FURNACE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_FURNACE_GLOW)
+ .glow()
+ .build()) };
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Furnace_Steel.java b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Furnace_Steel.java
new file mode 100644
index 0000000000..a151c724f3
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Furnace_Steel.java
@@ -0,0 +1,159 @@
+package gregtech.common.tileentities.machines.steam;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_FURNACE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_FURNACE_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_FURNACE_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_FURNACE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_FURNACE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_FURNACE_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_FURNACE_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_FURNACE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_FURNACE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_FURNACE_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_FURNACE_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_FURNACE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_FURNACE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_FURNACE_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_FURNACE_GLOW;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.enums.SoundResource;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine_Steel;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_Furnace_Steel extends GT_MetaTileEntity_BasicMachine_Steel {
+
+ public GT_MetaTileEntity_Furnace_Steel(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional, "Smelting things with compressed Steam", 1, 1, true);
+ }
+
+ public GT_MetaTileEntity_Furnace_Steel(String aName, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aDescription, aTextures, 1, 1, true);
+ }
+
+ @Override
+ protected boolean isBricked() {
+ return true;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Furnace_Steel(this.mName, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.furnaceRecipes;
+ }
+
+ @Override
+ public int checkRecipe() {
+ if (null != (this.mOutputItems[0] = GT_ModHandler.getSmeltingOutput(getInputAt(0), true, getOutputAt(0)))) {
+ this.mEUt = 8;
+ this.mMaxProgresstime = 128;
+ return FOUND_AND_SUCCESSFULLY_USED_RECIPE;
+ }
+ return DID_NOT_FIND_RECIPE;
+ }
+
+ @Override
+ protected boolean allowPutStackValidated(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return super.allowPutStackValidated(aBaseMetaTileEntity, aIndex, side, aStack)
+ && GT_ModHandler.getSmeltingOutput(GT_Utility.copyAmount(64, aStack), false, null) != null;
+ }
+
+ @Override
+ public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) {
+ super.startSoundLoop(aIndex, aX, aY, aZ);
+ if (aIndex == 1) {
+ GT_Utility.doSoundAtClient(SoundResource.IC2_MACHINES_ELECTROFURNACE_LOOP, 10, 1.0F, aX, aY, aZ);
+ }
+ }
+
+ @Override
+ public void startProcess() {
+ sendLoopStart((byte) 1);
+ }
+
+ @Override
+ public ITexture[] getSideFacingActive(byte aColor) {
+ return new ITexture[] { super.getSideFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_SIDE_STEAM_FURNACE_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_FURNACE_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getSideFacingInactive(byte aColor) {
+ return new ITexture[] { super.getSideFacingInactive(aColor)[0], TextureFactory.of(OVERLAY_SIDE_STEAM_FURNACE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_FURNACE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingActive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_FRONT_STEAM_FURNACE_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_FURNACE_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingInactive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingInactive(aColor)[0], TextureFactory.of(OVERLAY_FRONT_STEAM_FURNACE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_FURNACE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingActive(byte aColor) {
+ return new ITexture[] { super.getTopFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_TOP_STEAM_FURNACE_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_FURNACE_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingInactive(byte aColor) {
+ return new ITexture[] { super.getTopFacingInactive(aColor)[0], TextureFactory.of(OVERLAY_TOP_STEAM_FURNACE),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_STEAM_FURNACE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingActive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_FURNACE_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_FURNACE_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingInactive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_FURNACE), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_FURNACE_GLOW)
+ .glow()
+ .build() };
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Macerator_Bronze.java b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Macerator_Bronze.java
new file mode 100644
index 0000000000..cdbf9f2123
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Macerator_Bronze.java
@@ -0,0 +1,189 @@
+package gregtech.common.tileentities.machines.steam;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_MACERATOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_MACERATOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_MACERATOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_MACERATOR_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_MACERATOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_MACERATOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_MACERATOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_MACERATOR_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_MACERATOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_MACERATOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_MACERATOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_MACERATOR_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_MACERATOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_MACERATOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_MACERATOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_MACERATOR_GLOW;
+import static gregtech.api.objects.XSTR.XSTR_INSTANCE;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import gregtech.api.enums.ParticleFX;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.TierEU;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine_Bronze;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.WorldSpawnedEventBuilder.ParticleEventBuilder;
+
+public class GT_MetaTileEntity_Macerator_Bronze extends GT_MetaTileEntity_BasicMachine_Bronze {
+
+ public GT_MetaTileEntity_Macerator_Bronze(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional, "Macerating your Ores", 1, 1, false);
+ }
+
+ public GT_MetaTileEntity_Macerator_Bronze(String aName, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aDescription, aTextures, 1, 1, false);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Macerator_Bronze(this.mName, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPreTick(aBaseMetaTileEntity, aTick);
+ if (aBaseMetaTileEntity.isClientSide() && aBaseMetaTileEntity.isActive()) {
+
+ if (aBaseMetaTileEntity.getFrontFacing() != ForgeDirection.UP
+ && aBaseMetaTileEntity.getCoverIDAtSide(ForgeDirection.UP) == 0
+ && !aBaseMetaTileEntity.getOpacityAtSide(ForgeDirection.UP)) {
+
+ new ParticleEventBuilder().setMotion(0.0D, 0.0D, 0.0D)
+ .setIdentifier(ParticleFX.SMOKE)
+ .setPosition(
+ aBaseMetaTileEntity.getXCoord() + 0.8F - XSTR_INSTANCE.nextFloat() * 0.6F,
+ aBaseMetaTileEntity.getYCoord() + 0.9F + XSTR_INSTANCE.nextFloat() * 0.2F,
+ aBaseMetaTileEntity.getZCoord() + 0.8F - XSTR_INSTANCE.nextFloat() * 0.6F)
+ .setWorld(getBaseMetaTileEntity().getWorld())
+ .run();
+ }
+ }
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.maceratorRecipes;
+ }
+
+ @Override
+ public int checkRecipe() {
+ GT_Recipe tRecipe = getRecipeMap()
+ .findRecipe(getBaseMetaTileEntity(), mLastRecipe, false, TierEU.LV, null, null, getAllInputs());
+ if (tRecipe == null) return DID_NOT_FIND_RECIPE;
+ if (tRecipe.mCanBeBuffered) mLastRecipe = tRecipe;
+ if (!canOutput(tRecipe)) {
+ mOutputBlocked++;
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ }
+
+ if (!tRecipe.isRecipeInputEqual(true, new FluidStack[] { getFillableStack() }, getAllInputs()))
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ if (tRecipe.getOutput(0) != null) mOutputItems[0] = tRecipe.getOutput(0);
+ calculateCustomOverclock(tRecipe);
+ return FOUND_AND_SUCCESSFULLY_USED_RECIPE;
+ }
+
+ @Override
+ protected boolean allowPutStackValidated(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return super.allowPutStackValidated(aBaseMetaTileEntity, aIndex, side, aStack)
+ && RecipeMaps.maceratorRecipes.containsInput(GT_Utility.copyAmount(64, aStack));
+ }
+
+ @Override
+ public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) {
+ super.startSoundLoop(aIndex, aX, aY, aZ);
+ if (aIndex == 1) {
+ GT_Utility.doSoundAtClient(SoundResource.IC2_MACHINES_MACERATOR_OP, 10, 1.0F, aX, aY, aZ);
+ }
+ }
+
+ @Override
+ public void startProcess() {
+ sendLoopStart((byte) 1);
+ }
+
+ @Override
+ public ITexture[] getSideFacingActive(byte aColor) {
+ return new ITexture[] { super.getSideFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_SIDE_STEAM_MACERATOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_MACERATOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getSideFacingInactive(byte aColor) {
+ return new ITexture[] { super.getSideFacingInactive(aColor)[0], TextureFactory.of(OVERLAY_SIDE_STEAM_MACERATOR),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_MACERATOR_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingActive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_FRONT_STEAM_MACERATOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_MACERATOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingInactive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_FRONT_STEAM_MACERATOR), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_MACERATOR_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingActive(byte aColor) {
+ return new ITexture[] { super.getTopFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_TOP_STEAM_MACERATOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_STEAM_MACERATOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingInactive(byte aColor) {
+ return new ITexture[] { super.getTopFacingInactive(aColor)[0], TextureFactory.of(OVERLAY_TOP_STEAM_MACERATOR),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_STEAM_MACERATOR_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingActive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_MACERATOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_MACERATOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingInactive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_MACERATOR), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_MACERATOR_GLOW)
+ .glow()
+ .build() };
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Macerator_Steel.java b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Macerator_Steel.java
new file mode 100644
index 0000000000..95d510ffa3
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/steam/GT_MetaTileEntity_Macerator_Steel.java
@@ -0,0 +1,188 @@
+package gregtech.common.tileentities.machines.steam;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_MACERATOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_MACERATOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_MACERATOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_BOTTOM_STEAM_MACERATOR_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_MACERATOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_MACERATOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_MACERATOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_STEAM_MACERATOR_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_MACERATOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_MACERATOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_MACERATOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SIDE_STEAM_MACERATOR_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_MACERATOR;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_MACERATOR_ACTIVE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_MACERATOR_ACTIVE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_STEAM_MACERATOR_GLOW;
+import static gregtech.api.objects.XSTR.XSTR_INSTANCE;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import gregtech.api.enums.ParticleFX;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.TierEU;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine_Steel;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.WorldSpawnedEventBuilder;
+
+public class GT_MetaTileEntity_Macerator_Steel extends GT_MetaTileEntity_BasicMachine_Steel {
+
+ public GT_MetaTileEntity_Macerator_Steel(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional, "Macerating your Ores", 1, 1, true);
+ }
+
+ public GT_MetaTileEntity_Macerator_Steel(String aName, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aDescription, aTextures, 1, 1, true);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Macerator_Steel(this.mName, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPreTick(aBaseMetaTileEntity, aTick);
+ if (aBaseMetaTileEntity.isClientSide() && aBaseMetaTileEntity.isActive()) {
+ if (aBaseMetaTileEntity.getFrontFacing() != ForgeDirection.UP
+ && aBaseMetaTileEntity.getCoverIDAtSide(ForgeDirection.UP) == 0
+ && !aBaseMetaTileEntity.getOpacityAtSide(ForgeDirection.UP)) {
+
+ new WorldSpawnedEventBuilder.ParticleEventBuilder().setMotion(0.0D, 0.0D, 0.0D)
+ .setIdentifier(ParticleFX.SMOKE)
+ .setPosition(
+ aBaseMetaTileEntity.getXCoord() + 0.8F - XSTR_INSTANCE.nextFloat() * 0.6F,
+ aBaseMetaTileEntity.getYCoord() + 0.9F + XSTR_INSTANCE.nextFloat() * 0.2F,
+ aBaseMetaTileEntity.getZCoord() + 0.8F - XSTR_INSTANCE.nextFloat() * 0.6F)
+ .setWorld(getBaseMetaTileEntity().getWorld())
+ .run();
+ }
+ }
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.maceratorRecipes;
+ }
+
+ @Override
+ public int checkRecipe() {
+ GT_Recipe tRecipe = getRecipeMap()
+ .findRecipe(getBaseMetaTileEntity(), mLastRecipe, false, TierEU.LV, null, null, getAllInputs());
+ if (tRecipe == null) return DID_NOT_FIND_RECIPE;
+ if (tRecipe.mCanBeBuffered) mLastRecipe = tRecipe;
+ if (!canOutput(tRecipe)) {
+ mOutputBlocked++;
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ }
+
+ if (!tRecipe.isRecipeInputEqual(true, new FluidStack[] { getFillableStack() }, getAllInputs()))
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ if (tRecipe.getOutput(0) != null) mOutputItems[0] = tRecipe.getOutput(0);
+ calculateCustomOverclock(tRecipe);
+ return FOUND_AND_SUCCESSFULLY_USED_RECIPE;
+ }
+
+ @Override
+ public boolean allowPutStackValidated(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return super.allowPutStackValidated(aBaseMetaTileEntity, aIndex, side, aStack)
+ && RecipeMaps.maceratorRecipes.containsInput(GT_Utility.copyAmount(64, aStack));
+ }
+
+ @Override
+ public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) {
+ super.startSoundLoop(aIndex, aX, aY, aZ);
+ if (aIndex == 1) {
+ GT_Utility.doSoundAtClient(SoundResource.IC2_MACHINES_MACERATOR_OP, 10, 1.0F, aX, aY, aZ);
+ }
+ }
+
+ @Override
+ public void startProcess() {
+ sendLoopStart((byte) 1);
+ }
+
+ @Override
+ public ITexture[] getSideFacingActive(byte aColor) {
+ return new ITexture[] { super.getSideFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_SIDE_STEAM_MACERATOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_MACERATOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getSideFacingInactive(byte aColor) {
+ return new ITexture[] { super.getSideFacingInactive(aColor)[0], TextureFactory.of(OVERLAY_SIDE_STEAM_MACERATOR),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_SIDE_STEAM_MACERATOR_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingActive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_FRONT_STEAM_MACERATOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_MACERATOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingInactive(byte aColor) {
+ return new ITexture[] { super.getFrontFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_FRONT_STEAM_MACERATOR), TextureFactory.builder()
+ .addIcon(OVERLAY_FRONT_STEAM_MACERATOR_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingActive(byte aColor) {
+ return new ITexture[] { super.getTopFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_TOP_STEAM_MACERATOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_STEAM_MACERATOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTopFacingInactive(byte aColor) {
+ return new ITexture[] { super.getTopFacingInactive(aColor)[0], TextureFactory.of(OVERLAY_TOP_STEAM_MACERATOR),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_TOP_STEAM_MACERATOR_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingActive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingActive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_MACERATOR_ACTIVE), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_MACERATOR_ACTIVE_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingInactive(byte aColor) {
+ return new ITexture[] { super.getBottomFacingInactive(aColor)[0],
+ TextureFactory.of(OVERLAY_BOTTOM_STEAM_MACERATOR), TextureFactory.builder()
+ .addIcon(OVERLAY_BOTTOM_STEAM_MACERATOR_GLOW)
+ .glow()
+ .build() };
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/render/TileDrone.java b/src/main/java/gregtech/common/tileentities/render/TileDrone.java
new file mode 100644
index 0000000000..625157ff37
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/render/TileDrone.java
@@ -0,0 +1,24 @@
+package gregtech.common.tileentities.render;
+
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.AxisAlignedBB;
+
+public class TileDrone extends TileEntity {
+
+ public double rotation = 0;
+
+ @Override
+ public AxisAlignedBB getRenderBoundingBox() {
+ return INFINITE_EXTENT_AABB;
+ }
+
+ @Override
+ public double getMaxRenderDistanceSquared() {
+ return 65536;
+ }
+
+ @Override
+ public void updateEntity() {
+ rotation = (rotation + 50) % 360d;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/render/TileLaser.java b/src/main/java/gregtech/common/tileentities/render/TileLaser.java
new file mode 100644
index 0000000000..848eecdcf2
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/render/TileLaser.java
@@ -0,0 +1,116 @@
+package gregtech.common.tileentities.render;
+
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.network.NetworkManager;
+import net.minecraft.network.Packet;
+import net.minecraft.network.play.server.S35PacketUpdateTileEntity;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizon.structurelib.alignment.enumerable.Flip;
+import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation;
+
+public class TileLaser extends TileEntity {
+
+ public boolean shouldRender = false;
+ public float red = 0, green = 0, blue = 0;
+ public float counter = 0F;
+ public boolean realism = false;
+ public double rotAxisX = 0, rotAxisY = 0, rotAxisZ = 0, rotationAngle = 0;
+
+ @Override
+ public void writeToNBT(NBTTagCompound compound) {
+ super.writeToNBT(compound);
+ compound.setFloat("rgb_red", red);
+ compound.setFloat("rgb_green", green);
+ compound.setFloat("rgb_blue", blue);
+ compound.setBoolean("shouldRender", shouldRender);
+ compound.setDouble("rotAxisX", rotAxisX);
+ compound.setDouble("rotAxisY", rotAxisY);
+ compound.setDouble("rotAxisZ", rotAxisZ);
+ compound.setDouble("rotationAngle", rotationAngle);
+ }
+
+ @Override
+ public void readFromNBT(NBTTagCompound compound) {
+ super.readFromNBT(compound);
+ red = compound.getFloat("rgb_red");
+ blue = compound.getFloat("rgb_blue");
+ green = compound.getFloat("rgb_green");
+ shouldRender = compound.getBoolean("shouldRender");
+ rotAxisX = compound.getDouble("rotAxisX");
+ rotAxisY = compound.getDouble("rotAxisY");
+ rotAxisZ = compound.getDouble("rotAxisZ");
+ rotationAngle = compound.getDouble("rotationAngle");
+ }
+
+ public void setColors(float red, float green, float blue) {
+ this.red = red;
+ this.green = green;
+ this.blue = blue;
+ worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
+ }
+
+ public void setRotationFields(ForgeDirection direction, Rotation rotation, Flip flip) {
+ setRotationAngle(rotation, flip);
+ setRotationAxis(direction);
+ worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
+ }
+
+ private void setRotationAngle(Rotation rotation, Flip flip) {
+ int invert = (flip == Flip.HORIZONTAL || flip == Flip.VERTICAL) ? 1 : -1;
+ switch (rotation) {
+ case NORMAL -> rotationAngle = 0;
+ case CLOCKWISE -> rotationAngle = 90 * invert;
+ case COUNTER_CLOCKWISE -> rotationAngle = -90 * invert;
+ case UPSIDE_DOWN -> rotationAngle = 180;
+ }
+ worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
+ }
+
+ public void setRotationAxis(ForgeDirection direction) {
+ rotAxisX = direction.offsetX;
+ rotAxisY = direction.offsetY;
+ rotAxisZ = direction.offsetZ;
+ worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
+ }
+
+ public void setShouldRender(boolean shouldRender) {
+ this.shouldRender = shouldRender;
+ worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
+ }
+
+ public Boolean getShouldRender() {
+ return shouldRender;
+ }
+
+ public float getRed() {
+ return red;
+ }
+
+ public float getGreen() {
+ return green;
+ }
+
+ public float getBlue() {
+ return blue;
+ }
+
+ @Override
+ public Packet getDescriptionPacket() {
+ NBTTagCompound nbttagcompound = new NBTTagCompound();
+ writeToNBT(nbttagcompound);
+ return new S35PacketUpdateTileEntity(xCoord, yCoord, zCoord, 1, nbttagcompound);
+ }
+
+ @Override
+ public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt) {
+ readFromNBT(pkt.func_148857_g());
+ worldObj.markBlockRangeForRenderUpdate(xCoord, yCoord, zCoord, xCoord, yCoord, zCoord);
+ }
+
+ @Override
+ public double getMaxRenderDistanceSquared() {
+ return 4096;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/render/TileWormhole.java b/src/main/java/gregtech/common/tileentities/render/TileWormhole.java
new file mode 100644
index 0000000000..d482fd7ce1
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/render/TileWormhole.java
@@ -0,0 +1,87 @@
+package gregtech.common.tileentities.render;
+
+import java.util.Optional;
+
+import net.minecraft.block.Block;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.network.NetworkManager;
+import net.minecraft.network.Packet;
+import net.minecraft.network.play.server.S35PacketUpdateTileEntity;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.world.World;
+import net.minecraft.world.WorldProvider;
+
+import pers.gwyog.gtneioreplugin.plugin.block.ModBlocks;
+import pers.gwyog.gtneioreplugin.util.DimensionHelper;
+
+public class TileWormhole extends TileEntity {
+
+ public int dimID = 0;
+
+ public double targetRadius = 0;
+
+ @Override
+ public void writeToNBT(NBTTagCompound compound) {
+ super.writeToNBT(compound);
+ compound.setInteger("dimID", dimID);
+ compound.setDouble("targetRadius", targetRadius);
+ }
+
+ @Override
+ public void readFromNBT(NBTTagCompound compound) {
+ super.readFromNBT(compound);
+ dimID = compound.getInteger("dimID");
+ targetRadius = compound.getDouble("targetRadius");
+ }
+
+ public int getDimFromWorld(World target) {
+ if (target == null) return 0;
+ String dimName = Optional.ofNullable(target.provider)
+ .map(WorldProvider::getDimensionName)
+ .orElse(null);
+ if (dimName == null) return 0;
+ for (int i = 0; i < DimensionHelper.DimName.length; i++) {
+ if (dimName.equals(DimensionHelper.DimName[i])) return i;
+ }
+ return 0;
+ }
+
+ public void setDimFromWorld(World target) {
+ int newName = getDimFromWorld(target);
+ if (target != null & dimID != newName) {
+ dimID = newName;
+ this.markDirty();
+ worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
+ }
+ }
+
+ public void setRadius(double target) {
+ targetRadius = target;
+ this.markDirty();
+ worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
+ }
+
+ public Block getBlock() {
+ return ModBlocks.getBlock(DimensionHelper.DimNameDisplayed[dimID]);
+ }
+
+ @Override
+ public double getMaxRenderDistanceSquared() {
+ return 65536;
+ }
+
+ @Override
+ public Packet getDescriptionPacket() {
+ NBTTagCompound nbttagcompound = new NBTTagCompound();
+ writeToNBT(nbttagcompound);
+ return new S35PacketUpdateTileEntity(xCoord, yCoord, zCoord, 1, nbttagcompound);
+ }
+
+ @Override
+ public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt) {
+
+ readFromNBT(pkt.func_148857_g());
+ worldObj.markBlockRangeForRenderUpdate(xCoord, yCoord, zCoord, xCoord, yCoord, zCoord);
+ }
+
+}
diff --git a/src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_DigitalChestBase.java b/src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_DigitalChestBase.java
new file mode 100644
index 0000000000..1576f61ff8
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_DigitalChestBase.java
@@ -0,0 +1,559 @@
+package gregtech.common.tileentities.storage;
+
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASINGS;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SCHEST;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SCHEST_GLOW;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.Constants;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizons.modularui.api.NumberFormatMUI;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+
+import appeng.api.storage.IMEMonitor;
+import appeng.api.storage.IMEMonitorHandlerReceiver;
+import appeng.api.storage.StorageChannel;
+import appeng.api.storage.data.IAEItemStack;
+import appeng.api.storage.data.IItemList;
+import appeng.util.item.AEItemStack;
+import appeng.util.item.ItemList;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_TieredMachineBlock;
+import gregtech.api.objects.AE2DigitalChestHandler;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_LanguageManager;
+import gregtech.api.util.GT_Utility;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public abstract class GT_MetaTileEntity_DigitalChestBase extends GT_MetaTileEntity_TieredMachineBlock
+ implements IMEMonitor<IAEItemStack>, IAddUIWidgets {
+
+ protected boolean mVoidOverflow = false;
+ protected boolean mDisableFilter;
+ private Map<IMEMonitorHandlerReceiver<IAEItemStack>, Object> listeners = null;
+
+ public GT_MetaTileEntity_DigitalChestBase(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 3,
+ new String[] { "This Chest stores " + GT_Utility.formatNumbers(commonSizeCompute(aTier)) + " Blocks",
+ "Use a screwdriver to enable", "voiding items on overflow", "Will keep its contents when harvested", });
+ }
+
+ protected static int commonSizeCompute(int tier) {
+ return switch (tier) {
+ case 1 -> 4000000;
+ case 2 -> 8000000;
+ case 3 -> 16000000;
+ case 4 -> 32000000;
+ case 5 -> 64000000;
+ case 6 -> 128000000;
+ case 7 -> 256000000;
+ case 8 -> 512000000;
+ case 9 -> 1024000000;
+ case 10 -> 2147483640;
+ default -> 0;
+ };
+ }
+
+ public GT_MetaTileEntity_DigitalChestBase(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 3, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_DigitalChestBase(String aName, int aTier, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, 3, aDescription, aTextures);
+ }
+
+ @Override
+ public void addAdditionalTooltipInformation(ItemStack stack, List<String> tooltip) {
+ if (stack.hasTagCompound() && stack.stackTagCompound.hasKey("mItemStack")) {
+ final ItemStack tContents = ItemStack
+ .loadItemStackFromNBT(stack.stackTagCompound.getCompoundTag("mItemStack"));
+ final int tSize = stack.stackTagCompound.getInteger("mItemCount");
+ if (tContents != null && tSize > 0) {
+ tooltip.add(
+ GT_LanguageManager.addStringLocalization("TileEntity_CHEST_INFO", "Contains Item: ")
+ + EnumChatFormatting.YELLOW
+ + tContents.getDisplayName()
+ + EnumChatFormatting.GRAY);
+ tooltip.add(
+ GT_LanguageManager.addStringLocalization("TileEntity_CHEST_AMOUNT", "Item Amount: ")
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(tSize)
+ + EnumChatFormatting.GRAY);
+ }
+ }
+ }
+
+ public static void registerAEIntegration() {
+ appeng.api.AEApi.instance()
+ .registries()
+ .externalStorage()
+ .addExternalStorageInterface(new AE2DigitalChestHandler());
+ }
+
+ @Override
+ public void addListener(IMEMonitorHandlerReceiver<IAEItemStack> imeMonitorHandlerReceiver, Object o) {
+ if (listeners == null) listeners = new HashMap<>();
+ listeners.put(imeMonitorHandlerReceiver, o);
+ }
+
+ @Override
+ public void removeListener(IMEMonitorHandlerReceiver<IAEItemStack> imeMonitorHandlerReceiver) {
+ if (listeners == null) listeners = new HashMap<>();
+ listeners.remove(imeMonitorHandlerReceiver);
+ }
+
+ @Override
+ public appeng.api.config.AccessRestriction getAccess() {
+ return appeng.api.config.AccessRestriction.READ_WRITE;
+ }
+
+ @Override
+ public boolean isPrioritized(IAEItemStack iaeItemStack) {
+ ItemStack s = getItemStack();
+ if (s == null || iaeItemStack == null) return false;
+ return iaeItemStack.isSameType(s);
+ }
+
+ @Override
+ public boolean canAccept(IAEItemStack iaeItemStack) {
+ ItemStack s = getItemStack();
+ if (s == null || iaeItemStack == null) return true;
+ return iaeItemStack.isSameType(s);
+ }
+
+ @Override
+ public int getPriority() {
+ return 0;
+ }
+
+ @Override
+ public int getSlot() {
+ return 0;
+ }
+
+ @Override
+ public boolean validForPass(int i) {
+ return true;
+ }
+
+ protected abstract ItemStack getItemStack();
+
+ protected abstract void setItemStack(ItemStack s);
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public IItemList<IAEItemStack> getAvailableItems(final IItemList out, int iteration) {
+ ItemStack storedStack = getItemStack();
+ if (storedStack != null) {
+ AEItemStack s = AEItemStack.create(storedStack);
+ s.setStackSize(getItemCount());
+ out.add(s);
+ }
+ return out;
+ }
+
+ @Override
+ public IItemList<IAEItemStack> getStorageList() {
+ IItemList<IAEItemStack> res = new ItemList();
+ ItemStack storedStack = getItemStack();
+ if (storedStack != null) {
+ AEItemStack s = AEItemStack.create(storedStack);
+ s.setStackSize(getItemCount());
+ res.add(s);
+ }
+ return res;
+ }
+
+ protected abstract int getItemCount();
+
+ @Override
+ public abstract void setItemCount(int aCount);
+
+ @Override
+ public int getMaxItemCount() {
+ return commonSizeCompute(mTier);
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ return new ITexture[0][0][0];
+ }
+
+ @Override
+ public IAEItemStack injectItems(final IAEItemStack input, final appeng.api.config.Actionable mode,
+ final appeng.api.networking.security.BaseActionSource src) {
+ if (getBaseMetaTileEntity() == null) return input;
+
+ final ItemStack inputStack = input.getItemStack();
+ final int maxCapacity = getMaxItemCount();
+ final int itemCount = getItemCount();
+ final long toAdd = input.getStackSize();
+ final ItemStack storedStack = getItemStack();
+
+ if (storedStack != null && !GT_Utility.areStacksEqual(storedStack, inputStack)) {
+ // Can't stack with existing item, just return the input.
+ return input;
+ }
+
+ // Number of items not added because there's too much to add.
+ final long notAdded = itemCount + toAdd - maxCapacity;
+
+ if (mode == appeng.api.config.Actionable.MODULATE) {
+ final int newCount = (int) Math.min((long) maxCapacity, itemCount + toAdd);
+
+ if (storedStack == null) {
+ setItemStack(inputStack.copy());
+ }
+ setItemCount(newCount);
+ getBaseMetaTileEntity().markDirty();
+ }
+ if (mVoidOverflow || notAdded <= 0) {
+ return null;
+ } else {
+ return input.copy()
+ .setStackSize(notAdded);
+ }
+ }
+
+ @Override
+ public IAEItemStack extractItems(final IAEItemStack request, final appeng.api.config.Actionable mode,
+ final appeng.api.networking.security.BaseActionSource src) {
+ if (request.isSameType(getItemStack())) {
+ if (getBaseMetaTileEntity() == null) return null;
+ if (mode != appeng.api.config.Actionable.SIMULATE) getBaseMetaTileEntity().markDirty();
+ if (request.getStackSize() >= getItemCount()) {
+ AEItemStack result = AEItemStack.create(getItemStack());
+ result.setStackSize(getItemCount());
+ if (mode != appeng.api.config.Actionable.SIMULATE) setItemCount(0);
+ return result;
+ } else {
+ if (mode != appeng.api.config.Actionable.SIMULATE)
+ setItemCount(getItemCount() - (int) request.getStackSize());
+ return request.copy();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public StorageChannel getChannel() {
+ return StorageChannel.ITEMS;
+ }
+
+ @Override
+ public final void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ mVoidOverflow = !mVoidOverflow;
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ StatCollector.translateToLocal(
+ mVoidOverflow ? "GT5U.machines.digitalchest.voidoverflow.enabled"
+ : "GT5U.machines.digitalchest.voidoverflow.disabled"));
+ }
+
+ @Override
+ public boolean onSolderingToolRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer,
+ float aX, float aY, float aZ) {
+ if (super.onSolderingToolRightClick(side, wrenchingSide, aPlayer, aX, aY, aZ)) return true;
+ mDisableFilter = !mDisableFilter;
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ StatCollector.translateToLocal(
+ mDisableFilter ? "GT5U.machines.digitalchest.inputfilter.disabled"
+ : "GT5U.machines.digitalchest.inputfilter.enabled"));
+ return true;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) {
+
+ if (getBaseMetaTileEntity().isServerSide() && getBaseMetaTileEntity().isAllowedToWork()) {
+ if ((getItemCount() <= 0)) {
+ setItemStack(null);
+ setItemCount(0);
+ }
+ if (getItemStack() == null && mInventory[0] != null) {
+ setItemStack(mInventory[0].copy());
+ }
+ int count = getItemCount();
+ ItemStack stack = getItemStack();
+ int savedCount = count;
+
+ if ((mInventory[0] != null) && ((count < getMaxItemCount()) || mVoidOverflow)
+ && GT_Utility.areStacksEqual(mInventory[0], stack)) {
+ count += mInventory[0].stackSize;
+ if (count <= getMaxItemCount()) {
+ mInventory[0] = null;
+ } else {
+ if (mVoidOverflow) {
+ mInventory[0] = null;
+ } else {
+ mInventory[0].stackSize = (count - getMaxItemCount());
+ }
+ count = getMaxItemCount();
+ }
+ }
+ if (mInventory[1] == null && stack != null) {
+ mInventory[1] = stack.copy();
+ mInventory[1].stackSize = Math.min(stack.getMaxStackSize(), count);
+ count -= mInventory[1].stackSize;
+ } else if ((count > 0) && GT_Utility.areStacksEqual(mInventory[1], stack)
+ && mInventory[1].getMaxStackSize() > mInventory[1].stackSize) {
+ int tmp = Math.min(count, mInventory[1].getMaxStackSize() - mInventory[1].stackSize);
+ mInventory[1].stackSize += tmp;
+ count -= tmp;
+ }
+ setItemCount(count);
+ if (stack != null) {
+ mInventory[2] = stack.copy();
+ mInventory[2].stackSize = Math.min(stack.getMaxStackSize(), count);
+ } else {
+ mInventory[2] = null;
+ }
+
+ notifyListeners(count - savedCount, stack);
+ if (count != savedCount) getBaseMetaTileEntity().markDirty();
+ }
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return aIndex != 2;
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public int getProgresstime() {
+ return getItemCount() + (mInventory[0] == null ? 0 : mInventory[0].stackSize)
+ + (mInventory[1] == null ? 0 : mInventory[1].stackSize);
+ }
+
+ @Override
+ public int maxProgresstime() {
+ return getMaxItemCount();
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return true;
+ }
+
+ @Override
+ public String[] getInfoData() {
+
+ if (getItemStack() == null) {
+ return new String[] { EnumChatFormatting.BLUE + chestName() + EnumChatFormatting.RESET, "Stored Items:",
+ EnumChatFormatting.GOLD + "No Items" + EnumChatFormatting.RESET,
+ EnumChatFormatting.GREEN + "0"
+ + EnumChatFormatting.RESET
+ + " "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(getMaxItemCount())
+ + EnumChatFormatting.RESET };
+ }
+ return new String[] { EnumChatFormatting.BLUE + chestName() + EnumChatFormatting.RESET, "Stored Items:",
+ EnumChatFormatting.GOLD + getItemStack().getDisplayName() + EnumChatFormatting.RESET,
+ EnumChatFormatting.GREEN + GT_Utility.formatNumbers(getItemCount())
+ + EnumChatFormatting.RESET
+ + " "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(getMaxItemCount())
+ + EnumChatFormatting.RESET };
+ }
+
+ @Override
+ public ItemStack[] getStoredItemData() {
+ return mInventory;
+ }
+
+ protected abstract String chestName();
+
+ private void notifyListeners(int count, ItemStack stack) {
+ if (listeners == null) {
+ listeners = new HashMap<>();
+ return;
+ }
+ if (count == 0 || stack == null) return;
+ ItemList change = new ItemList();
+ AEItemStack s = AEItemStack.create(stack);
+ s.setStackSize(count);
+ change.add(s);
+ listeners.forEach((l, o) -> {
+ if (l.isValid(o)) l.postChange(this, change, null);
+ else removeListener(l);
+ });
+ }
+
+ private boolean hasActiveMEConnection() {
+ if (listeners == null || listeners.isEmpty()) return false;
+ for (Map.Entry<IMEMonitorHandlerReceiver<IAEItemStack>, Object> e : listeners.entrySet()) {
+ if ((e.getKey() instanceof appeng.api.parts.IPart)) {
+ appeng.api.networking.IGridNode n = ((appeng.api.parts.IPart) e.getKey()).getGridNode();
+ if (n != null && n.isActive()) return true;
+ }
+ }
+ // if there are no active storage buses - clear the listeners
+ listeners.clear();
+ return false;
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ aNBT.setInteger("mItemCount", getItemCount());
+ if (getItemStack() != null) aNBT.setTag("mItemStack", getItemStack().writeToNBT(new NBTTagCompound()));
+ aNBT.setBoolean("mVoidOverflow", mVoidOverflow);
+ aNBT.setBoolean("mDisableFilter", mDisableFilter);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ if (aNBT.hasKey("mItemCount")) setItemCount(aNBT.getInteger("mItemCount"));
+ if (aNBT.hasKey("mItemStack"))
+ setItemStack(ItemStack.loadItemStackFromNBT((NBTTagCompound) aNBT.getTag("mItemStack")));
+ mVoidOverflow = aNBT.getBoolean("mVoidOverflow");
+ mDisableFilter = aNBT.getBoolean("mDisableFilter");
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ if (GT_Values.disableDigitalChestsExternalAccess && hasActiveMEConnection()) return false;
+ return aIndex == 1;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ if (GT_Values.disableDigitalChestsExternalAccess && hasActiveMEConnection()) return false;
+ if (aIndex != 0) return false;
+ if ((mInventory[0] != null && !GT_Utility.areStacksEqual(mInventory[0], aStack))) return false;
+ if (mDisableFilter) return true;
+ if (getItemStack() == null) return mInventory[1] == null || GT_Utility.areStacksEqual(mInventory[1], aStack);
+ return GT_Utility.areStacksEqual(getItemStack(), aStack);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side != aFacing) return new ITexture[] { MACHINE_CASINGS[mTier][colorIndex + 1] };
+ return new ITexture[] { MACHINE_CASINGS[mTier][colorIndex + 1], TextureFactory.of(OVERLAY_SCHEST),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_SCHEST_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ super.getWailaBody(itemStack, currenttip, accessor, config);
+ final NBTTagCompound tag = accessor.getNBTData();
+ if (tag.hasKey("itemType", Constants.NBT.TAG_COMPOUND)) {
+ currenttip.add("Item Count: " + GT_Utility.parseNumberToString(tag.getInteger("itemCount")));
+ currenttip.add(
+ "Item Type: " + ItemStack.loadItemStackFromNBT(tag.getCompoundTag("itemType"))
+ .getDisplayName());
+ } else {
+ currenttip.add("Chest Empty");
+ }
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ ItemStack is = getItemStack();
+ if (GT_Utility.isStackInvalid(is)) return;
+ int realItemCount = getItemCount();
+ if (GT_Utility.isStackValid(mInventory[1]) && GT_Utility.areStacksEqual(mInventory[1], is))
+ realItemCount += mInventory[1].stackSize;
+ tag.setInteger("itemCount", realItemCount);
+ tag.setTag("itemType", is.writeToNBT(new NBTTagCompound()));
+ }
+
+ protected static final NumberFormatMUI numberFormat = new NumberFormatMUI();
+ protected int clientItemCount;
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ builder.widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_SCREEN_BLACK)
+ .setPos(7, 16)
+ .setSize(71, 45))
+ .widget(
+ new SlotWidget(inventoryHandler, 0)
+ .setBackground(getGUITextureSet().getItemSlot(), GT_UITextures.OVERLAY_SLOT_IN)
+ .setPos(79, 16))
+ .widget(
+ new SlotWidget(inventoryHandler, 1).setAccess(true, false)
+ .setBackground(getGUITextureSet().getItemSlot(), GT_UITextures.OVERLAY_SLOT_OUT)
+ .setPos(79, 52))
+ .widget(
+ SlotWidget.phantom(inventoryHandler, 2)
+ .disableInteraction()
+ .setBackground(GT_UITextures.TRANSPARENT)
+ .setPos(59, 42))
+ .widget(
+ new TextWidget("Item Amount").setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setPos(10, 20))
+ .widget(
+ new TextWidget().setStringSupplier(() -> numberFormat.format(clientItemCount))
+ .setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setPos(10, 30))
+ .widget(
+ new FakeSyncWidget.IntegerSyncer(
+ () -> this instanceof GT_MetaTileEntity_QuantumChest
+ ? ((GT_MetaTileEntity_QuantumChest) this).mItemCount
+ : 0,
+ value -> clientItemCount = value));
+
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_DigitalTankBase.java b/src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_DigitalTankBase.java
new file mode 100644
index 0000000000..cd609af343
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_DigitalTankBase.java
@@ -0,0 +1,694 @@
+package gregtech.common.tileentities.storage;
+
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASINGS;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_QTANK;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_QTANK_GLOW;
+import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY;
+import static gregtech.api.util.GT_Utility.formatNumbers;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidRegistry;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.FluidTankInfo;
+import net.minecraftforge.fluids.IFluidHandler;
+
+import com.gtnewhorizons.modularui.api.NumberFormatMUI;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.CycleButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.FluidSlotWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IFluidLockable;
+import gregtech.api.interfaces.modularui.IAddGregtechLogo;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicTank;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_LanguageManager;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.gui.modularui.widget.FluidLockWidget;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public abstract class GT_MetaTileEntity_DigitalTankBase extends GT_MetaTileEntity_BasicTank
+ implements IFluidLockable, IAddUIWidgets, IAddGregtechLogo {
+
+ public boolean mOutputFluid = false, mVoidFluidPart = false, mVoidFluidFull = false, mLockFluid = false;
+ protected String lockedFluidName = null;
+ public boolean mAllowInputFromOutputSide = false;
+
+ public GT_MetaTileEntity_DigitalTankBase(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 3,
+ new String[] {
+ StatCollector.translateToLocalFormatted(
+ "GT5U.machines.digitaltank.tooltip",
+ formatNumbers(commonSizeCompute(aTier))),
+ StatCollector.translateToLocal("GT5U.machines.digitaltank.tooltip1"), });
+ }
+
+ protected static int commonSizeCompute(int tier) {
+ return switch (tier) {
+ case 1 -> 4000000;
+ case 2 -> 8000000;
+ case 3 -> 16000000;
+ case 4 -> 32000000;
+ case 5 -> 64000000;
+ case 6 -> 128000000;
+ case 7 -> 256000000;
+ case 8 -> 512000000;
+ case 9 -> 1024000000;
+ case 10 -> 2147483640;
+ default -> 0;
+ };
+ }
+
+ private static int tierPump(int tier) {
+ return switch (tier) {
+ case 1 -> 2;
+ case 2 -> 3;
+ case 3 -> 3;
+ case 4 -> 4;
+ case 5 -> 4;
+ case 6 -> 5;
+ case 7 -> 5;
+ case 8 -> 6;
+ case 9 -> 7;
+ case 10 -> 8;
+ default -> 0;
+ };
+ }
+
+ public GT_MetaTileEntity_DigitalTankBase(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 3, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_DigitalTankBase(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 3, aDescription, aTextures);
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ return new ITexture[0][0][0];
+ }
+
+ @Override
+ public void addAdditionalTooltipInformation(ItemStack stack, List<String> tooltip) {
+ if (stack.hasTagCompound()
+ && (stack.stackTagCompound.hasKey("mFluid") || stack.stackTagCompound.hasKey("lockedFluidName"))) {
+ final FluidStack tContents = FluidStack
+ .loadFluidStackFromNBT(stack.stackTagCompound.getCompoundTag("mFluid"));
+ if (tContents != null && tContents.amount > 0) {
+ tooltip.add(
+ GT_LanguageManager.addStringLocalization("TileEntity_TANK_INFO", "Contains Fluid: ")
+ + EnumChatFormatting.YELLOW
+ + tContents.getLocalizedName()
+ + EnumChatFormatting.GRAY);
+ tooltip.add(
+ GT_LanguageManager.addStringLocalization("TileEntity_TANK_AMOUNT", "Fluid Amount: ")
+ + EnumChatFormatting.GREEN
+ + formatNumbers(tContents.amount)
+ + " L"
+ + EnumChatFormatting.GRAY);
+ } else if (stack.stackTagCompound.hasKey("lockedFluidName")) {
+ String fluidName = stack.stackTagCompound.getString("lockedFluidName");
+ Fluid fluid = FluidRegistry.getFluid(fluidName);
+ if (fluid == null) return;
+ // noinspection deprecation
+ tooltip.add(
+ StatCollector.translateToLocalFormatted(
+ "GT5U.item.tank.locked_to",
+ EnumChatFormatting.YELLOW + fluid.getLocalizedName()));
+ }
+ }
+ }
+
+ @Override
+ public void setItemNBT(NBTTagCompound aNBT) {
+ if (mFluid != null && mFluid.amount >= 0) {
+ aNBT.setTag("mFluid", mFluid.writeToNBT(new NBTTagCompound()));
+ }
+ if (mOutputFluid) aNBT.setBoolean("mOutputFluid", true);
+ if (mVoidFluidPart) aNBT.setBoolean("mVoidOverflow", true);
+ if (mVoidFluidFull) aNBT.setBoolean("mVoidFluidFull", true);
+ if (mLockFluid) aNBT.setBoolean("mLockFluid", true);
+ if (mLockFluid && GT_Utility.isStringValid(lockedFluidName)) aNBT.setString("lockedFluidName", lockedFluidName);
+ if (this.mAllowInputFromOutputSide) aNBT.setBoolean("mAllowInputFromOutputSide", true);
+
+ super.setItemNBT(aNBT);
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setBoolean("mOutputFluid", this.mOutputFluid);
+ aNBT.setBoolean("mVoidOverflow", this.mVoidFluidPart);
+ aNBT.setBoolean("mVoidFluidFull", this.mVoidFluidFull);
+ aNBT.setBoolean("mLockFluid", mLockFluid);
+ if (mLockFluid && GT_Utility.isStringValid(lockedFluidName)) aNBT.setString("lockedFluidName", lockedFluidName);
+ else aNBT.removeTag("lockedFluidName");
+ aNBT.setBoolean("mAllowInputFromOutputSide", this.mAllowInputFromOutputSide);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ mOutputFluid = aNBT.getBoolean("mOutputFluid");
+ mVoidFluidPart = aNBT.getBoolean("mVoidOverflow");
+ mVoidFluidFull = aNBT.getBoolean("mVoidFluidFull");
+ mLockFluid = aNBT.getBoolean("mLockFluid");
+ if (mLockFluid) {
+ setLockedFluidName(aNBT.getString("lockedFluidName"));
+ } else {
+ setLockedFluidName(null);
+ }
+ mAllowInputFromOutputSide = aNBT.getBoolean("mAllowInputFromOutputSide");
+ }
+
+ @Override
+ public boolean isFluidInputAllowed(FluidStack aFluid) {
+ return !mLockFluid || lockedFluidName == null
+ || lockedFluidName.equals(
+ aFluid.getFluid()
+ .getName());
+ }
+
+ @Override
+ public boolean isFluidChangingAllowed() {
+ return !mLockFluid || lockedFluidName == null;
+ }
+
+ @Override
+ public void onEmptyingContainerWhenEmpty() {
+ if (this.lockedFluidName == null && this.mFluid != null && isFluidLocked()) {
+ setLockedFluidName(
+ this.mFluid.getFluid()
+ .getName());
+ }
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public boolean doesFillContainers() {
+ return true;
+ }
+
+ @Override
+ public boolean doesEmptyContainers() {
+ return true;
+ }
+
+ @Override
+ public boolean canTankBeFilled() {
+ return true;
+ }
+
+ @Override
+ public boolean canTankBeEmptied() {
+ return true;
+ }
+
+ @Override
+ public boolean displaysItemStack() {
+ return true;
+ }
+
+ @Override
+ public boolean displaysStackSize() {
+ return false;
+ }
+
+ @Override
+ public void setLockedFluidName(String lockedFluidName) {
+ lockedFluidName = GT_Utility.isStringInvalid(lockedFluidName) ? null : lockedFluidName;
+ this.lockedFluidName = lockedFluidName;
+ if (lockedFluidName != null) {
+ Fluid fluid = FluidRegistry.getFluid(lockedFluidName);
+ if (fluid != null) {
+ // create new FluidStack, otherwise existing 0-amount FluidStack will
+ // prevent new fluid from being locked
+ setFillableStack(new FluidStack(fluid, getFluidAmount()));
+ mLockFluid = true;
+ }
+ }
+ // Don't unlock if lockedFluidName == null,
+ // as player might explicitly enable fluid locking with no fluid contained
+ }
+
+ @Override
+ public String getLockedFluidName() {
+ return this.lockedFluidName;
+ }
+
+ @Override
+ public void lockFluid(boolean lock) {
+ this.mLockFluid = lock;
+ if (!lock) {
+ setLockedFluidName(null);
+ }
+ }
+
+ @Override
+ public boolean isFluidLocked() {
+ return this.mLockFluid;
+ }
+
+ @Override
+ public boolean acceptsFluidLock(String name) {
+ if (name == null || getFluidAmount() == 0) return true;
+ return mFluid != null && mFluid.getFluid()
+ .getName()
+ .equals(name);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ if (sideDirection != ForgeDirection.UP) {
+ if (sideDirection == baseMetaTileEntity.getFrontFacing()) {
+ return new ITexture[] { MACHINE_CASINGS[mTier][colorIndex + 1], TextureFactory.of(OVERLAY_PIPE) };
+ } else return new ITexture[] { MACHINE_CASINGS[mTier][colorIndex + 1] };
+ }
+ return new ITexture[] { MACHINE_CASINGS[mTier][colorIndex + 1], TextureFactory.of(OVERLAY_QTANK),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_QTANK_GLOW)
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+
+ @Override
+ public final void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ if (side == getBaseMetaTileEntity().getFrontFacing()) {
+ mAllowInputFromOutputSide = !mAllowInputFromOutputSide;
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ mAllowInputFromOutputSide ? GT_Utility.getTrans("095") : GT_Utility.getTrans("096"));
+ }
+ }
+
+ @Override
+ public FluidStack setFillableStack(FluidStack aFluid) {
+ mFluid = aFluid;
+ if (mFluid != null) {
+ mFluid.amount = Math.min(mFluid.amount, getRealCapacity());
+ }
+ return mFluid;
+ }
+
+ @Override
+ public FluidStack setDrainableStack(FluidStack aFluid) {
+ mFluid = aFluid;
+ if (mFluid != null) {
+ mFluid.amount = Math.min(mFluid.amount, getRealCapacity());
+ }
+ return mFluid;
+ }
+
+ @Override
+ public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aBaseMetaTileEntity.isServerSide()) {
+ if (isFluidChangingAllowed() && getFillableStack() != null && getFillableStack().amount <= 0)
+ setFillableStack(null);
+
+ if (mVoidFluidFull && getFillableStack() != null) {
+ mVoidFluidPart = false;
+ mLockFluid = false;
+ setFillableStack(null);
+ }
+
+ if (doesEmptyContainers()) {
+ FluidStack tFluid = GT_Utility.getFluidForFilledItem(mInventory[getInputSlot()], true);
+ if (tFluid != null && isFluidInputAllowed(tFluid)) {
+ if (getFillableStack() == null) {
+ if (isFluidInputAllowed(tFluid)) {
+ if ((tFluid.amount <= getRealCapacity()) || mVoidFluidPart) {
+ tFluid = tFluid.copy();
+ if (aBaseMetaTileEntity.addStackToSlot(
+ getOutputSlot(),
+ GT_Utility.getContainerForFilledItem(mInventory[getInputSlot()], true),
+ 1)) {
+ setFillableStack(tFluid);
+ this.onEmptyingContainerWhenEmpty();
+ aBaseMetaTileEntity.decrStackSize(getInputSlot(), 1);
+ }
+ }
+ }
+ } else {
+ if (tFluid.isFluidEqual(getFillableStack())) {
+ if ((((long) tFluid.amount + getFillableStack().amount) <= (long) getRealCapacity())
+ || mVoidFluidPart
+ || mVoidFluidFull) {
+ if (aBaseMetaTileEntity.addStackToSlot(
+ getOutputSlot(),
+ GT_Utility.getContainerForFilledItem(mInventory[getInputSlot()], true),
+ 1)) {
+ getFillableStack().amount += Math
+ .min(tFluid.amount, getRealCapacity() - getFillableStack().amount);
+ aBaseMetaTileEntity.decrStackSize(getInputSlot(), 1);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (doesFillContainers()) {
+ ItemStack tOutput = GT_Utility
+ .fillFluidContainer(getDrainableStack(), mInventory[getInputSlot()], false, true);
+ if (tOutput != null && aBaseMetaTileEntity.addStackToSlot(getOutputSlot(), tOutput, 1)) {
+ FluidStack tFluid = GT_Utility.getFluidForFilledItem(tOutput, true);
+ aBaseMetaTileEntity.decrStackSize(getInputSlot(), 1);
+ if (tFluid != null) getDrainableStack().amount -= tFluid.amount;
+ if (getDrainableStack().amount <= 0 && isFluidChangingAllowed()) setDrainableStack(null);
+ }
+ }
+ }
+ }
+
+ @Override
+ public int fill(FluidStack aFluid, boolean doFill) {
+ if (aFluid == null || aFluid.getFluid()
+ .getID() <= 0 || aFluid.amount <= 0 || !canTankBeFilled() || !isFluidInputAllowed(aFluid)) return 0;
+ if (getFillableStack() != null && !getFillableStack().isFluidEqual(aFluid)) {
+ return 0;
+ }
+
+ FluidStack fillableStack = getFillableStack();
+ if (fillableStack == null) {
+ fillableStack = aFluid.copy();
+ fillableStack.amount = 0;
+ }
+
+ int amount = Math.min(aFluid.amount, getRealCapacity() - fillableStack.amount);
+ if (doFill) {
+ fillableStack.amount += amount;
+ if (getFillableStack() == null) setFillableStack(fillableStack);
+ getBaseMetaTileEntity().markDirty();
+ }
+ return (mVoidFluidPart || mVoidFluidFull) ? aFluid.amount : amount;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ if (aBaseMetaTileEntity.isServerSide()) {
+ if (mOutputFluid && getDrainableStack() != null && (aTick % 20 == 0)) {
+ IFluidHandler tTank = aBaseMetaTileEntity.getITankContainerAtSide(aBaseMetaTileEntity.getFrontFacing());
+ if (tTank != null) {
+ FluidStack tDrained = drain(20 * (1 << (3 + 2 * tierPump(mTier))), false);
+ if (tDrained != null) {
+ int tFilledAmount = tTank.fill(aBaseMetaTileEntity.getBackFacing(), tDrained, false);
+ if (tFilledAmount > 0)
+ tTank.fill(aBaseMetaTileEntity.getBackFacing(), drain(tFilledAmount, true), true);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection side) {
+ return true;
+ }
+
+ @Override
+ public boolean isInputFacing(ForgeDirection side) {
+ return true;
+ }
+
+ @Override
+ public boolean isOutputFacing(ForgeDirection side) {
+ return false;
+ }
+
+ @Override
+ public boolean isLiquidInput(ForgeDirection side) {
+ return mAllowInputFromOutputSide || side != getBaseMetaTileEntity().getFrontFacing();
+ }
+
+ @Override
+ public boolean isLiquidOutput(ForgeDirection side) {
+ return true;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public int getTankPressure() {
+ return 100;
+ }
+
+ public boolean allowOverflow() {
+ return mVoidFluidPart || mVoidFluidFull;
+ }
+
+ @Override
+ public int getCapacity() {
+ return allowOverflow() ? Integer.MAX_VALUE : getRealCapacity();
+ }
+
+ @Override
+ public int getRealCapacity() {
+ return commonSizeCompute(mTier);
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return true;
+ }
+
+ @Override
+ public FluidTankInfo getInfo() {
+ return new FluidTankInfo(getFluid(), getCapacity());
+ }
+
+ @Override
+ public FluidTankInfo[] getTankInfo(ForgeDirection side) {
+ return new FluidTankInfo[] { getInfo() };
+ }
+
+ @Nonnull
+ public FluidTankInfo[] getRealTankInfo(ForgeDirection side) {
+ return new FluidTankInfo[] { new FluidTankInfo(getFluid(), getRealCapacity()) };
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ super.getWailaBody(itemStack, currenttip, accessor, config);
+
+ NBTTagCompound tag = accessor.getNBTData();
+ FluidStack fluid = tag.hasKey("mFluid") ? FluidStack.loadFluidStackFromNBT(tag.getCompoundTag("mFluid")) : null;
+ if (fluid != null && fluid.amount >= 0) {
+ currenttip.remove(0);
+ currenttip.add(
+ 0,
+ formatNumbers(fluid.amount) + " / "
+ + formatNumbers(getRealCapacity())
+ + " L "
+ + fluid.getLocalizedName());
+ } else {
+ currenttip.add(0, "Tank Empty");
+ }
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ FluidStack fluid = getFluid();
+ if (fluid != null) tag.setTag("mFluid", fluid.writeToNBT(new NBTTagCompound()));
+ else if (tag.hasKey("mFluid")) tag.removeTag("mFluid");
+ }
+
+ protected static final NumberFormatMUI numberFormat = new NumberFormatMUI();
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ fluidTank.setAllowOverflow(allowOverflow());
+ fluidTank.setPreventDraining(mLockFluid);
+
+ FluidSlotWidget fluidSlotWidget = new FluidSlotWidget(fluidTank);
+ builder.widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_SCREEN_BLACK)
+ .setPos(7, 16)
+ .setSize(71, 45))
+ .widget(
+ new SlotWidget(inventoryHandler, getInputSlot())
+ .setBackground(getGUITextureSet().getItemSlot(), GT_UITextures.OVERLAY_SLOT_IN)
+ .setPos(79, 16))
+ .widget(
+ new SlotWidget(inventoryHandler, getOutputSlot()).setAccess(true, false)
+ .setBackground(getGUITextureSet().getItemSlot(), GT_UITextures.OVERLAY_SLOT_OUT)
+ .setPos(79, 43))
+ .widget(
+ fluidSlotWidget.setOnClickContainer(widget -> onEmptyingContainerWhenEmpty())
+ .setBackground(GT_UITextures.TRANSPARENT)
+ .setPos(58, 41))
+ .widget(
+ new TextWidget(StatCollector.translateToLocal("GT5U.machines.digitaltank.fluid.amount"))
+ .setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setPos(10, 20))
+ .widget(
+ new TextWidget().setStringSupplier(() -> numberFormat.format(mFluid != null ? mFluid.amount : 0))
+ .setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setPos(10, 30))
+ .widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_SCREEN_BLACK)
+ .setPos(98, 16)
+ .setSize(71, 45))
+ .widget(new FluidLockWidget(this).setPos(149, 41))
+ .widget(
+ new TextWidget(StatCollector.translateToLocal("GT5U.machines.digitaltank.lockfluid.label"))
+ .setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setPos(101, 20))
+ .widget(TextWidget.dynamicString(() -> {
+ FluidStack fluidStack = FluidRegistry.getFluidStack(lockedFluidName, 1);
+ return fluidStack != null ? fluidStack.getLocalizedName()
+ : StatCollector.translateToLocal("GT5U.machines.digitaltank.lockfluid.empty");
+ })
+ .setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setTextAlignment(Alignment.CenterLeft)
+ .setMaxWidth(65)
+ .setPos(101, 30))
+ .widget(new CycleButtonWidget().setToggle(() -> mOutputFluid, val -> {
+ mOutputFluid = val;
+ if (!mOutputFluid) {
+ GT_Utility.sendChatToPlayer(
+ buildContext.getPlayer(),
+ GT_Utility.trans("262", "Fluid Auto Output Disabled"));
+ } else {
+ GT_Utility.sendChatToPlayer(
+ buildContext.getPlayer(),
+ GT_Utility.trans("263", "Fluid Auto Output Enabled"));
+ }
+ })
+ .setVariableBackground(GT_UITextures.BUTTON_STANDARD_TOGGLE)
+ .setStaticTexture(GT_UITextures.OVERLAY_BUTTON_AUTOOUTPUT_FLUID)
+ .setGTTooltip(() -> mTooltipCache.getData("GT5U.machines.digitaltank.autooutput.tooltip"))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setPos(7, 63)
+ .setSize(18, 18))
+ .widget(new CycleButtonWidget().setToggle(() -> mLockFluid, val -> {
+ lockFluid(val);
+ fluidTank.setPreventDraining(mLockFluid);
+
+ String inBrackets;
+ if (mLockFluid) {
+ if (mFluid == null) {
+ setLockedFluidName(null);
+ inBrackets = GT_Utility
+ .trans("264", "currently none, will be locked to the next that is put in");
+ } else {
+ setLockedFluidName(
+ getDrainableStack().getFluid()
+ .getName());
+ inBrackets = getDrainableStack().getLocalizedName();
+ }
+ GT_Utility.sendChatToPlayer(
+ buildContext.getPlayer(),
+ String.format("%s (%s)", GT_Utility.trans("265", "1 specific Fluid"), inBrackets));
+ } else {
+ fluidTank.drain(0, true);
+ GT_Utility.sendChatToPlayer(
+ buildContext.getPlayer(),
+ GT_Utility.trans("266", "Lock Fluid Mode Disabled"));
+ }
+ fluidSlotWidget.notifyTooltipChange();
+ })
+ .setVariableBackground(GT_UITextures.BUTTON_STANDARD_TOGGLE)
+ .setStaticTexture(GT_UITextures.OVERLAY_BUTTON_LOCK)
+ .setGTTooltip(() -> mTooltipCache.getData("GT5U.machines.digitaltank.lockfluid.tooltip"))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setPos(25, 63)
+ .setSize(18, 18))
+ .widget(new CycleButtonWidget().setToggle(() -> mAllowInputFromOutputSide, val -> {
+ mAllowInputFromOutputSide = val;
+ if (!mAllowInputFromOutputSide) {
+ GT_Utility.sendChatToPlayer(buildContext.getPlayer(), GT_Utility.getTrans("096"));
+ } else {
+ GT_Utility.sendChatToPlayer(buildContext.getPlayer(), GT_Utility.getTrans("095"));
+ }
+ })
+ .setVariableBackground(GT_UITextures.BUTTON_STANDARD_TOGGLE)
+ .setStaticTexture(GT_UITextures.OVERLAY_BUTTON_INPUT_FROM_OUTPUT_SIDE)
+ .setGTTooltip(() -> mTooltipCache.getData("GT5U.machines.digitaltank.inputfromoutput.tooltip"))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setPos(43, 63)
+ .setSize(18, 18))
+ .widget(new CycleButtonWidget().setToggle(() -> mVoidFluidPart, val -> {
+ mVoidFluidPart = val;
+ fluidTank.setAllowOverflow(allowOverflow());
+ if (!mVoidFluidPart) {
+ GT_Utility.sendChatToPlayer(
+ buildContext.getPlayer(),
+ GT_Utility.trans("267", "Overflow Voiding Mode Disabled"));
+ } else {
+ GT_Utility.sendChatToPlayer(
+ buildContext.getPlayer(),
+ GT_Utility.trans("268", "Overflow Voiding Mode Enabled"));
+ }
+ })
+ .setVariableBackground(GT_UITextures.BUTTON_STANDARD_TOGGLE)
+ .setStaticTexture(GT_UITextures.OVERLAY_BUTTON_TANK_VOID_EXCESS)
+ .setGTTooltip(() -> mTooltipCache.getData("GT5U.machines.digitaltank.voidoverflow.tooltip"))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setPos(98, 63)
+ .setSize(18, 18))
+ .widget(new CycleButtonWidget().setToggle(() -> mVoidFluidFull, val -> {
+ mVoidFluidFull = val;
+ fluidTank.setAllowOverflow(allowOverflow());
+ if (!mVoidFluidFull) {
+ GT_Utility
+ .sendChatToPlayer(buildContext.getPlayer(), GT_Utility.trans("269", "Void Full Mode Disabled"));
+ } else {
+ GT_Utility
+ .sendChatToPlayer(buildContext.getPlayer(), GT_Utility.trans("270", "Void Full Mode Enabled"));
+ }
+ })
+ .setVariableBackground(GT_UITextures.BUTTON_STANDARD_TOGGLE)
+ .setStaticTexture(GT_UITextures.OVERLAY_BUTTON_TANK_VOID_ALL)
+ .setGTTooltip(() -> mTooltipCache.getData("GT5U.machines.digitaltank.voidfull.tooltip"))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setPos(116, 63)
+ .setSize(18, 18));
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_Locker.java b/src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_Locker.java
new file mode 100644
index 0000000000..abce9514de
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_Locker.java
@@ -0,0 +1,283 @@
+package gregtech.common.tileentities.storage;
+
+import static gregtech.api.enums.Textures.BlockIcons.LOCKERS;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASINGS;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAYS_ENERGY_IN;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_LOCKER;
+
+import java.util.List;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.enums.SoundResource;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_TieredMachineBlock;
+import gregtech.api.objects.GT_ItemStack;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Utility;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public class GT_MetaTileEntity_Locker extends GT_MetaTileEntity_TieredMachineBlock {
+
+ private static final String CHARGE_SLOT_WAILA_TAG = "charge_slot_";
+ public byte mType = 0;
+
+ public GT_MetaTileEntity_Locker(int aID, String aName, String aNameRegional, int aTier) {
+ super(aID, aName, aNameRegional, aTier, 4, "Stores and recharges Armor");
+ }
+
+ public GT_MetaTileEntity_Locker(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 4, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Locker(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 4, aDescription, aTextures);
+ }
+
+ @Override
+ public String[] getDescription() {
+ String[] desc = new String[mDescriptionArray.length + 1];
+ System.arraycopy(mDescriptionArray, 0, desc, 0, mDescriptionArray.length);
+ desc[mDescriptionArray.length] = "Click with Screwdriver to change Style";
+ return desc;
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ ITexture[][][] rTextures = new ITexture[3][17][];
+ for (byte i = -1; i < 16; i = (byte) (i + 1)) {
+ ITexture[] tmp0 = { MACHINE_CASINGS[this.mTier][(i + 1)] };
+ rTextures[0][(i + 1)] = tmp0;
+ ITexture[] tmp1 = { MACHINE_CASINGS[this.mTier][(i + 1)], OVERLAYS_ENERGY_IN[this.mTier] };
+ rTextures[1][(i + 1)] = tmp1;
+ ITexture[] tmp2 = { MACHINE_CASINGS[this.mTier][(i + 1)], TextureFactory.of(OVERLAY_LOCKER) };
+ rTextures[2][(i + 1)] = tmp2;
+ }
+ return rTextures;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ if (side == aFacing) {
+ return new ITexture[] { this.mTextures[2][(colorIndex + 1)][0], this.mTextures[2][(colorIndex + 1)][1],
+ LOCKERS[Math.abs(this.mType % LOCKERS.length)] };
+ }
+ return this.mTextures[0][(colorIndex + 1)];
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Locker(this.mName, this.mTier, this.mDescriptionArray, this.mTextures);
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return false;
+ }
+
+ @Override
+ public boolean isElectric() {
+ return true;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return (facing.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0;
+ }
+
+ @Override
+ public boolean isEnetInput() {
+ return true;
+ }
+
+ @Override
+ public boolean isInputFacing(ForgeDirection side) {
+ return side == getBaseMetaTileEntity().getBackFacing();
+ }
+
+ @Override
+ public boolean isTeleporterCompatible() {
+ return false;
+ }
+
+ @Override
+ public long maxEUStore() {
+ return gregtech.api.enums.GT_Values.V[this.mTier] * maxAmperesIn();
+ }
+
+ @Override
+ public long maxEUInput() {
+ return gregtech.api.enums.GT_Values.V[this.mTier];
+ }
+
+ @Override
+ public long maxAmperesIn() {
+ return this.mInventory.length * 2L;
+ }
+
+ @Override
+ public int rechargerSlotStartIndex() {
+ return 0;
+ }
+
+ @Override
+ public int rechargerSlotCount() {
+ return getBaseMetaTileEntity().isAllowedToWork() ? this.mInventory.length : 0;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ aNBT.setByte("mType", this.mType);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ this.mType = aNBT.getByte("mType");
+ }
+
+ @Override
+ public void onValueUpdate(byte aValue) {
+ this.mType = aValue;
+ }
+
+ @Override
+ public byte getUpdateData() {
+ return this.mType;
+ }
+
+ @Override
+ public void doSound(byte aIndex, double aX, double aY, double aZ) {
+ if (aIndex == 16) {
+ GT_Utility.doSoundAtClient(SoundResource.RANDOM_CLICK, 1, 1.0F);
+ }
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ if (side == getBaseMetaTileEntity().getFrontFacing()) {
+ this.mType = ((byte) (this.mType + 1));
+ }
+ }
+
+ @Override
+ public boolean allowCoverOnSide(ForgeDirection side, GT_ItemStack aStack) {
+ return side != getBaseMetaTileEntity().getFrontFacing();
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer, ForgeDirection side,
+ float aX, float aY, float aZ) {
+ if ((aBaseMetaTileEntity.isServerSide()) && (side == aBaseMetaTileEntity.getFrontFacing())) {
+ for (int i = 0; i < 4; i++) {
+ ItemStack tSwapStack = this.mInventory[i];
+ this.mInventory[i] = aPlayer.inventory.armorInventory[i];
+ aPlayer.inventory.armorInventory[i] = tSwapStack;
+ }
+ aPlayer.inventoryContainer.detectAndSendChanges();
+ sendSound((byte) 16);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ for (int i = 0; i < 4; i++) {
+ final ItemStack itemStack = this.mInventory[3 - i];
+
+ if (itemStack != null) {
+ tag.setTag(CHARGE_SLOT_WAILA_TAG + i, itemStack.writeToNBT(new NBTTagCompound()));
+ }
+ }
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ super.getWailaBody(itemStack, currentTip, accessor, config);
+
+ final NBTTagCompound tag = accessor.getNBTData();
+
+ for (int i = 0; i < 4; i++) {
+ final String index = GT_Utility.formatNumbers(i + 1);
+
+ if (tag.hasKey(CHARGE_SLOT_WAILA_TAG + i)) {
+ final ItemStack slotItem = ItemStack
+ .loadItemStackFromNBT(tag.getCompoundTag(CHARGE_SLOT_WAILA_TAG + i));
+ assert slotItem != null;
+
+ currentTip.add(
+ GT_ModHandler.getElectricItemCharge(slotItem)
+ .map(chargeInfo -> {
+ final float ratio = (float) chargeInfo[0] / (float) chargeInfo[1];
+ final EnumChatFormatting chargeFormat;
+
+ if (ratio == 0L) {
+ chargeFormat = EnumChatFormatting.GRAY;
+ } else if (ratio < 0.25) {
+ chargeFormat = EnumChatFormatting.RED;
+ } else if (ratio < 0.5) {
+ chargeFormat = EnumChatFormatting.GOLD;
+ } else if (ratio < 0.75) {
+ chargeFormat = EnumChatFormatting.YELLOW;
+ } else if (ratio < 1L) {
+ chargeFormat = EnumChatFormatting.GREEN;
+ } else {
+ chargeFormat = EnumChatFormatting.AQUA;
+ }
+
+ return StatCollector.translateToLocalFormatted(
+ "gt.locker.waila_armor_slot_charged",
+ index,
+ slotItem.getDisplayName(),
+ chargeFormat,
+ GT_Utility.formatNumbers(ratio * 100));
+ })
+ .orElseGet(
+ // Lazy initialization
+ () -> StatCollector.translateToLocalFormatted(
+ "gt.locker.waila_armor_slot_generic",
+ index,
+ slotItem.getDisplayName())));
+ } else {
+ currentTip.add(StatCollector.translateToLocalFormatted("gt.locker.waila_armor_slot_none", index));
+ }
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_QuantumChest.java b/src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_QuantumChest.java
new file mode 100644
index 0000000000..c830cd4e52
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_QuantumChest.java
@@ -0,0 +1,102 @@
+package gregtech.common.tileentities.storage;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+
+public class GT_MetaTileEntity_QuantumChest extends GT_MetaTileEntity_DigitalChestBase {
+
+ public int mItemCount = 0;
+ public ItemStack mItemStack = null;
+ NBTTagList mInvData = null;
+
+ public GT_MetaTileEntity_QuantumChest(int aID, String aName, String aNameRegional, int aTier) {
+ super(aID, aName, aNameRegional, aTier);
+ }
+
+ public GT_MetaTileEntity_QuantumChest(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_QuantumChest(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_QuantumChest(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public void setItemNBT(NBTTagCompound aNBT) {
+ mInvData = new NBTTagList();
+ for (int i = 0; i < 3; i++) {
+ if (mInventory[i] != null) {
+ NBTTagCompound tNBT = new NBTTagCompound();
+ tNBT.setByte("Count", (byte) mInventory[i].stackSize);
+ tNBT.setShort("Damage", (short) mInventory[i].getItemDamage());
+ tNBT.setShort("id", (short) Item.getIdFromItem(mInventory[i].getItem()));
+ tNBT.setInteger("IntSlot", i);
+ if (mInventory[i].hasTagCompound()) {
+ tNBT.setTag("tag", mInventory[i].getTagCompound());
+ }
+ mInvData.appendTag(tNBT);
+ }
+ }
+ if (mItemStack != null) aNBT.setTag("mItemStack", getItemStack().writeToNBT(new NBTTagCompound()));
+ aNBT.setTag("Inventory", mInvData);
+ aNBT.setInteger("mItemCount", getItemCount());
+ aNBT.setBoolean("mVoidOverflow", mVoidOverflow);
+
+ super.setItemNBT(aNBT);
+ }
+
+ @Override
+ protected String chestName() {
+ return "Quantum Chest";
+ }
+
+ @Override
+ protected int getItemCount() {
+ return mItemCount;
+ }
+
+ @Override
+ public void setItemCount(int aCount) {
+ mItemCount = aCount;
+ }
+
+ @Override
+ protected ItemStack getItemStack() {
+ return mItemStack;
+ }
+
+ @Override
+ protected void setItemStack(ItemStack s) {
+ mItemStack = s;
+ }
+
+ @Nullable
+ @Override
+ public List<ItemStack> getItemsForHoloGlasses() {
+ List<ItemStack> ret = new ArrayList<>();
+ ret.add(getStackInSlot(0));
+ ret.add(getStackInSlot(1));
+ if (mItemStack != null) {
+ ItemStack copy = mItemStack.copy();
+ copy.stackSize = mItemCount;
+ ret.add(copy);
+ }
+ return ret;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_QuantumTank.java b/src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_QuantumTank.java
new file mode 100644
index 0000000000..4d51d0b634
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_QuantumTank.java
@@ -0,0 +1,55 @@
+package gregtech.common.tileentities.storage;
+
+import net.minecraft.util.EnumChatFormatting;
+
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_QuantumTank extends GT_MetaTileEntity_DigitalTankBase {
+
+ public GT_MetaTileEntity_QuantumTank(int aID, String aName, String aNameRegional, int aTier) {
+ super(aID, aName, aNameRegional, aTier);
+ }
+
+ public GT_MetaTileEntity_QuantumTank(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_QuantumTank(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_QuantumTank(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public String[] getInfoData() {
+
+ if (mFluid == null) {
+ return new String[] { EnumChatFormatting.BLUE + "Quantum Tank" + EnumChatFormatting.RESET, "Stored Fluid:",
+ EnumChatFormatting.GOLD + "No Fluid" + EnumChatFormatting.RESET,
+ EnumChatFormatting.GREEN + Integer.toString(0)
+ + " L"
+ + EnumChatFormatting.RESET
+ + " "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(getCapacity())
+ + " L"
+ + EnumChatFormatting.RESET };
+ }
+ return new String[] { EnumChatFormatting.BLUE + "Quantum Tank" + EnumChatFormatting.RESET, "Stored Fluid:",
+ EnumChatFormatting.GOLD + mFluid.getLocalizedName() + EnumChatFormatting.RESET,
+ EnumChatFormatting.GREEN + GT_Utility.formatNumbers(mFluid.amount)
+ + " L"
+ + EnumChatFormatting.RESET
+ + " "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(getCapacity())
+ + " L"
+ + EnumChatFormatting.RESET };
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_SuperChest.java b/src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_SuperChest.java
new file mode 100644
index 0000000000..271402f0de
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_SuperChest.java
@@ -0,0 +1,30 @@
+package gregtech.common.tileentities.storage;
+
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+
+public class GT_MetaTileEntity_SuperChest extends GT_MetaTileEntity_QuantumChest {
+
+ public GT_MetaTileEntity_SuperChest(int aID, String aName, String aNameRegional, int aTier) {
+ super(aID, aName, aNameRegional, aTier);
+ }
+
+ public GT_MetaTileEntity_SuperChest(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_SuperChest(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ @Override
+ protected String chestName() {
+ return "Super Chest";
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_SuperChest(mName, mTier, mDescriptionArray, mTextures);
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_SuperTank.java b/src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_SuperTank.java
new file mode 100644
index 0000000000..4f0bdbc237
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/storage/GT_MetaTileEntity_SuperTank.java
@@ -0,0 +1,54 @@
+package gregtech.common.tileentities.storage;
+
+import net.minecraft.util.EnumChatFormatting;
+
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_SuperTank extends GT_MetaTileEntity_DigitalTankBase {
+
+ public GT_MetaTileEntity_SuperTank(int aID, String aName, String aNameRegional, int aTier) {
+ super(aID, aName, aNameRegional, aTier);
+ }
+
+ public GT_MetaTileEntity_SuperTank(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_SuperTank(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_SuperTank(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public String[] getInfoData() {
+
+ if (mFluid == null) {
+ return new String[] { EnumChatFormatting.BLUE + "Super Tank" + EnumChatFormatting.RESET, "Stored Fluid:",
+ EnumChatFormatting.GOLD + "No Fluid" + EnumChatFormatting.RESET,
+ EnumChatFormatting.GREEN + "0 L"
+ + EnumChatFormatting.RESET
+ + " "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(getCapacity())
+ + " L"
+ + EnumChatFormatting.RESET };
+ }
+ return new String[] { EnumChatFormatting.BLUE + "Super Tank" + EnumChatFormatting.RESET, "Stored Fluid:",
+ EnumChatFormatting.GOLD + mFluid.getLocalizedName() + EnumChatFormatting.RESET,
+ EnumChatFormatting.GREEN + GT_Utility.formatNumbers(mFluid.amount)
+ + " L"
+ + EnumChatFormatting.RESET
+ + " "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(getCapacity())
+ + " L"
+ + EnumChatFormatting.RESET };
+ }
+}