diff --git a/build.gradle b/build.gradle index 5887473d9..f7e4397b4 100644 --- a/build.gradle +++ b/build.gradle @@ -109,12 +109,12 @@ dependencies { modCompileOnly("mezz.jei:jei-$project.minecraft_version-common-api:$project.jei_version") { transitive = false } modImplementation("com.gregtechceu.gtceu:gtceu-$project.minecraft_version:$project.gtceu_version") { transitive = false } - // modImplementation("dev:gtceu:1.20.1:$project.gtceu_version") { transitive false } modImplementation("com.lowdragmc.ldlib:ldlib-forge-$project.minecraft_version:$project.ldlib_version") { transitive = false } modImplementation("com.tterrag.registrate:Registrate:$project.registrate_version") - modImplementation("dev.latvian.mods:kubejs-forge:$project.kubejs_version") + modImplementation("curse.maven:kubejs-238086:5012072") + modImplementation("curse.maven:rhino-416294:6186971") - modImplementation("maven.modrinth:jade-1.20.1-forge-$project.jade_version") + modImplementation("curse.maven:jade-324717:6855440") // Mixin Extras implementation(annotationProcessor("io.github.llamalad7:mixinextras-common:$project.mixinextras_version")) @@ -124,7 +124,7 @@ dependencies { compileOnly 'org.projectlombok:lombok:1.18.30' annotationProcessor 'org.projectlombok:lombok:1.18.30' - modImplementation 'appeng:appliedenergistics2-forge-15.2.1' + modImplementation('curse.maven:applied-energistics-2-223794:7148487') modImplementation("dev.architectury:architectury-forge:${project.architectury_version}") @@ -137,10 +137,12 @@ dependencies { modImplementation("earth.terrarium.botarium:botarium-forge-${project.minecraft_version}:${project.botarium_version}") modImplementation("earth.terrarium.adastra:ad_astra-forge-${project.minecraft_version}:${ad_astra_version}") { transitive false } - modImplementation("dev:gtmthings-1.3.4") - + modImplementation("dev:gtmthings-1.3.5.b") + modImplementation("curse.maven:spark-361579:4738952") modImplementation("curse.maven:glodium-957920:5006780") - modImplementation("dev:ExtendedAE-1.20-1.1.12-forge") + modImplementation("curse.maven:ex-pattern-provider-892005:7144417") + modImplementation("curse.maven:guideme-1173950:7127447") + modImplementation("dev:merequester-forge-1.20.1-1.1.5") modImplementation("org.moddingx:LibX:1.20.1-5.0.1") modImplementation("dev:TravelAnchors-1.20.1-5.0.1") @@ -148,12 +150,12 @@ dependencies { modCompileOnly("dev:Re-Avaritia-forged-1.20.1-1.3.8.3") // ftb-chunk - modImplementation("dev.ftb.mods:ftb-chunks-forge:2001.3.1"){ transitive = false } - modRuntimeOnly("dev.ftb.mods:ftb-library-forge:2001.2.4"){ transitive = false } - modRuntimeOnly("dev.ftb.mods:ftb-teams-forge:2001.3.0"){ transitive = false } + modImplementation("curse.maven:ftb-chunks-314906:5378090"){ transitive = false } + modRuntimeOnly("curse.maven:ftb-library-404465:5567591"){ transitive = false } + modRuntimeOnly("curse.maven:ftb-teams-404468:5267190"){ transitive = false } // ftbu - modImplementation("dev.ftb.mods:ftb-ultimine-forge:2001.1.5"){ transitive = false } + modImplementation("curse.maven:ftb-ultimine-386134:5363345"){ transitive = false } modRuntimeOnly("mezz.jei:jei-$project.minecraft_version-forge:$project.jei_version") { transitive = false } @@ -169,6 +171,8 @@ dependencies { // Modern UI core extensions modRuntimeOnly("icyllis.modernui:ModernUI-Forge:1.20.1-3.10.1.4") { transitive = false } // ModernUI + //Packet Fixer + include(modRuntimeOnly("curse.maven:packet-fixer-forge-689467:6195870")) } processResources { diff --git a/gradle.properties b/gradle.properties index 1d2533ad4..f188b47ed 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,13 +19,12 @@ mod_license=LGPLv3.0 # Dependencies gtceu_version=1.4.4 -ldlib_version=1.0.28.c +ldlib_version=1.0.29.b registrate_version=MC1.20-1.3.11 kubejs_version=2001.6.4-build.120 configuration_version=2.2.0 -jei_version=15.19.5.99 +jei_version=15.20.0.116 mixinextras_version=0.2.0 -jade_version=11.9.2 architectury_version=9.1.12 ad_astra_version=1.15.18 botarium_version=2.3.3 diff --git a/libs/ExtendedAE-1.20-1.1.12-forge.jar b/libs/ExtendedAE-1.20-1.1.12-forge.jar deleted file mode 100755 index 067194a7b..000000000 Binary files a/libs/ExtendedAE-1.20-1.1.12-forge.jar and /dev/null differ diff --git a/libs/Jade-1.20.1-forge-11.9.2.jar b/libs/Jade-1.20.1-forge-11.9.2.jar deleted file mode 100644 index f8053e7e3..000000000 Binary files a/libs/Jade-1.20.1-forge-11.9.2.jar and /dev/null differ diff --git a/libs/appliedenergistics2-forge-15.2.1.jar b/libs/appliedenergistics2-forge-15.2.1.jar deleted file mode 100644 index 178948fd7..000000000 Binary files a/libs/appliedenergistics2-forge-15.2.1.jar and /dev/null differ diff --git a/libs/ftb-chunks-forge-2001.3.1.jar b/libs/ftb-chunks-forge-2001.3.1.jar deleted file mode 100644 index dd176f751..000000000 Binary files a/libs/ftb-chunks-forge-2001.3.1.jar and /dev/null differ diff --git a/libs/ftb-library-forge-2001.2.4.jar b/libs/ftb-library-forge-2001.2.4.jar deleted file mode 100644 index f51b2f338..000000000 Binary files a/libs/ftb-library-forge-2001.2.4.jar and /dev/null differ diff --git a/libs/ftb-teams-forge-2001.3.0.jar b/libs/ftb-teams-forge-2001.3.0.jar deleted file mode 100644 index cf332320b..000000000 Binary files a/libs/ftb-teams-forge-2001.3.0.jar and /dev/null differ diff --git a/libs/ftb-ultimine-forge-2001.1.5.jar b/libs/ftb-ultimine-forge-2001.1.5.jar deleted file mode 100644 index 81c384193..000000000 Binary files a/libs/ftb-ultimine-forge-2001.1.5.jar and /dev/null differ diff --git a/libs/gtmthings-1.3.4.jar b/libs/gtmthings-1.3.5.b.jar similarity index 67% rename from libs/gtmthings-1.3.4.jar rename to libs/gtmthings-1.3.5.b.jar index 2b718e730..374ae1a05 100644 Binary files a/libs/gtmthings-1.3.4.jar and b/libs/gtmthings-1.3.5.b.jar differ diff --git a/libs/merequester-forge-1.20.1-1.1.5.jar b/libs/merequester-forge-1.20.1-1.1.5.jar new file mode 100644 index 000000000..80a1f5824 Binary files /dev/null and b/libs/merequester-forge-1.20.1-1.1.5.jar differ diff --git a/src/generated/resources/assets/gtlcore/lang/en_us.json b/src/generated/resources/assets/gtlcore/lang/en_us.json deleted file mode 100644 index 24ecc52ab..000000000 --- a/src/generated/resources/assets/gtlcore/lang/en_us.json +++ /dev/null @@ -1,152 +0,0 @@ -{ - "block.gtlcore.16m_storage": "16m Storage", - "block.gtlcore.1m_storage": "1m Storage", - "block.gtlcore.256m_storage": "256m Storage", - "block.gtlcore.4m_storage": "4m Storage", - "block.gtlcore.64m_storage": "64m Storage", - "block.gtlcore.advanced_assembly_line_unit": "Advanced Assembly Line Unit", - "block.gtlcore.advanced_compressed_fusion_coil": "Advanced Compressed Fusion Coil", - "block.gtlcore.advanced_fusion_coil": "Advanced Fusion Coil", - "block.gtlcore.advanced_stellar_containment_casing": "Advanced Stellar Containment Casing", - "block.gtlcore.aluminium_bronze_casing": "Aluminium Bronze Casing", - "block.gtlcore.antifreeze_heatproof_machine_casing": "Antifreeze Heatproof Machine Casing", - "block.gtlcore.blaze_blast_furnace_casing": "Blaze Blast Furnace Casing", - "block.gtlcore.cold_ice_casing": "Cold Ice Casing", - "block.gtlcore.component_assembly_line_casing_ev": "Component Assembly Line Casing Ev", - "block.gtlcore.component_assembly_line_casing_hv": "Component Assembly Line Casing Hv", - "block.gtlcore.component_assembly_line_casing_iv": "Component Assembly Line Casing Iv", - "block.gtlcore.component_assembly_line_casing_luv": "Component Assembly Line Casing Luv", - "block.gtlcore.component_assembly_line_casing_lv": "Component Assembly Line Casing Lv", - "block.gtlcore.component_assembly_line_casing_max": "Component Assembly Line Casing Max", - "block.gtlcore.component_assembly_line_casing_mv": "Component Assembly Line Casing Mv", - "block.gtlcore.component_assembly_line_casing_opv": "Component Assembly Line Casing Opv", - "block.gtlcore.component_assembly_line_casing_uev": "Component Assembly Line Casing Uev", - "block.gtlcore.component_assembly_line_casing_uhv": "Component Assembly Line Casing Uhv", - "block.gtlcore.component_assembly_line_casing_uiv": "Component Assembly Line Casing Uiv", - "block.gtlcore.component_assembly_line_casing_uv": "Component Assembly Line Casing Uv", - "block.gtlcore.component_assembly_line_casing_uxv": "Component Assembly Line Casing Uxv", - "block.gtlcore.component_assembly_line_casing_zpm": "Component Assembly Line Casing Zpm", - "block.gtlcore.compressed_fusion_coil": "Compressed Fusion Coil", - "block.gtlcore.compressed_fusion_coil_mk2": "Compressed Fusion Coil Mk2", - "block.gtlcore.compressed_fusion_coil_mk2_prototype": "Compressed Fusion Coil Mk2 Prototype", - "block.gtlcore.cooler": "Cooler", - "block.gtlcore.create_casing": "Create Casing", - "block.gtlcore.degenerate_rhenium_constrained_casing": "Degenerate Rhenium Constrained Casing", - "block.gtlcore.dimension_connection_casing": "Dimension Connection Casing", - "block.gtlcore.dimension_injection_casing": "Dimension Injection Casing", - "block.gtlcore.dimensionally_transcendent_casing": "Dimensionally Transcendent Casing", - "block.gtlcore.dragon_strength_tritanium_casing": "Dragon Strength Tritanium Casing", - "block.gtlcore.echo_casing": "Echo Casing", - "block.gtlcore.enhance_hyper_mechanical_casing": "Enhance Hyper Mechanical Casing", - "block.gtlcore.extreme_strength_tritanium_casing": "Extreme Strength Tritanium Casing", - "block.gtlcore.fission_fuel_assembly": "Fission Fuel Assembly", - "block.gtlcore.fission_reactor_casing": "Fission Reactor Casing", - "block.gtlcore.fusion_casing_mk4": "Fusion Casing Mk4", - "block.gtlcore.fusion_casing_mk5": "Fusion Casing Mk5", - "block.gtlcore.fusion_coil_mk2": "Fusion Coil Mk2", - "block.gtlcore.graviton_field_constraint_casing": "Graviton Field Constraint Casing", - "block.gtlcore.hsss_reinforced_borosilicate_glass": "Hsss Reinforced Borosilicate Glass", - "block.gtlcore.hyper_core": "Hyper Core", - "block.gtlcore.hyper_mechanical_casing": "Hyper Mechanical Casing", - "block.gtlcore.improved_superconductor_coil": "Improved Superconductor Coil", - "block.gtlcore.infinity_glass": "Infinity Glass", - "block.gtlcore.iridium_casing": "Iridium Casing", - "block.gtlcore.lafium_mechanical_casing": "Lafium Mechanical Casing", - "block.gtlcore.law_filter_casing": "Law Filter Casing", - "block.gtlcore.manipulator": "Manipulator", - "block.gtlcore.max_storage": "Max Storage", - "block.gtlcore.molecular_casing": "Molecular Casing", - "block.gtlcore.multi_functional_casing": "Multi Functional Casing", - "block.gtlcore.naquadah_alloy_casing": "Naquadah Alloy Casing", - "block.gtlcore.opv_hermetic_casing": "Hermetic Casing XIII", - "block.gtlcore.oxidation_resistant_hastelloy_n_mechanical_casing": "Oxidation Resistant Hastelloy N Mechanical Casing", - "block.gtlcore.pikyonium_machine_casing": "Pikyonium Machine Casing", - "block.gtlcore.power_core": "Power Core", - "block.gtlcore.power_module": "Power Module", - "block.gtlcore.power_module_2": "Power Module 2", - "block.gtlcore.power_module_3": "Power Module 3", - "block.gtlcore.power_module_4": "Power Module 4", - "block.gtlcore.power_module_5": "Power Module 5", - "block.gtlcore.process_machine_casing": "Process Machine Casing", - "block.gtlcore.qft_coil": "Qft Coil", - "block.gtlcore.rhenium_reinforced_energy_glass": "Rhenium Reinforced Energy Glass", - "block.gtlcore.space_elevator_mechanical_casing": "Space Elevator Mechanical Casing", - "block.gtlcore.space_elevator_support": "Space Elevator Support", - "block.gtlcore.spacetimebendingcore": "Spacetimebendingcore", - "block.gtlcore.spacetimecontinuumripper": "Spacetimecontinuumripper", - "block.gtlcore.sps_casing": "Sps Casing", - "block.gtlcore.stellar_containment_casing": "Stellar Containment Casing", - "block.gtlcore.super_computation_component": "Super Computation Component", - "block.gtlcore.super_cooler_component": "Super Cooler Component", - "block.gtlcore.supercritical_turbine_casing": "Supercritical Turbine Casing", - "block.gtlcore.uev_hermetic_casing": "Hermetic Casing X", - "block.gtlcore.uiv_hermetic_casing": "Hermetic Casing XI", - "block.gtlcore.ultimate_stellar_containment_casing": "Ultimate Stellar Containment Casing", - "block.gtlcore.uxv_hermetic_casing": "Hermetic Casing XII", - "config.jade.plugin_gtlcore.wireless_data_hatch_provider": "[GTL] 无线光学", - "item.gtlcore.bifidobacteriumm_petri_dish": "Bifidobacteriumm Petri Dish", - "item.gtlcore.brevibacterium_petri_dish": "Brevibacterium Petri Dish", - "item.gtlcore.cell_component_16m": "Cell Component 16m", - "item.gtlcore.cell_component_1m": "Cell Component 1m", - "item.gtlcore.cell_component_256m": "Cell Component 256m", - "item.gtlcore.cell_component_4m": "Cell Component 4m", - "item.gtlcore.cell_component_64m": "Cell Component 64m", - "item.gtlcore.cfg_copy": "Cfg Copy", - "item.gtlcore.contaminated_petri_dish": "Contaminated Petri Dish", - "item.gtlcore.cupriavidus_petri_dish": "Cupriavidus Petri Dish", - "item.gtlcore.debug_pattern_test": "Debug Pattern Test", - "item.gtlcore.debug_structure_writer": "Debug Structure Writer", - "item.gtlcore.electricaly_wired_petri_dish": "Electricaly Wired Petri Dish", - "item.gtlcore.eschericia_petri_dish": "Eschericia Petri Dish", - "item.gtlcore.extremely_max_battery": "Extremely Max Battery", - "item.gtlcore.fluid_infinity_cell": "Fluid Infinity Cell", - "item.gtlcore.fluid_storage_cell_16m": "Fluid Storage Cell 16m", - "item.gtlcore.fluid_storage_cell_1m": "Fluid Storage Cell 1m", - "item.gtlcore.fluid_storage_cell_256m": "Fluid Storage Cell 256m", - "item.gtlcore.fluid_storage_cell_4m": "Fluid Storage Cell 4m", - "item.gtlcore.fluid_storage_cell_64m": "Fluid Storage Cell 64m", - "item.gtlcore.highly_insulating_foil": "Highly Insulating Foil", - "item.gtlcore.infinite_cell_component": "Infinite Cell Component", - "item.gtlcore.insanely_max_battery": "Insanely Max Battery", - "item.gtlcore.item_infinity_cell": "Item Infinity Cell", - "item.gtlcore.item_storage_cell_16m": "Item Storage Cell 16m", - "item.gtlcore.item_storage_cell_1m": "Item Storage Cell 1m", - "item.gtlcore.item_storage_cell_256m": "Item Storage Cell 256m", - "item.gtlcore.item_storage_cell_4m": "Item Storage Cell 4m", - "item.gtlcore.item_storage_cell_64m": "Item Storage Cell 64m", - "item.gtlcore.max_conveyor_module": "Max Conveyor Module", - "item.gtlcore.max_electric_motor": "Max Electric Motor", - "item.gtlcore.max_electric_piston": "Max Electric Piston", - "item.gtlcore.max_electric_pump": "Max Electric Pump", - "item.gtlcore.max_emitter": "Max Emitter", - "item.gtlcore.max_field_generator": "Max Field Generator", - "item.gtlcore.max_robot_arm": "Max Robot Arm", - "item.gtlcore.max_sensor": "Max Sensor", - "item.gtlcore.mega_max_battery": "Mega Max Battery", - "item.gtlcore.microfocus_x_ray_tube": "Microfocus X Ray Tube", - "item.gtlcore.pattern_modifier": "Pattern Modifier", - "item.gtlcore.primitive_fluid_regulator": "Primitive Fluid Regulator", - "item.gtlcore.primitive_robot_arm": "Primitive Robot Arm", - "item.gtlcore.protonated_fullerene_sieving_matrix": "Protonated Fullerene Sieving Matrix", - "item.gtlcore.really_max_battery": "Really Max Battery", - "item.gtlcore.saturated_fullerene_sieving_matrix": "Saturated Fullerene Sieving Matrix", - "item.gtlcore.separation_electromagnet": "Separation Electromagnet", - "item.gtlcore.shewanella_petri_dish": "Shewanella Petri Dish", - "item.gtlcore.sterilized_petri_dish": "Sterilized Petri Dish", - "item.gtlcore.streptococcus_petri_dish": "Streptococcus Petri Dish", - "item.gtlcore.super_portable_fluid_storage_cell": "Super Portable Fluid Storage Cell", - "item.gtlcore.super_portable_item_storage_cell": "Super Portable Item Storage Cell", - "item.gtlcore.transcendent_max_battery": "Transcendent Max Battery", - "item.gtlcore.structure_detect.error.0": "在 %s 处需要:", - "item.gtlcore.structure_detect.error.1": "在 %s 处的", - "item.gtlcore.structure_detect.error.2": "[%s, %s, %s]-(%s)", - "item.gtlcore.structure_detect.error.3": "翻转坐标", - "item.gtlcore.structure_detect.error.4": "正常坐标", - "item.gtlcore.structure_detect.tooltip.0": "潜行右键多方快结构主方块即可", - "item.gtlcore.structure_detect.tooltip.1": "检测大型结构时可能会出现卡顿", - "itemGroup.gtlcore.creative_tab": "GTL Core", - "gtlcore.machine.me_dual_hatch_stock.turns.0": "自动拉取已禁用", - "gtlcore.machine.me_dual_hatch_stock.turns.1": "拉取流体/物品", - "gtlcore.machine.me_dual_hatch_stock.turns.2": "仅拉取物品", - "gtlcore.machine.me_dual_hatch_stock.turns.3": "仅拉取流体" -} \ No newline at end of file diff --git a/src/generated/resources/assets/gtlcore/models/item/fast_infinity_cell.json b/src/generated/resources/assets/gtlcore/models/item/fast_infinity_cell.json new file mode 100644 index 000000000..d336143e1 --- /dev/null +++ b/src/generated/resources/assets/gtlcore/models/item/fast_infinity_cell.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "gtlcore:item/fast_infinity_cell" + } +} \ No newline at end of file diff --git a/src/main/java/org/gtlcore/gtlcore/api/capability/IInt128EnergyContainer.java b/src/main/java/org/gtlcore/gtlcore/api/capability/IInt128EnergyContainer.java new file mode 100644 index 000000000..279727821 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/capability/IInt128EnergyContainer.java @@ -0,0 +1,30 @@ +package org.gtlcore.gtlcore.api.capability; + +import org.gtlcore.gtlcore.utils.datastructure.Int128; + +public interface IInt128EnergyContainer { + + /** + * @return input eu/s + */ + default Int128 getInt128InputPerSec() { + return Int128.ZERO(); + } + + /** + * @return output eu/s + */ + default Int128 getInt128OutputPerSec() { + return Int128.ZERO(); + } + + default void addEnergyPerSec(long energy) {} + + default Int128 getInt128EnergyStored() { + return Int128.ZERO(); + } + + default Int128 getInt128EnergyCapacity() { + return Int128.ZERO(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/capability/IMERecipeHandler.java b/src/main/java/org/gtlcore/gtlcore/api/capability/IMERecipeHandler.java new file mode 100644 index 000000000..566b82758 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/capability/IMERecipeHandler.java @@ -0,0 +1,178 @@ +package org.gtlcore.gtlcore.api.capability; + +import com.gregtechceu.gtceu.api.capability.recipe.IFilteredHandler; +import com.gregtechceu.gtceu.api.capability.recipe.RecipeCapability; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.IntCollection; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.objects.*; + +import java.util.*; +import java.util.function.Predicate; + +/** + * @author Dragonators + * + * ME样板总成配方处理接口 + * 支持基于slot的配方处理和内容管理 + * + * @param Ingredient/FluidIngredient + */ +public interface IMERecipeHandler, S> extends IFilteredHandler { + + /** + * @return ItemCAP/FluidCAP + */ + RecipeCapability getCapability(); + + // ==================== Active Slots Management ==================== + + /** + * 获取激活的slot列表 + * 意味着该slot拥有样板且包含任意CAP的Input (禁止纯催化剂槽位) + * 因此同一MEPatternBuffer的任意Handler都应当返回相同结果 + * + * @return 激活的slot index[] + */ + Set getActiveSlots(); + + // ==================== Content Management ==================== + + /** + * 获取所有激活且未缓存GTRecipe的slot -> 与handler对应内容的映射 + * 激活意味着该slot拥有样板且包含任意CAP的Input (禁止纯催化剂槽位) + * 查询GTRecipe使用,所有Content缩限到单个元素与Int.MAX上限 + * Object类型说明: + * - Ingredient -> ItemStack + * - FluidIngredient -> FluidStack + * + * @return slot到限制内容列表的映射 + */ + Int2ObjectMap> getActiveAndUnCachedSlotsLimitContentsMap(); + + /** + * 获取Collection中第一个active槽位内所有与stack -> amount的映射 or Empty Map + * FirstAvailable的结果应当对于不同泛型的Handler保持一致性 + * 计算并行使用,amount不缩限 + * + * @param slots 要查询的slot列表 + * @return 内容到数量的映射 + */ + Object2LongMap getStackMapFromFirstAvailableSlot(IntCollection slots); + + /** + * 获取单个slot中所有与handler对应内容 -> amount的映射 + * + * @param slot 要查询的slot + * @return 内容到数量的映射 + */ + default Object2LongMap getSingleSlotStackMap(int slot) { + return getStackMapFromFirstAvailableSlot(IntList.of(slot)); + } + + @SuppressWarnings("unchecked") + default T copyContent(Object content) { + return getCapability().copyInner((T) content); + } + + // ==================== Recipe Handling ==================== + + /** + * 尝试使用指定slot处理配方 + * 注意: 在调用此方法前必须先调用initMEHandleContents为ME样板总成的Handler初始化待处理Map + * + * @param recipe 要处理的配方 + * @param simulate 是否模拟 + * @param trySlot 尝试使用的slot + * @return 处理是否成功 + */ + default boolean meHandleRecipe(GTRecipe recipe, boolean simulate, int trySlot) { + return meHandleRecipeInner(recipe, getPreparedMEHandleContents(), simulate, trySlot); + } + + /** + * ME配方处理的内部实现 + * 由具体实现类提供处理逻辑 + * + * @param recipe 配方 + * @param preparedContents 准备好的内容映射 + * @param simulate 是否模拟 + * @param trySlot 尝试使用的slot + * @return 处理是否成功 + */ + boolean meHandleRecipeInner(GTRecipe recipe, Object2LongMap preparedContents, boolean simulate, int trySlot); + + /** + * ME输出处理的内部实现 + * 由具体实现类提供处理逻辑 + * + * @param contents 类型化的内容列表 + * @return 剩余内容 + */ + List meHandleRecipeOutputInner(List contents, boolean simulate); + + /** + * @return 当前MERecipeHandler是否配置过滤 + */ + default boolean outputHasFilter() { + return false; + } + + // ==================== Content Preparation ==================== + + /** + * 初始化ME处理内容 + * 将配方待处理内容List转换为ME样板总成处理器内部处理的Map形式 + * 同时预处理催化剂/电路相关内容,在后续Map中只存在实际消耗的物品 + * + * @param recipe 配方 + * @param leftContents 配方待处理内容 + * @param simulate 是否模拟 + */ + default void initMEHandleContents(GTRecipe recipe, List leftContents, boolean simulate) { + if (leftContents.isEmpty()) return; + + List typedContents = new ObjectArrayList<>(leftContents.size()); + for (Object leftObj : leftContents) { + typedContents.add(this.copyContent(leftObj)); + } + + prepareMEHandleContents(recipe, typedContents, simulate); + } + + /** + * 将配方待处理内容List转换为List以调用实际处理 + * 只有非simulate模式 + * + * @param leftContents 待处理输出内容 + * @return 剩余内容 + */ + default List meHandleRecipeOutput(List leftContents, boolean simulate) { + if (leftContents.isEmpty() || (simulate && !outputHasFilter())) return List.of(); + + List typedContents = new ObjectArrayList<>(leftContents.size()); + for (Object leftObj : leftContents) { + typedContents.add(this.copyContent(leftObj)); + } + + return meHandleRecipeOutputInner(typedContents, simulate); + } + + /** + * 准备ME处理内容, 由具体的ME样板总成实现 + * + * @param recipe 配方 + * @param contents 类型化的内容列表 + * @param simulate 是否模拟 + */ + void prepareMEHandleContents(GTRecipe recipe, List contents, boolean simulate); + + /** + * 获取准备好的ME处理内容 + * + * @return 准备好的内容映射 + */ + Object2LongMap getPreparedMEHandleContents(); +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/gui/BlockMapSelectorWidget.java b/src/main/java/org/gtlcore/gtlcore/api/gui/BlockMapSelectorWidget.java new file mode 100644 index 000000000..769aebe49 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/gui/BlockMapSelectorWidget.java @@ -0,0 +1,104 @@ +package org.gtlcore.gtlcore.api.gui; + +import org.gtlcore.gtlcore.common.block.BlockMap; + +import com.gregtechceu.gtceu.api.gui.GuiTextures; + +import com.lowdragmc.lowdraglib.gui.editor.ColorPattern; +import com.lowdragmc.lowdraglib.gui.texture.ItemStackTexture; +import com.lowdragmc.lowdraglib.gui.widget.*; + +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; + +import java.util.*; +import java.util.function.*; + +import static net.minecraft.network.chat.Component.translatable; + +public class BlockMapSelectorWidget extends WidgetGroup { + + public static final Component SC = translatable("gui.gtlcore.stellar_thermal_container"); + public static final Component SEPM = translatable("gui.gtlcore.space_elevator_module"); + public static final Component CAL = translatable("gui.gtlcore.component_assembly_casing"); + public static final Component COIL = translatable("gui.gtlcore.coil"); + + private final BiConsumer onChanged; + private List blocks; + private String currentType; + + public BlockMapSelectorWidget(int y, int width, BiConsumer onChanged) { + super(0, y, width, 56); + this.onChanged = onChanged; + this.setVisible(false); + } + + public static Component getBlock(String string) { + return switch (string) { + case "sc" -> SC; + case "sepm" -> SEPM; + case "cal" -> CAL; + case "coil" -> COIL; + default -> throw new IllegalStateException("Unexpected value: " + string); + }; + } + + public void setInit(ItemStack itemStack) { + var tag = itemStack.getOrCreateTag(); + var block = tag.getString("blocks"); + if (!block.isEmpty()) { + this.blocks = Arrays.stream(BlockMap.tierBlockMap.get(block).get()).toList(); + this.currentType = block; + } + } + + public void showType(boolean isShow) { + if (!isShow) this.setVisible(false); + else { + WidgetGroup group = new WidgetGroup(0, 2, 80, 52); + group.setBackground(GuiTextures.BACKGROUND_INVERSE); + var blockType = new DraggableScrollableWidgetGroup(4, 4, 72, 44); + blockType.setYScrollBarWidth(2) + .setYBarStyle(null, ColorPattern.T_WHITE.rectTexture().setRadius(1.0F)) + .setBackground(GuiTextures.DISPLAY); + int y = 0; + for (var key : BlockMap.tierBlockMap.keySet()) { + blockType.addWidget((new WidgetGroup(2, 2 + y, 66, 15)) + .addWidget(new ExtendLabelWidget(0, 0, getBlock(key))) + .addWidget(new ButtonWidget(0, 0, 64, 15, (cd) -> { + showTier(Arrays.stream(BlockMap.tierBlockMap.get(key).get()).toList()); + currentType = key; + }))); + y += 15; + } + this.addWidget(group.addWidget(blockType)); + this.setVisible(true); + if (this.blocks != null && !this.blocks.isEmpty()) showTier(this.blocks); + } + } + + public void showTier(List blocks) { + if (!blocks.isEmpty()) { + this.blocks = blocks; + WidgetGroup group = new WidgetGroup(80, 2, 120, 52); + group.setBackground(GuiTextures.BACKGROUND_INVERSE); + var blockTier = new DraggableScrollableWidgetGroup(4, 4, 112, 44); + blockTier.setYScrollBarWidth(2) + .setYBarStyle(null, ColorPattern.T_WHITE.rectTexture().setRadius(1.0F)) + .setBackground(GuiTextures.DISPLAY); + for (int i = 0; i < blocks.size(); i++) { + int finalI = i; + var block = blocks.get(finalI); + blockTier.addWidget(new WidgetGroup(2, 2 + i * 20, 110, 22) + .addWidget(new ImageWidget(2, 0, 18, 18, + new ItemStackTexture(block.asItem().getDefaultInstance()))) + .addWidget(new ExtendLabelWidget(20, 4, block.getName())) + .addWidget(new ButtonWidget(20, 0, 88, 18, (cd) -> { + if (onChanged != null) onChanged.accept(currentType, finalI); + }))); + } + this.addWidget(group.addWidget(blockTier)); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/gui/ExtendLabelWidget.java b/src/main/java/org/gtlcore/gtlcore/api/gui/ExtendLabelWidget.java new file mode 100644 index 000000000..05ee3ca25 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/gui/ExtendLabelWidget.java @@ -0,0 +1,13 @@ +package org.gtlcore.gtlcore.api.gui; + +import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; + +import net.minecraft.network.chat.Component; + +public class ExtendLabelWidget extends LabelWidget { + + public ExtendLabelWidget(int xPosition, int yPosition, Component component) { + super(xPosition, yPosition, component); + this.setText(component.getString()); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/gui/ExtendPatternPreviewWidget.java b/src/main/java/org/gtlcore/gtlcore/api/gui/ExtendPatternPreviewWidget.java new file mode 100644 index 000000000..bd4a1895b --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/gui/ExtendPatternPreviewWidget.java @@ -0,0 +1,66 @@ +package org.gtlcore.gtlcore.api.gui; + +import com.gregtechceu.gtceu.api.block.MetaMachineBlock; +import com.gregtechceu.gtceu.api.machine.MultiblockMachineDefinition; + +import com.lowdragmc.lowdraglib.utils.*; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Fluid; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; + +import java.util.*; + +public class ExtendPatternPreviewWidget { + + public static Map gatherBlockDrops(Map blocks, TrackedDummyWorld level) { + Map partsMap = new Object2ObjectOpenHashMap<>(); + for (var entry : blocks.entrySet()) { + BlockPos pos = entry.getKey(); + BlockState blockState = level.getBlockState(pos); + ItemStack itemStack = blockState.getBlock().getCloneItemStack(level, pos, blockState); + + if (itemStack.isEmpty() && !blockState.getFluidState().isEmpty()) { + Fluid fluid = blockState.getFluidState().getType(); + itemStack = fluid.getBucket().getDefaultInstance(); + } + + ItemStackKey itemStackKey = new ItemStackKey(itemStack); + partsMap.computeIfAbsent(itemStackKey, key -> new PartInfo(key, entry.getValue())).amount++; + } + return partsMap; + } + + public static class PartInfo { + + final ItemStackKey itemStackKey; + public boolean isController = false; + public boolean isTile; + public final int blockId; + public int amount = 0; + + PartInfo(final ItemStackKey itemStackKey, final BlockInfo blockInfo) { + this.itemStackKey = itemStackKey; + this.blockId = Block.getId(blockInfo.getBlockState()); + this.isTile = blockInfo.hasBlockEntity(); + + if (blockInfo.getBlockState().getBlock() instanceof MetaMachineBlock block) { + if (block.definition instanceof MultiblockMachineDefinition) + this.isController = true; + } + } + + public List getItemStack() { + return Arrays.stream(itemStackKey.getItemStack()) + .map(itemStack -> { + var item = itemStack.copy(); + item.setCount(amount); + return item; + }).filter((itemStack) -> !itemStack.isEmpty()).toList(); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/gui/MEPatternCatalystUIManager.java b/src/main/java/org/gtlcore/gtlcore/api/gui/MEPatternCatalystUIManager.java new file mode 100644 index 000000000..ccf088291 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/gui/MEPatternCatalystUIManager.java @@ -0,0 +1,264 @@ +package org.gtlcore.gtlcore.api.gui; + +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.api.gui.widget.IntInputWidget; + +import com.lowdragmc.lowdraglib.gui.widget.*; +import com.lowdragmc.lowdraglib.jei.IngredientIO; +import com.lowdragmc.lowdraglib.misc.FluidTransferList; +import com.lowdragmc.lowdraglib.side.fluid.IFluidTransfer; +import com.lowdragmc.lowdraglib.side.item.IItemTransfer; +import com.lowdragmc.lowdraglib.utils.Position; + +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import org.jetbrains.annotations.NotNull; + +import java.util.function.Consumer; +import java.util.function.IntConsumer; +import java.util.function.Supplier; + +public class MEPatternCatalystUIManager extends WidgetGroup { + + static final int SLOT_SIZE = 18; + static final int PAD_OUT = 8; + static final int PAD_IN = 4; + static final int MAX_COLS = 9; + private static final int[] OPTIMAL_COLUMNS = new int[MAX_COLS + 1]; + // dragging + protected double lastDeltaX, lastDeltaY; + protected int dragOffsetX, dragOffsetY; + protected boolean isDragging; + + static { + for (int slots = 1; slots <= MAX_COLS; slots++) { + OPTIMAL_COLUMNS[slots] = calculateOptimalColumnsInternal(slots); + } + } + + private int lastIndex = -1; + + private final IItemTransfer[] itemTransfers; + private final FluidTransferList[] fluidTankTransfers; + private final byte[] cacheRecipeCount; + private final IntConsumer onCacheCountChange; + + public MEPatternCatalystUIManager(int dockX, IItemTransfer[] itemTransfers, FluidTransferList[] fluidTankTransfers, byte[] cacheRecipeCount, IntConsumer onCacheCountChange) { + // 初始高度给个最小值,后面会根据内容 resize + super(dockX, 16, 16, 16); + this.setBackground(GuiTextures.BACKGROUND); + this.setVisible(false).setActive(false); + this.itemTransfers = itemTransfers; + this.fluidTankTransfers = fluidTankTransfers; + this.cacheRecipeCount = cacheRecipeCount; + this.onCacheCountChange = onCacheCountChange; + } + + /** + * 为指定 pattern 槽位切换显示/隐藏: + * - 如果点击同一slot执行开关 + * - 否则即时创建新的催化剂 UI 并显示它。 + */ + public void toggleFor(int index) { + if (lastIndex == index) { + this.setVisible(!this.isVisible()).setActive(!this.isActive()); + return; + } + show(index, itemTransfers[index], fluidTankTransfers[index]); + } + + private void show(int index, IItemTransfer itemInventory, FluidTransferList tanks) { + this.clearAllWidgets(); + + final int itemSlots = (itemInventory != null) ? itemInventory.getSlots() : 0; + final int fluidTanks = (tanks != null) ? tanks.transfers.length : 0; + + int currentY = 3; + int maxWidth = 0; + + Widget cacheCountWidget = createCacheCountInputWidget(() -> (int) cacheRecipeCount[index], value -> { + if (cacheRecipeCount[index] != value.byteValue()) { + cacheRecipeCount[index] = value.byteValue(); + onCacheCountChange.accept(index); + } + }); + cacheCountWidget.setSelfPosition(0, currentY); + this.addWidget(cacheCountWidget); + currentY += cacheCountWidget.getSize().height + 4; + maxWidth = Math.max(maxWidth, cacheCountWidget.getSize().width + PAD_OUT); + + if (itemSlots > 0) { + Widget itemContainer = createInventoryContainer(itemInventory, itemSlots); + itemContainer.setSelfPosition(0, currentY); + this.addWidget(itemContainer); + currentY += itemContainer.getSize().height; + maxWidth = Math.max(maxWidth, itemContainer.getSize().width); + } + + if (fluidTanks > 0) { + Widget fluidContainer = createFluidContainer(tanks, fluidTanks); + fluidContainer.setSelfPosition(0, currentY); + this.addWidget(fluidContainer); + currentY += fluidContainer.getSize().height; + maxWidth = Math.max(maxWidth, fluidContainer.getSize().width); + } + + if (maxWidth == 0) maxWidth = 16; + if (currentY <= 0) currentY = 16; + + this.setSize(maxWidth, currentY); + this.setVisible(true).setActive(true); + lastIndex = index; + } + + private static @NotNull Widget createCacheCountInputWidget(Supplier supplier, Consumer consumer) { + WidgetGroup group = new WidgetGroup(new Position(0, 0)); + group.addWidget(new LabelWidget(PAD_OUT, 2, "实际配方数量")); + group.addWidget(new IntInputWidget(new Position(PAD_OUT, PAD_OUT + 4), supplier, consumer) + .setMin(1) + .setMax((int) Byte.MAX_VALUE)); + return group; + } + + private static @NotNull Widget createInventoryContainer(IItemTransfer inventory, int slots) { + final int cols = calculateOptimalColumns(slots); + final int rows = (slots + cols - 1) / cols; + final int containerW = PAD_IN * 2 + cols * SLOT_SIZE; + final int containerH = PAD_IN * 2 + rows * SLOT_SIZE; + final int groupW = PAD_OUT * 2 + containerW; + final int groupH = PAD_OUT * 2 + containerH; + + WidgetGroup group = new WidgetGroup(0, 0, groupW, groupH); + group.addWidget(new LabelWidget(PAD_OUT, 2, "物品催化剂槽")); + WidgetGroup container = new WidgetGroup(PAD_OUT, PAD_OUT + 4, containerW, containerH); + + int index = 0; + for (int y = 0; y < rows && index < slots; ++y) { + for (int x = 0; x < cols && index < slots; ++x) { + int sx = PAD_IN + x * SLOT_SIZE; + int sy = PAD_IN + y * SLOT_SIZE; + container.addWidget(createSlotWidget(inventory, index++, sx, sy)); + } + } + + container.setBackground(GuiTextures.BACKGROUND_INVERSE); + group.addWidget(container); + return group; + } + + private static @NotNull Widget createFluidContainer(FluidTransferList tanks, int slots) { + final int cols = calculateOptimalColumns(slots); + final int rows = (slots + cols - 1) / cols; + final int containerW = PAD_IN * 2 + cols * SLOT_SIZE; + final int containerH = PAD_IN * 2 + rows * SLOT_SIZE; + final int groupW = PAD_OUT * 2 + containerW; + final int groupH = PAD_OUT * 2 + containerH; + + WidgetGroup group = new WidgetGroup(0, 0, groupW, groupH); + group.addWidget(new LabelWidget(PAD_OUT, 2, "流体催化剂槽")); + WidgetGroup container = new WidgetGroup(PAD_OUT, PAD_OUT + 4, containerW, containerH); + + int index = 0; + for (int y = 0; y < rows && index < slots; ++y) { + for (int x = 0; x < cols && index < slots; ++x) { + int sx = PAD_IN + x * SLOT_SIZE; + int sy = PAD_IN + y * SLOT_SIZE; + container.addWidget(createTankWidget(tanks.transfers[index], sx, sy)); + index++; + } + } + + container.setBackground(GuiTextures.BACKGROUND_INVERSE); + group.addWidget(container); + return group; + } + + private static int calculateOptimalColumns(int slots) { + if (slots <= 0) return 1; + if (slots <= MAX_COLS) return OPTIMAL_COLUMNS[slots]; + return MAX_COLS; + } + + private static int calculateOptimalColumnsInternal(int slots) { + if (slots <= 0) return 1; + int bestCols = 1; + double bestRatio = Double.MAX_VALUE; + for (int cols = 1; cols <= Math.min(slots, MAX_COLS); cols++) { + int rows = (slots + cols - 1) / cols; + double ratio = Math.abs((double) cols / rows - 1.0); // 越接近 1 越“方” + if (ratio < bestRatio) { + bestRatio = ratio; + bestCols = cols; + } + } + return bestCols; + } + + private static @NotNull Widget createSlotWidget(IItemTransfer inventory, int slotIndex, int sx, int sy) { + return new SlotWidget(inventory, slotIndex, sx, sy, true, true) + .setBackgroundTexture(GuiTextures.SLOT) + .setIngredientIO(IngredientIO.INPUT); + } + + private static @NotNull Widget createTankWidget(IFluidTransfer storage, int sx, int sy) { + return new TankWidget(storage, 0, sx, sy, true, true) + .setBackground(GuiTextures.FLUID_SLOT); + } + + private boolean isMouseover(int x, int y, int width, int height, double mouseX, double mouseY) { + boolean b = mouseX >= x && mouseY >= y && x + width > mouseX && y + height > mouseY; + if (b) { + l: + for (var ui : this.widgets) { + if (ui instanceof WidgetGroup wg) for (var u : wg.widgets) + if (u instanceof WidgetGroup w) { + int pX = w.getPositionX(), pY = w.getPositionY(), pW = w.getSizeWidth(), pH = w.getSizeHeight(); + b = !(mouseX >= pX && mouseY >= pY && pX + pW > mouseX && pY + pH > mouseY); + if (!b) break l; + } + } + return b; + } + return false; + } + + @Override + @OnlyIn(Dist.CLIENT) + public boolean mouseClicked(double mouseX, double mouseY, int button) { + this.lastDeltaX = 0; + this.lastDeltaY = 0; + this.isDragging = false; + if (isMouseover(getPositionX(), getPositionY(), getSizeWidth(), getSizeHeight(), mouseX, mouseY)) { + isDragging = true; + return true; + } + return super.mouseClicked(mouseX, mouseY, button) || isMouseOverElement(mouseX, mouseY); + } + + @Override + @OnlyIn(Dist.CLIENT) + public boolean mouseDragged(double mouseX, double mouseY, int button, double dragX, double dragY) { + double dx = dragX + lastDeltaX; + double dy = dragY + lastDeltaY; + dragX = (int) dx; + dragY = (int) dy; + lastDeltaX = dx - dragX; + lastDeltaY = dy - dragY; + if (isDragging) { + this.dragOffsetX += (int) dragX; + this.dragOffsetY += (int) dragY; + this.addSelfPosition((int) dragX, (int) dragY); + } + return super.mouseDragged(mouseX, mouseY, button, dragX, dragY) || isMouseOverElement(mouseX, mouseY); + } + + @Override + @OnlyIn(Dist.CLIENT) + public boolean mouseReleased(double mouseX, double mouseY, int button) { + this.lastDeltaX = 0; + this.lastDeltaY = 0; + this.isDragging = false; + return super.mouseReleased(mouseX, mouseY, button) || isMouseOverElement(mouseX, mouseY); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/gui/MachineModeConfigurator.java b/src/main/java/org/gtlcore/gtlcore/api/gui/MachineModeConfigurator.java new file mode 100644 index 000000000..033d95fa1 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/gui/MachineModeConfigurator.java @@ -0,0 +1,37 @@ +package org.gtlcore.gtlcore.api.gui; + +import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; + +import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; + +import net.minecraft.network.FriendlyByteBuf; + +public class MachineModeConfigurator extends WidgetGroup { + + protected IRecipeLogicMachine machine; + + public MachineModeConfigurator(int x, int y, int width, int height, IRecipeLogicMachine machine) { + super(x, y, width, height); + this.machine = machine; + } + + @Override + public void writeInitialData(FriendlyByteBuf buffer) { + buffer.writeVarInt(machine.getActiveRecipeType()); + } + + @Override + public void readInitialData(FriendlyByteBuf buffer) { + machine.setActiveRecipeType(buffer.readVarInt()); + } + + @Override + public void detectAndSendChanges() { + this.writeUpdateInfo(0, buf -> buf.writeVarInt(machine.getActiveRecipeType())); + } + + @Override + public void readUpdateInfo(int id, FriendlyByteBuf buffer) { + if (id == 0) machine.setActiveRecipeType(buffer.readVarInt()); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/gui/PatternPreviewWidget.java b/src/main/java/org/gtlcore/gtlcore/api/gui/PatternPreviewWidget.java new file mode 100644 index 000000000..9afb0067c --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/gui/PatternPreviewWidget.java @@ -0,0 +1,411 @@ +package org.gtlcore.gtlcore.api.gui; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.block.MetaMachineBlock; +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.MultiblockMachineDefinition; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; +import com.gregtechceu.gtceu.api.pattern.BlockPattern; +import com.gregtechceu.gtceu.api.pattern.MultiblockShapeInfo; +import com.gregtechceu.gtceu.api.pattern.TraceabilityPredicate; +import com.gregtechceu.gtceu.api.pattern.predicates.SimplePredicate; +import com.gregtechceu.gtceu.config.ConfigHolder; + +import com.lowdragmc.lowdraglib.gui.editor.ColorPattern; +import com.lowdragmc.lowdraglib.gui.texture.*; +import com.lowdragmc.lowdraglib.gui.util.ClickData; +import com.lowdragmc.lowdraglib.gui.widget.*; +import com.lowdragmc.lowdraglib.jei.IngredientIO; +import com.lowdragmc.lowdraglib.utils.BlockInfo; +import com.lowdragmc.lowdraglib.utils.CycleItemStackHandler; +import com.lowdragmc.lowdraglib.utils.ItemStackKey; +import com.lowdragmc.lowdraglib.utils.TrackedDummyWorld; + +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Fluid; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import com.mojang.blaze3d.systems.RenderSystem; +import it.unimi.dsi.fastutil.longs.*; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author KilaBash + * @date 2023/3/5 + * @implNote PatterShapeInfoWidget + */ +@OnlyIn(Dist.CLIENT) +public class PatternPreviewWidget extends WidgetGroup { + + private static TrackedDummyWorld LEVEL; + private static BlockPos LAST_POS = new BlockPos(0, 50, 0); + private static final Map CACHE = new Object2ObjectOpenHashMap<>(); + private final SceneWidget sceneWidget; + private final DraggableScrollableWidgetGroup scrollableWidgetGroup; + private final MultiblockMachineDefinition controllerDefinition; + private final MBPattern[] patterns; + private final List predicates; + private int index; + private int layer; + private SlotWidget[] slotWidgets; + private SlotWidget[] candidates; + + protected PatternPreviewWidget(MultiblockMachineDefinition controllerDefinition) { + super(0, 0, 160, 160); + try { + setClientSideWidget(); + this.controllerDefinition = controllerDefinition; + predicates = new ObjectArrayList<>(); + layer = -1; + + addWidget(sceneWidget = new SceneWidget(3, 3, 150, 150, LEVEL) + .setOnSelected(this::onPosSelected) + .setRenderFacing(false) + .setRenderFacing(false)); + + scrollableWidgetGroup = new DraggableScrollableWidgetGroup(3, 132, 154, 22) + .setXScrollBarHeight(4) + .setXBarStyle(GuiTextures.SLIDER_BACKGROUND, GuiTextures.BUTTON) + .setScrollable(true) + .setDraggable(true); + scrollableWidgetGroup.setScrollWheelDirection(DraggableScrollableWidgetGroup.ScrollWheelDirection.HORIZONTAL); + scrollableWidgetGroup.setScrollYOffset(0); + addWidget(scrollableWidgetGroup); + + if (ConfigHolder.INSTANCE.client.useVBO) { + if (!RenderSystem.isOnRenderThread()) { + RenderSystem.recordRenderCall(sceneWidget::useCacheBuffer); + } else { + sceneWidget.useCacheBuffer(); + } + } + + addWidget(new ImageWidget(3, 3, 160, 10, + new TextTexture(controllerDefinition.getDescriptionId(), -1) + .setType(TextTexture.TextType.ROLL) + .setWidth(170) + .setDropShadow(true))); + + this.patterns = CACHE.computeIfAbsent(controllerDefinition, definition -> { + HashSet drops = new HashSet<>(); + drops.add(new ItemStackKey(this.controllerDefinition.asStack())); + return controllerDefinition.getMatchingShapes().stream() + .map(it -> initializePattern(it, drops)) + .filter(Objects::nonNull) + .toArray(MBPattern[]::new); + }); + + addWidget(new ButtonWidget(138, 30, 18, 18, new GuiTextureGroup( + ColorPattern.T_GRAY.rectTexture(), + new TextTexture("1").setSupplier(() -> "P:" + index)), + (x) -> setPage((index + 1 >= patterns.length) ? 0 : index + 1, x)) + .setHoverBorderTexture(1, -1)); + + addWidget(new ButtonWidget(138, 50, 18, 18, new GuiTextureGroup( + ColorPattern.T_GRAY.rectTexture(), + new TextTexture("1").setSupplier(() -> layer >= 0 ? "L:" + layer : "ALL")), + this::updateLayer) + .setHoverBorderTexture(1, -1)); + + setPage(0, null); + } catch (Exception e) { + throw new IllegalStateException("The jei preview creation for the Multi Block Machine [" + controllerDefinition.getId().toString() + "] failed! "); + } + } + + private void updateLayer(ClickData cd) { + MBPattern pattern = patterns[index]; + if (layer + 1 >= -1 && layer + 1 <= pattern.maxY - pattern.minY && !cd.isShiftClick) { + layer += 1; + if (pattern.controllerBase.isFormed()) { + onFormedSwitch(false); + } + } else { + layer = -1; + if (!pattern.controllerBase.isFormed()) { + onFormedSwitch(true); + } + } + setupScene(pattern); + } + + private void setupScene(MBPattern pattern) { + Stream stream = pattern.blockMap.keySet().stream() + .filter(pos -> layer == -1 || layer + pattern.minY == pos.getY()); + if (pattern.controllerBase.isFormed()) { + LongSet set = pattern.controllerBase.getMultiblockState().getMatchContext().getOrDefault("renderMask", + LongSets.EMPTY_SET); + Set modelDisabled = set.longStream().mapToObj(BlockPos::of).collect(Collectors.toSet()); + if (!modelDisabled.isEmpty()) { + sceneWidget.setRenderedCore( + stream.filter(pos -> !modelDisabled.contains(pos)).collect(Collectors.toList()), null); + } else { + sceneWidget.setRenderedCore(stream.toList(), null); + } + } else { + sceneWidget.setRenderedCore(stream.toList(), null); + } + } + + public static PatternPreviewWidget getPatternWidget(MultiblockMachineDefinition controllerDefinition) { + if (LEVEL == null) { + if (Minecraft.getInstance().level == null) { + GTCEu.LOGGER.error("Try to init pattern previews before level load"); + throw new IllegalStateException(); + } + LEVEL = new TrackedDummyWorld(); + } + return new PatternPreviewWidget(controllerDefinition); + } + + public void setPage(int index, ClickData x) { + if (x != null) { + if (x.isShiftClick) index = 0; + else if (x.isCtrlClick) index = patterns.length - 1; + } + if (index >= patterns.length || index < 0) return; + this.index = index; + this.layer = -1; + MBPattern pattern = patterns[index]; + setupScene(pattern); + if (slotWidgets != null) { + for (SlotWidget slotWidget : slotWidgets) { + scrollableWidgetGroup.removeWidget(slotWidget); + } + } + slotWidgets = new SlotWidget[Math.min(pattern.parts.size(), 36)]; + var itemHandler = new CycleItemStackHandler(pattern.parts); + for (int i = 0; i < slotWidgets.length; i++) { + final var itemStack = pattern.parts.get(i).get(0); + slotWidgets[i] = new SlotWidget(itemHandler, i, 4 + i * 18, 0, false, false) + .setBackgroundTexture(ColorPattern.T_GRAY.rectTexture()) + .setIngredientIO(IngredientIO.INPUT) + .setOnAddedTooltips((w, tips) -> tips.add(Component.translatable("gtceu.machine.quantum_chest.items_stored") + .withStyle(ChatFormatting.DARK_AQUA) + .append(Component.literal(String.valueOf(itemStack.getCount()))))); + scrollableWidgetGroup.addWidget(slotWidgets[i]); + } + } + + private void onFormedSwitch(boolean isFormed) { + MBPattern pattern = patterns[index]; + IMultiController controllerBase = pattern.controllerBase; + if (isFormed) { + this.layer = -1; + loadControllerFormed(pattern.blockMap.keySet(), controllerBase); + } else { + sceneWidget.setRenderedCore(pattern.blockMap.keySet(), null); + controllerBase.onStructureInvalid(); + } + } + + private void onPosSelected(BlockPos pos, Direction facing) { + if (index >= patterns.length || index < 0) return; + TraceabilityPredicate predicate = patterns[index].predicateMap.get(pos); + if (predicate != null) { + predicates.clear(); + predicates.addAll(predicate.common); + predicates.addAll(predicate.limited); + predicates.removeIf(p -> p == null || p.candidates == null); // why it happens? + if (candidates != null) { + for (SlotWidget candidate : candidates) { + removeWidget(candidate); + } + } + List> candidateStacks = new ObjectArrayList<>(); + List> predicateTips = new ObjectArrayList<>(); + for (SimplePredicate simplePredicate : predicates) { + List itemStacks = simplePredicate.getCandidates(); + if (!itemStacks.isEmpty()) { + candidateStacks.add(itemStacks); + predicateTips.add(simplePredicate.getToolTips(predicate)); + } + } + candidates = new SlotWidget[candidateStacks.size()]; + CycleItemStackHandler itemHandler = new CycleItemStackHandler(candidateStacks); + int maxCol = (160 - (((slotWidgets.length - 1) / 9 + 1) * 18) - 35) % 18; + for (int i = 0; i < candidateStacks.size(); i++) { + int finalI = i; + candidates[i] = new SlotWidget(itemHandler, i, 3 + (i / maxCol) * 18, 3 + (i % maxCol) * 18, false, + false) + .setIngredientIO(IngredientIO.INPUT) + .setBackgroundTexture(new ColorRectTexture(0x4fffffff)) + .setOnAddedTooltips((slot, list) -> list.addAll(predicateTips.get(finalI))); + addWidget(candidates[i]); + } + } + } + + public static BlockPos locateNextRegion(int range) { + BlockPos pos = LAST_POS; + LAST_POS = LAST_POS.offset(range, 0, range); + return pos; + } + + @Override + public void drawInBackground(@NotNull GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { + RenderSystem.enableBlend(); + super.drawInBackground(graphics, mouseX, mouseY, partialTicks); + } + + private MBPattern initializePattern(MultiblockShapeInfo shapeInfo, HashSet blockDrops) { + Map blockMap = new Object2ObjectOpenHashMap<>(); + IMultiController controllerBase = null; + BlockPos multiPos = locateNextRegion(500); + + BlockInfo[][][] blocks = shapeInfo.getBlocks(); + for (int x = 0; x < blocks.length; x++) { + BlockInfo[][] aisle = blocks[x]; + for (int y = 0; y < aisle.length; y++) { + BlockInfo[] column = aisle[y]; + for (int z = 0; z < column.length; z++) { + var block = column[z]; + if (block == null) continue; + BlockState blockState = block.getBlockState(); + BlockPos pos = multiPos.offset(x, y, z); + if (block.getBlockEntity(pos) instanceof IMachineBlockEntity holder && + holder.getMetaMachine() instanceof IMultiController controller) { + holder.getSelf().setLevel(LEVEL); + controllerBase = controller; + } + blockMap.put(pos, BlockInfo.fromBlockState(blockState)); + } + } + } + + LEVEL.addBlocks(blockMap); + if (controllerBase != null) { + LEVEL.setInnerBlockEntity(controllerBase.self().holder.getSelf()); + } + + Map parts = gatherBlockDrops(blockMap); + blockDrops.addAll(parts.keySet()); + + Map predicateMap = new Object2ObjectOpenHashMap<>(); + if (controllerBase != null) { + loadControllerFormed(predicateMap.keySet(), controllerBase); + predicateMap = controllerBase.getMultiblockState().getMatchContext().get("predicates"); + } + return controllerBase == null ? null : new MBPattern(blockMap, parts.values().stream().sorted((one, two) -> { + if (one.isController) return -1; + if (two.isController) return +1; + if (one.isTile && !two.isTile) return -1; + if (two.isTile && !one.isTile) return +1; + if (one.blockId != two.blockId) return two.blockId - one.blockId; + return two.amount - one.amount; + }).map(PartInfo::getItemStack).filter(list -> !list.isEmpty()).collect(Collectors.toList()), predicateMap, + controllerBase); + } + + private void loadControllerFormed(Collection poses, IMultiController controllerBase) { + BlockPattern pattern = controllerBase.getPattern(); + if (pattern != null && pattern.checkPatternAt(controllerBase.getMultiblockState(), true)) { + controllerBase.onStructureFormed(); + } + if (controllerBase.isFormed()) { + LongSet set = controllerBase.getMultiblockState().getMatchContext().getOrDefault("renderMask", + LongSets.EMPTY_SET); + Set modelDisabled = set.longStream().mapToObj(BlockPos::of).collect(Collectors.toSet()); + if (!modelDisabled.isEmpty()) { + sceneWidget.setRenderedCore( + poses.stream().filter(pos -> !modelDisabled.contains(pos)).collect(Collectors.toList()), null); + } else { + sceneWidget.setRenderedCore(poses, null); + } + } else { + GTCEu.LOGGER.warn("Pattern formed checking failed: {}", controllerBase.self().getDefinition()); + } + } + + private Map gatherBlockDrops(Map blocks) { + Map partsMap = new Object2ObjectOpenHashMap<>(); + for (Map.Entry entry : blocks.entrySet()) { + BlockPos pos = entry.getKey(); + BlockState blockState = LEVEL.getBlockState(pos); + ItemStack itemStack = blockState.getBlock().getCloneItemStack(LEVEL, pos, blockState); + + if (itemStack.isEmpty() && !blockState.getFluidState().isEmpty()) { + Fluid fluid = blockState.getFluidState().getType(); + itemStack = fluid.getBucket().getDefaultInstance(); + } + + ItemStackKey itemStackKey = new ItemStackKey(itemStack); + partsMap.computeIfAbsent(itemStackKey, key -> new PartInfo(key, entry.getValue())).amount++; + } + return partsMap; + } + + private static class PartInfo { + + final ItemStackKey itemStackKey; + boolean isController = false; + boolean isTile; + final int blockId; + int amount = 0; + + PartInfo(final ItemStackKey itemStackKey, final BlockInfo blockInfo) { + this.itemStackKey = itemStackKey; + this.blockId = Block.getId(blockInfo.getBlockState()); + this.isTile = blockInfo.hasBlockEntity(); + + if (blockInfo.getBlockState().getBlock() instanceof MetaMachineBlock block) { + if (block.definition instanceof MultiblockMachineDefinition) + this.isController = true; + } + } + + public List getItemStack() { + return Arrays.stream(itemStackKey.getItemStack()) + .map(itemStack -> { + var item = itemStack.copy(); + item.setCount(amount); + return item; + }).filter(item -> !item.isEmpty()).toList(); + } + } + + private static class MBPattern { + + @NotNull + final List> parts; + @NotNull + final Map predicateMap; + @NotNull + final Map blockMap; + @NotNull + final IMultiController controllerBase; + final int maxY, minY; + + public MBPattern(@NotNull Map blockMap, @NotNull List> parts, + @NotNull Map predicateMap, + @NotNull IMultiController controllerBase) { + this.parts = parts; + this.blockMap = blockMap; + this.predicateMap = predicateMap; + this.controllerBase = controllerBase; + int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE; + for (BlockPos pos : blockMap.keySet()) { + min = Math.min(min, pos.getY()); + max = Math.max(max, pos.getY()); + } + minY = min; + maxY = max; + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/item/tool/ae2/patternTool/Ae2BaseProcessingPattern.java b/src/main/java/org/gtlcore/gtlcore/api/item/tool/ae2/patternTool/Ae2BaseProcessingPattern.java index f35ffe219..949d9fdc1 100644 --- a/src/main/java/org/gtlcore/gtlcore/api/item/tool/ae2/patternTool/Ae2BaseProcessingPattern.java +++ b/src/main/java/org/gtlcore/gtlcore/api/item/tool/ae2/patternTool/Ae2BaseProcessingPattern.java @@ -64,8 +64,6 @@ public void useSetScale(int newScale, boolean div, long maxItemStack, long maxFl } public void setScale(int newScale, boolean div, long maxItemStack, long maxFluidStack) { - maxFluidStack = Math.min(maxFluidStack, 1000000L); - maxItemStack = Math.min(maxItemStack, 1000000L); useSetScale(newScale, div, maxItemStack, maxFluidStack); } diff --git a/src/main/java/org/gtlcore/gtlcore/api/item/tool/ae2/patternTool/Ae2BaseProcessingPatternHelper.java b/src/main/java/org/gtlcore/gtlcore/api/item/tool/ae2/patternTool/Ae2BaseProcessingPatternHelper.java index 1cbac06df..cb868410d 100644 --- a/src/main/java/org/gtlcore/gtlcore/api/item/tool/ae2/patternTool/Ae2BaseProcessingPatternHelper.java +++ b/src/main/java/org/gtlcore/gtlcore/api/item/tool/ae2/patternTool/Ae2BaseProcessingPatternHelper.java @@ -23,7 +23,7 @@ public class Ae2BaseProcessingPatternHelper { public static ItemStack multiplyScale(int scale, boolean div, AEProcessingPattern patternDetail, long maxItemStack, long maxFluidStack) { var input = patternDetail.getSparseInputs(); var output = patternDetail.getOutputs(); - if (checkModify(input, scale, div, maxItemStack, maxFluidStack) && checkModify(output, scale, div, 10000000L, 1000000L)) { + if (checkModify(input, scale, div, maxItemStack, maxFluidStack) && checkModify(output, scale, div, maxItemStack, maxFluidStack)) { var mulInput = new GenericStack[input.length]; var mulOutput = new GenericStack[output.length]; modifyStacks(input, mulInput, scale, div); diff --git a/src/main/java/org/gtlcore/gtlcore/api/item/tool/ae2/patternTool/RecipeStackHelper.java b/src/main/java/org/gtlcore/gtlcore/api/item/tool/ae2/patternTool/RecipeStackHelper.java index de34d4619..e50fe3218 100644 --- a/src/main/java/org/gtlcore/gtlcore/api/item/tool/ae2/patternTool/RecipeStackHelper.java +++ b/src/main/java/org/gtlcore/gtlcore/api/item/tool/ae2/patternTool/RecipeStackHelper.java @@ -17,6 +17,7 @@ import java.util.List; import java.util.Objects; import java.util.function.Function; +import java.util.stream.Collectors; public class RecipeStackHelper { @@ -87,6 +88,13 @@ public static String getFluidTranslatedName(FluidStack fluidStack) { return fluidStack.getDisplayName().getString() + "(" + Objects.requireNonNull(ForgeRegistries.FLUIDS.getKey(fluidStack.getFluid())) + " " + fluidStack.getAmount() + ") "; } + public static List getOutputItems(GTRecipe recipe) { + return recipe.getOutputContents(ItemRecipeCapability.CAP).stream() + .map(content -> ItemRecipeCapability.CAP.of(content.getContent())) + .map(ingredient -> ingredient.getItems()[0]) + .collect(Collectors.toList()); + } + private static @NotNull Function getContentFluidStackFunction() { return content -> { if (content == null || content.getContent() == null) { diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/MEExtendedOutputFancyConfigurator.java b/src/main/java/org/gtlcore/gtlcore/api/machine/MEExtendedOutputFancyConfigurator.java new file mode 100644 index 000000000..94bb11943 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/MEExtendedOutputFancyConfigurator.java @@ -0,0 +1,43 @@ +package org.gtlcore.gtlcore.api.machine; + +import com.gregtechceu.gtceu.api.gui.fancy.FancyMachineUIWidget; +import com.gregtechceu.gtceu.api.gui.fancy.IFancyUIProvider; +import com.gregtechceu.gtceu.common.data.GTItems; + +import com.lowdragmc.lowdraglib.gui.texture.IGuiTexture; +import com.lowdragmc.lowdraglib.gui.texture.ItemStackTexture; +import com.lowdragmc.lowdraglib.gui.widget.Widget; + +import net.minecraft.network.chat.Component; + +import java.util.List; +import java.util.function.Supplier; + +public class MEExtendedOutputFancyConfigurator implements IFancyUIProvider { + + protected final Supplier widgetSupplier; + + public MEExtendedOutputFancyConfigurator(Supplier widgetSupplier) { + this.widgetSupplier = widgetSupplier; + } + + @Override + public Widget createMainPage(FancyMachineUIWidget fancyMachineUIWidget) { + return widgetSupplier.get(); + } + + @Override + public IGuiTexture getTabIcon() { + return new ItemStackTexture(GTItems.ITEM_FILTER.asStack()); + } + + @Override + public Component getTitle() { + return Component.translatable("gtlcore.gui.filter_config.title"); + } + + @Override + public List getTabTooltips() { + return List.of(Component.translatable("tooltip.gtlcore.change_item_fluid_filter_priority")); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/CoilWorkableElectricMultipleRecipesMachine.java b/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/CoilWorkableElectricMultipleRecipesMachine.java index bf76d1984..b394454bb 100644 --- a/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/CoilWorkableElectricMultipleRecipesMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/CoilWorkableElectricMultipleRecipesMachine.java @@ -1,5 +1,6 @@ package org.gtlcore.gtlcore.api.machine.multiblock; +import org.gtlcore.gtlcore.api.recipe.RecipeResult; import org.gtlcore.gtlcore.common.machine.trait.MultipleRecipesLogic; import com.gregtechceu.gtceu.api.GTValues; @@ -21,14 +22,21 @@ */ public class CoilWorkableElectricMultipleRecipesMachine extends CoilWorkableElectricMultiblockMachine implements ParallelMachine { - private static final BiPredicate EBF_CHECK = (data, machine) -> { + protected static final BiPredicate EBF_CHECK = (data, machine) -> { var tm = (CoilWorkableElectricMultiblockMachine) machine; var temp = tm.getCoilType().getCoilTemperature() + 100L * Math.max(0, tm.getTier() - GTValues.MV); - return temp > data.getInt("ebf_temp"); + if (temp > data.getInt("ebf_temp")) return true; + else { + RecipeResult.of(machine, RecipeResult.FAIL_NO_ENOUGH_TEMPERATURE); + return false; + } }; - public CoilWorkableElectricMultipleRecipesMachine(IMachineBlockEntity holder) { + public CoilWorkableElectricMultipleRecipesMachine(IMachineBlockEntity holder, Object @NotNull... args) { super(holder); + if (args.length == 2 && args[0] instanceof Number reductionEUt && args[1] instanceof Number reductionDuration) { + getRecipeLogic().setReduction(reductionEUt.doubleValue(), reductionDuration.doubleValue()); + } } @Override diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/GTLPartAbility.java b/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/GTLPartAbility.java index 53b8e554f..21e8fb429 100644 --- a/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/GTLPartAbility.java +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/GTLPartAbility.java @@ -6,4 +6,5 @@ public class GTLPartAbility { public static final PartAbility NEUTRON_ACCELERATOR = new PartAbility("neutron_accelerator"); public static final PartAbility ITEMS_OUTPUT = new PartAbility("items_output"); + public static final PartAbility MOLECULAR_ASSEMBLER_MATRIX = new PartAbility("molecular_assembler_matrix"); } diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/IModularMachineHost.java b/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/IModularMachineHost.java new file mode 100644 index 000000000..68a473b6a --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/IModularMachineHost.java @@ -0,0 +1,123 @@ +package org.gtlcore.gtlcore.api.machine.multiblock; + +import com.gregtechceu.gtceu.api.machine.MetaMachine; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; + +import com.google.common.primitives.Ints; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.Set; + +/** + * 通用的模块化机器主机接口 + * 主机负责管理连接的模块,并提供模块扫描和验证功能 + * 支持一个主机连接多种不同类型的模块 + * + * @param 主机自身类型(自引用) + */ +public interface IModularMachineHost> { + + // ==================== Must Have Implementation ==================== + + @NotNull + Set> getModuleSet(); + + BlockPos[] getModuleScanPositions(); + + // ==================== Implementation From WorkableMachine ==================== + + BlockPos getPos(); + + Level getLevel(); + + boolean isFormed(); + + // ==================== Default Implementation ==================== + + default > void addModule(@NotNull M module) { + getModuleSet().add(module); + } + + default > void removeModule(@NotNull M module) { + getModuleSet().remove(module); + } + + @NotNull + default Set> getModules() { + return Collections.unmodifiableSet(getModuleSet()); + } + + default void safeClearModules() { + for (IModularMachineModule module : getModules()) { + @SuppressWarnings("unchecked") + H self = (H) this; + module.removeFromHost(self); + } + getModuleSet().clear(); + } + + default void scanAndConnectModules() { + final Level level = getLevel(); + if (level == null) return; + + final BlockPos[] positions = getModuleScanPositions(); + + @SuppressWarnings("unchecked") + H self = (H) this; + + for (BlockPos pos : positions) { + MetaMachine machine = MetaMachine.getMachine(level, pos); + if (isValidModule(machine)) { + @SuppressWarnings("unchecked") + IModularMachineModule module = (IModularMachineModule) machine; + assert module != null; + module.connectToHost(self); + } + } + } + + default boolean isValidModule(MetaMachine machine) { + if (!(machine instanceof IModularMachineModule module)) { + return false; + } + if (!module.isFormed()) { + return false; + } + try { + return module.getHostType().isInstance(this); + } catch (Exception e) { + return false; + } + } + + default int getFormedModuleCount() { + return Ints.saturatedCast(getModules().stream() + .filter(IModularMachineModule::isFormed) + .count()); + } + + default > int getFormedModuleCount(Class moduleClass) { + return Ints.saturatedCast(getModules().stream() + .filter(module -> moduleClass.isInstance(module) && module.isFormed()) + .count()); + } + + @NotNull + default > Set getModulesOfType(Class moduleClass) { + return getModules().stream() + .filter(moduleClass::isInstance) + .map(moduleClass::cast) + .collect(java.util.stream.Collectors.toSet()); + } + + default boolean exceedsModuleLimit() { + return getFormedModuleCount() > getMaxModuleCount(); + } + + default int getMaxModuleCount() { + return Integer.MAX_VALUE; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/IModularMachineModule.java b/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/IModularMachineModule.java new file mode 100644 index 000000000..afacd7e3c --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/IModularMachineModule.java @@ -0,0 +1,119 @@ +package org.gtlcore.gtlcore.api.machine.multiblock; + +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.MetaMachine; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * 通用的模块化机器模块接口 + * 模块负责连接到主机,并在主机提供的上下文中工作 + * + * @param 主机类型,必须实现 IModularMachineHost + * @param 模块自身类型(自引用) + */ +public interface IModularMachineModule, M extends IModularMachineModule> { + + // ==================== Must Have Implementation ==================== + + @Nullable + BlockPos getHostPosition(); + + void setHostPosition(@Nullable BlockPos pos); + + @Nullable + H getHost(); + + void setHost(@Nullable H host); + + // For Validation + @NotNull + Class getHostType(); + + BlockPos[] getHostScanPositions(); + + // ==================== Implementation From WorkableMachine ==================== + + Level getLevel(); + + boolean isFormed(); + + // ==================== Default Implementation ==================== + + default void connectToHost(@NotNull H host) { + setHost(host); + setHostPosition(host.getPos()); + @SuppressWarnings("unchecked") + M self = (M) this; + host.addModule(self); + onConnected(host); + } + + default void removeFromHost(@Nullable H host) { + setHostPosition(null); + setHost(null); + if (host != null) { + @SuppressWarnings("unchecked") + M self = (M) this; + host.removeModule(self); + } + onDisconnected(); + } + + default boolean findAndConnectToHost() { + final Level level = getLevel(); + if (!(level instanceof ServerLevel serverLevel)) return false; + + // Persisted Position First + final BlockPos savedPos = getHostPosition(); + if (savedPos != null) { + if (serverLevel.getBlockEntity(savedPos) instanceof IMachineBlockEntity imbe) { + MetaMachine machine = imbe.getMetaMachine(); + if (isValidHost(machine)) { + @SuppressWarnings("unchecked") + H host = (H) machine; + connectToHost(host); + return true; + } + } + } + + // Scan Others + final BlockPos[] positions = getHostScanPositions(); + for (BlockPos pos : positions) { + if (serverLevel.getBlockEntity(pos) instanceof IMachineBlockEntity imbe) { + MetaMachine machine = imbe.getMetaMachine(); + if (isValidHost(machine)) { + @SuppressWarnings("unchecked") + H host = (H) machine; + connectToHost(host); + return true; + } + } + } + + return false; + } + + default boolean isValidHost(MetaMachine machine) { + if (machine == null) return false; + if (!getHostType().isInstance(machine)) return false; + if (!(machine instanceof IModularMachineHost host)) return false; + return host.isFormed(); + } + + default boolean isConnectedToHost() { + return getHost() != null; + } + + // ==================== CallBack ==================== + + default void onConnected(@NotNull H host) {} + + default void onDisconnected() {} +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/MolecularAssemblerMultiblockMachine.java b/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/MolecularAssemblerMultiblockMachine.java new file mode 100644 index 000000000..0a25c978b --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/MolecularAssemblerMultiblockMachine.java @@ -0,0 +1,121 @@ +package org.gtlcore.gtlcore.api.machine.multiblock; + +import org.gtlcore.gtlcore.api.machine.trait.ICheckPatternMachine; + +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.api.gui.fancy.ConfiguratorPanel; +import com.gregtechceu.gtceu.api.gui.fancy.FancyMachineUIWidget; +import com.gregtechceu.gtceu.api.gui.fancy.IFancyUIProvider; +import com.gregtechceu.gtceu.api.gui.fancy.TooltipsPanel; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.feature.IFancyUIMachine; +import com.gregtechceu.gtceu.api.machine.feature.IInteractedMachine; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.IDisplayUIMachine; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiPart; +import com.gregtechceu.gtceu.utils.FormattingUtil; + +import com.lowdragmc.lowdraglib.gui.modular.ModularUI; +import com.lowdragmc.lowdraglib.gui.widget.*; + +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.HoverEvent; +import net.minecraft.network.chat.Style; +import net.minecraft.world.entity.player.Player; + +import java.util.*; + +public class MolecularAssemblerMultiblockMachine extends MolecularAssemblerMultiblockMachineBase implements IFancyUIMachine, IDisplayUIMachine, ICheckPatternMachine, IInteractedMachine { + + public MolecularAssemblerMultiblockMachine(IMachineBlockEntity holder, Object... args) { + super(holder, args); + } + + // ======================================== + // GUI + // ======================================== + + @Override + public void addDisplayText(List textList) { + IDisplayUIMachine.super.addDisplayText(textList); + if (isFormed()) { + textList.add(Component.translatable(getRecipeType().registryName.toLanguageKey()) + .setStyle(Style.EMPTY.withColor(ChatFormatting.AQUA) + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, + Component.translatable("gtceu.gui.machinemode.title"))))); + if (!isWorkingEnabled()) { + textList.add(Component.translatable("gtceu.multiblock.work_paused")); + } else if (isActive()) { + textList.add(Component.translatable("gtceu.multiblock.running")); + int currentProgress = (int) (recipeLogic.getProgressPercent() * 100); + textList.add(Component.translatable("gtceu.multiblock.progress", currentProgress)); + } else { + textList.add(Component.translatable("gtceu.multiblock.idling")); + } + if (recipeLogic.isWaiting()) { + textList.add(Component.translatable("gtceu.multiblock.waiting") + .setStyle(Style.EMPTY.withColor(ChatFormatting.RED))); + } + if (maxParallel > 1) { + textList.add(Component.translatable("gtceu.multiblock.parallel", Component.literal(FormattingUtil.formatNumbers(maxParallel)).withStyle(ChatFormatting.DARK_PURPLE)) + .withStyle(ChatFormatting.GRAY)); + } + textList.add(Component.translatable("gtlcore.multiblock.tick_Duration", Component.literal(FormattingUtil.formatNumbers(tickDuration)).withStyle(ChatFormatting.BLUE)) + .withStyle(ChatFormatting.GRAY)); + textList.add(Component.translatable("gtlcore.multiblock.contains_Patttern", Component.literal(FormattingUtil.formatNumbers(patternSize)).withStyle(ChatFormatting.GOLD)) + .withStyle(ChatFormatting.GRAY)); + } else { + Component tooltip = Component.translatable("gtceu.multiblock.invalid_structure.tooltip") + .withStyle(ChatFormatting.GRAY); + textList.add(Component.translatable("gtceu.multiblock.invalid_structure") + .withStyle(Style.EMPTY.withColor(ChatFormatting.RED) + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, tooltip)))); + } + getDefinition().getAdditionalDisplay().accept(this, textList); + } + + @Override + public void attachConfigurators(ConfiguratorPanel configuratorPanel) { + IFancyUIMachine.super.attachConfigurators(configuratorPanel); + ICheckPatternMachine.attachConfigurators(configuratorPanel, this); + } + + @Override + public Widget createUIWidget() { + var group = new WidgetGroup(0, 0, 182 + 8, 117 + 8); + group.addWidget(new DraggableScrollableWidgetGroup(4, 4, 182, 117) + .setBackground(getScreenTexture()) + .addWidget(new LabelWidget(4, 5, self().getBlockState().getBlock().getDescriptionId())) + .addWidget(new ComponentPanelWidget(4, 17, this::addDisplayText) + .setMaxWidthLimit(150) + .clickHandler(this::handleDisplayClick))); + group.setBackground(GuiTextures.BACKGROUND_INVERSE); + return group; + } + + @Override + public ModularUI createUI(Player entityPlayer) { + return new ModularUI(198, 208, this, entityPlayer) + .widget(new FancyMachineUIWidget(this, 198, 208)); + } + + @Override + public List getSubTabs() { + return getParts().stream() + .filter(Objects::nonNull) + .map(IFancyUIProvider.class::cast) + .toList(); + } + + @Override + public void attachTooltips(TooltipsPanel tooltipsPanel) { + for (IMultiPart part : getParts()) { + part.attachFancyTooltipsToController(this, tooltipsPanel); + } + } + + @Override + public boolean hasButton() { + return true; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/MolecularAssemblerMultiblockMachineBase.java b/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/MolecularAssemblerMultiblockMachineBase.java new file mode 100644 index 000000000..5c1ad11e2 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/MolecularAssemblerMultiblockMachineBase.java @@ -0,0 +1,319 @@ +package org.gtlcore.gtlcore.api.machine.multiblock; + +import org.gtlcore.gtlcore.api.machine.trait.AECraft.*; +import org.gtlcore.gtlcore.api.machine.trait.IMolecularAssemblerMachine; +import org.gtlcore.gtlcore.api.recipe.RecipeResult; +import org.gtlcore.gtlcore.common.machine.trait.MolecularAssemblerRecipesLogic; + +import com.gregtechceu.gtceu.api.block.ActiveBlock; +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.capability.recipe.IRecipeHandler; +import com.gregtechceu.gtceu.api.capability.recipe.RecipeCapability; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.feature.ICleanroomProvider; +import com.gregtechceu.gtceu.api.machine.feature.IMufflableMachine; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiPart; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.IWorkableMultiController; +import com.gregtechceu.gtceu.api.machine.multiblock.MultiblockControllerMachine; +import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.GTRecipeType; + +import com.lowdragmc.lowdraglib.syncdata.ISubscription; +import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; +import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; +import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.state.BlockState; + +import com.google.common.collect.ImmutableTable; +import com.google.common.collect.Table; +import com.google.common.primitives.Ints; +import it.unimi.dsi.fastutil.longs.LongSet; +import it.unimi.dsi.fastutil.longs.LongSets; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectArraySet; +import it.unimi.dsi.fastutil.objects.ObjectSet; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Objects; + +public abstract class MolecularAssemblerMultiblockMachineBase extends MultiblockControllerMachine implements IWorkableMultiController, IMufflableMachine, ParallelMachine, IMolecularAssemblerMachine { + + protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( + MolecularAssemblerMultiblockMachineBase.class, MultiblockControllerMachine.MANAGED_FIELD_HOLDER); + + private final GTRecipeType[] recipeTypes = this.getDefinition().getRecipeTypes(); + protected final List traitSubscriptions; + + @Persisted + @DescSynced + public final RecipeLogic recipeLogic; + + @Persisted + private int activeRecipeType = 0; + protected @Nullable LongSet activeBlocks; + + @Setter + @Getter + @Persisted + @DescSynced + protected boolean isMuffled; + protected boolean previouslyMuffled = true; + + @Nullable + protected IMolecularAssemblerHandler molecularAssemblerHandler = null; + + @Getter + protected int tickDuration = 40; + + @Getter + protected int maxParallel = 0; + + @Getter + protected int patternSize = 0; + + public MolecularAssemblerMultiblockMachineBase(IMachineBlockEntity holder, Object... args) { + super(holder); + this.recipeLogic = this.createRecipeLogic(args); + this.traitSubscriptions = new ObjectArrayList<>(); + } + + protected RecipeLogic createRecipeLogic(Object... args) { + return new MolecularAssemblerRecipesLogic(this); + } + + @Override + public IMolecularAssemblerHandler getMAHandler() { + return molecularAssemblerHandler; + } + + public void updateActiveBlocks(boolean active) { + if (activeBlocks != null) { + for (long pos : activeBlocks) { + var blockPos = BlockPos.of(pos); + var blockState = Objects.requireNonNull(getLevel()).getBlockState(blockPos); + var block = blockState.getBlock(); + if (block instanceof ActiveBlock activeBlock) { + BlockState newState = activeBlock.changeActive(blockState, active); + if (newState != blockState) { + this.getLevel().setBlockAndUpdate(blockPos, newState); + } + } + } + } + } + + // ======================================== + // Structure + // ======================================== + + @Override + public void onUnload() { + super.onUnload(); + this.traitSubscriptions.forEach(ISubscription::unsubscribe); + this.traitSubscriptions.clear(); + this.recipeLogic.inValid(); + } + + @Override + public void onPartUnload() { + super.onPartUnload(); + this.activeBlocks = null; + this.traitSubscriptions.forEach(ISubscription::unsubscribe); + this.traitSubscriptions.clear(); + this.recipeLogic.updateTickSubscription(); + } + + @Override + public void onStructureFormed() { + super.onStructureFormed(); + this.activeBlocks = this.getMultiblockState().getMatchContext().getOrDefault("vaBlocks", LongSets.emptySet()); + this.traitSubscriptions.forEach(ISubscription::unsubscribe); + this.traitSubscriptions.clear(); + + if (!isRemote()) update(); + + RecipeResult.of(this, null); + RecipeResult.ofWorking(this, null); + } + + private void update() { + final ObjectSet patternInventoryPos = new ObjectArraySet<>(); + int speedTier = 0; + int totalSlots = 0; + long totalParallel = 0; + IMECraftIOPart meCraftIOPart = null; + + for (IMultiPart part : this.getParts()) { + if (part instanceof IMECraftPatternContainer craftPatternContainer) { + patternInventoryPos.add(craftPatternContainer.getBlockPos()); + totalSlots += craftPatternContainer.getSlots(); + } else if (part instanceof IMECraftParallelCore parallelCore) { + totalParallel += parallelCore.getParallel(); + + } else if (part instanceof IMECraftSpeedCore speedCore) { + speedTier += speedCore.getSpeedTier(); + } else if (part instanceof IMECraftIOPart ioPart) { + if (meCraftIOPart == null) { + meCraftIOPart = ioPart; + } else { + throw new IllegalStateException("MolecularAssemblerIOMachine already exists"); + } + } + } + + if (totalParallel == 0) totalParallel = 1; + if (meCraftIOPart == null) throw new IllegalStateException("MolecularAssemblerIOMachine doesn't exist"); + + this.patternSize = totalSlots; + this.tickDuration = speedTier <= 39 ? 40 - speedTier : 1; + this.maxParallel = Ints.saturatedCast(totalParallel); + meCraftIOPart.init(patternInventoryPos); + + var handlerTrait = meCraftIOPart.getNotifiableMAHandlerTrait(); + this.traitSubscriptions.add(handlerTrait.addChangedListener(recipeLogic::updateTickSubscription)); + this.molecularAssemblerHandler = handlerTrait; + this.recipeLogic.updateTickSubscription(); + } + + @Override + public void onStructureInvalid() { + super.onStructureInvalid(); + this.activeBlocks = null; + this.traitSubscriptions.forEach(ISubscription::unsubscribe); + this.traitSubscriptions.clear(); + this.patternSize = 0; + this.tickDuration = 40; + this.maxParallel = 0; + this.molecularAssemblerHandler = null; + this.recipeLogic.resetRecipeLogic(); + } + + @Override + public void clientTick() { + super.clientTick(); + if (this.previouslyMuffled != this.isMuffled) { + this.previouslyMuffled = this.isMuffled; + if (this.recipeLogic != null) { + this.recipeLogic.updateSound(); + } + } + } + + // ======================================== + // Other + // ======================================== + + @Override + public @NotNull ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } + + @Override + public @NotNull GTRecipeType[] getRecipeTypes() { + return new GTRecipeType[0]; + } + + @Override + public @NotNull GTRecipeType getRecipeType() { + return this.recipeTypes[this.activeRecipeType]; + } + + @Override + public int getActiveRecipeType() { + return this.activeRecipeType; + } + + @Override + public void setActiveRecipeType(int activeRecipeType) { + this.activeRecipeType = activeRecipeType; + } + + @Override + public @NotNull RecipeLogic getRecipeLogic() { + return this.recipeLogic; + } + + @Override + public @Nullable ICleanroomProvider getCleanroom() { + return null; + } + + @Override + public void setCleanroom(ICleanroomProvider iCleanroomProvider) {} + + @Override + public @NotNull Table, List>> getCapabilitiesProxy() { + return ImmutableTable.of(); + } + + @Override + public boolean keepSubscribing() { + return false; + } + + @Override + public void notifyStatusChanged(RecipeLogic.Status oldStatus, RecipeLogic.Status newStatus) { + IWorkableMultiController.super.notifyStatusChanged(oldStatus, newStatus); + if (newStatus == RecipeLogic.Status.WORKING || oldStatus == RecipeLogic.Status.WORKING) { + this.updateActiveBlocks(newStatus == RecipeLogic.Status.WORKING); + } + } + + @Override + public boolean isRecipeLogicAvailable() { + return this.isFormed && !this.getMultiblockState().hasError(); + } + + public void afterWorking() { + for (IMultiPart part : this.getParts()) { + part.afterWorking(this); + } + + IWorkableMultiController.super.afterWorking(); + } + + public boolean beforeWorking(@Nullable GTRecipe recipe) { + for (IMultiPart part : this.getParts()) { + if (!part.beforeWorking(this)) { + return false; + } + } + + return IWorkableMultiController.super.beforeWorking(recipe); + } + + public boolean onWorking() { + for (IMultiPart part : this.getParts()) { + if (!part.onWorking(this)) { + return false; + } + } + + return IWorkableMultiController.super.onWorking(); + } + + public void onWaiting() { + for (IMultiPart part : this.getParts()) { + part.onWaiting(this); + } + + IWorkableMultiController.super.onWaiting(); + } + + public void setWorkingEnabled(boolean isWorkingAllowed) { + if (!isWorkingAllowed) { + for (IMultiPart part : this.getParts()) { + part.onPaused(this); + } + } + + IWorkableMultiController.super.setWorkingEnabled(isWorkingAllowed); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/NoEnergyMultiblockMachine.java b/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/NoEnergyMultiblockMachine.java index 77fb3ce8e..5278e088b 100644 --- a/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/NoEnergyMultiblockMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/multiblock/NoEnergyMultiblockMachine.java @@ -1,6 +1,9 @@ package org.gtlcore.gtlcore.api.machine.multiblock; +import org.gtlcore.gtlcore.api.machine.trait.ICheckPatternMachine; + import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.api.gui.fancy.ConfiguratorPanel; import com.gregtechceu.gtceu.api.gui.fancy.FancyMachineUIWidget; import com.gregtechceu.gtceu.api.gui.fancy.IFancyUIProvider; import com.gregtechceu.gtceu.api.gui.fancy.TooltipsPanel; @@ -27,7 +30,7 @@ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -public class NoEnergyMultiblockMachine extends WorkableMultiblockMachine implements IFancyUIMachine, IDisplayUIMachine { +public class NoEnergyMultiblockMachine extends WorkableMultiblockMachine implements IFancyUIMachine, IDisplayUIMachine, ICheckPatternMachine { protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( NoEnergyMultiblockMachine.class, WorkableMultiblockMachine.MANAGED_FIELD_HOLDER); @@ -67,6 +70,12 @@ public void addDisplayText(List textList) { getDefinition().getAdditionalDisplay().accept(this, textList); } + @Override + public void attachConfigurators(ConfiguratorPanel configuratorPanel) { + IFancyUIMachine.super.attachConfigurators(configuratorPanel); + ICheckPatternMachine.attachConfigurators(configuratorPanel, this); + } + @Override public Widget createUIWidget() { var group = new WidgetGroup(0, 0, 182 + 8, 117 + 8); @@ -105,4 +114,9 @@ public void attachTooltips(TooltipsPanel tooltipsPanel) { public ManagedFieldHolder getFieldHolder() { return MANAGED_FIELD_HOLDER; } + + @Override + public boolean hasButton() { + return true; + } } diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/AECraft/IMECraftIOPart.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/AECraft/IMECraftIOPart.java new file mode 100644 index 000000000..b9d617aaf --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/AECraft/IMECraftIOPart.java @@ -0,0 +1,18 @@ +package org.gtlcore.gtlcore.api.machine.trait.AECraft; + +import org.gtlcore.gtlcore.common.machine.multiblock.part.ae.NotifiableMAHandlerTrait; + +import net.minecraft.core.BlockPos; + +import appeng.api.networking.crafting.ICraftingProvider; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +public interface IMECraftIOPart extends ICraftingProvider { + + void init(@NotNull Set proxies); + + @NotNull + NotifiableMAHandlerTrait getNotifiableMAHandlerTrait(); +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/AECraft/IMECraftParallelCore.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/AECraft/IMECraftParallelCore.java new file mode 100644 index 000000000..413f06cfe --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/AECraft/IMECraftParallelCore.java @@ -0,0 +1,6 @@ +package org.gtlcore.gtlcore.api.machine.trait.AECraft; + +public interface IMECraftParallelCore { + + int getParallel(); +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/AECraft/IMECraftPatternContainer.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/AECraft/IMECraftPatternContainer.java new file mode 100644 index 000000000..06196fad1 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/AECraft/IMECraftPatternContainer.java @@ -0,0 +1,21 @@ +package org.gtlcore.gtlcore.api.machine.trait.AECraft; + +import com.lowdragmc.lowdraglib.side.item.IItemTransfer; + +import net.minecraft.core.BlockPos; + +public interface IMECraftPatternContainer { + + static int sumNonEmpty(IItemTransfer itemTransfer) { + int sum = 0; + for (int i = 0, n = itemTransfer.getSlots(); i < n; i++) { + var stack = itemTransfer.getStackInSlot(i); + if (!stack.isEmpty()) sum++; + } + return sum; + } + + BlockPos getBlockPos(); + + int getSlots(); +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/AECraft/IMECraftSpeedCore.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/AECraft/IMECraftSpeedCore.java new file mode 100644 index 000000000..373ea0f79 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/AECraft/IMECraftSpeedCore.java @@ -0,0 +1,6 @@ +package org.gtlcore.gtlcore.api.machine.trait.AECraft; + +public interface IMECraftSpeedCore { + + int getSpeedTier(); +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/AECraft/IMolecularAssemblerHandler.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/AECraft/IMolecularAssemblerHandler.java new file mode 100644 index 000000000..25e505b2b --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/AECraft/IMolecularAssemblerHandler.java @@ -0,0 +1,13 @@ +package org.gtlcore.gtlcore.api.machine.trait.AECraft; + +import com.gregtechceu.gtceu.api.recipe.GTRecipe; + +public interface IMolecularAssemblerHandler { + + void handleRecipeOutput(GTRecipe recipe); + + /** + * extract from MolecularAssemblerSupportedPatterns, limited by parallelAmount + */ + GTRecipe extractGTRecipe(long parallelAmount, int tickDuration); +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/AECraft/IMolecularAssemblerHandlerTrait.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/AECraft/IMolecularAssemblerHandlerTrait.java new file mode 100644 index 000000000..d28d29e28 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/AECraft/IMolecularAssemblerHandlerTrait.java @@ -0,0 +1,8 @@ +package org.gtlcore.gtlcore.api.machine.trait.AECraft; + +import com.lowdragmc.lowdraglib.syncdata.ISubscription; + +public interface IMolecularAssemblerHandlerTrait extends IMolecularAssemblerHandler { + + ISubscription addChangedListener(Runnable listener); +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/ICheckPatternMachine.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/ICheckPatternMachine.java new file mode 100644 index 000000000..207835c15 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/ICheckPatternMachine.java @@ -0,0 +1,43 @@ +package org.gtlcore.gtlcore.api.machine.trait; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.gui.fancy.ConfiguratorPanel; +import com.gregtechceu.gtceu.api.gui.fancy.IFancyConfiguratorButton; +import com.gregtechceu.gtceu.api.machine.MetaMachine; + +import com.lowdragmc.lowdraglib.gui.texture.ResourceTexture; + +import net.minecraft.network.chat.Component; + +import java.util.List; + +/** + * 代码参考自gto + * @line ... + */ + +public interface ICheckPatternMachine { + + ResourceTexture STRUCTURE_CHECK = new ResourceTexture(GTCEu.id("textures/gui/structure_check.png")); + + default void setTime(int time) {} + + default int getTime() { + return 0; + } + + default boolean hasButton() { + return false; + } + + static void attachConfigurators(ConfiguratorPanel configuratorPanel, MetaMachine machine) { + if (machine instanceof ICheckPatternMachine checkPatternMachine) { + configuratorPanel.attachConfigurators(new IFancyConfiguratorButton.Toggle( + STRUCTURE_CHECK.getSubTexture(0, 0, 1, 0.5), + STRUCTURE_CHECK.getSubTexture(0, 0.5, 1, 0.5), + () -> checkPatternMachine.getTime() < 1, (clickData, pressed) -> { + if (checkPatternMachine.getTime() > 0) checkPatternMachine.setTime(0); + }).setTooltipsSupplier(pressed -> List.of(Component.translatable("gtceu.machine.structure_check")))); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/IDistinctMachine.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/IDistinctMachine.java deleted file mode 100644 index 84989fe3d..000000000 --- a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/IDistinctMachine.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.gtlcore.gtlcore.api.machine.trait; - -import org.gtlcore.gtlcore.api.recipe.RecipeRunner; - -import net.minecraft.resources.ResourceLocation; - -import java.util.List; - -/** - * 部分代码参考自gto - * @line ... - */ - -public interface IDistinctMachine { - - default List getRecipeHandleParts() { - return List.of(); - } - - default void setRecipeHandleParts(List recipeHandleParts) {} - - default RecipeRunner.RecipeHandlePart getDistinctHatch() { - return null; - } - - default void setDistinctHatch(RecipeRunner.RecipeHandlePart hatch) {} - - default ResourceLocation getRecipeId() { - return null; - } - - default void setRecipeId(ResourceLocation recipeId) {} - - default void upDate() {} -} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/ILockRecipe.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/ILockRecipe.java new file mode 100644 index 000000000..7662dbbd5 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/ILockRecipe.java @@ -0,0 +1,42 @@ +package org.gtlcore.gtlcore.api.machine.trait; + +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.api.gui.fancy.ConfiguratorPanel; +import com.gregtechceu.gtceu.api.gui.fancy.IFancyConfiguratorButton; +import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; + +import net.minecraft.network.chat.Component; + +import java.util.*; + +/** + * 代码参考自gto + * @line ... + */ + +public interface ILockRecipe { + + default boolean isLock() { + return false; + } + + default void setLock(boolean lockRecipe) {} + + default GTRecipe getLockRecipe() { + return null; + } + + default void setLockRecipe(GTRecipe lockRecipe) {} + + static void attachRecipeLockable(ConfiguratorPanel configuratorPanel, RecipeLogic logic) { + if (logic instanceof ILockRecipe iLockRecipe) { + configuratorPanel.attachConfigurators(new IFancyConfiguratorButton.Toggle( + GuiTextures.BUTTON_PUBLIC_PRIVATE.getSubTexture(0, 0, 1, 0.5), + GuiTextures.BUTTON_PUBLIC_PRIVATE.getSubTexture(0, 0.5, 1, 0.5), + iLockRecipe::isLock, (clickData, pressed) -> iLockRecipe.setLock(pressed)) + .setTooltipsSupplier(pressed -> List.of(Component.translatable("config.gtceu.option.recipes") + .append("[").append(Component.translatable(pressed ? "theoneprobe.ae2.locked" : "theoneprobe.ae2.unlocked").append("]"))))); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/IMERecipeHandlerTrait.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/IMERecipeHandlerTrait.java new file mode 100644 index 000000000..47b552ffd --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/IMERecipeHandlerTrait.java @@ -0,0 +1,30 @@ +package org.gtlcore.gtlcore.api.machine.trait; + +import org.gtlcore.gtlcore.api.capability.IMERecipeHandler; + +import com.gregtechceu.gtceu.api.capability.recipe.IO; + +import com.lowdragmc.lowdraglib.syncdata.ISubscription; + +import java.util.function.Predicate; + +/** + * @author Dragonators + * @param + */ +public interface IMERecipeHandlerTrait, S> extends IMERecipeHandler { + + IO getIo(); + + ISubscription addChangedListener(Runnable var1); + + ISubscription addBufferChangedListener(Runnable var1); + + default int getPriority() { + return switch (getIo()) { + case OUT -> 10; + case BOTH -> 5; + case IN, NONE -> 0; + }; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/IMolecularAssemblerMachine.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/IMolecularAssemblerMachine.java new file mode 100644 index 000000000..62e9970bc --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/IMolecularAssemblerMachine.java @@ -0,0 +1,11 @@ +package org.gtlcore.gtlcore.api.machine.trait; + +import org.gtlcore.gtlcore.api.machine.trait.AECraft.IMolecularAssemblerHandler; + +import org.jetbrains.annotations.Nullable; + +public interface IMolecularAssemblerMachine { + + @Nullable + IMolecularAssemblerHandler getMAHandler(); +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/IRecipeCapabilityMachine.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/IRecipeCapabilityMachine.java new file mode 100644 index 000000000..2acc93c2e --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/IRecipeCapabilityMachine.java @@ -0,0 +1,108 @@ +package org.gtlcore.gtlcore.api.machine.trait; + +import com.gregtechceu.gtceu.api.capability.IDataAccessHatch; +import com.gregtechceu.gtceu.api.capability.IParallelHatch; +import com.gregtechceu.gtceu.api.capability.recipe.*; +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.api.gui.fancy.ConfiguratorPanel; +import com.gregtechceu.gtceu.api.gui.fancy.IFancyConfiguratorButton; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMaintenanceMachine; +import com.gregtechceu.gtceu.api.machine.multiblock.WorkableElectricMultiblockMachine; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; + +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.Style; + +import it.unimi.dsi.fastutil.objects.ReferenceSet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * 部分代码参考自GTO + * @line ... + */ + +public interface IRecipeCapabilityMachine { + + @NotNull + List getNormalRecipeHandlePart(IO io); + + @Nullable + RecipeHandlePart getSharedRecipeHandlePart(); + + boolean emptyRecipeHandlePart(); + + boolean emptyHandlePart(); + + // ==================== ME ==================== + + List getMEPatternRecipeHandleParts(); + + List> getMEOutputRecipeHandleParts(); + + void tryAddAndActiveMERhp(MEPatternRecipeHandlePart part, GTRecipe recipe, int slot); + + void sortMEOutput(); + + // ==================== Cache ==================== + + @Nullable + IRecipeHandlePart getActiveRecipeHandle(GTRecipe recipe); + + @NotNull + Iterator<@NotNull IRecipeHandlePart> getAllCachedRecipeHandlesIter(GTRecipe recipe); + + @NotNull + ReferenceSet<@NotNull IRecipeHandlePart> getAllCachedRecipeHandles(GTRecipe recipe); + + void tryAddAndActiveRhp(GTRecipe recipe, IRecipeHandlePart part); + + // ==================== Structure Form ==================== + + void upDate(); + + boolean isDistinct(); + + void setDistinct(boolean isDistinct); + + default boolean itemOutPutAlwaysMatch() { + return false; + } + + default boolean fluidOutPutAlwaysMatch() { + return false; + } + + default boolean isRecipeOutputAlwaysMatch(GTRecipe recipe) { + return false; + } + + default IParallelHatch getParallelHatch() { + return null; + } + + default IMaintenanceMachine getMaintenanceMachine() { + return null; + } + + default IDataAccessHatch getDataAccessHatch() { + return null; + } + + static void attachConfigurators(ConfiguratorPanel configuratorPanel, WorkableElectricMultiblockMachine machine) { + if (machine instanceof IRecipeCapabilityMachine distinctMachine) { + configuratorPanel.attachConfigurators(new IFancyConfiguratorButton.Toggle( + GuiTextures.BUTTON_DISTINCT_BUSES.getSubTexture(0, 0.5, 1, 0.5), + GuiTextures.BUTTON_DISTINCT_BUSES.getSubTexture(0, 0, 1, 0.5), + distinctMachine::isDistinct, (clickData, pressed) -> { + distinctMachine.setDistinct(pressed); + distinctMachine.upDate(); + }) + .setTooltipsSupplier(pressed -> List.of(Component.translatable("gtceu.multiblock.universal.distinct.all").setStyle(Style.EMPTY.withColor(ChatFormatting.YELLOW)) + .append(Component.translatable(pressed ? "gtceu.multiblock.universal.distinct.yes" : "gtceu.multiblock.universal.distinct.no"))))); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/IRecipeHandlePart.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/IRecipeHandlePart.java new file mode 100644 index 000000000..ace0c83e8 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/IRecipeHandlePart.java @@ -0,0 +1,3 @@ +package org.gtlcore.gtlcore.api.machine.trait; + +public interface IRecipeHandlePart {} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/IRecipeStatus.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/IRecipeStatus.java new file mode 100644 index 000000000..7c9e65df4 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/IRecipeStatus.java @@ -0,0 +1,18 @@ +package org.gtlcore.gtlcore.api.machine.trait; + +import org.gtlcore.gtlcore.api.recipe.RecipeResult; + +public interface IRecipeStatus { + + default void setRecipeStatus(RecipeResult result) {} + + default RecipeResult getRecipeStatus() { + return null; + } + + default void setWorkingStatus(RecipeResult result) {} + + default RecipeResult getWorkingStatus() { + return null; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEIORecipeHandlePart.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEIORecipeHandlePart.java new file mode 100644 index 000000000..fa141f5ed --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEIORecipeHandlePart.java @@ -0,0 +1,91 @@ +package org.gtlcore.gtlcore.api.machine.trait; + +import org.gtlcore.gtlcore.api.capability.IMERecipeHandler; +import org.gtlcore.gtlcore.api.machine.trait.MEPart.IMEFilterIOPartMachine; +import org.gtlcore.gtlcore.api.machine.trait.MEPart.IMEFilterIOTrait; + +import com.gregtechceu.gtceu.api.capability.recipe.FluidRecipeCapability; +import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; +import com.gregtechceu.gtceu.api.capability.recipe.RecipeCapability; +import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; + +import com.lowdragmc.lowdraglib.side.fluid.FluidStack; + +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + +import it.unimi.dsi.fastutil.objects.*; +import org.jetbrains.annotations.NotNull; + +import java.util.Comparator; +import java.util.List; +import java.util.function.Predicate; + +public class MEIORecipeHandlePart { + + public static final Comparator> COMPARATOR = Comparator.comparingInt(MEIORecipeHandlePart::getTotalPriority); + + protected final @NotNull T meTrait; + protected final @NotNull IMERecipeHandler itemHandler; + protected final @NotNull IMERecipeHandler fluidHandler; + protected final IMERecipeHandler[] handlers; + + public MEIORecipeHandlePart(@NotNull T meTrait, @NotNull IMERecipeHandler itemHandler, @NotNull IMERecipeHandler fluidHandler) { + this.itemHandler = itemHandler; + this.fluidHandler = fluidHandler; + this.handlers = new IMERecipeHandler[] { itemHandler, fluidHandler }; + this.meTrait = meTrait; + } + + public static MEIORecipeHandlePart of(IMEFilterIOPartMachine machine) { + var meTrait = machine.getMETrait(); + var pair = machine.getMERecipeHandlerTraits(); + return new MEIORecipeHandlePart<>(meTrait, pair.left(), pair.right()); + } + + public boolean hasItemFilter() { + return meTrait.hasItemFilter(); + } + + public boolean hasFluidFilter() { + return meTrait.hasFluidFilter(); + } + + @SuppressWarnings("unchecked") + public @NotNull , S> IMERecipeHandler getMECapability(RecipeCapability cap) { + if (cap == ItemRecipeCapability.CAP) return (IMERecipeHandler) itemHandler; + else if (cap == FluidRecipeCapability.CAP) return (IMERecipeHandler) fluidHandler; + else throw new AssertionError("Invalid recipe capability"); + } + + public Reference2ObjectOpenHashMap, List> meHandleOutput(Reference2ObjectOpenHashMap, List> contents, boolean simulate) { + boolean hasOutput = false; + for (var it = Reference2ObjectMaps.fastIterator(contents); it.hasNext();) { + var entry = it.next(); + var content = entry.getValue(); + if (content.isEmpty()) { + it.remove(); + continue; + } + var cap = entry.getKey(); + var result = meHandleOutput(cap, content, simulate); + if (result.size() != content.size()) { + hasOutput = true; + if (result.isEmpty()) it.remove(); + else entry.setValue(result); + } + } + if (!simulate && hasOutput) meTrait.notifySelfIO(); + return contents; + } + + @SuppressWarnings("unchecked") + public List meHandleOutput(RecipeCapability cap, List content, boolean simulate) { + var meHandler = getMECapability(cap); + return (List) (Object) meHandler.meHandleRecipeOutput(content, simulate); + } + + private int getTotalPriority() { + return itemHandler.getPriority() + fluidHandler.getPriority(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPart/IMEFilterIOPartMachine.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPart/IMEFilterIOPartMachine.java new file mode 100644 index 000000000..e29e4491d --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPart/IMEFilterIOPartMachine.java @@ -0,0 +1,21 @@ +package org.gtlcore.gtlcore.api.machine.trait.MEPart; + +import org.gtlcore.gtlcore.api.machine.trait.IMERecipeHandlerTrait; + +import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; + +import com.lowdragmc.lowdraglib.side.fluid.FluidStack; + +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + +import it.unimi.dsi.fastutil.Pair; +import org.jetbrains.annotations.NotNull; + +public interface IMEFilterIOPartMachine extends IMEIOPartMachine { + + Pair, IMERecipeHandlerTrait> getMERecipeHandlerTraits(); + + @NotNull + IMEFilterIOTrait getMETrait(); +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPart/IMEFilterIOTrait.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPart/IMEFilterIOTrait.java new file mode 100644 index 000000000..dbed6a42e --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPart/IMEFilterIOTrait.java @@ -0,0 +1,12 @@ +package org.gtlcore.gtlcore.api.machine.trait.MEPart; + +public interface IMEFilterIOTrait extends IMEIOTrait { + + default boolean hasItemFilter() { + return false; + } + + default boolean hasFluidFilter() { + return false; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPart/IMEIOPartMachine.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPart/IMEIOPartMachine.java new file mode 100644 index 000000000..058f4c7d4 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPart/IMEIOPartMachine.java @@ -0,0 +1,9 @@ +package org.gtlcore.gtlcore.api.machine.trait.MEPart; + +import org.jetbrains.annotations.NotNull; + +public interface IMEIOPartMachine { + + @NotNull + IMEIOTrait getMETrait(); +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPart/IMEIOTrait.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPart/IMEIOTrait.java new file mode 100644 index 000000000..9579b023a --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPart/IMEIOTrait.java @@ -0,0 +1,10 @@ +package org.gtlcore.gtlcore.api.machine.trait.MEPart; + +import com.gregtechceu.gtceu.api.capability.recipe.IO; + +public interface IMEIOTrait { + + IO getIO(); + + void notifySelfIO(); +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPart/IMEOutputPart.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPart/IMEOutputPart.java new file mode 100644 index 000000000..d58955b40 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPart/IMEOutputPart.java @@ -0,0 +1,36 @@ +package org.gtlcore.gtlcore.api.machine.trait.MEPart; + +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.api.gui.fancy.ConfiguratorPanel; +import com.gregtechceu.gtceu.api.gui.fancy.IFancyConfiguratorButton; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.config.ConfigHolder; + +import net.minecraft.network.chat.Component; + +import java.util.List; + +public interface IMEOutputPart { + + int ME_UPDATE_INTERVAL = ConfigHolder.INSTANCE.compat.ae2.updateIntervals; + + default boolean isReturn() { + return this.getTime() % IMEOutputPart.ME_UPDATE_INTERVAL == 0; + } + + byte getTime(); + + void setTime(byte time); + + void returnStorage(); + + static void attachRecipeLockable(ConfiguratorPanel configuratorPanel, MetaMachine machine) { + if (machine instanceof IMEOutputPart part) { + configuratorPanel.attachConfigurators(new IFancyConfiguratorButton.Toggle( + GuiTextures.BUTTON_ALLOW_IMPORT_EXPORT.getSubTexture(0, 0, 1, 0.5), + GuiTextures.BUTTON_ALLOW_IMPORT_EXPORT.getSubTexture(0, 0.5, 1, 0.5), + part::isReturn, (clickData, pressed) -> part.returnStorage()) + .setTooltipsSupplier(pressed -> List.of(Component.translatable("config.gtceu.option.hand.output")))); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPart/IMEPatternPartMachine.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPart/IMEPatternPartMachine.java new file mode 100644 index 000000000..ecb7ecea7 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPart/IMEPatternPartMachine.java @@ -0,0 +1,10 @@ +package org.gtlcore.gtlcore.api.machine.trait.MEPart; + +import org.jetbrains.annotations.NotNull; + +public interface IMEPatternPartMachine extends IMEFilterIOPartMachine { + + @Override + @NotNull + IMEPatternTrait getMETrait(); +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPart/IMEPatternTrait.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPart/IMEPatternTrait.java new file mode 100644 index 000000000..1ba7b2265 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPart/IMEPatternTrait.java @@ -0,0 +1,23 @@ +package org.gtlcore.gtlcore.api.machine.trait.MEPart; + +import com.gregtechceu.gtceu.api.recipe.GTRecipe; + +import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; +import it.unimi.dsi.fastutil.ints.IntConsumer; +import it.unimi.dsi.fastutil.objects.ObjectSet; +import org.jetbrains.annotations.NotNull; + +public interface IMEPatternTrait extends IMEFilterIOTrait { + + @NotNull + ObjectSet<@NotNull GTRecipe> getCachedGTRecipe(); + + void setSlotCacheRecipe(int index, GTRecipe recipe); + + @NotNull + Int2ReferenceMap> getSlot2RecipesCache(); + + void setOnPatternChange(IntConsumer removeMapOnSlot); + + boolean hasCacheInSlot(int slot); +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPatternRecipeHandlePart.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPatternRecipeHandlePart.java new file mode 100644 index 000000000..838e1eace --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEPatternRecipeHandlePart.java @@ -0,0 +1,126 @@ +package org.gtlcore.gtlcore.api.machine.trait; + +import org.gtlcore.gtlcore.api.capability.IMERecipeHandler; +import org.gtlcore.gtlcore.api.machine.trait.MEPart.IMEPatternPartMachine; +import org.gtlcore.gtlcore.api.machine.trait.MEPart.IMEPatternTrait; +import org.gtlcore.gtlcore.utils.datastructure.GTRecipe2IntBiMultiMap; + +import com.gregtechceu.gtceu.api.capability.recipe.FluidRecipeCapability; +import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; +import com.gregtechceu.gtceu.api.capability.recipe.RecipeCapability; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; + +import com.lowdragmc.lowdraglib.side.fluid.FluidStack; + +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + +import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps; +import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.objects.*; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.Predicate; + +public class MEPatternRecipeHandlePart extends MEIORecipeHandlePart implements IRecipeHandlePart { + + private final GTRecipe2IntBiMultiMap recipes2SlotsMap = new GTRecipe2IntBiMultiMap(); + + public MEPatternRecipeHandlePart(@NotNull IMEPatternTrait meTrait, @NotNull IMERecipeHandler itemHandler, @NotNull IMERecipeHandler fluidHandler) { + super(meTrait, itemHandler, fluidHandler); + } + + public static MEPatternRecipeHandlePart of(IMEPatternPartMachine machine) { + var meTrait = machine.getMETrait(); + var pair = machine.getMERecipeHandlerTraits(); + return new MEPatternRecipeHandlePart(meTrait, pair.left(), pair.right()); + } + + public IMERecipeHandler[] getMERecipeHandlers() { + return this.handlers; + } + + public @NotNull ObjectSet<@NotNull GTRecipe> getCachedGTRecipe() { + return meTrait.getCachedGTRecipe(); + } + + public void setLastRecipe2Slot(@NotNull GTRecipe gTRecipe, int slot) { + recipes2SlotsMap.put(gTRecipe, slot); + } + + public void restoreMachineCache(BiConsumer consumer) { + recipes2SlotsMap.clear(); + for (var entry : Int2ReferenceMaps.fastIterable(meTrait.getSlot2RecipesCache())) { + int slot = entry.getIntKey(); + for (GTRecipe recipe : entry.getValue()) { + recipes2SlotsMap.put(recipe, slot); + } + } + meTrait.setOnPatternChange(recipes2SlotsMap::removeByValue); + + for (var key : recipes2SlotsMap.keySet()) { + consumer.accept(key, this); + } + } + + /** + * @param cap ItemCAP or FluidCAP + * @param recipe GTRecipe -> slots to collect contents + * @return contents in specific slot, especially the one cached for this GTRecipe during the last match, or an empty + * map if no available slot is found + */ + public , S> Object2LongMap getMEContent(RecipeCapability cap, GTRecipe recipe) { + return this.getMECapability(cap).getStackMapFromFirstAvailableSlot(recipes2SlotsMap.getValues(recipe)); + } + + /** + * List won't be empty + * + * @return fail: -1, success and hasCache: -2, success and unCache: slot + */ + public int handleRecipe(GTRecipe recipe, + Reference2ObjectMap, List> contents, + boolean simulate, boolean setSlotCache) { + List itemContent = null; + List fluidContent = null; + + for (var entry : Reference2ObjectMaps.fastIterable(contents)) { + var key = entry.getKey(); + if (key == ItemRecipeCapability.CAP) itemContent = entry.getValue(); + else if (key == FluidRecipeCapability.CAP) fluidContent = entry.getValue(); + else throw new AssertionError("Invalid recipe capability entry"); + } + + // Priority 1: Init Contents + boolean hasItem = itemContent != null; + boolean hasFluid = fluidContent != null; + if (hasItem) itemHandler.initMEHandleContents(recipe, itemContent, simulate); + if (hasFluid) fluidHandler.initMEHandleContents(recipe, fluidContent, simulate); + + // Priority 2: Try all cached slots + IntSet cachedSlots = this.recipes2SlotsMap.getValues(recipe); + if (!cachedSlots.isEmpty()) { + for (int slot : cachedSlots) { + if (hasItem && !itemHandler.meHandleRecipe(recipe, simulate, slot)) continue; + if (hasFluid && !fluidHandler.meHandleRecipe(recipe, simulate, slot)) continue; + return slot; // all success + } + } + + // Priority 3: Try all active slots + for (int slot : itemHandler.getActiveSlots()) { + if (cachedSlots.contains(slot)) continue; + if (hasItem && !itemHandler.meHandleRecipe(recipe, simulate, slot)) continue; + if (hasFluid && !fluidHandler.meHandleRecipe(recipe, simulate, slot)) continue; + + // all success, then cache + if (!meTrait.hasCacheInSlot(slot) && setSlotCache) meTrait.setSlotCacheRecipe(slot, recipe); + return slot; + } + + return -1; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEStock/ExportOnlyAEConfigureFluidSlot.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEStock/ExportOnlyAEConfigureFluidSlot.java new file mode 100644 index 000000000..d97cf338f --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEStock/ExportOnlyAEConfigureFluidSlot.java @@ -0,0 +1,36 @@ +package org.gtlcore.gtlcore.api.machine.trait.MEStock; + +import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEFluidSlot; + +import appeng.api.stacks.GenericStack; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.Nullable; + +import static com.lowdragmc.lowdraglib.LDLib.isRemote; + +public class ExportOnlyAEConfigureFluidSlot extends ExportOnlyAEFluidSlot implements IMESlot { + + @Getter + @Setter + private Runnable onConfigChanged; + + public ExportOnlyAEConfigureFluidSlot(@Nullable GenericStack config, @Nullable GenericStack stock) { + super(config, stock); + } + + public ExportOnlyAEConfigureFluidSlot() { + super(); + } + + @Override + public void setConfig(@Nullable GenericStack config) { + super.setConfig(config); + if (!isRemote()) onConfigChanged.run(); + } + + @Override + public void setConfigWithoutNotify(@Nullable GenericStack config) { + this.config = config; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEStock/ExportOnlyAEConfigureItemSlot.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEStock/ExportOnlyAEConfigureItemSlot.java new file mode 100644 index 000000000..0d27b6601 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEStock/ExportOnlyAEConfigureItemSlot.java @@ -0,0 +1,36 @@ +package org.gtlcore.gtlcore.api.machine.trait.MEStock; + +import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEItemSlot; + +import appeng.api.stacks.GenericStack; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.Nullable; + +import static com.lowdragmc.lowdraglib.LDLib.isRemote; + +public class ExportOnlyAEConfigureItemSlot extends ExportOnlyAEItemSlot implements IMESlot { + + @Setter + @Getter + private Runnable onConfigChanged; + + public ExportOnlyAEConfigureItemSlot(@Nullable GenericStack config, @Nullable GenericStack stock) { + super(config, stock); + } + + public ExportOnlyAEConfigureItemSlot() { + super(); + } + + @Override + public void setConfigWithoutNotify(@Nullable GenericStack config) { + this.config = config; + } + + @Override + public void setConfig(@Nullable GenericStack config) { + super.setConfig(config); + if (!isRemote()) onConfigChanged.run(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEStock/IMEPartMachine.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEStock/IMEPartMachine.java new file mode 100644 index 000000000..6261a6b61 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEStock/IMEPartMachine.java @@ -0,0 +1,40 @@ +package org.gtlcore.gtlcore.api.machine.trait.MEStock; + +import com.lowdragmc.lowdraglib.side.fluid.FluidStack; + +import net.minecraft.world.item.ItemStack; + +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +import javax.annotation.Nullable; + +public interface IMEPartMachine { + + default @Nullable Object2LongMap getMEItemMap() { + return null; + } + + default @NotNull List getMEFluidList() { + return List.of(); + } + + default Object2LongOpenHashMap getItemMap() { + return null; + } + + default @NotNull List getFluidList() { + return List.of(); + } + + default void setChanged(boolean value) {} + + default boolean getChanged() { + return false; + } + + void onConfigChanged(); +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEStock/IMESlot.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEStock/IMESlot.java new file mode 100644 index 000000000..1089788a5 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/MEStock/IMESlot.java @@ -0,0 +1,13 @@ +package org.gtlcore.gtlcore.api.machine.trait.MEStock; + +import appeng.api.stacks.GenericStack; +import org.jetbrains.annotations.Nullable; + +public interface IMESlot { + + void setOnConfigChanged(Runnable onConfigChanged); + + Runnable getOnConfigChanged(); + + void setConfigWithoutNotify(@Nullable GenericStack config); +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/NotifiableCircuitItemStackHandler.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/NotifiableCircuitItemStackHandler.java index bb80e25d3..74d99ec4b 100644 --- a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/NotifiableCircuitItemStackHandler.java +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/NotifiableCircuitItemStackHandler.java @@ -3,11 +3,17 @@ import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.machine.trait.NotifiableItemStackHandler; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; import com.gregtechceu.gtceu.common.data.GTItems; +import com.gregtechceu.gtceu.common.item.IntCircuitBehaviour; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; public class NotifiableCircuitItemStackHandler extends NotifiableItemStackHandler { @@ -29,4 +35,24 @@ public ItemStack insertItem(int slot, @NotNull ItemStack stack, boolean simulate public int getSlotLimit(int slot) { return 1; } + + @Override + public List handleRecipeInner(IO io, GTRecipe recipe, List left, @Nullable String slotName, boolean simulate) { + if (!simulate) return left; + + for (var it = left.iterator(); it.hasNext();) { + var items = it.next().getItems(); + + if (items.length == 0 || items[0].isEmpty()) { + continue; + } + + if (GTItems.INTEGRATED_CIRCUIT.is(items[0].getItem()) && IntCircuitBehaviour.getCircuitConfiguration(items[0]) == IntCircuitBehaviour.getCircuitConfiguration(storage.getStackInSlot(0))) { + it.remove(); + break; + } + } + + return left.isEmpty() ? null : left; + } } diff --git a/src/main/java/org/gtlcore/gtlcore/api/machine/trait/RecipeHandlePart.java b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/RecipeHandlePart.java new file mode 100644 index 000000000..f4b84102a --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/machine/trait/RecipeHandlePart.java @@ -0,0 +1,170 @@ +package org.gtlcore.gtlcore.api.machine.trait; + +import org.gtlcore.gtlcore.api.machine.trait.MEStock.IMEPartMachine; + +import com.gregtechceu.gtceu.api.capability.recipe.*; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; + +import com.lowdragmc.lowdraglib.side.fluid.FluidStack; + +import net.minecraft.world.item.ItemStack; + +import com.hepdd.gtmthings.common.block.machine.trait.CatalystFluidStackHandler; +import com.hepdd.gtmthings.common.block.machine.trait.CatalystItemStackHandler; +import it.unimi.dsi.fastutil.objects.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.Predicate; + +public class RecipeHandlePart implements IRecipeHandlePart { + + public static final Comparator COMPARATOR = (h1, h2) -> { + int cmp = Long.compare(h1.getPriority(), h2.getPriority()); + if (cmp != 0) return cmp; + boolean b1 = h1.getTotalContentAmount() > 0; + boolean b2 = h2.getTotalContentAmount() > 0; + return Boolean.compare(b1, b2); + }; + + private final Reference2ObjectMap, List>> handlerMap = new Reference2ObjectOpenHashMap<>(); + private final List> sharedFluidHandlers = new ObjectArrayList<>(); + + public RecipeHandlePart(Iterable> handlers) { + for (var handler : handlers) { + handlerMap.computeIfAbsent(handler.getCapability(), c -> new ObjectArrayList<>()).add(handler); + } + } + + public static RecipeHandlePart of(IO io, Iterable> handlers) { + RecipeHandlePart rhl = new RecipeHandlePart(handlers); + if (io == IO.OUT) { + for (var entry : rhl.getHandlerFastIterable()) { + entry.getValue().sort(IRecipeHandler.ENTRY_COMPARATOR); + } + } + return rhl; + } + + public void addSharedFluidHandlers(Iterable> handlers) { + for (var handler : handlers) { + if (handler.getCapability() == FluidRecipeCapability.CAP) sharedFluidHandlers.add(handler); + } + } + + public ObjectIterable, List>>> getHandlerFastIterable() { + return Reference2ObjectMaps.fastIterable(handlerMap); + } + + @SuppressWarnings("unchecked") + public , S> Object2LongMap getSelfContent(RecipeCapability cap) { + if (cap == ItemRecipeCapability.CAP) { + return (Object2LongMap) createItemMap(this.getCapability(cap), new Object2LongOpenHashMap<>()); + } else if (cap == FluidRecipeCapability.CAP) { + return (Object2LongMap) createFluidMap(this.getCapability(cap), new Object2LongOpenHashMap<>()); + } + return Object2LongMaps.EMPTY_MAP; + } + + @SuppressWarnings("unchecked") + public , S> Object2LongMap getContentWithShared(RecipeCapability cap) { + if (cap == ItemRecipeCapability.CAP) { + return (Object2LongMap) createItemMap(this.getCapability(cap), new Object2LongOpenHashMap<>()); + } else if (cap == FluidRecipeCapability.CAP) { + return (Object2LongMap) createFluidMap(this.sharedFluidHandlers, createFluidMap(this.getCapability(cap), new Object2LongOpenHashMap<>())); + } + return Object2LongMaps.EMPTY_MAP; + } + + @SuppressWarnings("unchecked") + public @NotNull List> getCapability(RecipeCapability cap) { + List handlers = handlerMap.getOrDefault(cap, Collections.emptyList()); + return (List>) handlers; + } + + public Reference2ObjectOpenHashMap, List> handleRecipe(IO io, GTRecipe recipe, + Map, List> contents, + boolean simulate) { + var copy = new Reference2ObjectOpenHashMap<>(contents); + if (!handlerMap.isEmpty()) { + for (var it = copy.reference2ObjectEntrySet().fastIterator(); it.hasNext();) { + var entry = it.next(); + var handlerList = getCapability(entry.getKey()); + for (var handler : handlerList) { + var left = handler.handleRecipe(io, recipe, entry.getValue(), null, simulate); + if (left == null) { + it.remove(); + break; + } else entry.setValue(new ArrayList<>(left)); + } + } + } + return copy; + } + + @Nullable + public List handleRecipe(IO io, GTRecipe recipe, RecipeCapability cap, List contents, boolean simulate) { + var handlerList = getCapability(cap); + for (var handler : handlerList) { + contents = handler.handleRecipe(io, recipe, contents, null, simulate); + if (contents == null) return null; + } + return contents; + } + + private static Object2LongOpenHashMap createItemMap(List> handlers, Object2LongOpenHashMap itemContent) { + for (var handler : handlers) { + if (handler instanceof CatalystItemStackHandler || handler instanceof NotifiableCircuitItemStackHandler) continue; + if (handler instanceof IMEPartMachine aeItemHandler) { + final var map = aeItemHandler.getMEItemMap(); + if (map != null) { + for (var it = Object2LongMaps.fastIterator(map); it.hasNext();) { + var entry = it.next(); + itemContent.addTo(entry.getKey(), entry.getLongValue()); + } + } + } else { + for (var o : handler.getContents()) { + if (o instanceof ItemStack stack) { + itemContent.addTo(stack, stack.getCount()); + } + } + } + } + return itemContent; + } + + private static Object2LongOpenHashMap createFluidMap(List> handlers, Object2LongOpenHashMap fluidContent) { + for (var fluid : handlers) { + if (fluid instanceof CatalystFluidStackHandler) continue; + for (var o : fluid.getContents()) { + if (o instanceof FluidStack stack) { + fluidContent.addTo(stack, stack.getAmount()); + } + } + } + return fluidContent; + } + + private long getPriority() { + long priority = 0; + for (List> list : handlerMap.values()) { + for (IRecipeHandler handler : list) { + priority += handler.getPriority(); + } + } + return priority; + } + + private double getTotalContentAmount() { + double sum = 0; + for (List> list : handlerMap.values()) { + for (IRecipeHandler handler : list) { + sum += handler.getTotalContentAmount(); + } + } + return sum; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/pattern/AdvancedBlockPattern.java b/src/main/java/org/gtlcore/gtlcore/api/pattern/AdvancedBlockPattern.java new file mode 100644 index 000000000..8af99fc67 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/pattern/AdvancedBlockPattern.java @@ -0,0 +1,473 @@ +package org.gtlcore.gtlcore.api.pattern; + +import org.gtlcore.gtlcore.common.item.UltimateTerminalBehavior; + +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; +import com.gregtechceu.gtceu.api.pattern.BlockPattern; +import com.gregtechceu.gtceu.api.pattern.MultiblockState; +import com.gregtechceu.gtceu.api.pattern.TraceabilityPredicate; +import com.gregtechceu.gtceu.api.pattern.predicates.SimplePredicate; +import com.gregtechceu.gtceu.api.pattern.util.RelativeDirection; + +import com.lowdragmc.lowdraglib.utils.BlockInfo; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.*; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.Property; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraftforge.common.capabilities.ForgeCapabilities; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.items.IItemHandler; + +import com.mojang.datafixers.util.Pair; +import it.unimi.dsi.fastutil.ints.IntObjectPair; +import it.unimi.dsi.fastutil.objects.*; +import org.apache.commons.lang3.ArrayUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import oshi.util.tuples.Triplet; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.*; +import java.util.function.*; + +/** + * 代码参考自gtmthings + * @line ... + */ + +public class AdvancedBlockPattern extends BlockPattern { + + static Direction[] FACINGS = { Direction.SOUTH, Direction.NORTH, Direction.WEST, Direction.EAST, Direction.UP, + Direction.DOWN }; + static Direction[] FACINGS_H = { Direction.SOUTH, Direction.NORTH, Direction.WEST, Direction.EAST }; + + public final int[][] aisleRepetitions; + public final RelativeDirection[] structureDir; + protected final TraceabilityPredicate[][][] blockMatches; // [z][y][x] + protected final int fingerLength; // z size + protected final int thumbLength; // y size + protected final int palmLength; // x size + protected final int[] centerOffset; // x, y, z, minZ, maxZ + + public AdvancedBlockPattern(TraceabilityPredicate[][][] predicatesIn, RelativeDirection[] structureDir, int[][] aisleRepetitions, int[] centerOffset) { + super(predicatesIn, structureDir, aisleRepetitions, centerOffset); + this.blockMatches = predicatesIn; + this.fingerLength = predicatesIn.length; + this.structureDir = structureDir; + this.aisleRepetitions = aisleRepetitions; + + if (this.fingerLength > 0) { + this.thumbLength = predicatesIn[0].length; + + if (this.thumbLength > 0) { + this.palmLength = predicatesIn[0][0].length; + } else { + this.palmLength = 0; + } + } else { + this.thumbLength = 0; + this.palmLength = 0; + } + + this.centerOffset = centerOffset; + } + + public static AdvancedBlockPattern getAdvancedBlockPattern(BlockPattern blockPattern) { + try { + Class clazz = BlockPattern.class; + // blockMatches + Field blockMatchesField = clazz.getDeclaredField("blockMatches"); + blockMatchesField.setAccessible(true); + TraceabilityPredicate[][][] blockMatches = (TraceabilityPredicate[][][]) blockMatchesField.get(blockPattern); + // structureDir + Field structureDirField = clazz.getDeclaredField("structureDir"); + structureDirField.setAccessible(true); + RelativeDirection[] structureDir = (RelativeDirection[]) structureDirField.get(blockPattern); + // aisleRepetitions + Field aisleRepetitionsField = clazz.getDeclaredField("aisleRepetitions"); + aisleRepetitionsField.setAccessible(true); + int[][] aisleRepetitions = (int[][]) aisleRepetitionsField.get(blockPattern); + // centerOffset + Field centerOffsetField = clazz.getDeclaredField("centerOffset"); + centerOffsetField.setAccessible(true); + int[] centerOffset = (int[]) centerOffsetField.get(blockPattern); + + return new AdvancedBlockPattern(blockMatches, structureDir, aisleRepetitions, centerOffset); + } catch (Exception ignored) {} + return null; + } + + public void autoBuild(Player player, MultiblockState worldState, + UltimateTerminalBehavior.AutoBuildSetting autoBuildSetting) { + Level world = player.level(); + int minZ = -centerOffset[4]; + clearWorldState(worldState); + IMultiController controller = worldState.getController(); + BlockPos centerPos = controller.self().getPos(); + Direction facing = controller.self().getFrontFacing(); + Direction upwardsFacing = controller.self().getUpwardsFacing(); + boolean isFlipped = autoBuildSetting.isFlipped(); + Object2IntOpenHashMap cacheGlobal = new Object2IntOpenHashMap<>(worldState.getGlobalCount()); + Object2IntOpenHashMap cacheLayer = new Object2IntOpenHashMap<>(worldState.getLayerCount()); + Object2ObjectOpenHashMap blocks = new Object2ObjectOpenHashMap<>(); + ObjectOpenHashSet placeBlockPos = new ObjectOpenHashSet<>(); + blocks.put(centerPos, controller); + if (controller.isFormed() && autoBuildSetting.isReplaceMode()) controller.onStructureInvalid(); + + int[] repeat = new int[this.fingerLength]; + for (int h = 0; h < this.fingerLength; h++) { + var minH = aisleRepetitions[h][0]; + var maxH = aisleRepetitions[h][1]; + if (minH != maxH) { + repeat[h] = Math.max(minH, Math.min(maxH, autoBuildSetting.getRepeatCount())); + } else repeat[h] = minH; + } + + for (int c = 0, z = minZ++, r; c < this.fingerLength; c++) { + for (r = 0; r < repeat[c]; r++) { + cacheLayer.clear(); + for (int b = 0, y = -centerOffset[1]; b < this.thumbLength; b++, y++) { + for (int a = 0, x = -centerOffset[0]; a < this.palmLength; a++, x++) { + TraceabilityPredicate predicate = this.blockMatches[c][b][a]; + if (predicate.isAny()) continue; + BlockPos pos = setActualRelativeOffset(x, y, z, facing, upwardsFacing, isFlipped) + .offset(centerPos.getX(), centerPos.getY(), centerPos.getZ()); + updateWorldState(worldState, pos, predicate); + ItemStack itemStack = null; + if (!world.isEmptyBlock(pos)) { + Block block = world.getBlockState(pos).getBlock(); + if (autoBuildSetting.getBlocks().contains(block) && autoBuildSetting.isReplaceMode()) { + itemStack = block.asItem().getDefaultInstance(); + } else { + blocks.put(pos, world.getBlockState(pos)); + for (SimplePredicate limit : predicate.limited) limit.testLimited(worldState); + continue; + } + } + + boolean find = false; + BlockInfo[] infos = new BlockInfo[0]; + for (var limit : predicate.limited) { + if (limit.minLayerCount > 0 && autoBuildSetting.isPlaceHatch(limit.candidates.get())) { + int curr = cacheLayer.getInt(limit); + if (curr < limit.minLayerCount && + (limit.maxLayerCount == -1 || curr < limit.maxLayerCount)) { + cacheLayer.addTo(limit, 1); + } else continue; + } else continue; + infos = limit.candidates == null ? null : limit.candidates.get(); + find = true; + break; + } + if (!find) { + for (var limit : predicate.limited) { + if (limit.minCount > 0 && autoBuildSetting.isPlaceHatch(limit.candidates.get())) { + int curr = cacheGlobal.getInt(limit); + if (curr < limit.minCount && (limit.maxCount == -1 || curr < limit.maxCount)) { + cacheGlobal.addTo(limit, 1); + } else continue; + } else continue; + infos = limit.candidates == null ? null : limit.candidates.get(); + find = true; + break; + } + } + if (!find) { // no limited + for (SimplePredicate limit : predicate.limited) { + if (!autoBuildSetting.isPlaceHatch(limit.candidates.get())) continue; + if (limit.maxLayerCount != -1 && + cacheLayer.getOrDefault(limit, Integer.MAX_VALUE) == limit.maxLayerCount) { + continue; + } + if (limit.maxCount != -1 && + cacheGlobal.getOrDefault(limit, Integer.MAX_VALUE) == limit.maxCount) { + continue; + } + cacheLayer.addTo(limit, 1); + cacheGlobal.addTo(limit, 1); + infos = ArrayUtils.addAll(infos, limit.candidates == null ? null : limit.candidates.get()); + } + for (SimplePredicate common : predicate.common) { + if (common.candidates != null && predicate.common.size() > 1 && !autoBuildSetting.isPlaceHatch(common.candidates.get())) { + continue; + } + infos = ArrayUtils.addAll(infos, common.candidates == null ? null : common.candidates.get()); + } + } + + List candidates = autoBuildSetting.apply(infos); + + if (autoBuildSetting.isReplaceMode() && itemStack != null && + ItemStack.isSameItem(candidates.get(0), itemStack)) + continue; + + // check inventory + var result = foundItem(player, candidates, item -> item instanceof BlockItem); + ItemStack found = result.getA(); + IItemHandler handler = result.getB(); + int foundSlot = result.getC(); + + if (found == null) continue; + + // check can get old coilBlock + IItemHandler holderHandler = null; + int holderSlot = -1; + if (autoBuildSetting.isReplaceMode() && itemStack != null) { + Pair holderResult = foundHolderSlot(player, itemStack); + holderHandler = holderResult.getFirst(); + holderSlot = holderResult.getSecond(); + + if (holderHandler != null && holderSlot < 0) { + continue; + } + } + + if (autoBuildSetting.isReplaceMode() && itemStack != null) { + world.removeBlock(pos, true); + if (holderHandler != null) holderHandler.insertItem(holderSlot, itemStack, false); + } + + BlockItem itemBlock = (BlockItem) found.getItem(); + BlockPlaceContext context = new BlockPlaceContext(world, player, InteractionHand.MAIN_HAND, + found, BlockHitResult.miss(player.getEyePosition(0), Direction.UP, pos)); + InteractionResult interactionResult = itemBlock.place(context); + if (interactionResult != InteractionResult.FAIL) { + placeBlockPos.add(pos); + if (handler != null) handler.extractItem(foundSlot, 1, false); + } + if (world.getBlockEntity(pos) instanceof IMachineBlockEntity machineBlockEntity) { + blocks.put(pos, machineBlockEntity.getMetaMachine()); + } else blocks.put(pos, world.getBlockState(pos)); + } + } + z++; + } + } + Direction frontFacing = controller.self().getFrontFacing(); + blocks.object2ObjectEntrySet().fastForEach((entry -> { + // adjust facing + var pos = entry.getKey(); + var block = entry.getValue(); + if (!(block instanceof IMultiController)) { + if (block instanceof BlockState && placeBlockPos.contains(pos)) { + resetFacing(pos, (BlockState) block, frontFacing, (p, f) -> { + Object object = blocks.get(p.relative(f)); + return object == null || + (object instanceof BlockState && ((BlockState) object).getBlock() == Blocks.AIR); + }, state -> world.setBlock(pos, state, 3)); + } else if (block instanceof MetaMachine machine) { + resetFacing(pos, machine.getBlockState(), frontFacing, (p, f) -> { + Object object = blocks.get(p.relative(f)); + if (object == null || (object instanceof BlockState blockState && blockState.isAir())) { + return machine.isFacingValid(f); + } + return false; + }, state -> world.setBlock(pos, state, 3)); + } + } + })); + } + + public static Triplet foundItem(Player player, + List candidates, + Predicate test) { + ItemStack found = null; + IItemHandler handler = null; + int foundSlot = -1; + if (!player.isCreative()) { + var foundHandler = getMatchStackWithHandler(candidates, + player.getCapability(ForgeCapabilities.ITEM_HANDLER), test); + if (foundHandler != null) { + foundSlot = foundHandler.firstInt(); + handler = foundHandler.second(); + found = handler.getStackInSlot(foundSlot).copy(); + } + } else { + for (ItemStack candidate : candidates) { + found = candidate.copy(); + if (!found.isEmpty() && test.test(found.getItem())) break; + found = null; + } + } + return new Triplet<>(found, handler, foundSlot); + } + + private Pair foundHolderSlot(Player player, ItemStack coilItemStack) { + IItemHandler handler = null; + int foundSlot = -1; + if (!player.isCreative()) { + handler = player.getCapability(ForgeCapabilities.ITEM_HANDLER).orElse(null); + for (int i = 0; i < handler.getSlots(); i++) { + @NotNull + ItemStack stack = handler.getStackInSlot(i); + if (stack.isEmpty()) { + if (foundSlot < 0) foundSlot = i; + } else if (ItemStack.isSameItemSameTags(coilItemStack, stack) && (stack.getCount() + 1) <= stack.getMaxStackSize()) { + foundSlot = i; + } + } + } + + return new Pair<>(handler, foundSlot); + } + + private void clearWorldState(MultiblockState worldState) { + try { + Class clazz = Class.forName("com.gregtechceu.gtceu.api.pattern.MultiblockState"); + Method method = clazz.getDeclaredMethod("clean"); + method.setAccessible(true); + method.invoke(worldState); + } catch (Exception ignored) {} + } + + private void updateWorldState(MultiblockState worldState, BlockPos posIn, TraceabilityPredicate predicate) { + try { + Class clazz = Class.forName("com.gregtechceu.gtceu.api.pattern.MultiblockState"); + Method method = clazz.getDeclaredMethod("update", BlockPos.class, TraceabilityPredicate.class); + method.setAccessible(true); + method.invoke(worldState, posIn, predicate); + } catch (Exception ignored) {} + } + + private BlockPos setActualRelativeOffset(int x, int y, int z, Direction facing, Direction upwardsFacing, + boolean isFlipped) { + int[] c0 = new int[] { x, y, z }, c1 = new int[3]; + if (facing == Direction.UP || facing == Direction.DOWN) { + Direction of = facing == Direction.DOWN ? upwardsFacing : upwardsFacing.getOpposite(); + for (int i = 0; i < 3; i++) { + switch (structureDir[i].getActualFacing(of)) { + case UP -> c1[1] = c0[i]; + case DOWN -> c1[1] = -c0[i]; + case WEST -> c1[0] = -c0[i]; + case EAST -> c1[0] = c0[i]; + case NORTH -> c1[2] = -c0[i]; + case SOUTH -> c1[2] = c0[i]; + } + } + int xOffset = upwardsFacing.getStepX(); + int zOffset = upwardsFacing.getStepZ(); + int tmp; + if (xOffset == 0) { + tmp = c1[2]; + c1[2] = zOffset > 0 ? c1[1] : -c1[1]; + c1[1] = zOffset > 0 ? -tmp : tmp; + } else { + tmp = c1[0]; + c1[0] = xOffset > 0 ? c1[1] : -c1[1]; + c1[1] = xOffset > 0 ? -tmp : tmp; + } + if (isFlipped) { + if (upwardsFacing == Direction.NORTH || upwardsFacing == Direction.SOUTH) { + c1[0] = -c1[0]; // flip X-axis + } else { + c1[2] = -c1[2]; // flip Z-axis + } + } + } else { + for (int i = 0; i < 3; i++) { + switch (structureDir[i].getActualFacing(facing)) { + case UP -> c1[1] = c0[i]; + case DOWN -> c1[1] = -c0[i]; + case WEST -> c1[0] = -c0[i]; + case EAST -> c1[0] = c0[i]; + case NORTH -> c1[2] = -c0[i]; + case SOUTH -> c1[2] = c0[i]; + } + } + if (upwardsFacing == Direction.WEST || upwardsFacing == Direction.EAST) { + int xOffset = upwardsFacing == Direction.EAST ? facing.getClockWise().getStepX() : + facing.getClockWise().getOpposite().getStepX(); + int zOffset = upwardsFacing == Direction.EAST ? facing.getClockWise().getStepZ() : + facing.getClockWise().getOpposite().getStepZ(); + int tmp; + if (xOffset == 0) { + tmp = c1[2]; + c1[2] = zOffset > 0 ? -c1[1] : c1[1]; + c1[1] = zOffset > 0 ? tmp : -tmp; + } else { + tmp = c1[0]; + c1[0] = xOffset > 0 ? -c1[1] : c1[1]; + c1[1] = xOffset > 0 ? tmp : -tmp; + } + } else if (upwardsFacing == Direction.SOUTH) { + c1[1] = -c1[1]; + if (facing.getStepX() == 0) { + c1[0] = -c1[0]; + } else { + c1[2] = -c1[2]; + } + } + if (isFlipped) { + if (upwardsFacing == Direction.NORTH || upwardsFacing == Direction.SOUTH) { + if (facing == Direction.NORTH || facing == Direction.SOUTH) { + c1[0] = -c1[0]; // flip X-axis + } else c1[2] = -c1[2]; // flip Z-axis + } else c1[1] = -c1[1]; // flip Y-axis + } + } + return new BlockPos(c1[0], c1[1], c1[2]); + } + + @Nullable + private static IntObjectPair getMatchStackWithHandler(List candidates, + LazyOptional cap, + Predicate test) { + IItemHandler handler = cap.resolve().orElse(null); + if (handler == null) return null; + for (int i = 0; i < handler.getSlots(); i++) { + @NotNull + ItemStack stack = handler.getStackInSlot(i); + if (stack.isEmpty()) continue; + + @NotNull + LazyOptional stackCap = stack.getCapability(ForgeCapabilities.ITEM_HANDLER); + if (stackCap.isPresent()) { + var rt = getMatchStackWithHandler(candidates, stackCap, test); + if (rt != null) return rt; + } else if (candidates.stream().anyMatch(candidate -> ItemStack.isSameItemSameTags(candidate, stack)) && + !stack.isEmpty() && test.test(stack.getItem())) { + return IntObjectPair.of(i, handler); + } + } + return null; + } + + private void resetFacing(BlockPos pos, BlockState blockState, Direction facing, + BiPredicate checker, Consumer consumer) { + if (blockState.hasProperty(BlockStateProperties.FACING)) { + tryFacings(blockState, pos, checker, consumer, BlockStateProperties.FACING, + facing == null ? FACINGS : ArrayUtils.addAll(new Direction[] { facing }, FACINGS)); + } else if (blockState.hasProperty(BlockStateProperties.HORIZONTAL_FACING)) { + tryFacings(blockState, pos, checker, consumer, BlockStateProperties.HORIZONTAL_FACING, + facing == null || facing.getAxis() == Direction.Axis.Y ? FACINGS_H : + ArrayUtils.addAll(new Direction[] { facing }, FACINGS_H)); + } + } + + private void tryFacings(BlockState blockState, BlockPos pos, BiPredicate checker, + Consumer consumer, Property property, Direction[] facings) { + Direction found = null; + for (Direction facing : facings) { + if (checker.test(pos, facing)) { + found = facing; + break; + } + } + if (found == null) found = Direction.NORTH; + consumer.accept(blockState.setValue(property, found)); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/pattern/GTLPredicates.java b/src/main/java/org/gtlcore/gtlcore/api/pattern/GTLPredicates.java index 56e8a0b00..d3137f38b 100644 --- a/src/main/java/org/gtlcore/gtlcore/api/pattern/GTLPredicates.java +++ b/src/main/java/org/gtlcore/gtlcore/api/pattern/GTLPredicates.java @@ -3,12 +3,10 @@ import org.gtlcore.gtlcore.api.pattern.util.IValueContainer; import org.gtlcore.gtlcore.api.pattern.util.SimpleValueContainer; -import com.gregtechceu.gtceu.api.block.ActiveBlock; import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.machine.feature.ITieredMachine; import com.gregtechceu.gtceu.api.machine.multiblock.PartAbility; import com.gregtechceu.gtceu.api.pattern.MultiblockState; -import com.gregtechceu.gtceu.api.pattern.Predicates; import com.gregtechceu.gtceu.api.pattern.TraceabilityPredicate; import com.gregtechceu.gtceu.api.pattern.error.PatternStringError; import com.gregtechceu.gtceu.api.pattern.predicates.PredicateBlocks; @@ -19,41 +17,33 @@ import net.minecraft.network.chat.Component; import net.minecraft.world.level.block.Block; -import java.util.Arrays; -import java.util.Map; -import java.util.Objects; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.objects.*; +import lombok.NonNull; + +import java.util.*; import java.util.function.Predicate; import java.util.function.Supplier; +import static com.gregtechceu.gtceu.api.pattern.Predicates.blocks; + public class GTLPredicates { - public static TraceabilityPredicate tierCasings(Map> map, String tierType) { - return new TraceabilityPredicate(blockWorldState -> { - var blockState = blockWorldState.getBlockState(); - for (var entry : map.entrySet()) { - if (blockState.is(entry.getValue().get())) { - var stats = entry.getKey(); - Object currentCoil = blockWorldState.getMatchContext().getOrPut(tierType, stats); - if (!currentCoil.equals(stats)) { - blockWorldState.setError(new PatternStringError("gtceu.multiblock.pattern.error.tier")); - return false; - } - return true; - } - } - return false; - }, () -> map.values().stream() - .map(blockSupplier -> BlockInfo.fromBlockState(blockSupplier.get().defaultBlockState())) - .toArray(BlockInfo[]::new)) - .addTooltips(Component.translatable("gtceu.multiblock.pattern.error.tier")); - } + public static TraceabilityPredicate tierCasings(Int2ObjectMap> map, String tierType) { + BlockInfo[] blockInfos = new BlockInfo[map.size()]; + int index = 0; + + for (var entry = map.values().iterator(); entry.hasNext(); ++index) { + var blockSupplier = entry.next(); + var block = (Block) blockSupplier.get(); + blockInfos[index] = BlockInfo.fromBlockState(block.defaultBlockState()); + } - public static TraceabilityPredicate tierActiveCasings(Map> map, String tierType) { return new TraceabilityPredicate(blockWorldState -> { var blockState = blockWorldState.getBlockState(); - for (var entry : map.entrySet()) { - if (blockState.is(entry.getValue().get())) { - var stats = entry.getKey(); + for (var entry : map.int2ObjectEntrySet()) { + if (blockState.is((Block) entry.getValue().get())) { + var stats = entry.getIntKey(); Object currentCoil = blockWorldState.getMatchContext().getOrPut(tierType, stats); if (!currentCoil.equals(stats)) { blockWorldState.setError(new PatternStringError("gtceu.multiblock.pattern.error.tier")); @@ -63,14 +53,11 @@ public static TraceabilityPredicate tierActiveCasings(Map map.values().stream() - .map(blockSupplier -> BlockInfo.fromBlockState(blockSupplier.get().defaultBlockState())) - .toArray(BlockInfo[]::new)) - .addTooltips(Component.translatable("gtceu.multiblock.pattern.error.tier")); + }, () -> blockInfos).addTooltips(Component.translatable("gtceu.multiblock.pattern.error.tier")); } public static TraceabilityPredicate countBlock(String name, Block... blocks) { - TraceabilityPredicate inner = Predicates.blocks(blocks); + TraceabilityPredicate inner = blocks(blocks); Predicate predicate = state -> { if (inner.test(state)) { IValueContainer currentContainer = state.getMatchContext().getOrPut(name + "Value", @@ -116,4 +103,25 @@ public boolean test(MultiblockState blockWorldState) { } }); } + + // group1 - group2 + public static TraceabilityPredicate diffAbilities(@NonNull Collection group1, @NonNull Collection group2) { + return blocks(diffBlocks(group1, group2).toArray(Block[]::new)); + } + + private static ObjectSet unionBlocks(@NonNull Collection group) { + if (group.isEmpty()) return ObjectSets.emptySet(); + ObjectSet out = new ObjectOpenHashSet<>(); + for (PartAbility ability : group) { + out.addAll(ability.getAllBlocks()); + } + return out; + } + + private static ObjectSet diffBlocks(Collection group1, + Collection group2) { + ObjectSet g1 = unionBlocks(group1); + g1.removeAll(unionBlocks(group2)); + return g1; + } } diff --git a/src/main/java/org/gtlcore/gtlcore/api/pattern/util/IMultiblockStateGet.java b/src/main/java/org/gtlcore/gtlcore/api/pattern/util/IMultiblockStateGet.java new file mode 100644 index 000000000..8ed1f9e00 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/pattern/util/IMultiblockStateGet.java @@ -0,0 +1,12 @@ +package org.gtlcore.gtlcore.api.pattern.util; + +import com.gregtechceu.gtceu.api.pattern.TraceabilityPredicate; + +import net.minecraft.core.BlockPos; + +public interface IMultiblockStateGet { + + void cleanState(); + + boolean updateState(BlockPos posIn, TraceabilityPredicate predicate); +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/recipe/IAdditionalRecipeIterator.java b/src/main/java/org/gtlcore/gtlcore/api/recipe/IAdditionalRecipeIterator.java new file mode 100644 index 000000000..d84ee4ac8 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/recipe/IAdditionalRecipeIterator.java @@ -0,0 +1,18 @@ +package org.gtlcore.gtlcore.api.recipe; + +import com.gregtechceu.gtceu.api.recipe.GTRecipe; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public interface IAdditionalRecipeIterator { + + void setAdditionalRecipes(@NotNull List<@NotNull GTRecipe> additionalRecipes); + + @Nullable + List getAdditionalRecipes(); + + void setUseDiveIngredientTreeFind(boolean useDiveIngredientTreeFind); +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/recipe/IAdvancedContentModifier.java b/src/main/java/org/gtlcore/gtlcore/api/recipe/IAdvancedContentModifier.java new file mode 100644 index 000000000..4e8228507 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/recipe/IAdvancedContentModifier.java @@ -0,0 +1,14 @@ +package org.gtlcore.gtlcore.api.recipe; + +import com.gregtechceu.gtceu.api.recipe.content.ContentModifier; + +public interface IAdvancedContentModifier { + + void setDivision(long numerator, long denominator); + + static ContentModifier preciseDivision(long numerator, long denominator) { + var modifier = new ContentModifier(0, 0); + ((IAdvancedContentModifier) modifier).setDivision(numerator, denominator); + return modifier; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/recipe/IAdvancedOCResult.java b/src/main/java/org/gtlcore/gtlcore/api/recipe/IAdvancedOCResult.java new file mode 100644 index 000000000..b87f4d486 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/recipe/IAdvancedOCResult.java @@ -0,0 +1,12 @@ +package org.gtlcore.gtlcore.api.recipe; + +public interface IAdvancedOCResult { + + void init(long eut, int duration, int parallel, long parallelEUt, int baseOCLevel, int totalOCLevel, double durationFactor, double voltageFactor); + + int getBaseOCLevel(); + + double getDurationFactor(); + + double getVoltageFactor(); +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/recipe/IGTRecipe.java b/src/main/java/org/gtlcore/gtlcore/api/recipe/IGTRecipe.java new file mode 100644 index 000000000..b778fb04c --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/recipe/IGTRecipe.java @@ -0,0 +1,18 @@ +package org.gtlcore.gtlcore.api.recipe; + +import com.gregtechceu.gtceu.api.recipe.GTRecipe; + +public interface IGTRecipe { + + static IGTRecipe of(GTRecipe recipe) { + return (IGTRecipe) recipe; + } + + void setHasTick(boolean hasTick); + + int getEuTier(); + + long getRealParallels(); + + void setRealParallels(long realParallels); +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/recipe/IParallelLogic.java b/src/main/java/org/gtlcore/gtlcore/api/recipe/IParallelLogic.java new file mode 100644 index 000000000..487ebf1e8 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/recipe/IParallelLogic.java @@ -0,0 +1,306 @@ +package org.gtlcore.gtlcore.api.recipe; + +import org.gtlcore.gtlcore.api.machine.trait.IRecipeCapabilityMachine; +import org.gtlcore.gtlcore.api.machine.trait.MEPatternRecipeHandlePart; +import org.gtlcore.gtlcore.api.machine.trait.RecipeHandlePart; +import org.gtlcore.gtlcore.api.recipe.chance.LongChanceLogic; +import org.gtlcore.gtlcore.api.recipe.ingredient.LongIngredient; + +import com.gregtechceu.gtceu.api.capability.recipe.*; +import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.content.Content; +import com.gregtechceu.gtceu.api.recipe.content.ContentModifier; +import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; +import com.gregtechceu.gtceu.api.recipe.ingredient.SizedIngredient; +import com.gregtechceu.gtceu.utils.FluidStackHashStrategy; +import com.gregtechceu.gtceu.utils.IngredientEquality; +import com.gregtechceu.gtceu.utils.ItemStackHashStrategy; + +import com.lowdragmc.lowdraglib.side.fluid.FluidStack; + +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + +import com.hepdd.gtmthings.common.block.machine.trait.CatalystFluidStackHandler; +import it.unimi.dsi.fastutil.objects.*; + +import java.util.*; +import java.util.function.Predicate; + +/** + * 部分代码参考自gto + * @line ... + */ + +public interface IParallelLogic { + + static GTRecipe getRecipeOutputChance(IRecipeCapabilityHolder holder, GTRecipe recipe) { + Reference2ObjectOpenHashMap, List> recipeContents = new Reference2ObjectOpenHashMap<>(); + for (var entry : recipe.outputs.entrySet()) { + var cap = entry.getKey(); + List chancedContents = new ObjectArrayList<>(); + var contentList = recipeContents.computeIfAbsent(cap, c -> new ObjectArrayList<>()); + for (var cont : entry.getValue()) { + if (cont.chance >= cont.maxChance) contentList.add(cont); + else chancedContents.add(cont); + } + if (!chancedContents.isEmpty()) { + var function = recipe.getType().getChanceFunction(); + int holderTier = holder.getChanceTier(); + var cache = ((IRecipeLogicMachine) holder).getRecipeLogic().getChanceCaches().get(cap); + chancedContents = LongChanceLogic.OR.roll(chancedContents, function, ((IGTRecipe) recipe).getEuTier(), holderTier, cache, ((IGTRecipe) recipe).getRealParallels(), cap); + if (chancedContents != null) { + for (var cont : chancedContents) { + contentList.add(new Content(cont.content, 10000, 10000, 0, null, null)); + } + } + } + if (contentList.isEmpty()) recipeContents.remove(cap); + } + var copy = new GTRecipe(recipe.recipeType, recipe.id, recipe.inputs, recipeContents, recipe.tickInputs, recipe.tickOutputs, + recipe.inputChanceLogics, recipe.outputChanceLogics, recipe.tickInputChanceLogics, recipe.tickOutputChanceLogics, + recipe.conditions, recipe.ingredientActions, recipe.data, recipe.duration, recipe.isFuel); + ((IGTRecipe) copy).setRealParallels(((IGTRecipe) recipe).getRealParallels()); + copy.ocTier = recipe.ocTier; + return copy; + } + + static long getParallel(IRecipeCapabilityHolder holder, GTRecipe recipe, long parallelAmount) { + if (parallelAmount <= 1) return parallelAmount; + long maxParallel = getMaxParallel(holder, recipe, parallelAmount); + if (maxParallel == 0L) return 0L; + return getMinParallel(holder, recipe, maxParallel); + } + + static long getMaxParallel(IRecipeCapabilityHolder holder, GTRecipe recipe, long parallelAmount) { + for (var cap : recipe.inputs.keySet()) { + if (cap == ItemRecipeCapability.CAP) { + parallelAmount = Math.min(parallelAmount, getInputItemParallel(holder, recipe, parallelAmount)); + if (parallelAmount == 0L) break; + } else if (cap == FluidRecipeCapability.CAP) { + parallelAmount = Math.min(parallelAmount, getInputFluidParallel(holder, recipe, parallelAmount)); + if (parallelAmount == 0L) break; + } + } + return parallelAmount; + } + + static long getMinParallel(IRecipeCapabilityHolder holder, GTRecipe recipe, long parallelAmount) { + for (var entry : recipe.outputs.entrySet()) { + if (entry.getKey() == ItemRecipeCapability.CAP && holder instanceof IRecipeLogicMachine machine && !machine.canVoidRecipeOutputs(ItemRecipeCapability.CAP)) { + parallelAmount = Math.min(parallelAmount, getOutputItemParallel(holder, recipe, entry.getValue(), parallelAmount)); + if (parallelAmount == 0L) break; + } else if (entry.getKey() == FluidRecipeCapability.CAP && holder instanceof IRecipeLogicMachine machine && !machine.canVoidRecipeOutputs(FluidRecipeCapability.CAP)) { + parallelAmount = Math.min(parallelAmount, getOutputFluidParallel(holder, recipe, entry.getValue(), parallelAmount)); + if (parallelAmount == 0L) break; + } + } + return parallelAmount; + } + + static long getInputItemParallel(IRecipeCapabilityHolder holder, GTRecipe recipe, long parallelAmount) { + if (parallelAmount <= 1) return parallelAmount; + if (!(holder instanceof IRecipeCapabilityMachine machine)) return 1; + if (machine.emptyRecipeHandlePart()) return 0; + + Object2LongOpenCustomHashMap countableMap = new Object2LongOpenCustomHashMap<>(IngredientEquality.IngredientHashStrategy.INSTANCE); + + for (Content content : recipe.getInputContents(ItemRecipeCapability.CAP)) { + if (content.chance <= 0) continue; + Ingredient ingredient = ItemRecipeCapability.CAP.of(content.content); + long ingredientCount; + if (ingredient instanceof LongIngredient longIngredient) { + ingredientCount = longIngredient.getAmount(); + } else if (ingredient instanceof SizedIngredient sizedIngredient) { + ingredientCount = sizedIngredient.getAmount(); + } else ingredientCount = 1; + countableMap.addTo(ingredient, ingredientCount); + } + + if (countableMap.isEmpty()) return parallelAmount; + + Object2LongOpenCustomHashMap ingredientStacks = new Object2LongOpenCustomHashMap<>(ItemStackHashStrategy.comparingAllButCount()); + + var handle = machine.getActiveRecipeHandle(recipe); + if (handle instanceof MEPatternRecipeHandlePart mePatternRecipeHandlePart) { + for (var entry : Object2LongMaps.fastIterable(mePatternRecipeHandlePart.getMEContent(ItemRecipeCapability.CAP, recipe))) { + ingredientStacks.addTo(entry.getKey(), entry.getLongValue()); + } + + } else if (handle instanceof RecipeHandlePart recipeHandlePart) { + for (var entry : Object2LongMaps.fastIterable(recipeHandlePart.getSelfContent(ItemRecipeCapability.CAP))) { + ingredientStacks.addTo(entry.getKey(), entry.getLongValue()); + } + } else { + var sharedRecipeHandlePart = machine.getSharedRecipeHandlePart(); + if (sharedRecipeHandlePart != null) { + for (var entry : Object2LongMaps.fastIterable(sharedRecipeHandlePart.getSelfContent(ItemRecipeCapability.CAP))) { + ingredientStacks.addTo(entry.getKey(), entry.getLongValue()); + } + } + } + return calculate(parallelAmount, countableMap, ingredientStacks); + } + + static long getInputFluidParallel(IRecipeCapabilityHolder holder, GTRecipe recipe, long parallelAmount) { + if (parallelAmount <= 1) return parallelAmount; + if (!(holder instanceof IRecipeCapabilityMachine machine)) return 1; + if (machine.emptyRecipeHandlePart()) return 0; + + Object2LongOpenHashMap fluidCountMap = new Object2LongOpenHashMap<>(); + + for (Content content : recipe.getInputContents(FluidRecipeCapability.CAP)) { + FluidIngredient fluidInput = FluidRecipeCapability.CAP.of(content.content); + if (content.chance > 0) { + fluidCountMap.addTo(fluidInput, fluidInput.getAmount()); + } + } + + if (fluidCountMap.isEmpty()) return parallelAmount; + + Object2LongOpenCustomHashMap ingredientStacks = new Object2LongOpenCustomHashMap<>(FluidStackHashStrategy.comparingAllButAmount()); + + var handle = machine.getActiveRecipeHandle(recipe); + if (handle instanceof MEPatternRecipeHandlePart mePatternRecipeHandlePart) { + for (var entry : Object2LongMaps.fastIterable(mePatternRecipeHandlePart.getMEContent(FluidRecipeCapability.CAP, recipe))) { + ingredientStacks.addTo(entry.getKey(), entry.getLongValue()); + } + } else if (handle instanceof RecipeHandlePart recipeHandlePart) { + for (var entry : Object2LongMaps.fastIterable(machine.isDistinct() ? recipeHandlePart.getSelfContent(FluidRecipeCapability.CAP) : recipeHandlePart.getContentWithShared(FluidRecipeCapability.CAP))) { + ingredientStacks.addTo(entry.getKey(), entry.getLongValue()); + } + } else { + var sharedRecipeHandlePart = machine.getSharedRecipeHandlePart(); + if (sharedRecipeHandlePart != null) { + for (var handler : sharedRecipeHandlePart.getCapability(FluidRecipeCapability.CAP)) { + if (handler instanceof CatalystFluidStackHandler) continue; + for (var object : handler.getContents()) { + if (object instanceof FluidStack fs) { + ingredientStacks.addTo(fs, fs.getAmount()); + } + } + } + + } + } + return calculate(parallelAmount, fluidCountMap, ingredientStacks); + } + + private static , S> long calculate(long parallelLimit, + Object2LongMap countableMap, + Object2LongMap ingredientStacks) { + if (ingredientStacks.isEmpty()) return 0; + + for (var entry : Object2LongMaps.fastIterable(countableMap)) { + I ingredient = entry.getKey(); + long needed = entry.getLongValue(); + long available = 0; + + for (var it = Object2LongMaps.fastIterator(ingredientStacks); it.hasNext();) { + var input = it.next(); + if (ingredient.test(input.getKey())) { + available = input.getLongValue(); + it.remove(); + break; + } + } + + if (available < needed) return 0; + + parallelLimit = Math.min(parallelLimit, available / needed); + } + return parallelLimit; + } + + static long getOutputItemParallel(IRecipeCapabilityHolder holder, GTRecipe recipe, List contents, long multiplier) { + if (multiplier <= 1L || contents.isEmpty()) return multiplier; + if (!(holder instanceof IRecipeCapabilityMachine machine)) return 1; + if (machine.itemOutPutAlwaysMatch()) return multiplier; + + List inners = new ObjectArrayList<>(contents.size()); + for (var content : contents) { + inners.add(content.content); + } + + for (var meIOHandler : machine.getMEOutputRecipeHandleParts()) { + inners = meIOHandler.meHandleOutput(ItemRecipeCapability.CAP, inners, true); + if (inners.isEmpty()) return multiplier; + } + + List ingredients = new ObjectArrayList<>(inners.size()); + for (var inner : inners) { + Ingredient ingredient = ItemRecipeCapability.CAP.of(inner); + if (ingredient instanceof LongIngredient longIngredient) { + if (longIngredient.getAmount() > 0) ingredients.add(ingredient); + } else if (ingredient instanceof SizedIngredient sizedIngredient) { + if (sizedIngredient.getAmount() > 0) ingredients.add(ingredient); + } else ingredients.add(ingredient); + } + + if (ingredients.isEmpty()) return multiplier; + + return binarySearchMaxParallel(machine, recipe, ItemRecipeCapability.CAP, ingredients, multiplier); + } + + static long getOutputFluidParallel(IRecipeCapabilityHolder holder, GTRecipe recipe, List contents, long multiplier) { + if (multiplier <= 1L || contents.isEmpty()) return multiplier; + if (!(holder instanceof IRecipeCapabilityMachine machine)) return 1; + if (machine.fluidOutPutAlwaysMatch()) return multiplier; + + List inners = new ObjectArrayList<>(contents.size()); + for (var content : contents) { + inners.add(content.content); + } + + for (var meIOHandler : machine.getMEOutputRecipeHandleParts()) { + inners = meIOHandler.meHandleOutput(FluidRecipeCapability.CAP, inners, true); + if (inners.isEmpty()) return multiplier; + } + + List ingredients = new ObjectArrayList<>(inners.size()); + for (var inner : inners) { + FluidIngredient ingredient = FluidRecipeCapability.CAP.of(inner); + if (ingredient.getAmount() > 0) ingredients.add(ingredient); + } + + if (ingredients.isEmpty()) return multiplier; + + return binarySearchMaxParallel(machine, recipe, FluidRecipeCapability.CAP, ingredients, multiplier); + } + + @SuppressWarnings("unchecked") + private static long binarySearchMaxParallel(IRecipeCapabilityMachine machine, GTRecipe recipe, + RecipeCapability capability, List ingredients, long initialMultiplier) { + var handlers = machine.getNormalRecipeHandlePart(IO.OUT); + if (handlers.isEmpty()) return 0L; + + long left = 1L, right = initialMultiplier; + + while (left < right) { + long mid = left + (right - left + 1) / 2; + + List copied = new ObjectArrayList<>(ingredients.size()); + for (var ing : ingredients) { + copied.add(capability.copyWithModifier(ing, ContentModifier.multiplier((double) mid))); + } + + boolean canHandle = false; + for (var handler : handlers) { + copied = (List) handler.handleRecipe(IO.OUT, recipe, capability, copied, true); + if (copied == null || copied.isEmpty()) { + canHandle = true; + break; + } + } + + if (canHandle) { + left = mid; + } else { + right = mid - 1; + } + } + + return left; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/recipe/IRecipeIterator.java b/src/main/java/org/gtlcore/gtlcore/api/recipe/IRecipeIterator.java new file mode 100644 index 000000000..2b6770590 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/recipe/IRecipeIterator.java @@ -0,0 +1,51 @@ +package org.gtlcore.gtlcore.api.recipe; + +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.lookup.AbstractMapIngredient; +import com.gregtechceu.gtceu.api.recipe.lookup.Branch; + +import com.mojang.datafixers.util.Either; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.Predicate; + +public interface IRecipeIterator { + + static @Nullable GTRecipe diveIngredientTreeFindRecipe(@NotNull List ingredients, @NotNull Branch branchMap, + @NotNull Predicate canHandle) { + if (ingredients.isEmpty()) return null; + for (var ingredient : ingredients) { + var targetMap = determineRootNodes(ingredient, branchMap); + var result = targetMap.get(ingredient); + if (result != null) { + GTRecipe r = result.map((potentialRecipe) -> canHandle.test(potentialRecipe) ? potentialRecipe : null, + (potentialBranch) -> diveIngredientTreeFindRecipe(ingredients, potentialBranch, canHandle)); + if (r != null) { + return r; + } + } + } + return null; + } + + static GTRecipe diveIngredientTreeFindRecipeCollection(@NotNull List ingredients, @NotNull Branch branchMap, + @NotNull Predicate canHandle, Set recipeSet) { + if (ingredients.isEmpty()) return null; + for (var ingredient : ingredients) { + var targetMap = determineRootNodes(ingredient, branchMap); + var result = targetMap.get(ingredient); + if (result != null) { + GTRecipe r = result.map((potentialRecipe) -> canHandle.test(potentialRecipe) ? potentialRecipe : null, + (potentialBranch) -> diveIngredientTreeFindRecipeCollection(ingredients, potentialBranch, canHandle, recipeSet)); + if (r != null) recipeSet.add(r); + } + } + return null; + } + + static @NotNull Map> determineRootNodes(@NotNull AbstractMapIngredient ingredient, @NotNull Branch branchMap) { + return ingredient.isSpecialIngredient() ? branchMap.getSpecialNodes() : branchMap.getNodes(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/recipe/RecipeCacheStrategy.java b/src/main/java/org/gtlcore/gtlcore/api/recipe/RecipeCacheStrategy.java new file mode 100644 index 000000000..dd841b422 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/recipe/RecipeCacheStrategy.java @@ -0,0 +1,30 @@ +package org.gtlcore.gtlcore.api.recipe; + +public enum RecipeCacheStrategy { + + /** + * Full caching - cache everything including ME pattern machine internals + * Used for normal recipe processing + */ + FULL_CACHE(true, true), + + /** + * Partial caching - cache recipe-> handlePart mapping but not ME pattern internals + * Used for dummy recipes that need parallel calculation but shouldn't pollute ME recipe queue + */ + HANDLE_PART_CACHE_ONLY(true, false), + + /** + * No caching - skip all caching operations + * Used for temporary validation recipes + */ + NO_CACHE(false, false); + + public final boolean cacheToHandlePartMap; // Cache recipe->handler in machine + public final boolean cacheToMEInternal; // Cache recipe in MEPatternPartMachine internal + + RecipeCacheStrategy(boolean cacheToHandlePartMap, boolean cacheToMEInternal) { + this.cacheToHandlePartMap = cacheToHandlePartMap; + this.cacheToMEInternal = cacheToMEInternal; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/recipe/RecipeResult.java b/src/main/java/org/gtlcore/gtlcore/api/recipe/RecipeResult.java new file mode 100644 index 000000000..3bb2cc565 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/recipe/RecipeResult.java @@ -0,0 +1,45 @@ +package org.gtlcore.gtlcore.api.recipe; + +import org.gtlcore.gtlcore.api.machine.trait.IRecipeStatus; + +import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; + +import net.minecraft.network.chat.Component; + +import org.jetbrains.annotations.Nullable; + +/** + * 思路参考自gto + * @line ... + */ + +public record RecipeResult(boolean isSuccess, @Nullable Component reason) { + + public static void of(IRecipeLogicMachine machine, RecipeResult result) { + if (machine.getRecipeLogic() instanceof IRecipeStatus status) status.setRecipeStatus(result); + } + + public static void ofWorking(IRecipeLogicMachine machine, RecipeResult result) { + if (machine.getRecipeLogic() instanceof IRecipeStatus status) status.setWorkingStatus(result); + } + + public static final RecipeResult SUCCESS = new RecipeResult(true, null); + public static final RecipeResult FAIL_FIND = fail(Component.translatable("gtceu.recipe.fail.find")); + public static final RecipeResult FAIL_INPUT = fail(Component.translatable("gtceu.recipe.fail.Input")); + public static final RecipeResult FAIL_OUTPUT = fail(Component.translatable("gtceu.recipe.fail.Output")); + public static final RecipeResult FAIL_VOLTAGE_TIER = fail(Component.translatable("gtceu.recipe.fail.voltage.tier")); + public static final RecipeResult FAIL_NO_ENOUGH_EU_IN = fail(Component.translatable("gtceu.recipe.fail.no.enough.eu.in")); + public static final RecipeResult FAIL_NO_ENOUGH_EU_OUT = fail(Component.translatable("gtceu.recipe.fail.no.enough.eu.out")); + public static final RecipeResult FAIL_NO_ENOUGH_CWU_IN = fail(Component.translatable("gtceu.recipe.fail.no.enough.cwu.in")); + public static final RecipeResult FAIL_NO_SKYLIGHT = fail(Component.translatable("gtceu.recipe.fail.no.skylight")); + public static final RecipeResult FAIL_PROCESSING_PLANT_NO_INPUT = fail(Component.translatable("gtceu.recipe.fail.processing.plant.no.input")); + public static final RecipeResult FAIL_PROCESSING_PLANT_WRONG_INPUT = fail(Component.translatable("gtceu.recipe.fail.processing.plant.wrong.input")); + public static final RecipeResult FAIL_NO_FIND_RESEARCHED = fail(Component.translatable("gtceu.recipe.fail.no.find.researched")); + public static final RecipeResult FAIL_LACK_FLUID = fail(Component.translatable("recipe.condition.rock_breaker.tooltip")); + public static final RecipeResult FAIL_NO_ENOUGH_TEMPERATURE = fail(Component.translatable("gtceu.recipe.fail.no.enough.temperature")); + public static final RecipeResult FAIL_NO_ENOUGH_TIER = fail(Component.translatable("gtceu.recipe.fail.no.enough.recipe.tier")); + + public static RecipeResult fail(@Nullable Component reason) { + return new RecipeResult(false, reason); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/recipe/RecipeRunner.java b/src/main/java/org/gtlcore/gtlcore/api/recipe/RecipeRunner.java index 6570882ec..210639e9e 100644 --- a/src/main/java/org/gtlcore/gtlcore/api/recipe/RecipeRunner.java +++ b/src/main/java/org/gtlcore/gtlcore/api/recipe/RecipeRunner.java @@ -1,19 +1,20 @@ package org.gtlcore.gtlcore.api.recipe; -import org.gtlcore.gtlcore.api.machine.trait.IDistinctMachine; +import org.gtlcore.gtlcore.api.machine.trait.*; +import org.gtlcore.gtlcore.api.recipe.chance.LongChanceLogic; +import org.gtlcore.gtlcore.api.recipe.ingredient.LongIngredient; import com.gregtechceu.gtceu.api.capability.recipe.*; import com.gregtechceu.gtceu.api.recipe.GTRecipe; -import com.gregtechceu.gtceu.api.recipe.RecipeHelper; import com.gregtechceu.gtceu.api.recipe.chance.boost.ChanceBoostFunction; -import com.gregtechceu.gtceu.api.recipe.chance.logic.ChanceLogic; import com.gregtechceu.gtceu.api.recipe.content.Content; -import com.gregtechceu.gtceu.api.recipe.content.ContentModifier; +import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; +import com.gregtechceu.gtceu.api.recipe.ingredient.SizedIngredient; -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import org.jetbrains.annotations.NotNull; +import net.minecraft.network.chat.Component; + +import it.unimi.dsi.fastutil.objects.*; +import org.jetbrains.annotations.Nullable; import java.util.*; @@ -22,69 +23,58 @@ * @line ... */ -public class RecipeRunner { - - private final GTRecipe recipe; - private final RecipeHandlePart recipeHandlePart; - private final IO io; - private final boolean isTick; - private final IRecipeCapabilityHolder holder; - private final Map, Object2IntMap> chanceCaches; - private final boolean simulated; - private Object2ObjectOpenHashMap, List> recipeContent; - - public RecipeRunner(GTRecipe recipe, IO io, boolean isTick, IRecipeCapabilityHolder holder, - Map, Object2IntMap> chanceCaches, boolean simulated) { - RecipeHandlePart recipeHandlePart = null; - if (io == IO.IN && holder instanceof IDistinctMachine iDistinctMachine) { - if (recipe.id.equals(iDistinctMachine.getRecipeId())) { - recipeHandlePart = iDistinctMachine.getDistinctHatch(); - } else { - iDistinctMachine.setRecipeId(recipe.id); - iDistinctMachine.setDistinctHatch(null); - } - } - this.recipeHandlePart = recipeHandlePart; - this.recipe = recipe; - this.io = io; - this.isTick = isTick; - this.holder = holder; - this.chanceCaches = chanceCaches; - this.recipeContent = new Object2ObjectOpenHashMap<>(); - this.simulated = simulated; +public final class RecipeRunner { + + private RecipeRunner() { + throw new AssertionError("Utility class should not be instantiated"); } - public GTRecipe.ActionResult handle(Map, List> entry) { - this.fillContent(entry); - if (this.recipeContent.isEmpty()) return GTRecipe.ActionResult.SUCCESS; - return this.handleContents(); + public static RecipeResult handle(GTRecipe recipe, IO io, IRecipeCapabilityHolder holder, + Map, List> contents, + Map, Object2IntMap> chanceCaches, + boolean simulated, RecipeCacheStrategy strategy) { + var recipeContent = fillContent(contents, recipe, holder, chanceCaches, simulated); + + if (recipeContent.isEmpty()) { + return RecipeResult.SUCCESS; + } + + return handleContentsInternal(io, recipe, holder, recipeContent, simulated, strategy) ? RecipeResult.SUCCESS : io == IO.IN ? RecipeResult.FAIL_INPUT : simulated ? generateOutputFailReason(recipeContent) : RecipeResult.fail(null); } - private void fillContent(Map, List> entries) { + private static Reference2ObjectOpenHashMap, List> fillContent( + Map, List> entries, + GTRecipe recipe, + IRecipeCapabilityHolder holder, + Map, Object2IntMap> chanceCaches, + boolean simulated) { + var recipeContent = new Reference2ObjectOpenHashMap, List>(); + for (var entry : entries.entrySet()) { RecipeCapability cap = entry.getKey(); if (!cap.doMatchInRecipe()) continue; - if (entry.getValue().isEmpty()) continue; - List chancedContents = new ArrayList<>(); - var contentList = this.recipeContent.computeIfAbsent(cap, c -> new ObjectArrayList<>()); - for (Content cont : entry.getValue()) { + + List contents = entry.getValue(); + if (contents.isEmpty()) continue; + + List chancedContents = new ObjectArrayList<>(); + List contentList = recipeContent.computeIfAbsent(cap, c -> new ObjectArrayList<>()); + for (Content cont : contents) { if (simulated) { contentList.add(cont.content); } else { if (cont.chance >= cont.maxChance) { contentList.add(cont.content); - } else { - chancedContents.add(cont.copy(cap, ContentModifier.multiplier(1.0 / recipe.parallels))); + } else if (cont.chance != 0) { + chancedContents.add(cont); } } } if (!chancedContents.isEmpty()) { ChanceBoostFunction function = recipe.getType().getChanceFunction(); - ChanceLogic logic = recipe.getChanceLogicForCapability(cap, this.io, this.isTick); - int recipeTier = RecipeHelper.getPreOCRecipeEuTier(recipe); int holderTier = holder.getChanceTier(); - var cache = this.chanceCaches.get(cap); - chancedContents = logic.roll(chancedContents, function, recipeTier, holderTier, cache, recipe.parallels, cap); + var cache = chanceCaches.get(cap); + chancedContents = LongChanceLogic.OR.roll(chancedContents, function, ((IGTRecipe) recipe).getEuTier(), holderTier, cache, ((IGTRecipe) recipe).getRealParallels(), cap); if (chancedContents != null) { for (Content cont : chancedContents) { contentList.add(cont.content); @@ -93,115 +83,335 @@ private void fillContent(Map, List> entries) { } if (contentList.isEmpty()) recipeContent.remove(cap); } + + return recipeContent; } - private GTRecipe.@NotNull ActionResult handleContents() { - return this.handleContentsInternal(this.io) ? GTRecipe.ActionResult.SUCCESS : GTRecipe.ActionResult.fail(null); + private static boolean handleContentsInternal(IO capIO, GTRecipe recipe, IRecipeCapabilityHolder holder, + Reference2ObjectOpenHashMap, List> recipeContent, + boolean simulated, RecipeCacheStrategy strategy) { + if (!(holder instanceof IRecipeCapabilityMachine machine)) { + return false; + } + + if (machine.emptyHandlePart()) { + return false; + } + + if (capIO == IO.IN) { + // Use different handling based on cache strategy + if (strategy == RecipeCacheStrategy.NO_CACHE) { + return machine.isDistinct() ? + handleInputDistinctNocache(machine, recipe, recipeContent, simulated) : + handleInputNotDistinctNocache(machine, recipe, recipeContent, simulated); + } else { + return machine.isDistinct() ? + handleInputDistinct(machine, recipe, recipeContent, simulated, strategy) : + handleInputNotDistinct(machine, recipe, recipeContent, simulated, strategy); + } + } else { + recipeContent = handleMEOutput(machine.getMEOutputRecipeHandleParts(), recipeContent, simulated); + if (recipeContent.isEmpty()) return true; + recipeContent = handleNormalOutput(machine.getNormalRecipeHandlePart(IO.OUT), recipe, recipeContent, simulated); + return recipeContent.isEmpty(); + } } - private boolean handleContentsInternal(IO capIO) { - if (this.recipeContent.isEmpty()) return true; - if (holder instanceof IDistinctMachine iDistinctMachine) { - if (this.recipeHandlePart != null) { - var result = this.handleRecipe(this.recipeHandlePart, IO.IN, simulated, !simulated); - return result.isEmpty(); - } else if (!iDistinctMachine.getRecipeHandleParts().isEmpty()) { - for (var recipeHandlePart : iDistinctMachine.getRecipeHandleParts().stream().filter(h -> h.io == capIO).toList()) { - var result = this.handleRecipe(recipeHandlePart, capIO, true, false); - if (result.isEmpty()) { - if (!this.simulated) { - this.recipeContent = this.handleRecipe(recipeHandlePart, capIO, false, true); - return this.recipeContent.isEmpty(); - } else { - this.recipeContent.clear(); - return true; - } + // ======================================== + // Input + // ======================================== + + @SuppressWarnings("DuplicatedCode") + private static boolean handleInputDistinct(IRecipeCapabilityMachine machine, GTRecipe recipe, + Reference2ObjectOpenHashMap, List> recipeContent, + boolean simulated, RecipeCacheStrategy strategy) { + // Priority 1: Try all cached handlers (active is first in iterator) + for (var it = machine.getAllCachedRecipeHandlesIter(recipe); it.hasNext();) { + var handler = it.next(); + + if (handler instanceof MEPatternRecipeHandlePart cachedMEPart) { + var slot = cachedMEPart.handleRecipe(recipe, recipeContent, simulated, strategy.cacheToMEInternal); + if (slot != -1) { + if (simulated && strategy.cacheToHandlePartMap) { + if (slot == -2) machine.tryAddAndActiveRhp(recipe, cachedMEPart); + else machine.tryAddAndActiveMERhp(cachedMEPart, recipe, slot); } + return true; + } + } else if (handler instanceof RecipeHandlePart cachedNormalPart) { + var result = cachedNormalPart.handleRecipe(IO.IN, recipe, recipeContent, simulated); + if (result.isEmpty()) { + if (simulated && strategy.cacheToHandlePartMap) { + machine.tryAddAndActiveRhp(recipe, cachedNormalPart); + } + return true; + } + } + } + + var cachedHandlers = machine.getAllCachedRecipeHandles(recipe); + + // Priority 2: Try uncached ME Pattern parts + for (var part : machine.getMEPatternRecipeHandleParts()) { + if (cachedHandlers.contains(part)) continue; + var slot = part.handleRecipe(recipe, recipeContent, simulated, strategy.cacheToMEInternal); + if (slot >= 0) { + if (simulated && strategy.cacheToHandlePartMap) { + machine.tryAddAndActiveMERhp(part, recipe, slot); } + return true; } } + + // Priority 3: Try uncached normal parts + for (var part : machine.getNormalRecipeHandlePart(IO.IN)) { + if (cachedHandlers.contains(part)) continue; + var result = part.handleRecipe(IO.IN, recipe, recipeContent, simulated); + if (result.isEmpty()) { + if (simulated && strategy.cacheToHandlePartMap) { + machine.tryAddAndActiveRhp(recipe, part); + } + return true; + } + } + return false; } - private Object2ObjectOpenHashMap, List> handleRecipe(RecipeHandlePart handlePart, IO io, boolean isSimulate, boolean isProcess) { - if (handlePart.allHandles().isEmpty()) return this.recipeContent; - var copy = isProcess ? this.recipeContent : new Object2ObjectOpenHashMap<>(this.recipeContent); - for (var entry = copy.object2ObjectEntrySet().fastIterator(); entry.hasNext();) { - var content = entry.next(); - List left = content.getValue(); - var handlerList = handlePart.allHandles().get(content.getKey()); - if (handlerList != null) { - for (IRecipeHandler proxy : handlerList) { - left = proxy.handleRecipe(io, recipe, left, null, isSimulate); - if (left == null || left.isEmpty()) { - entry.remove(); - break; + @SuppressWarnings("DuplicatedCode") + private static boolean handleInputNotDistinct(IRecipeCapabilityMachine machine, GTRecipe recipe, + Reference2ObjectOpenHashMap, List> recipeContent, + boolean simulated, RecipeCacheStrategy strategy) { + boolean fluidHandleResult = false; + boolean hasFluidTry = false; + + // Priority 1: Try all cached handlers (active is first in iterator) + for (var it = machine.getAllCachedRecipeHandlesIter(recipe); it.hasNext();) { + var handler = it.next(); + + if (handler instanceof MEPatternRecipeHandlePart cachedMEPart) { + var slot = cachedMEPart.handleRecipe(recipe, recipeContent, simulated, strategy.cacheToMEInternal); + if (slot != -1) { + if (simulated && strategy.cacheToHandlePartMap) { + if (slot == -2) machine.tryAddAndActiveRhp(recipe, cachedMEPart); + else machine.tryAddAndActiveMERhp(cachedMEPart, recipe, slot); } + return true; + } + } else if (handler instanceof RecipeHandlePart cachedNormalPart) { + if (!hasFluidTry) { + fluidHandleResult = tryNotDistinctFluid(machine.getSharedRecipeHandlePart(), recipe, recipeContent, simulated); + hasFluidTry = true; + } + if (fluidHandleResult && tryNotDistinctItem(cachedNormalPart, recipe, recipeContent, simulated)) { + if (simulated && strategy.cacheToHandlePartMap) { + machine.tryAddAndActiveRhp(recipe, cachedNormalPart); + } + return true; } } } - return copy; - } - public boolean simulatedHandle() { - if (this.holder instanceof IDistinctMachine iDistinctMachine) { - if (iDistinctMachine.getRecipeHandleParts().isEmpty()) return false; - this.fillContent(this.recipe.inputs); - List itemContent = this.recipeContent.computeIfAbsent(ItemRecipeCapability.CAP, k -> new ObjectArrayList<>()); - List fluidContent = this.recipeContent.computeIfAbsent(FluidRecipeCapability.CAP, k -> new ObjectArrayList<>()); - if (itemContent.isEmpty() && fluidContent.isEmpty()) return false; - if (this.recipeHandlePart != null) { - return this.recipeHandlePart.testRecipeHandle(iDistinctMachine, this.recipe, itemContent, fluidContent); - } - List recipeHandlingResultList = iDistinctMachine.getRecipeHandleParts().stream().filter(h -> h.io == IO.IN).toList(); - for (RecipeHandlePart recipeHandlePart : recipeHandlingResultList) { - if (recipeHandlePart.testRecipeHandle(iDistinctMachine, this.recipe, itemContent, fluidContent)) { - return true; + var cachedHandlers = machine.getAllCachedRecipeHandles(recipe); + + // Priority 2: Try uncached ME Pattern parts + for (var part : machine.getMEPatternRecipeHandleParts()) { + if (cachedHandlers.contains(part)) continue; + var slot = part.handleRecipe(recipe, recipeContent, simulated, strategy.cacheToMEInternal); + if (slot >= 0) { + if (simulated && strategy.cacheToHandlePartMap) { + machine.tryAddAndActiveMERhp(part, recipe, slot); + } + return true; + } + } + + // Priority 3: Try uncached normal parts + RecipeHandlePart sharedPart = machine.getSharedRecipeHandlePart(); + + List fluidContent = recipeContent.getOrDefault(FluidRecipeCapability.CAP, Collections.emptyList()); + if (!fluidContent.isEmpty()) { + if (sharedPart == null) return false; + fluidContent = sharedPart.handleRecipe(IO.IN, recipe, FluidRecipeCapability.CAP, fluidContent, simulated); + if (fluidContent != null && !fluidContent.isEmpty()) return false; + } + + List itemContent = recipeContent.getOrDefault(ItemRecipeCapability.CAP, Collections.emptyList()); + if (itemContent.isEmpty()) { + if (simulated && strategy.cacheToHandlePartMap) { + machine.tryAddAndActiveRhp(recipe, sharedPart); + } + return true; + } + + for (var part : machine.getNormalRecipeHandlePart(IO.IN)) { + if (cachedHandlers.contains(part)) continue; + var result = part.handleRecipe(IO.IN, recipe, ItemRecipeCapability.CAP, itemContent, simulated); + if (result == null || result.isEmpty()) { + if (simulated && strategy.cacheToHandlePartMap) { + machine.tryAddAndActiveRhp(recipe, part); } + return true; } } + + if (sharedPart != null && !cachedHandlers.contains(sharedPart)) { + var result = sharedPart.handleRecipe(IO.IN, recipe, ItemRecipeCapability.CAP, itemContent, simulated); + if (result == null || result.isEmpty()) { + if (simulated && strategy.cacheToHandlePartMap) { + machine.tryAddAndActiveRhp(recipe, sharedPart); + } + return true; + } + } + return false; } - public record RecipeHandlePart(IO io, Object2ObjectOpenHashMap, List>> allHandles) { + private static boolean tryNotDistinctFluid(@Nullable RecipeHandlePart sharedPart, GTRecipe recipe, + Reference2ObjectOpenHashMap, List> recipeContent, + boolean simulated) { + List fluidContent = recipeContent.getOrDefault(FluidRecipeCapability.CAP, Collections.emptyList()); + if (fluidContent.isEmpty()) return true; + if (sharedPart == null) return false; - private boolean testRecipeHandle(IDistinctMachine iDistinctMachine, GTRecipe recipe, List itemContent, List fluidContent) { - if (itemContent.isEmpty()) { - List copyFluid = new ObjectArrayList<>(fluidContent); - for (var handle : this.allHandles.get(FluidRecipeCapability.CAP)) { - copyFluid = handle.handleRecipe(IO.IN, recipe, copyFluid, null, true); - if (copyFluid == null) { - iDistinctMachine.setDistinctHatch(this); - return true; + fluidContent = sharedPart.handleRecipe(IO.IN, recipe, FluidRecipeCapability.CAP, fluidContent, simulated); + return fluidContent == null || fluidContent.isEmpty(); + } + + private static boolean tryNotDistinctItem(RecipeHandlePart cachedPart, GTRecipe recipe, + Reference2ObjectOpenHashMap, List> recipeContent, + boolean simulated) { + List itemContent = recipeContent.getOrDefault(ItemRecipeCapability.CAP, Collections.emptyList()); + if (itemContent.isEmpty()) return true; + + var result = cachedPart.handleRecipe(IO.IN, recipe, ItemRecipeCapability.CAP, itemContent, simulated); + return result == null || result.isEmpty(); + } + + // ======================================== + // Output + // ======================================== + + private static Reference2ObjectOpenHashMap, List> handleNormalOutput( + List handlers, GTRecipe recipe, + Reference2ObjectOpenHashMap, List> recipeContent, + boolean simulated) { + if (handlers.isEmpty()) return recipeContent; + + // Sort only if not already sorted (assuming handlers list is stable) + // Consider caching sorted handlers in machine if this becomes a bottleneck + handlers.sort(RecipeHandlePart.COMPARATOR.reversed()); + + for (var handler : handlers) { + recipeContent = handler.handleRecipe(IO.OUT, recipe, recipeContent, simulated); + if (recipeContent.isEmpty()) break; + } + + return recipeContent; + } + + private static Reference2ObjectOpenHashMap, List> handleMEOutput( + List> meHandlers, + Reference2ObjectOpenHashMap, List> recipeContent, + boolean simulated) { + for (MEIORecipeHandlePart meHandler : meHandlers) { + recipeContent = meHandler.meHandleOutput(recipeContent, simulated); + if (recipeContent.isEmpty()) break; + } + return recipeContent; + } + + private static RecipeResult generateOutputFailReason(Reference2ObjectOpenHashMap, List> recipeContent) { + var builder = new StringBuilder(); + for (var entry : Reference2ObjectMaps.fastIterable(recipeContent)) { + var cap = entry.getKey(); + for (var ing : entry.getValue()) { + if (cap == ItemRecipeCapability.CAP) { + if (ing instanceof LongIngredient li) { + builder.append(li.getItems()[0].getDisplayName().getString()).append("x ").append(li.getActualAmount()).append(" "); + } else if (ing instanceof SizedIngredient si) { + builder.append(si.getItems()[0].getDisplayName().getString()).append("x ").append(si.getAmount()).append(" "); } - } - } else if (fluidContent.isEmpty()) { - List copyItem = new ObjectArrayList<>(itemContent); - for (var handle : this.allHandles.get(ItemRecipeCapability.CAP)) { - copyItem = handle.handleRecipe(IO.IN, recipe, copyItem, null, true); - if (copyItem == null) { - iDistinctMachine.setDistinctHatch(this); - return true; + } else if (cap == FluidRecipeCapability.CAP) { + if (ing instanceof FluidIngredient fi) { + builder.append(fi.getStacks()[0].getDisplayName().getString()).append("x ").append(fi.getAmount()).append(" "); } } - } else { - List copyItem = new ObjectArrayList<>(itemContent); - for (var handle : this.allHandles.get(ItemRecipeCapability.CAP)) { - copyItem = handle.handleRecipe(IO.IN, recipe, copyItem, null, true); - if (copyItem == null) { - List copyFluid = new ObjectArrayList<>(fluidContent); - for (var h : this.allHandles.get(FluidRecipeCapability.CAP)) { - copyFluid = h.handleRecipe(IO.IN, recipe, copyFluid, null, true); - if (copyFluid == null) { - iDistinctMachine.setDistinctHatch(this); - return true; - } - } - copyItem = new ObjectArrayList<>(itemContent); - } + } + } + return RecipeResult.fail(Component.translatable("gtceu.recipe.fail.Output.Content", builder)); + } + + // ======================================== + // No-Cache versions (for DUMMY_RECIPES) + // Only handles input, output doesn't need nocache + // ======================================== + + private static boolean handleInputDistinctNocache(IRecipeCapabilityMachine machine, GTRecipe recipe, + Reference2ObjectOpenHashMap, List> recipeContent, + boolean simulated) { + for (var part : machine.getNormalRecipeHandlePart(IO.IN)) { + var result = part.handleRecipe(IO.IN, recipe, recipeContent, simulated); + if (result.isEmpty()) { + return true; + } + } + + for (var part : machine.getMEPatternRecipeHandleParts()) { + var slot = part.handleRecipe(recipe, recipeContent, simulated, false); + if (slot >= 0) { + return true; + } + } + + return false; + } + + @SuppressWarnings("DuplicatedCode") + private static boolean handleInputNotDistinctNocache(IRecipeCapabilityMachine machine, GTRecipe recipe, + Reference2ObjectOpenHashMap, List> recipeContent, + boolean simulated) { + RecipeHandlePart sharedPart = machine.getSharedRecipeHandlePart(); + boolean fluidHandled = true; + + List fluidContent = recipeContent.getOrDefault(FluidRecipeCapability.CAP, Collections.emptyList()); + if (!fluidContent.isEmpty()) { + if (sharedPart == null) fluidHandled = false; + else { + fluidContent = sharedPart.handleRecipe(IO.IN, recipe, FluidRecipeCapability.CAP, fluidContent, simulated); + if (fluidContent != null && !fluidContent.isEmpty()) fluidHandled = false; + } + } + + if (fluidHandled) { + List itemContent = recipeContent.getOrDefault(ItemRecipeCapability.CAP, Collections.emptyList()); + if (itemContent.isEmpty()) { + return true; + } + + for (var part : machine.getNormalRecipeHandlePart(IO.IN)) { + var result = part.handleRecipe(IO.IN, recipe, ItemRecipeCapability.CAP, itemContent, simulated); + if (result == null || result.isEmpty()) { + return true; } } - return false; + + if (sharedPart != null) { + var result = sharedPart.handleRecipe(IO.IN, recipe, ItemRecipeCapability.CAP, itemContent, simulated); + if (result == null || result.isEmpty()) { + return true; + } + } + } + + for (var part : machine.getMEPatternRecipeHandleParts()) { + var slot = part.handleRecipe(recipe, recipeContent, simulated, false); + if (slot >= 0) { + return true; + } } + + return false; } } diff --git a/src/main/java/org/gtlcore/gtlcore/api/recipe/RecipeRunnerHelper.java b/src/main/java/org/gtlcore/gtlcore/api/recipe/RecipeRunnerHelper.java index 1dc0ea269..96410624b 100644 --- a/src/main/java/org/gtlcore/gtlcore/api/recipe/RecipeRunnerHelper.java +++ b/src/main/java/org/gtlcore/gtlcore/api/recipe/RecipeRunnerHelper.java @@ -1,11 +1,14 @@ package org.gtlcore.gtlcore.api.recipe; +import org.gtlcore.gtlcore.api.machine.trait.IRecipeCapabilityMachine; + import com.gregtechceu.gtceu.api.capability.recipe.*; import com.gregtechceu.gtceu.api.machine.WorkableTieredMachine; import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; import com.gregtechceu.gtceu.api.machine.steam.SteamWorkableMachine; import com.gregtechceu.gtceu.api.recipe.GTRecipe; import com.gregtechceu.gtceu.api.recipe.content.Content; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.research.ResearchStationMachine; import com.gregtechceu.gtceu.common.machine.multiblock.primitive.PrimitiveWorkableMachine; import it.unimi.dsi.fastutil.objects.Object2IntMap; @@ -28,13 +31,10 @@ public static boolean matchRecipeInput(IRecipeCapabilityHolder holder, GTRecipe public static boolean matchRecipeOutput(IRecipeCapabilityHolder holder, GTRecipe recipe) { if (recipe.outputs.isEmpty()) return true; + if (holder instanceof IRecipeCapabilityMachine machine && machine.isRecipeOutputAlwaysMatch(recipe)) return true; return handleRecipe(IO.OUT, holder, recipe.outputs, Collections.emptyMap(), false, recipe, true).isSuccess(); } - public static boolean handleRecipeIO(IRecipeLogicMachine holder, GTRecipe recipe) { - return handleRecipeInput(holder, recipe) && handleRecipeOutput(holder, recipe); - } - public static boolean handleRecipeInput(IRecipeLogicMachine holder, GTRecipe recipe) { return handleRecipe(IO.IN, holder, recipe.inputs, holder.getRecipeLogic().getChanceCaches(), true, recipe, false).isSuccess(); } @@ -43,17 +43,45 @@ public static boolean handleRecipeOutput(IRecipeLogicMachine holder, GTRecipe re return handleRecipe(IO.OUT, holder, recipe.outputs, holder.getRecipeLogic().getChanceCaches(), true, recipe, false).isSuccess(); } - public static GTRecipe.ActionResult handleRecipe(IO io, IRecipeCapabilityHolder holder, Map, List> contents, - Map, Object2IntMap> chanceCaches, boolean isTick, GTRecipe recipe, boolean isSimulate) { - if (holder instanceof WorkableTieredMachine || holder instanceof SteamWorkableMachine || holder instanceof PrimitiveWorkableMachine) { - if (isSimulate) return recipe.matchRecipe(holder); - else return recipe.handleRecipe(io, holder, isTick, contents, chanceCaches) ? GTRecipe.ActionResult.SUCCESS : GTRecipe.ActionResult.fail(null); - } - RecipeRunner runner = new RecipeRunner(recipe, io, isTick, holder, chanceCaches, isSimulate); - if (isSimulate && io == IO.IN) return runner.simulatedHandle() ? GTRecipe.ActionResult.SUCCESS : GTRecipe.ActionResult.fail(null); - if (runner.handle(contents).isSuccess()) { - return GTRecipe.ActionResult.SUCCESS; + public static RecipeResult handleRecipe(IO io, IRecipeCapabilityHolder holder, Map, List> contents, + Map, Object2IntMap> chanceCaches, boolean isTick, GTRecipe recipe, boolean isSimulate) { + return handleRecipe(io, holder, contents, chanceCaches, isTick, recipe, isSimulate, RecipeCacheStrategy.FULL_CACHE); + } + + public static RecipeResult handleRecipe(IO io, IRecipeCapabilityHolder holder, Map, List> contents, + Map, Object2IntMap> chanceCaches, boolean isTick, GTRecipe recipe, boolean isSimulate, RecipeCacheStrategy cacheStrategy) { + if (holder instanceof PrimitiveWorkableMachine || holder instanceof SteamWorkableMachine || + holder instanceof WorkableTieredMachine || holder instanceof ResearchStationMachine) { + if (isSimulate) { + var result = recipe.matchRecipeContents(io, holder, io == IO.IN ? recipe.inputs : recipe.outputs, isTick); + if (result.isSuccess()) { + RecipeResult.of((IRecipeLogicMachine) holder, RecipeResult.SUCCESS); + return RecipeResult.SUCCESS; + } else RecipeResult.of((IRecipeLogicMachine) holder, io == IO.IN ? RecipeResult.FAIL_INPUT : RecipeResult.FAIL_OUTPUT); + } else if (recipe.handleRecipe(io, holder, isTick, contents, chanceCaches)) return RecipeResult.SUCCESS; + } else { + var result = RecipeRunner.handle(recipe, io, holder, contents, chanceCaches, isSimulate, cacheStrategy); + RecipeResult.of((IRecipeLogicMachine) holder, result); + return result; } - return GTRecipe.ActionResult.fail(null); + return RecipeResult.fail(null); + } + + // ============================================ + // Custom-Cache versions (for DUMMY_RECIPES) + // ============================================ + + public static boolean matchRecipeInputNoMEInnerCache(IRecipeCapabilityHolder holder, GTRecipe recipe) { + if (recipe.inputs.isEmpty()) return true; + return handleRecipe(IO.IN, holder, recipe.inputs, Collections.emptyMap(), false, recipe, true, RecipeCacheStrategy.HANDLE_PART_CACHE_ONLY).isSuccess(); + } + + public static boolean matchRecipeInputNocache(IRecipeCapabilityHolder holder, GTRecipe recipe) { + if (recipe.inputs.isEmpty()) return true; + return handleRecipe(IO.IN, holder, recipe.inputs, Collections.emptyMap(), false, recipe, true, RecipeCacheStrategy.NO_CACHE).isSuccess(); + } + + public static boolean handleRecipeInputNocache(IRecipeLogicMachine holder, GTRecipe recipe) { + return handleRecipe(IO.IN, holder, recipe.inputs, holder.getRecipeLogic().getChanceCaches(), true, recipe, false, RecipeCacheStrategy.NO_CACHE).isSuccess(); } } diff --git a/src/main/java/org/gtlcore/gtlcore/api/recipe/RecipeText.java b/src/main/java/org/gtlcore/gtlcore/api/recipe/RecipeText.java new file mode 100644 index 000000000..f2b4ee26c --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/recipe/RecipeText.java @@ -0,0 +1,68 @@ +package org.gtlcore.gtlcore.api.recipe; + +import org.gtlcore.gtlcore.utils.NumberUtils; + +import com.gregtechceu.gtceu.api.capability.recipe.FluidRecipeCapability; +import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.utils.FormattingUtil; + +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; + +public class RecipeText { + + public static MutableComponent getRecipeInputText(GTRecipe recipe) { + return getIngredientText(recipe, true); + } + + public static MutableComponent getRecipeOutputText(GTRecipe recipe) { + return getIngredientText(recipe, false); + } + + private static MutableComponent getIngredientText(GTRecipe recipe, boolean io) { + if (recipe != null) { + MutableComponent buffer = io ? + Component.translatable("gtceu.top.recipe_input").withStyle(ChatFormatting.WHITE) : + Component.translatable("gtceu.top.recipe_output").withStyle(ChatFormatting.WHITE); + var items = io ? recipe.getInputContents(ItemRecipeCapability.CAP) : recipe.getOutputContents(ItemRecipeCapability.CAP); + var fluids = io ? recipe.getInputContents(FluidRecipeCapability.CAP) : recipe.getOutputContents(FluidRecipeCapability.CAP); + for (var item : items) { + var stacks = ItemRecipeCapability.CAP.of(item.content).getItems(); + if (stacks.length == 0) continue; + var stack = stacks[0]; + int count = stack.getCount(); + float chance = 100.0F * (float) item.chance / (float) item.maxChance; + String percent = FormattingUtil.formatPercent(chance); + if (chance == 100.0F) { + buffer.append(Component.translatable("gtceu.machine.lockrecipe.line.0", stack.getHoverName(), count).withStyle(ChatFormatting.GRAY)); + } else if (chance == 0.0F) { + buffer.append(Component.translatable("gtceu.machine.lockrecipe.line.2", stack.getHoverName(), count).withStyle(ChatFormatting.GRAY)); + } else { + buffer.append(Component.translatable("gtceu.machine.lockrecipe.line.1", stack.getHoverName(), count, percent).withStyle(ChatFormatting.GRAY)); + } + } + for (var fluid : fluids) { + var stacks = FluidRecipeCapability.CAP.of(fluid.content).getStacks(); + if (stacks.length == 0) continue; + var stack = stacks[0]; + long amount = stack.getAmount(); + String s; + if (amount < 1000) s = amount + "mB"; + else s = NumberUtils.formatLong(amount / 1000) + "B"; + float chance = 100.0F * (float) fluid.chance / (float) fluid.maxChance; + String percent = FormattingUtil.formatPercent(chance); + if (chance == 100.0F) { + buffer.append(Component.translatable("gtceu.machine.lockrecipe.line.0", stack.getDisplayName(), s).withStyle(ChatFormatting.GRAY)); + } else if (chance == 0.0F) { + buffer.append(Component.translatable("gtceu.machine.lockrecipe.line.2", stack.getDisplayName(), s).withStyle(ChatFormatting.GRAY)); + } else { + buffer.append(Component.translatable("gtceu.machine.lockrecipe.line.1", stack.getDisplayName(), s, percent).withStyle(ChatFormatting.GRAY)); + } + } + return buffer; + } + return null; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/recipe/chance/LongChanceLogic.java b/src/main/java/org/gtlcore/gtlcore/api/recipe/chance/LongChanceLogic.java new file mode 100644 index 000000000..9dd9b1953 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/recipe/chance/LongChanceLogic.java @@ -0,0 +1,140 @@ +package org.gtlcore.gtlcore.api.recipe.chance; + +import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.api.capability.recipe.RecipeCapability; +import com.gregtechceu.gtceu.api.recipe.chance.boost.ChanceBoostFunction; +import com.gregtechceu.gtceu.api.recipe.chance.logic.ChanceLogic; +import com.gregtechceu.gtceu.api.recipe.content.Content; +import com.gregtechceu.gtceu.api.registry.GTRegistries; + +import net.minecraft.network.chat.Component; + +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.List; + +import static org.gtlcore.gtlcore.api.recipe.IAdvancedContentModifier.preciseDivision; + +public abstract class LongChanceLogic extends ChanceLogic { + + public static final LongChanceLogic OR; + + public LongChanceLogic(String id) { + super(id); + } + + public static int getChance(@NotNull Content entry, @NotNull ChanceBoostFunction boostFunction, int baseTier, int machineTier) { + return boostFunction.getBoostedChance(entry, baseTier, machineTier); + } + + public static int getCachedChance(Content entry, @Nullable Object2IntMap cache) { + if (cache == null) return GTValues.RNG.nextInt(entry.maxChance); + return cache.getOrDefault(entry.content, GTValues.RNG.nextInt(entry.maxChance)); + } + + @SuppressWarnings("all") + public static void updateCachedChance(Object ingredient, @Nullable Object2IntMap cache, int chance) { + if (cache != null) { + ((Object2IntMap) cache).put(ingredient, chance); + } + } + + /** + * Safe calculation (times * chance) / maxChance and (times * chance) % maxChance + * <=> times = q * maxChance + r + * <=> times * chance = q * maxChance * chance + r * chance + */ + public static void modifyByChanceSafe(@Nullable Object2IntMap cache, long times, RecipeCapability cap, List out, Content entry, int maxChance, int chance) { + long timesQuotient = times / maxChance; + long timesRemainder = times % maxChance; + + // guaranteed = (times * chance) / maxChance + // = (timesQuotient * maxChance * chance + timesRemainder * chance) / maxChance + // = timesQuotient * chance + (timesRemainder * chance) / maxChance + long guaranteed = timesQuotient * chance + (timesRemainder * chance) / maxChance; + + if (guaranteed > 0) out.add(entry.copy(cap, preciseDivision(guaranteed, times))); + + // newChance = (times * chance) % maxChance + // = (timesRemainder * chance) % maxChance + // timesRemainder < maxChance && chance < maxChance, < Int.MAX + int newChance = (int) ((timesRemainder * chance) % maxChance); + + int cached = getCachedChance(entry, cache); + int chanceSum = newChance + cached; + if (chanceSum >= maxChance) { + int bonusCount = chanceSum / maxChance; + out.add(entry.copy(cap, preciseDivision(bonusCount, times))); + newChance -= bonusCount * maxChance; + } + + updateCachedChance(entry.content, cache, newChance / 2 + cached); + } + + @Nullable + @Unmodifiable + public abstract List<@NotNull Content> roll( + @NotNull @Unmodifiable List<@NotNull Content> chancedEntries, + @NotNull ChanceBoostFunction boostFunction, + int baseTier, int machineTier, + @Nullable Object2IntMap cache, long times, + RecipeCapability cap); + + static { + GTRegistries.CHANCE_LOGICS.unfreeze(); + OR = new LongChanceLogic("longOr") { + + @Override + public @Nullable @Unmodifiable List<@NotNull Content> roll(@NotNull List chancedEntries, + @NotNull ChanceBoostFunction boostFunction, + int baseTier, + int machineTier, + @Nullable Object2IntMap cache, + long times, + RecipeCapability cap) { + List out = new ObjectArrayList<>(chancedEntries.size()); + + for (Content entry : chancedEntries) { + int maxChance = entry.maxChance; + int newChance = getChance(entry, boostFunction, baseTier, machineTier); + modifyByChanceSafe(cache, times, cap, out, entry, maxChance, newChance); + } + + return out.isEmpty() ? null : out; + } + + @Override + public @Nullable @Unmodifiable List<@NotNull Content> roll(@NotNull List chancedEntries, + @NotNull ChanceBoostFunction boostFunction, + int baseTier, + int machineTier, + @Nullable Object2IntMap cache, + int times, + RecipeCapability cap) { + List out = new ObjectArrayList<>(chancedEntries.size()); + + for (Content entry : chancedEntries) { + int maxChance = entry.maxChance; + int newChance = getChance(entry, boostFunction, baseTier, machineTier); + modifyByChanceSafe(cache, times, cap, out, entry, maxChance, newChance); + } + + return out.isEmpty() ? null : out; + } + + @Override + public @NotNull Component getTranslation() { + return Component.translatable("gtceu.chance_logic.or"); + } + + public String toString() { + return "LongChanceLogic{OR}"; + } + }; + GTRegistries.CHANCE_LOGICS.freeze(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/recipe/ingredient/CacheHashStrategies.java b/src/main/java/org/gtlcore/gtlcore/api/recipe/ingredient/CacheHashStrategies.java new file mode 100644 index 000000000..ad59006da --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/recipe/ingredient/CacheHashStrategies.java @@ -0,0 +1,234 @@ +package org.gtlcore.gtlcore.api.recipe.ingredient; + +import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; +import com.gregtechceu.gtceu.api.recipe.ingredient.IntProviderIngredient; +import com.gregtechceu.gtceu.api.recipe.ingredient.SizedIngredient; +import com.gregtechceu.gtceu.core.mixins.*; +import com.gregtechceu.gtceu.utils.ItemStackHashStrategy; + +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraftforge.common.crafting.IntersectionIngredient; +import net.minecraftforge.common.crafting.PartialNBTIngredient; +import net.minecraftforge.common.crafting.StrictNBTIngredient; + +import com.google.common.collect.Lists; +import it.unimi.dsi.fastutil.Hash; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; + +public class CacheHashStrategies { + + public static class IngredientHashStrategy implements Hash.Strategy { + + public static final IngredientHashStrategy INSTANCE = new IngredientHashStrategy(); + private static final ItemStackHashStrategy ITEM_TAG_STRATEGY = ItemStackHashStrategy.comparingAllButCount(); + private static final ItemStackHashStrategy ITEM_STRATEGY = ItemStackHashStrategy.builder().compareItem(true).build(); + + @Override + public int hashCode(Ingredient o) { + int hashCode = 537; + if (o instanceof StrictNBTIngredientAccessor strict) { + hashCode *= 31 * ITEM_TAG_STRATEGY.hashCode(strict.getStack()); + } else if (o instanceof PartialNBTIngredientAccessor partial) { + hashCode *= 31 * partial.getNbt().hashCode(); + hashCode *= 31 * partial.getItems().hashCode(); + } else if (o instanceof IntersectionIngredientAccessor intersection) { + for (Ingredient ingredient : intersection.getChildren()) { + hashCode *= 31 * this.hashCode(ingredient); + } + } else if (o instanceof IngredientAccessor ingredient) { + for (Ingredient.Value value : ingredient.getValues()) { + if (value instanceof TagValueAccessor tagValue) { + hashCode *= 31 * tagValue.getTag().hashCode(); + } else { + for (ItemStack stack : value.getItems()) { + hashCode *= 31 * ITEM_STRATEGY.hashCode(stack); + } + } + } + } + return hashCode; + } + + @Override + public boolean equals(Ingredient a, Ingredient b) { + return IngredientEquality.ingredientEquals(a, b); + } + } + + /** + * FluidIngredient Hash策略 - 忽略amount数量 + * 基于FluidIngredient源代码的完整实现,支持TagValue和FluidValue + */ + public static class FluidIngredientHashStrategy implements Hash.Strategy { + + public static final FluidIngredientHashStrategy INSTANCE = new FluidIngredientHashStrategy(); + + @Override + public int hashCode(FluidIngredient ingredient) { + if (ingredient == null) return 0; + + int result = Arrays.hashCode(ingredient.values); + result = 31 * result + Objects.hashCode(ingredient.getNbt()); + return result; + } + + @Override + public boolean equals(FluidIngredient a, FluidIngredient b) { + return fluidIngredientEqualsIgnoreAmount(a, b); + } + + private static boolean fluidIngredientEqualsIgnoreAmount(FluidIngredient a, FluidIngredient b) { + if (a == b) return true; + if (a == null || b == null) return false; + + if (!Objects.equals(a.getNbt(), b.getNbt())) { + return false; + } + + if (a.values.length != b.values.length) { + return false; + } + + for (FluidIngredient.Value value1 : a.values) { + for (FluidIngredient.Value value2 : b.values) { + if (value1 instanceof FluidIngredient.TagValue tagValue1) { + if (!(value2 instanceof FluidIngredient.TagValue tagValue2)) { + return false; + } + if (tagValue1.getTag() != tagValue2.getTag()) { + return false; + } + } else if (value1 instanceof FluidIngredient.FluidValue first) { + if (!(value2 instanceof FluidIngredient.FluidValue second)) { + return false; + } + if (first.hashCode() != second.hashCode()) { + return false; + } + } + } + } + return true; + } + } + + public class IngredientEquality { + + public static final Comparator ITEM_COMPARATOR = Comparator.comparing(BuiltInRegistries.ITEM::getKey); + + public static final Comparator INGREDIENT_VALUE_COMPARATOR = (value1, value2) -> { + if (value1 instanceof TagValueAccessor first) { + if (!(value2 instanceof TagValueAccessor second)) { + return 10; + } + if (first.getTag() != second.getTag()) { + return 1; + } + } else if (value1 instanceof ItemValueAccessor first) { + if (!(value2 instanceof ItemValueAccessor second)) { + return 10; + } + return ITEM_COMPARATOR.compare(first.getItem().getItem(), second.getItem().getItem()); + } + return 0; + }; + + public static final Comparator INGREDIENT_COMPARATOR = new Comparator<>() { + + @Override + public int compare(Ingredient first, Ingredient second) { + if (first instanceof StrictNBTIngredient strict1) { + if (second instanceof StrictNBTIngredientAccessor strict2) { + return strict1.test(strict2.getStack()) ? 0 : 1; + } + return 1; + } + if (first instanceof PartialNBTIngredient partial1) { + if (second instanceof PartialNBTIngredient partial2) { + if (partial1.getItems().length != partial2.getItems().length) + return 1; + for (ItemStack stack : partial1.getItems()) { + if (!partial2.test(stack)) { + return 1; + } + } + return 0; + } + return 1; + } + + if (first instanceof IntersectionIngredient intersection1) { + if (second instanceof IntersectionIngredient intersection2) { + List ingredients1 = Lists + .newArrayList(((IntersectionIngredientAccessor) intersection1).getChildren()); + List ingredients2 = Lists + .newArrayList(((IntersectionIngredientAccessor) intersection2).getChildren()); + if (ingredients1.size() != ingredients2.size()) return 1; + + ingredients1.sort(this); + ingredients2.sort(this); + + for (int i = 0; i < ingredients1.size(); ++i) { + Ingredient ingredient1 = ingredients1.get(i); + Ingredient ingredient2 = ingredients2.get(i); + int result = compare(ingredient1, ingredient2); + if (result != 0) { + return result; + } + } + return 0; + } + return 1; + } + + Ingredient.Value[] firstValues = ((IngredientAccessor) first).getValues(); + Ingredient.Value[] secondValues = ((IngredientAccessor) second).getValues(); + if (firstValues.length != secondValues.length) return 1; + + firstValues = firstValues.clone(); + secondValues = secondValues.clone(); + Arrays.parallelSort(firstValues, INGREDIENT_VALUE_COMPARATOR); + Arrays.parallelSort(secondValues, INGREDIENT_VALUE_COMPARATOR); + + for (int i = 0; i < firstValues.length; ++i) { + Ingredient.Value value1 = firstValues[i]; + Ingredient.Value value2 = secondValues[i]; + int result = INGREDIENT_VALUE_COMPARATOR.compare(value1, value2); + if (result != 0) { + return result; + } + } + return 0; + } + }; + + public static boolean ingredientEquals(Ingredient first, Ingredient second) { + if (first == second) return true; + if (first == null || second == null) return false; + + first = getInner(first); + second = getInner(second); + return cmp(first, second); + } + + private static boolean cmp(Ingredient first, Ingredient second) { + return INGREDIENT_COMPARATOR.compare(first, second) == 0; + } + + private static Ingredient getInner(Ingredient ingredient) { + if (ingredient instanceof SizedIngredient sizedIngredient) { + return getInner(sizedIngredient.getInner()); + } else if (ingredient instanceof IntProviderIngredient intProviderIngredient) { + return getInner(intProviderIngredient.getInner()); + } + return ingredient; + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/api/recipe/ingredient/LongIngredient.java b/src/main/java/org/gtlcore/gtlcore/api/recipe/ingredient/LongIngredient.java new file mode 100644 index 000000000..0463b36cc --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/api/recipe/ingredient/LongIngredient.java @@ -0,0 +1,178 @@ +package org.gtlcore.gtlcore.api.recipe.ingredient; + +import org.gtlcore.gtlcore.mixin.gtm.recipe.Ingredient.IntProviderIngredientAccessor; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.recipe.ingredient.*; +import com.gregtechceu.gtceu.core.mixins.*; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraftforge.common.crafting.IIngredientSerializer; + +import com.google.common.primitives.Ints; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import lombok.Getter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public class LongIngredient extends SizedIngredient { + + public static final ResourceLocation TYPE = GTCEu.id("Long"); + + @Getter + protected long actualAmount; + private int hashCode = 0; + private boolean changed = true; + @Getter + private final boolean isEmpty; + private final Value value; + + protected LongIngredient(Ingredient inner, long actualAmount) { + super(inner, Ints.saturatedCast(actualAmount)); + this.actualAmount = actualAmount; + this.isEmpty = inner.isEmpty(); + if (isEmpty || inner.getClass() != Ingredient.class) { + this.value = null; + } else { + var values = ((IngredientAccessor) inner).getValues(); + this.value = values.length == 1 ? values[0] : null; + } + } + + protected LongIngredient(@NotNull TagKey tag, long actualAmount) { + this(Ingredient.of(tag), actualAmount); + } + + protected LongIngredient(ItemStack itemStack, long actualAmount) { + this(itemStack.hasTag() ? NBTIngredient.createNBTIngredient(itemStack) : Ingredient.of(itemStack), actualAmount); + } + + public static LongIngredient create(Ingredient inner, long amount) { + return new LongIngredient(inner, amount); + } + + public static LongIngredient create(Ingredient inner) { + return new LongIngredient(inner, 1); + } + + public static Ingredient copy(Ingredient ingredient) { + if (ingredient instanceof LongIngredient longIngredient) { + var copy = LongIngredient.create(longIngredient.inner, longIngredient.actualAmount); + copy.hashCode = longIngredient.hashCode; + return copy; + } else if (ingredient instanceof SizedIngredient sizedIngredient) { + if (sizedIngredient.getInner() instanceof IntProviderIngredient intProviderIngredient) { + return copy(intProviderIngredient); + } + return LongIngredient.create(sizedIngredient.getInner(), (long) sizedIngredient.getAmount()); + } else if (ingredient instanceof IntCircuitIngredient circuit) { + return circuit.copy(); + } else if (ingredient instanceof IntProviderIngredient intProviderIngredient) { + IntProviderIngredient copied = new IntProviderIngredient(intProviderIngredient.getInner(), intProviderIngredient.getCountProvider()); + final var accessor = (IntProviderIngredientAccessor) intProviderIngredient; + if (accessor.getItemStack() != null) { + copied.setItemStacks(Arrays.stream(accessor.getItemStack()).map(ItemStack::copy) + .toArray(ItemStack[]::new)); + } + if (accessor.getSampledCount() != -1) { + copied.setSampledCount(accessor.getSampledCount()); + } + return copied; + } else { + return create(ingredient); + } + } + + @Override + @NotNull + public IIngredientSerializer getSerializer() { + return SERIALIZER; + } + + public static SizedIngredient fromJson(JsonObject json) { + return SERIALIZER.parse(json); + } + + @Override + public @NotNull JsonElement toJson() { + JsonObject json = new JsonObject(); + json.addProperty("type", TYPE.toString()); + json.addProperty("actualAmount", this.actualAmount); + json.add("ingredient", this.inner.toJson()); + return json; + } + + @Override + public boolean test(@Nullable ItemStack stack) { + if (stack == null) return false; + if (this.isEmpty) return stack.isEmpty(); + + if (this.value instanceof TagValueAccessor tagValue) { + return stack.is(tagValue.getTag()); + } else if (this.value instanceof ItemValueAccessor itemValue) { + return ItemStack.isSameItem(stack, itemValue.getItem()); + } + return this.inner.test(stack); + } + + @Override + public ItemStack @NotNull [] getItems() { + if (getInner() instanceof IntProviderIngredient intProviderIngredient) { + return intProviderIngredient.getItems(); + } + if (changed || itemStacks == null) { + if (isEmpty) return new ItemStack[0]; + var items = new ObjectArrayList(inner.getItems().length); + for (ItemStack item : this.inner.getItems()) { + items.add(item.copyWithCount(Ints.saturatedCast(actualAmount))); + } + itemStacks = items.toArray(new ItemStack[0]); + changed = false; + } + return itemStacks; + } + + @Override + public int hashCode() { + if (this.hashCode == 0) { + this.hashCode = Objects.hash(this.actualAmount, Objects.hashCode(this.inner)); + } + return this.hashCode; + } + + public void setActualAmount(long actualAmount) { + this.actualAmount = actualAmount; + this.changed = true; + } + + public static final IIngredientSerializer SERIALIZER = new IIngredientSerializer<>() { + + @Override + public @NotNull LongIngredient parse(FriendlyByteBuf buffer) { + long amount = buffer.readVarLong(); + return new LongIngredient(Ingredient.fromNetwork(buffer), amount); + } + + @Override + public @NotNull LongIngredient parse(JsonObject json) { + long amount = json.get("actualAmount").getAsLong(); + Ingredient inner = Ingredient.fromJson(json.get("ingredient")); + return new LongIngredient(inner, amount); + } + + @Override + public void write(FriendlyByteBuf buffer, LongIngredient ingredient) { + buffer.writeVarLong(ingredient.getActualAmount()); + ingredient.inner.toNetwork(buffer); + } + }; +} diff --git a/src/main/java/org/gtlcore/gtlcore/client/gui/widget/IExtendedClickData.java b/src/main/java/org/gtlcore/gtlcore/client/gui/widget/IExtendedClickData.java new file mode 100644 index 000000000..f1054958d --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/client/gui/widget/IExtendedClickData.java @@ -0,0 +1,13 @@ +package org.gtlcore.gtlcore.client.gui.widget; + +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; + +public interface IExtendedClickData { + + void setUUID(@Nullable UUID uuid); + + @Nullable + UUID getUUID(); +} diff --git a/src/main/java/org/gtlcore/gtlcore/client/gui/widget/MEOutListGridWidget.java b/src/main/java/org/gtlcore/gtlcore/client/gui/widget/MEOutListGridWidget.java new file mode 100644 index 000000000..4a8130eac --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/client/gui/widget/MEOutListGridWidget.java @@ -0,0 +1,295 @@ +package org.gtlcore.gtlcore.client.gui.widget; + +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.client.TooltipsHandler; + +import com.lowdragmc.lowdraglib.gui.util.DrawerHelper; +import com.lowdragmc.lowdraglib.gui.widget.DraggableScrollableWidgetGroup; +import com.lowdragmc.lowdraglib.gui.widget.Widget; +import com.lowdragmc.lowdraglib.side.fluid.FluidStack; +import com.lowdragmc.lowdraglib.utils.Position; +import com.lowdragmc.lowdraglib.utils.Size; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import appeng.api.stacks.AEFluidKey; +import appeng.api.stacks.AEItemKey; +import appeng.api.stacks.AEKey; +import appeng.api.stacks.GenericStack; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import it.unimi.dsi.fastutil.objects.Object2LongMaps; +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Optional; + +import static com.gregtechceu.gtceu.integration.ae2.gui.widget.slot.AEConfigSlotWidget.drawSelectionOverlay; +import static com.lowdragmc.lowdraglib.gui.util.DrawerHelper.drawItemStack; +import static com.lowdragmc.lowdraglib.gui.util.DrawerHelper.drawText; + +public class MEOutListGridWidget extends DraggableScrollableWidgetGroup { + + protected final Object2LongMap list; + private final int slotAmountY; + private int slotRowsAmount; + private static final int TYPE_ITEM = 0; + private static final int TYPE_FLUID = 1; + protected final static int ROW_CHANGE_ID = 2; + protected final static int CONTENT_CHANGE_ID = 3; + + protected final Object2LongMap changeMap = new Object2LongOpenHashMap<>(); + protected final Object2LongMap cached = new Object2LongOpenHashMap<>(); + protected final List displayList = new ObjectArrayList<>(); + + public MEOutListGridWidget(int x, int y, int slotsY, @NotNull Object2LongMap internalList) { + super(x, y, 18 + 140, slotsY * 18); + this.list = internalList; + this.slotAmountY = slotsY; + } + + public GenericStack getAt(int index) { + return index >= 0 && index < displayList.size() ? displayList.get(index) : null; + } + + private void addSlotRows(int amount) { + for (int i = 0; i < amount; i++) { + int widgetAmount = this.widgets.size(); + Widget widget = createDisplayWidget(0, i * 18, widgetAmount); + this.addWidget(widget); + } + } + + private void removeSlotRows(int amount) { + for (int i = 0; i < amount; i++) { + Widget slotWidget = this.widgets.remove(this.widgets.size() - 1); + removeWidget(slotWidget); + } + } + + private void modifySlotRows(int delta) { + if (delta > 0) { + addSlotRows(delta); + } else { + removeSlotRows(delta); + } + } + + protected void writeListChange(FriendlyByteBuf buffer) { + this.changeMap.clear(); + + // Remove: 缓存里有但当前 list 里没有 => 负增量并移除缓存 + for (var it = Object2LongMaps.fastIterator(cached); it.hasNext();) { + var entry = it.next(); + AEKey cachedKey = entry.getKey(); + if (!list.containsKey(cachedKey)) { + this.changeMap.put(cachedKey, -entry.getLongValue()); + it.remove(); + } + } + + // Change/Add: list 与 cached 的差异 + for (var it = Object2LongMaps.fastIterator(list); it.hasNext();) { + var entry = it.next(); + AEKey key = entry.getKey(); + long value = entry.getLongValue(); + long cacheValue = cached.getOrDefault(key, 0L); + long delta = value - cacheValue; + if (cacheValue == 0L) { + // 新增 + if (value != 0L) { + this.changeMap.put(key, value); + this.cached.put(key, value); + } + } else if (delta != 0L) { + // 变更 + this.changeMap.put(key, delta); + this.cached.put(key, value); + } + } + + buffer.writeVarInt(this.changeMap.size()); + for (var it = Object2LongMaps.fastIterator(this.changeMap); it.hasNext();) { + var entry = it.next(); + writeAnyKey(buffer, entry.getKey()); + buffer.writeVarLong(entry.getLongValue()); + } + } + + protected void readListChange(FriendlyByteBuf buffer) { + int size = buffer.readVarInt(); + for (int i = 0; i < size; i++) { + AEKey key = readAnyKey(buffer); + long delta = buffer.readVarLong(); + + boolean found = false; + var li = displayList.listIterator(); + while (li.hasNext()) { + var stack = li.next(); + if (stack.what().equals(key)) { + long newAmount = stack.amount() + delta; + if (newAmount > 0) { + li.set(new GenericStack(key, newAmount)); + } else { + li.remove(); + } + found = true; + break; + } + } + if (!found) { + if (delta > 0) { + displayList.add(new GenericStack(key, delta)); + } + } + } + } + + protected Widget createDisplayWidget(int x, int y, int index) { + return new DisplaySlot(x, y, this, index); + } + + @Override + public void detectAndSendChanges() { + super.detectAndSendChanges(); + int slotRowsRequired = Math.max(this.slotAmountY, list.size()); + if (this.slotRowsAmount != slotRowsRequired) { + int slotsToAdd = slotRowsRequired - this.slotRowsAmount; + this.slotRowsAmount = slotRowsRequired; + this.writeUpdateInfo(ROW_CHANGE_ID, buf -> buf.writeVarInt(slotsToAdd)); + this.modifySlotRows(slotsToAdd); + } + this.writeUpdateInfo(CONTENT_CHANGE_ID, this::writeListChange); + } + + @Override + @OnlyIn(Dist.CLIENT) + public void readUpdateInfo(int id, FriendlyByteBuf buffer) { + super.readUpdateInfo(id, buffer); + if (id == ROW_CHANGE_ID) { + int slotsToAdd = buffer.readVarInt(); + this.modifySlotRows(slotsToAdd); + } + if (id == CONTENT_CHANGE_ID) { + this.readListChange(buffer); + } + } + + @Override + public void writeInitialData(FriendlyByteBuf buffer) { + super.writeInitialData(buffer); + int slotRowsRequired = Math.max(this.slotAmountY, list.size()); + int slotsToAdd = slotRowsRequired - this.slotRowsAmount; + this.slotRowsAmount = slotRowsRequired; + this.modifySlotRows(slotsToAdd); + buffer.writeVarInt(slotsToAdd); + this.writeListChange(buffer); + } + + @Override + @OnlyIn(Dist.CLIENT) + public void readInitialData(FriendlyByteBuf buffer) { + super.readInitialData(buffer); + this.modifySlotRows(buffer.readVarInt()); + this.readListChange(buffer); + } + + private static void writeAnyKey(FriendlyByteBuf buf, AEKey key) { + if (key instanceof AEItemKey) { + buf.writeVarInt(TYPE_ITEM); + key.writeToPacket(buf); + } else if (key instanceof AEFluidKey) { + buf.writeVarInt(TYPE_FLUID); + key.writeToPacket(buf); + } + } + + private static AEKey readAnyKey(FriendlyByteBuf buf) { + int t = buf.readVarInt(); + return switch (t) { + case TYPE_ITEM -> AEItemKey.fromPacket(buf); + case TYPE_FLUID -> AEFluidKey.fromPacket(buf); + default -> throw new IllegalStateException("Unknown AEKey type id: " + t); + }; + } + + public static class DisplaySlot extends Widget { + + private final MEOutListGridWidget gridWidget; + private final int index; + + public DisplaySlot(int x, int y, MEOutListGridWidget gridWidget, int index) { + super(new Position(x, y), new Size(18, 18)); + this.gridWidget = gridWidget; + this.index = index; + } + + @Override + public void drawInBackground(@NotNull GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { + super.drawInBackground(graphics, mouseX, mouseY, partialTicks); + Position pos = getPosition(); + GenericStack gs = this.gridWidget.getAt(this.index); + + if (gs != null && gs.what() instanceof AEFluidKey) { + GuiTextures.FLUID_SLOT.draw(graphics, mouseX, mouseY, pos.x, pos.y, 18, 18); + } else { + GuiTextures.SLOT.draw(graphics, mouseX, mouseY, pos.x, pos.y, 18, 18); + } + GuiTextures.NUMBER_BACKGROUND.draw(graphics, mouseX, mouseY, pos.x + 18, pos.y, 140, 18); + + int stackX = pos.x + 1; + int stackY = pos.y + 1; + + if (gs != null) { + AEKey key = gs.what(); + long amt = gs.amount(); + + if (key instanceof AEItemKey itemKey) { + ItemStack stackForRender = new ItemStack(itemKey.getItem()); + drawItemStack(graphics, stackForRender, stackX, stackY, -1, null); + String amountStr = String.format("x%,d", amt); + drawText(graphics, amountStr, stackX + 20, stackY + 5, 1, 0xFFFFFFFF); + } else if (key instanceof AEFluidKey fluidKey) { + FluidStack fs = FluidStack.create(fluidKey.getFluid(), amt, fluidKey.getTag()); + DrawerHelper.drawFluidForGui(graphics, fs, amt, stackX, stackY, 16, 16); + String amountStr = String.format("x%,d", amt); + drawText(graphics, amountStr, stackX + 20, stackY + 5, 1, 0xFFFFFFFF); + } + } + + if (isMouseOverElement(mouseX, mouseY)) { + drawSelectionOverlay(graphics, stackX, stackY, 16, 16); + } + } + + @Override + public void drawInForeground(@NotNull GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { + if (!isMouseOverElement(mouseX, mouseY)) return; + + GenericStack gs = this.gridWidget.getAt(this.index); + if (gs == null) return; + + AEKey key = gs.what(); + long amt = gs.amount(); + + if (key instanceof AEItemKey) { + graphics.renderTooltip(Minecraft.getInstance().font, GenericStack.wrapInItemStack(gs), mouseX, mouseY); + } else if (key instanceof AEFluidKey fluidKey) { + FluidStack fs = FluidStack.create(fluidKey.getFluid(), amt, fluidKey.getTag()); + List tips = new ObjectArrayList<>(); + tips.add(fs.getDisplayName()); + tips.add(Component.literal(String.format("%,d mB", amt))); + TooltipsHandler.appendFluidTooltips(fs.getFluid(), fs.getAmount(), tips::add, TooltipFlag.NORMAL); + graphics.renderTooltip(Minecraft.getInstance().font, tips, Optional.empty(), mouseX, mouseY); + } + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/CommonProxy.java b/src/main/java/org/gtlcore/gtlcore/common/CommonProxy.java index e3bf409f6..430dd9dcb 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/CommonProxy.java +++ b/src/main/java/org/gtlcore/gtlcore/common/CommonProxy.java @@ -5,6 +5,7 @@ import org.gtlcore.gtlcore.common.data.machines.GeneratorMachine; import org.gtlcore.gtlcore.config.ConfigHolder; import org.gtlcore.gtlcore.integration.ae2.InfinityCellGuiHandler; +import org.gtlcore.gtlcore.integration.ae2.storage.FastInfinityCellHandler; import org.gtlcore.gtlcore.integration.ae2.storage.InfinityCellHandler; import com.gregtechceu.gtceu.api.GTCEuAPI; @@ -49,6 +50,7 @@ public static void init() { private void commonSetup(final FMLCommonSetupEvent event) { StorageCells.addCellHandler(InfinityCellHandler.INSTANCE); + StorageCells.addCellHandler(FastInfinityCellHandler.INSTANCE); StorageCells.addCellGuiHandler(new InfinityCellGuiHandler()); event.enqueueWork(this::postRegistrationInitialization).whenComplete((res, err) -> { if (err != null) { diff --git a/src/main/java/org/gtlcore/gtlcore/common/block/BlockMap.java b/src/main/java/org/gtlcore/gtlcore/common/block/BlockMap.java new file mode 100644 index 000000000..d3ccc770b --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/block/BlockMap.java @@ -0,0 +1,36 @@ +package org.gtlcore.gtlcore.common.block; + +import com.gregtechceu.gtceu.api.GTCEuAPI; + +import net.minecraft.world.level.block.Block; +import net.minecraftforge.common.util.Lazy; + +import it.unimi.dsi.fastutil.ints.*; +import it.unimi.dsi.fastutil.objects.*; + +import java.util.*; +import java.util.function.Supplier; + +public interface BlockMap { + + Object2ObjectOpenHashMap> tierBlockMap = new Object2ObjectOpenHashMap<>(4); + + Int2ObjectOpenHashMap> scMap = new Int2ObjectOpenHashMap<>(4); + Int2ObjectOpenHashMap> sepmMap = new Int2ObjectOpenHashMap<>(8); + Int2ObjectOpenHashMap> calMap = new Int2ObjectOpenHashMap<>(16); + + static void init() { + tierBlockMap.put("sc", Lazy.of(() -> scMap.int2ObjectEntrySet().stream() + .sorted(Comparator.comparingInt(Int2ObjectMap.Entry::getIntKey)) + .map(Int2ObjectMap.Entry::getValue).map(Supplier::get).toArray(Block[]::new))); + tierBlockMap.put("sepm", Lazy.of(() -> sepmMap.int2ObjectEntrySet().stream() + .sorted(Comparator.comparingInt(Int2ObjectMap.Entry::getIntKey)) + .map(Int2ObjectMap.Entry::getValue).map(Supplier::get).toArray(Block[]::new))); + tierBlockMap.put("cal", Lazy.of(() -> calMap.int2ObjectEntrySet().stream() + .sorted(Comparator.comparingInt(Int2ObjectMap.Entry::getIntKey)) + .map(Int2ObjectMap.Entry::getValue).map(Supplier::get).toArray(Block[]::new))); + tierBlockMap.put("coil", Lazy.of(() -> GTCEuAPI.HEATING_COILS.entrySet().stream() + .sorted(Comparator.comparingInt(e -> e.getKey().getCoilTemperature())) + .map(Map.Entry::getValue).map(Supplier::get).toArray(Block[]::new))); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/data/GTLBlocks.java b/src/main/java/org/gtlcore/gtlcore/common/data/GTLBlocks.java index 4c405223b..8d75beafd 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/data/GTLBlocks.java +++ b/src/main/java/org/gtlcore/gtlcore/common/data/GTLBlocks.java @@ -1,6 +1,7 @@ package org.gtlcore.gtlcore.common.data; import org.gtlcore.gtlcore.GTLCore; +import org.gtlcore.gtlcore.common.block.BlockMap; import org.gtlcore.gtlcore.common.block.CleanroomFilterType; import org.gtlcore.gtlcore.common.block.CraftingUnitType; import org.gtlcore.gtlcore.common.block.GTLFusionCasingBlock; @@ -40,13 +41,11 @@ import com.tterrag.registrate.util.nullness.NonNullBiConsumer; import com.tterrag.registrate.util.nullness.NonNullFunction; import com.tterrag.registrate.util.nullness.NonNullSupplier; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; +import java.util.*; import java.util.function.Supplier; import static com.gregtechceu.gtceu.common.data.GTBlocks.ALL_FUSION_CASINGS; @@ -54,14 +53,11 @@ public class GTLBlocks { - public static Map> scmap = new HashMap<>(); - public static Map> sepmmap = new HashMap<>(); - public static Map> calmap = new HashMap<>(); - public static void init() { + BlockMap.init(); for (int i = 1; i < 15; i++) { GTLBlocks.createTierCasings("component_assembly_line_casing_" + GTValues.VN[i].toLowerCase(), - GTLCore.id("block/casings/component_assembly_line/component_assembly_line_casing_" + GTValues.VN[i].toLowerCase()), calmap, i); + GTLCore.id("block/casings/component_assembly_line/component_assembly_line_casing_" + GTValues.VN[i].toLowerCase()), BlockMap.calMap, i); } } @@ -128,8 +124,7 @@ private static BlockEntry registerCraftingUnitBlock(int tier, @SuppressWarnings("all") public static BlockEntry createActiveCasing(String name, String baseModelPath) { return REGISTRATE.block(name, ActiveBlock::new) - .initialProperties(() -> Blocks.IRON_BLOCK) - .addLayer(() -> RenderType::cutoutMipped) + .initialProperties(() -> Blocks.IRON_BLOCK).addLayer(() -> RenderType::cutoutMipped) .blockstate(GTModels.createActiveModel(GTLCore.id(baseModelPath))) .tag(GTToolType.WRENCH.harvestTags.get(0), BlockTags.MINEABLE_WITH_PICKAXE) .item(BlockItem::new) @@ -140,7 +135,7 @@ public static BlockEntry createActiveCasing(String name, String bas @SuppressWarnings("all") public static BlockEntry createTierCasings(String name, ResourceLocation texture, - Map> map, int tier) { + Int2ObjectMap> map, int tier) { BlockEntry Block = REGISTRATE.block(name, p -> (Block) new Block(p) { @Override @@ -164,7 +159,7 @@ public void appendHoverText(@NotNull ItemStack stack, @Nullable BlockGetter leve @SuppressWarnings("all") public static BlockEntry createActiveTierCasing(String name, String baseModelPath, - Map> map, int tier) { + Int2ObjectMap> map, int tier) { BlockEntry Block = REGISTRATE.block("%s".formatted(name), p -> (ActiveBlock) new ActiveBlock(p) { @Override @@ -381,22 +376,22 @@ private static BlockEntry createHermeticCasing(int tier) { "block/variant/space_elevator_support"); public static final BlockEntry STELLAR_CONTAINMENT_CASING = createTierCasings( - "stellar_containment_casing", GTLCore.id("block/stellar_containment_casing"), scmap, 1); + "stellar_containment_casing", GTLCore.id("block/stellar_containment_casing"), BlockMap.scMap, 1); public static final BlockEntry ADVANCED_STELLAR_CONTAINMENT_CASING = createTierCasings( "advanced_stellar_containment_casing", GTLCore.id("block/stellar_containment_casing"), - scmap, 2); + BlockMap.scMap, 2); public static final BlockEntry ULTIMATE_STELLAR_CONTAINMENT_CASING = createTierCasings( "ultimate_stellar_containment_casing", GTLCore.id("block/stellar_containment_casing"), - scmap, 3); + BlockMap.scMap, 3); public static final BlockEntry POWER_MODULE = GTLBlocks.createActiveTierCasing("power_module", - "block/variant/power_module", sepmmap, 1); + "block/variant/power_module", BlockMap.sepmMap, 1); public static final BlockEntry POWER_MODULE_2 = GTLBlocks.createActiveTierCasing("power_module_2", - "block/variant/power_module", sepmmap, 2); + "block/variant/power_module", BlockMap.sepmMap, 2); public static final BlockEntry POWER_MODULE_3 = GTLBlocks.createActiveTierCasing("power_module_3", - "block/variant/power_module", sepmmap, 3); + "block/variant/power_module", BlockMap.sepmMap, 3); public static final BlockEntry POWER_MODULE_4 = GTLBlocks.createActiveTierCasing("power_module_4", - "block/variant/power_module", sepmmap, 4); + "block/variant/power_module", BlockMap.sepmMap, 4); public static final BlockEntry POWER_MODULE_5 = GTLBlocks.createActiveTierCasing("power_module_5", - "block/variant/power_module", sepmmap, 5); + "block/variant/power_module", BlockMap.sepmMap, 5); } diff --git a/src/main/java/org/gtlcore/gtlcore/common/data/GTLItems.java b/src/main/java/org/gtlcore/gtlcore/common/data/GTLItems.java index b3c7bd34e..a03dd09f3 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/data/GTLItems.java +++ b/src/main/java/org/gtlcore/gtlcore/common/data/GTLItems.java @@ -1,6 +1,7 @@ package org.gtlcore.gtlcore.common.data; import org.gtlcore.gtlcore.common.item.*; +import org.gtlcore.gtlcore.integration.ae2.FastInfinityCell; import org.gtlcore.gtlcore.integration.ae2.InfinityCell; import org.gtlcore.gtlcore.utils.TextUtil; @@ -12,6 +13,7 @@ import com.gregtechceu.gtceu.common.item.CoverPlaceBehavior; import com.gregtechceu.gtceu.common.item.TooltipBehavior; +import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; import net.minecraft.world.item.Item; @@ -106,6 +108,7 @@ private static ItemEntry registerStorageCell(int tier, public static final ItemEntry ITEM_INFINITY_CELL = REGISTRATE.item("item_infinity_cell", p -> new InfinityCell(AEKeyType.items())).register(); public static final ItemEntry FLUID_INFINITY_CELL = REGISTRATE.item("fluid_infinity_cell", p -> new InfinityCell(AEKeyType.fluids())).register(); + public static final ItemEntry FAST_INFINITY_CELL = REGISTRATE.item("fast_infinity_cell", p -> new FastInfinityCell()).register(); public static void InitUpgrades() { String storageCellGroup = GuiText.StorageCells.getTranslationKey(); @@ -145,21 +148,21 @@ public static void InitUpgrades() { public static ItemEntry REALLY_ULTIMATE_BATTERY = REGISTRATE .item("really_max_battery", ComponentItem::create) .onRegister( - attach(new TooltipBehavior(lines -> lines.add(Component.literal("§7填满就能通关GregTechCEu Modern"))))) + attach(new TooltipBehavior(lines -> lines.add(Component.translatable("tooltip.gtlcore.complete_gtceu_modern").withStyle(ChatFormatting.GRAY))))) .onRegister(modelPredicate(GTCEu.id("battery"), ElectricStats::getStoredPredicate)) .onRegister(attach(ElectricStats.createRechargeableBattery(Long.MAX_VALUE, GTValues.UEV))) .register(); public static ItemEntry TRANSCENDENT_ULTIMATE_BATTERY = REGISTRATE .item("transcendent_max_battery", ComponentItem::create) .onRegister( - attach(new TooltipBehavior(lines -> lines.add(Component.literal("§7填满就能通关GregTech Leisure"))))) + attach(new TooltipBehavior(lines -> lines.add(Component.translatable("tooltip.gtlcore.complete_gt_leisure").withStyle(ChatFormatting.GRAY))))) .onRegister(modelPredicate(GTCEu.id("battery"), ElectricStats::getStoredPredicate)) .onRegister(attach(ElectricStats.createRechargeableBattery(Long.MAX_VALUE, GTValues.UIV))) .register(); public static ItemEntry EXTREMELY_ULTIMATE_BATTERY = REGISTRATE .item("extremely_max_battery", ComponentItem::create) .onRegister( - attach(new TooltipBehavior(lines -> lines.add(Component.literal("§7有生之年将它填满"))))) + attach(new TooltipBehavior(lines -> lines.add(Component.translatable("tooltip.gtlcore.fill_in_lifetime").withStyle(ChatFormatting.GRAY))))) .onRegister(modelPredicate(GTCEu.id("battery"), ElectricStats::getStoredPredicate)) .onRegister(attach(ElectricStats.createRechargeableBattery(Long.MAX_VALUE, GTValues.UXV))) .register(); @@ -167,7 +170,7 @@ public static void InitUpgrades() { .item("insanely_max_battery", ComponentItem::create) .onRegister( attach(new TooltipBehavior( - lines -> lines.add(Component.literal(TextUtil.dark_purplish_red("填满也就图一乐")))))) + lines -> lines.add(Component.literal(TextUtil.dark_purplish_red(Component.translatable("tooltip.gtlcore.fill_for_fun").getString())))))) .onRegister(modelPredicate(GTCEu.id("battery"), ElectricStats::getStoredPredicate)) .onRegister(attach(ElectricStats.createRechargeableBattery(Long.MAX_VALUE, GTValues.OpV))) .register(); @@ -175,7 +178,7 @@ public static void InitUpgrades() { .item("mega_max_battery", ComponentItem::create) .onRegister( attach(new TooltipBehavior( - lines -> lines.add(Component.literal(TextUtil.full_color("填满电池 机械飞升")))))) + lines -> lines.add(Component.literal(TextUtil.full_color(Component.translatable("tooltip.gtlcore.fill_battery_ascension").getString())))))) .onRegister(modelPredicate(GTCEu.id("battery"), ElectricStats::getStoredPredicate)) .onRegister(attach(ElectricStats.createRechargeableBattery(Long.MAX_VALUE, GTValues.MAX))) .register(); @@ -283,6 +286,19 @@ private static ItemEntry registerTieredCover(int amperage) { .model(NonNullBiConsumer.noop()) .register(); + public static ItemEntry ULTIMATE_TERMINAL = REGISTRATE + .item("ultimate_terminal", ComponentItem::create) + .properties((p) -> p.stacksTo(1)) + .onRegister(GTItems.attach(new UltimateTerminalBehavior())) + .register(); + + public static ItemEntry ME_PATTERN_BUFFER_COPY = REGISTRATE + .item("me_pattern_buffer_copy", ComponentItem::create) + .properties(stack -> stack.stacksTo(1)) + .onRegister(attach(MEPatternBufferCopyBehavior.INSTANCE)) + .model(NonNullBiConsumer.noop()) + .register(); + private static ItemEntry register(String id, boolean defaultModel) { return defaultModel ? REGISTRATE.item(id, Item::new).register() : REGISTRATE.item(id, Item::new).model(NonNullBiConsumer.noop()).register(); } diff --git a/src/main/java/org/gtlcore/gtlcore/common/data/GTLMachines.java b/src/main/java/org/gtlcore/gtlcore/common/data/GTLMachines.java index 9a1f1e7ca..7238334d8 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/data/GTLMachines.java +++ b/src/main/java/org/gtlcore/gtlcore/common/data/GTLMachines.java @@ -7,6 +7,7 @@ import org.gtlcore.gtlcore.common.machine.generator.MagicEnergyMachine; import org.gtlcore.gtlcore.common.machine.multiblock.electric.CoilWorkableElectricMultipleRecipesMultiblockMachine; import org.gtlcore.gtlcore.common.machine.multiblock.part.*; +import org.gtlcore.gtlcore.common.machine.multiblock.part.ae.*; import org.gtlcore.gtlcore.common.machine.multiblock.part.maintenance.*; import com.gregtechceu.gtceu.GTCEu; @@ -24,10 +25,14 @@ import com.gregtechceu.gtceu.client.renderer.machine.OverlayTieredMachineRenderer; import com.gregtechceu.gtceu.client.renderer.machine.SimpleGeneratorMachineRenderer; import com.gregtechceu.gtceu.client.util.TooltipHelper; -import com.gregtechceu.gtceu.common.data.GTCompassSections; -import com.gregtechceu.gtceu.common.data.GTCreativeModeTabs; -import com.gregtechceu.gtceu.common.data.GTMachines; -import com.gregtechceu.gtceu.common.data.GTRecipeTypes; +import com.gregtechceu.gtceu.common.data.*; +import com.gregtechceu.gtceu.common.registry.GTRegistration; +import com.gregtechceu.gtceu.integration.ae2.machine.MEInputBusPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.MEInputHatchPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.MEOutputBusPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.MEOutputHatchPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.MEStockingBusPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.MEStockingHatchPartMachine; import com.gregtechceu.gtceu.utils.FormattingUtil; import com.lowdragmc.lowdraglib.side.fluid.FluidHelper; @@ -63,8 +68,8 @@ public class GTLMachines { public static final BiConsumer> CHEMICAL_PLANT_DISPLAY = (controller, components) -> { if (controller.isFormed()) { double value = 1 - ((CoilWorkableElectricMultiblockMachine) controller).getCoilTier() * 0.05; - components.add(Component.translatable("gtceu.machine.eut_multiplier.tooltip", value * 0.8)); - components.add(Component.translatable("gtceu.machine.duration_multiplier.tooltip", value * 0.6)); + components.add(Component.translatable("gtceu.machine.eut_multiplier.tooltip", FormattingUtil.formatPercent(value * 0.8))); + components.add(Component.translatable("gtceu.machine.duration_multiplier.tooltip", FormattingUtil.formatPercent(value * 0.6))); } }; @@ -108,6 +113,7 @@ public static void init() { MultiBlockMachineA.init(); AdvancedMultiBlockMachine.init(); AdditionalMultiBlockMachine.init(); + GTAEMachines.init(); } static { @@ -585,4 +591,238 @@ private static MachineDefinition[] registerHugeFluidHatches(String name, String Component.translatable("gtceu.universal.enabled")) .compassNode("dual_hatch") .register(); + + public static class GTAEMachines { + + public static final MachineDefinition ITEM_IMPORT_BUS_ME = GTRegistration.REGISTRATE + .machine("me_input_bus", MEInputBusPartMachine::new) + .langValue("ME Input Bus") + .tier(4) + .rotationState(RotationState.ALL) + .abilities(PartAbility.IMPORT_ITEMS) + .overlayTieredHullRenderer("me_item_bus.import") + .tooltips(Component.translatable("gtceu.machine.item_bus.import.tooltip"), + Component.translatable("gtceu.machine.me.item_import.tooltip"), + Component.translatable("gtceu.machine.me.copy_paste.tooltip"), + Component.translatable("gtceu.universal.enabled")) + .compassNode("item_bus") + .register(); + + public static final MachineDefinition STOCKING_IMPORT_BUS_ME = GTRegistration.REGISTRATE + .machine("me_stocking_input_bus", MEStockingBusPartMachine::new) + .langValue("ME Stocking Input Bus") + .tier(6) + .rotationState(RotationState.ALL) + .abilities(PartAbility.IMPORT_ITEMS) + .overlayTieredHullRenderer("me_item_bus.import") + .tooltips(Component.translatable("gtceu.machine.item_bus.import.tooltip"), + Component.translatable("gtceu.machine.me.stocking_item.tooltip.0"), + Component.translatable("gtceu.machine.me_import_item_hatch.configs.tooltip"), + Component.translatable("gtceu.machine.me.copy_paste.tooltip"), + Component.translatable("gtceu.machine.me.stocking_item.tooltip.1"), + Component.translatable("gtceu.universal.enabled")) + .compassNode("item_bus") + .register(); + + public static final MachineDefinition ITEM_EXPORT_BUS_ME = GTRegistration.REGISTRATE + .machine("me_output_bus", MEOutputBusPartMachine::new) + .langValue("ME Output Bus") + .tier(4) + .rotationState(RotationState.ALL) + .abilities(PartAbility.EXPORT_ITEMS) + .overlayTieredHullRenderer("me_item_bus.export") + .tooltips(Component.translatable("gtceu.machine.item_bus.export.tooltip"), + Component.translatable("gtceu.machine.me.item_export.tooltip"), + Component.translatable("gtceu.machine.me.export.tooltip"), + Component.translatable("gtceu.universal.enabled")) + .compassNode("item_bus") + .register(); + + public static final MachineDefinition FLUID_IMPORT_HATCH_ME = GTRegistration.REGISTRATE + .machine("me_input_hatch", MEInputHatchPartMachine::new) + .langValue("ME Input Hatch") + .tier(4) + .rotationState(RotationState.ALL) + .abilities(PartAbility.IMPORT_FLUIDS) + .overlayTieredHullRenderer("me_fluid_hatch.import") + .tooltips(Component.translatable("gtceu.machine.fluid_hatch.import.tooltip"), + Component.translatable("gtceu.machine.me.fluid_import.tooltip"), + Component.translatable("gtceu.machine.me.copy_paste.tooltip"), + Component.translatable("gtceu.universal.enabled")) + .compassNode("fluid_hatch") + .register(); + + public static final MachineDefinition STOCKING_IMPORT_HATCH_ME = GTRegistration.REGISTRATE + .machine("me_stocking_input_hatch", MEStockingHatchPartMachine::new) + .langValue("ME Stocking Input Hatch") + .tier(6) + .rotationState(RotationState.ALL) + .abilities(PartAbility.IMPORT_FLUIDS) + .overlayTieredHullRenderer("me_fluid_hatch.import") + .tooltips(Component.translatable("gtceu.machine.fluid_hatch.import.tooltip"), + Component.translatable("gtceu.machine.me.stocking_fluid.tooltip.0"), + Component.translatable("gtceu.machine.me_import_fluid_hatch.configs.tooltip"), + Component.translatable("gtceu.machine.me.copy_paste.tooltip"), + Component.translatable("gtceu.machine.me.stocking_fluid.tooltip.1"), + Component.translatable("gtceu.universal.enabled")) + .compassNode("fluid_hatch") + .register(); + + public static final MachineDefinition FLUID_EXPORT_HATCH_ME = GTRegistration.REGISTRATE + .machine("me_output_hatch", MEOutputHatchPartMachine::new) + .langValue("ME Output Hatch") + .tier(4) + .rotationState(RotationState.ALL) + .abilities(PartAbility.EXPORT_FLUIDS) + .overlayTieredHullRenderer("me_fluid_hatch.export") + .tooltips(Component.translatable("gtceu.machine.fluid_hatch.export.tooltip"), + Component.translatable("gtceu.machine.me.fluid_export.tooltip"), + Component.translatable("gtceu.machine.me.export.tooltip"), + Component.translatable("gtceu.universal.enabled")) + .compassNode("fluid_hatch") + .register(); + + public static final MachineDefinition ME_MINI_PATTERN_BUFFER = REGISTRATE + .machine("me_mini_pattern_buffer", (h) -> new MEPatternBufferPartMachine(h, 9, IO.IN)) + .tier(5) + .rotationState(RotationState.ALL) + .abilities(PartAbility.IMPORT_ITEMS, PartAbility.IMPORT_FLUIDS) + .overlayTieredHullRenderer("me_mini_pattern_buffer") + .langValue("ME Mini Pattern Buffer") + .tooltips(Component.translatable("block.gtceu.pattern_buffer.desc.0"), + Component.translatable("gtceu.machine.me_pattern_buffer.desc.0"), + Component.translatable("gtceu.machine.me_pattern_buffer.desc.1"), + Component.translatable("gtceu.machine.me_pattern_buffer.desc.2"), + Component.translatable("gtceu.machine.me_pattern_buffer.desc.3"), + Component.translatable("gtceu.machine.me_pattern_buffer.desc.4"), + Component.translatable("gtceu.machine.me_mini_pattern_buffer.desc.0"), + Component.translatable("block.gtceu.pattern_buffer.desc.2"), + Component.translatable("gtceu.universal.enabled")) + .tooltipBuilder(GTL_ADD) + .register(); + + public static final MachineDefinition ME_EXTEND_PATTERN_BUFFER = REGISTRATE + .machine("me_extend_pattern_buffer", (h) -> new MEPatternBufferPartMachine(h, 36, IO.BOTH)) + .tier(8) + .rotationState(RotationState.ALL) + .abilities(PartAbility.IMPORT_ITEMS, PartAbility.IMPORT_FLUIDS, PartAbility.EXPORT_ITEMS, PartAbility.EXPORT_FLUIDS) + .overlayTieredHullRenderer("me_pattern_buffer") + .langValue("ME Extend Pattern Buffer") + .tooltips(Component.translatable("block.gtceu.pattern_buffer.desc.0"), + Component.translatable("gtceu.machine.me_pattern_buffer.desc.0"), + Component.translatable("gtceu.machine.me_pattern_buffer.desc.1"), + Component.translatable("gtceu.machine.me_pattern_buffer.desc.2"), + Component.translatable("gtceu.machine.me_pattern_buffer.desc.3"), + Component.translatable("gtceu.machine.me_pattern_buffer.desc.4"), + Component.translatable("gtceu.machine.me_pattern_buffer.desc.5"), + Component.translatable("block.gtceu.pattern_buffer.desc.2"), + Component.translatable("gtceu.universal.enabled")) + .tooltipBuilder(GTL_ADD) + .register(); + + public static final MachineDefinition ME_FINAL_PATTERN_BUFFER = REGISTRATE + .machine("me_final_pattern_buffer", (h) -> new MEPatternBufferPartMachine(h, 72, IO.BOTH)) + .tier(10) + .rotationState(RotationState.ALL) + .abilities(PartAbility.IMPORT_ITEMS, PartAbility.IMPORT_FLUIDS, PartAbility.EXPORT_ITEMS, PartAbility.EXPORT_FLUIDS) + .overlayTieredHullRenderer("me_pattern_buffer") + .langValue("ME Final Pattern Buffer") + .tooltips(Component.translatable("tooltip.gtlcore.bigger_stronger").withStyle(ChatFormatting.GOLD), + Component.translatable("block.gtceu.pattern_buffer.desc.0"), + Component.translatable("gtceu.machine.me_pattern_buffer.desc.0"), + Component.translatable("gtceu.machine.me_pattern_buffer.desc.1"), + Component.translatable("gtceu.machine.me_pattern_buffer.desc.2"), + Component.translatable("gtceu.machine.me_pattern_buffer.desc.3"), + Component.translatable("gtceu.machine.me_pattern_buffer.desc.4"), + Component.translatable("gtceu.machine.me_pattern_buffer.desc.5"), + Component.translatable("block.gtceu.pattern_buffer.desc.2"), + Component.translatable("gtceu.universal.enabled")) + .tooltipBuilder(GTL_ADD) + .register(); + + public static final MachineDefinition ME_PATTERN_BUFFER_PROXY = REGISTRATE + .machine("me_pattern_buffer_proxy", MEPatternBufferProxyPartMachine::new) + .tier(8) + .rotationState(RotationState.ALL) + .abilities(PartAbility.IMPORT_ITEMS, PartAbility.IMPORT_FLUIDS, PartAbility.EXPORT_FLUIDS, PartAbility.EXPORT_ITEMS) + .overlayTieredHullRenderer("me_pattern_buffer_proxy") + .langValue("ME Pattern Buffer Proxy") + .tooltips(Component.translatable("block.gtceu.pattern_buffer_proxy.desc.0"), + Component.translatable("block.gtceu.pattern_buffer_proxy.desc.1"), + Component.translatable("block.gtceu.pattern_buffer_proxy.desc.2"), + Component.translatable("gtceu.machine.me_pattern_buffer_proxy.desc.0"), + Component.translatable("gtceu.universal.enabled")) + .tooltipBuilder(GTL_MODIFY) + .register(); + + public static final MachineDefinition ME_EXTENDED_EXPORT_BUFFER = REGISTRATE + .machine("me_extended_export_buffer", MEExtendedOutputPartMachine::new) + .tier(9) + .rotationState(RotationState.ALL) + .abilities(PartAbility.EXPORT_FLUIDS, PartAbility.EXPORT_ITEMS) + .overlayTieredHullRenderer("me_extended_export_buffer") + .langValue("ME Extended Export Buffer") + .tooltips(Component.translatable("gtmthings.machine.me_export_buffer.tooltip"), + Component.translatable("gtceu.machine.me_extended_export_buffer.tooltip.0")) + .tooltipBuilder(GTL_ADD) + .register(); + + public static final MachineDefinition ME_EXTENDED_ASYNC_EXPORT_BUFFER = REGISTRATE + .machine("me_extended_async_export_buffer", MEExtendedAsyncOutputPartMachine::new) + .tier(9) + .rotationState(RotationState.ALL) + .abilities(PartAbility.EXPORT_FLUIDS, PartAbility.EXPORT_ITEMS) + .overlayTieredHullRenderer("me_extended_export_buffer") + .langValue("ME Extended Async Export Buffer") + .tooltips(Component.translatable("gtmthings.machine.me_export_buffer.tooltip"), + Component.translatable("gtceu.machine.me_extended_async_export_buffer.tooltip.0"), + Component.translatable("gtceu.machine.me_extended_async_export_buffer.tooltip.1")) + .tooltipBuilder(GTL_ADD) + .register(); + public static final MachineDefinition ME_CRAFT_SPEED_CORE = REGISTRATE + .machine("me_craft_speed_core", MECraftSpeedCorePartMachine::new) + .rotationState(RotationState.ALL) + .abilities(GTLPartAbility.MOLECULAR_ASSEMBLER_MATRIX) + .workableCasingRenderer(GTCEu.id("block/casings/speed_core_casing"), GTCEu.id("block/casings/speed_core_casing")) + .langValue("ME CRAFT Speed Core") + .tooltips(Component.translatable("gtceu.universal.disabled"), + Component.translatable("gtceu.machine.me_craft_speed_core.tooltip.0")) + .tooltipBuilder(GTL_ADD) + .register(); + + public static final MachineDefinition ME_CRAFT_PARALLEL_CORE = REGISTRATE + .machine("me_craft_parallel_core", MECraftParallelCorePartMachine::new) + .rotationState(RotationState.ALL) + .abilities(GTLPartAbility.MOLECULAR_ASSEMBLER_MATRIX) + .workableCasingRenderer(GTCEu.id("block/casings/crafter_core_casing"), GTCEu.id("block/casings/crafter_core_casing")) + .langValue("ME CRAFT Parallel Core") + .tooltips(Component.translatable("gtceu.universal.disabled"), + Component.translatable("gtceu.machine.me_craft_parallel_core.tooltip.0")) + .tooltipBuilder(GTL_ADD) + .register(); + + public static final MachineDefinition ME_CRAFT_PATTERN_CONTAINER = REGISTRATE.machine("me_craft_pattern_container", MECraftPatternContainerPartMachine::new) + .rotationState(RotationState.ALL) + .abilities(GTLPartAbility.MOLECULAR_ASSEMBLER_MATRIX) + .workableCasingRenderer(GTCEu.id("block/casings/pattern_core_casing"), GTCEu.id("block/casings/pattern_core_casing")) + .langValue("ME Craft Pattern Container") + .tooltips(Component.translatable("gtceu.universal.disabled"), Component.translatable("gtceu.machine.me_craft_pattern_core.tooltip", + Component.literal(FormattingUtil.formatNumbers(12 * 9)).withStyle(ChatFormatting.GOLD))) + .tooltipBuilder(GTL_ADD) + .register(); + + public static final MachineDefinition ME_MOLECULAR_ASSEMBLER_IO = REGISTRATE + .machine("me_molecular_assembler_io", MEMolecularAssemblerIOPartMachine::new) + .tier(8) + .rotationState(RotationState.ALL) + .overlayTieredHullRenderer("me_pattern_buffer") + .langValue("ME Molecular Assembler IO") + .tooltips(Component.translatable("gtceu.universal.disabled"), + Component.translatable("gtceu.machine.me_molecular_assembler_io.tooltip.0"), + Component.translatable("gtceu.machine.me_molecular_assembler_io.tooltip.1")) + .tooltipBuilder(GTL_ADD) + .register(); + + public static void init() {} + } } diff --git a/src/main/java/org/gtlcore/gtlcore/common/data/GTLRecipeModifiers.java b/src/main/java/org/gtlcore/gtlcore/common/data/GTLRecipeModifiers.java index 924b98c96..3ec0c83ee 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/data/GTLRecipeModifiers.java +++ b/src/main/java/org/gtlcore/gtlcore/common/data/GTLRecipeModifiers.java @@ -1,16 +1,19 @@ package org.gtlcore.gtlcore.common.data; +import org.gtlcore.gtlcore.api.machine.trait.IRecipeCapabilityMachine; +import org.gtlcore.gtlcore.api.machine.trait.MEPatternRecipeHandlePart; +import org.gtlcore.gtlcore.api.machine.trait.RecipeHandlePart; +import org.gtlcore.gtlcore.api.recipe.RecipeResult; import org.gtlcore.gtlcore.common.machine.multiblock.electric.StorageMachine; import org.gtlcore.gtlcore.common.machine.multiblock.steam.LargeSteamParallelMultiblockMachine; -import com.gregtechceu.gtceu.api.capability.IParallelHatch; import com.gregtechceu.gtceu.api.capability.recipe.EURecipeCapability; import com.gregtechceu.gtceu.api.capability.recipe.FluidRecipeCapability; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.machine.feature.IOverclockMachine; +import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; -import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiPart; import com.gregtechceu.gtceu.api.machine.multiblock.CoilWorkableElectricMultiblockMachine; import com.gregtechceu.gtceu.api.machine.multiblock.WorkableElectricMultiblockMachine; import com.gregtechceu.gtceu.api.recipe.GTRecipe; @@ -25,11 +28,14 @@ import com.lowdragmc.lowdraglib.side.fluid.FluidStack; +import net.minecraft.network.chat.Component; +import net.minecraft.world.level.material.Fluid; + +import it.unimi.dsi.fastutil.objects.Object2LongMaps; import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.Objects; -import java.util.Optional; public class GTLRecipeModifiers { @@ -72,55 +78,54 @@ public static GTRecipe processingPlantOverclock(MetaMachine machine, @NotNull GT public static GTRecipe nanoForgeOverclock(MetaMachine machine, @NotNull GTRecipe recipe, @NotNull OCParams params, @NotNull OCResult result, int tier) { if (machine instanceof StorageMachine storageMachine) { + int t = recipe.data.getInt("nano_forge_tier"); + if (t > tier) { + RecipeResult.of((IRecipeLogicMachine) machine, + RecipeResult.fail(Component.translatable("gtceu.recipe.fail.nano_forge.tier"))); + return null; + } if (tier == 1) { - if (recipe.data.getInt("nano_forge_tier") > 1 && !Objects.equals(storageMachine.getMachineStorageItem().kjs$getId(), "gtceu:carbon_nanoswarm")) { + if (!Objects.equals(storageMachine.getMachineStorageItem().kjs$getId(), "gtceu:carbon_nanoswarm")) { + RecipeResult.of((IRecipeLogicMachine) machine, + RecipeResult.fail(Component.translatable("message.gtlcore.need_carbon_nano_swarm"))); return null; } } else if (tier == 2) { - if (recipe.data.getInt("nano_forge_tier") > 2 && !Objects.equals(storageMachine.getMachineStorageItem().kjs$getId(), "gtceu:neutronium_nanoswarm")) { + if (!Objects.equals(storageMachine.getMachineStorageItem().kjs$getId(), "gtceu:neutronium_nanoswarm")) { + RecipeResult.of((IRecipeLogicMachine) machine, + RecipeResult.fail(Component.translatable("message.gtlcore.need_neutronium_nano_swarm"))); return null; } } else if (tier == 3) { if (!Objects.equals(storageMachine.getMachineStorageItem().kjs$getId(), "gtceu:draconium_nanoswarm")) { + RecipeResult.of((IRecipeLogicMachine) machine, + RecipeResult.fail(Component.translatable("message.gtlcore.need_dragon_nano_swarm"))); return null; } } - GTRecipe recipe1 = GTRecipeModifiers.accurateParallel(machine, recipe, (int) (storageMachine.getMachineStorageItem().getCount() * Math.pow(2, tier - recipe.data.getInt("nano_forge_tier"))), false).getFirst(); - return RecipeHelper.applyOverclock(new OverclockingLogic(1 / Math.pow(2, 1 + tier - recipe.data.getInt("nano_forge_tier")), 4, false), recipe1, storageMachine.getOverclockVoltage(), params, result); + GTRecipe recipe1 = GTRecipeModifiers.accurateParallel(machine, recipe, (int) (storageMachine.getMachineStorageItem().getCount() * Math.pow(2, tier - t)), false).getFirst(); + return RecipeHelper.applyOverclock(new OverclockingLogic(1 / Math.pow(2, 1 + tier - t), 4, false), recipe1, storageMachine.getOverclockVoltage(), params, result); } return null; } public static GTRecipe dissolvingTankOverclock(MetaMachine machine, @NotNull GTRecipe recipe, @NotNull OCParams params, @NotNull OCResult result) { - if (machine instanceof WorkableElectricMultiblockMachine workableElectricMultiblockMachine) { + if (machine instanceof WorkableElectricMultiblockMachine workmachine) { List fluidList = recipe.inputs.getOrDefault(FluidRecipeCapability.CAP, null); FluidStack fluidStack1 = FluidRecipeCapability.CAP.of(fluidList.get(0).getContent()).getStacks()[0]; FluidStack fluidStack2 = FluidRecipeCapability.CAP.of(fluidList.get(1).getContent()).getStacks()[0]; - long a = 0, b = 0; - for (IMultiPart part : workableElectricMultiblockMachine.getParts()) { - for (var handler : part.getRecipeHandlers()) { - if (handler.getHandlerIO() == IO.IN) { - for (Object contents : handler.getContents()) { - if (contents instanceof FluidStack fluidStack) { - if (fluidStack.getFluid() == fluidStack1.getFluid()) { - a += fluidStack.getAmount(); - } - if (fluidStack.getFluid() == fluidStack2.getFluid()) { - b += fluidStack.getAmount(); - } - } - } - } - } - } + + FluidAmounts amounts = countFluidAmounts(workmachine, recipe, fluidStack1.getFluid(), fluidStack2.getFluid()); + long a = amounts.first(), b = amounts.second(); if (b == 0) return null; GTRecipe hatchedParallel = GTRecipeModifiers.hatchParallel(machine, recipe, false, params, result); if (hatchedParallel == null) return null; - GTRecipe recipe1 = RecipeHelper.applyOverclock(OverclockingLogic.NON_PERFECT_OVERCLOCK_SUBTICK, hatchedParallel, workableElectricMultiblockMachine.getOverclockVoltage(), params, result); + GTRecipe recipe1 = RecipeHelper.applyOverclock(OverclockingLogic.NON_PERFECT_OVERCLOCK_SUBTICK, hatchedParallel, workmachine.getOverclockVoltage(), params, result); if (a / b != fluidStack1.getAmount() / fluidStack2.getAmount()) { + RecipeResult.ofWorking((IRecipeLogicMachine) machine, RecipeResult.fail(Component.translatable("gtceu.recipe.fail.no.ratio"))); recipe1.outputs.clear(); - } + } else RecipeResult.ofWorking((IRecipeLogicMachine) machine, RecipeResult.SUCCESS); return recipe1; } return null; @@ -148,13 +153,9 @@ public static GTRecipe reduction(MetaMachine machine, @NotNull GTRecipe recipe, } public static int getHatchParallel(MetaMachine machine) { - if (machine instanceof IMultiController controller && controller.isFormed()) { - Optional optional = controller.getParts().stream().filter(IParallelHatch.class::isInstance) - .map(IParallelHatch.class::cast).findAny(); - if (optional.isPresent()) { - IParallelHatch hatch = optional.get(); - return hatch.getCurrentParallel(); - } + if (machine instanceof IMultiController controller && controller.isFormed() && controller instanceof IRecipeCapabilityMachine recipeCapabilityMachine) { + final var parallelHatch = recipeCapabilityMachine.getParallelHatch(); + if (parallelHatch != null) return parallelHatch.getCurrentParallel(); } return 1; } @@ -183,4 +184,71 @@ public static GTRecipe standardOverclocking(WorkableElectricMultiblockMachine ma List.of(new Content((long) resultVoltage, ChanceLogic.getMaxChancedValue(), ChanceLogic.getMaxChancedValue(), 0, null, null))); return recipe1; } + + private static FluidAmounts countFluidAmounts(WorkableElectricMultiblockMachine workmachine, GTRecipe recipe, Fluid fluid1, Fluid fluid2) { + long a = 0, b = 0; + + if (workmachine instanceof IRecipeCapabilityMachine rcm) { + var handlePart = rcm.getActiveRecipeHandle(recipe); + if (handlePart != null) { + if (handlePart instanceof RecipeHandlePart rhp) { + FluidAmounts amounts = countFluidInRecipeHandlePart(rhp, fluid1, fluid2); + a += amounts.first(); + b += amounts.second(); + } else if (handlePart instanceof MEPatternRecipeHandlePart meRhp) { + FluidAmounts amounts = countFluidInMERecipeHandlePart(meRhp, recipe, fluid1, fluid2); + a += amounts.first(); + b += amounts.second(); + } + } else { + FluidAmounts amounts = countFluidInParts(workmachine, fluid1, fluid2); + a += amounts.first(); + b += amounts.second(); + } + } + + return new FluidAmounts(a, b); + } + + private static FluidAmounts countFluidInRecipeHandlePart(RecipeHandlePart rhp, Fluid fluid1, Fluid fluid2) { + long a = 0, b = 0; + for (var p : rhp.getCapability(FluidRecipeCapability.CAP)) { + for (var contents : p.getContents()) { + if (contents instanceof FluidStack fluidStack) { + if (fluidStack.getFluid() == fluid1) a += fluidStack.getAmount(); + if (fluidStack.getFluid() == fluid2) b += fluidStack.getAmount(); + } + } + } + return new FluidAmounts(a, b); + } + + private static FluidAmounts countFluidInMERecipeHandlePart(MEPatternRecipeHandlePart meRhp, GTRecipe recipe, Fluid fluid1, Fluid fluid2) { + long a = 0, b = 0; + for (var it = Object2LongMaps.fastIterator(meRhp.getMEContent(FluidRecipeCapability.CAP, recipe)); it.hasNext();) { + var entry = it.next(); + if (fluid1 == entry.getKey().getFluid()) a += entry.getLongValue(); + if (fluid2 == entry.getKey().getFluid()) b += entry.getLongValue(); + } + return new FluidAmounts(a, b); + } + + private static FluidAmounts countFluidInParts(WorkableElectricMultiblockMachine workMachine, Fluid fluid1, Fluid fluid2) { + long a = 0, b = 0; + for (var part : workMachine.getParts()) { + for (var handler : part.getRecipeHandlers()) { + if (handler.getHandlerIO() == IO.IN) { + for (var contents : handler.getContents()) { + if (contents instanceof FluidStack fluidStack) { + if (fluidStack.getFluid() == fluid1) a += fluidStack.getAmount(); + if (fluidStack.getFluid() == fluid2) b += fluidStack.getAmount(); + } + } + } + } + } + return new FluidAmounts(a, b); + } + + private record FluidAmounts(long first, long second) {} } diff --git a/src/main/java/org/gtlcore/gtlcore/common/data/GTLRecipeTypes.java b/src/main/java/org/gtlcore/gtlcore/common/data/GTLRecipeTypes.java index 455edd4f1..15408e9e9 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/data/GTLRecipeTypes.java +++ b/src/main/java/org/gtlcore/gtlcore/common/data/GTLRecipeTypes.java @@ -247,7 +247,7 @@ public static String getSCTier(int tier) { public static final GTRecipeType WORLD_DATA_SCANNER_RECIPES = register("world_data_scanner", ELECTRIC) .setEUIO(IO.IN) - .setMaxIOSize(2, 1, 2, 0) + .setMaxIOSize(3, 1, 2, 0) .setProgressBar(GuiTextures.PROGRESS_BAR_ARROW, LEFT_TO_RIGHT) .setSound(GTSoundEntries.SCIENCE); @@ -688,6 +688,12 @@ private static String getGrindball(int tier) { .setProgressBar(GuiTextures.PROGRESS_BAR_ARROW, LEFT_TO_RIGHT) .setSound(GTSoundEntries.MIXER); + public static final GTRecipeType MOLECULAR_ASSEMBLER = register("molecular_assembler", MULTIBLOCK) + .setMaxIOSize(3, 12, 1, 1) + .setMaxTooltips(1) + .setProgressBar(GuiTextures.PROGRESS_BAR_MACERATE, LEFT_TO_RIGHT) + .setSound(GTSoundEntries.SCIENCE); + public static void init() { RecipeModify.init(); } diff --git a/src/main/java/org/gtlcore/gtlcore/common/data/machines/AdditionalMultiBlockMachine.java b/src/main/java/org/gtlcore/gtlcore/common/data/machines/AdditionalMultiBlockMachine.java index 43e70c104..2f63d9d7d 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/data/machines/AdditionalMultiBlockMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/data/machines/AdditionalMultiBlockMachine.java @@ -1,6 +1,8 @@ package org.gtlcore.gtlcore.common.data.machines; import org.gtlcore.gtlcore.GTLCore; +import org.gtlcore.gtlcore.api.machine.multiblock.GTLPartAbility; +import org.gtlcore.gtlcore.api.machine.multiblock.MolecularAssemblerMultiblockMachine; import org.gtlcore.gtlcore.common.data.*; import org.gtlcore.gtlcore.utils.Registries; @@ -18,6 +20,7 @@ import com.gregtechceu.gtceu.api.recipe.GTRecipe; import com.gregtechceu.gtceu.api.recipe.OverclockingLogic; import com.gregtechceu.gtceu.api.recipe.RecipeHelper; +import com.gregtechceu.gtceu.common.data.GCyMBlocks; import com.gregtechceu.gtceu.common.data.GTBlocks; import com.gregtechceu.gtceu.common.data.GTMaterials; import com.gregtechceu.gtceu.common.data.GTRecipeModifiers; @@ -28,6 +31,8 @@ import net.minecraft.network.chat.Style; import net.minecraft.world.level.block.Blocks; +import static com.gregtechceu.gtceu.api.pattern.Predicates.abilities; +import static com.gregtechceu.gtceu.api.pattern.Predicates.blocks; import static com.gregtechceu.gtceu.common.data.GTRecipeModifiers.ELECTRIC_OVERCLOCK; import static com.gregtechceu.gtceu.common.registry.GTRegistration.REGISTRATE; @@ -199,7 +204,7 @@ public static void init() {} .aisle("BEEEEEEEEEEEE E EEEEEEEEEEEEB", "AAAAAAAAAAAAECCCEAAAAAAAAAAAA", " B L L B ECCCE B L L B ", " AHA L L AHAECCCEAHA L L AHA ", " B L L B ECCCE B L L B ", " A HHH A ECCCE A HHH A ", " HHHHH ECCCE HHHHH ", " HHMMMHH ECCCE HHMMMHH ", " HH HH ECCCE HH HH ", " H H CCC H H ", " H H J H H ", " H H H H ", " H H H H ", " H H H H ", " K K K K ", " H H H H ", " H H H H ", " H H H H ", " H H H H ", " HH HH HH HH ", " HH HH HH HH ", " HBBBH HBBBH ", " ", " O O ", " ", " ") .aisle("BEEEEEEEEEEEE E EEEEEEEEEEEEB", "AAAAAAAAAAAAEEEEEAAAAAAAAAAAA", " B B EEEEE B B ", " AHA AHAEEEEEAHA AHA ", " B B EEEEE B B ", " AA AA EEEEE AA AA ", " HHH EEEEE HHH ", " HHHHH EEEEE HHHHH ", " H H EEEEE H H ", " HH HH HH HH ", " HH HH J HH HH ", " HH HH HH HH ", " HH HH HH HH ", " HH HH HH HH ", " KK KK KK KK ", " HH HH HH HH ", " HH HH HH HH ", " HH HH HH HH ", " HH HH HH HH ", " HHH HHH HHH HHH ", " HHHHHHH HHHHHHH ", " HHHHH HHHHH ", " ", " O O ", " ", " ") .aisle("AEEEEEEEEEEE E EEEEEEEEEEEA", "AAAAAAAAAAAA AAAAAAAAAAAA", "AAAA AAA AAA AAAA", " AAAAAAAAAAA AAAAAAAAAAA ", " AAABBBBBAAA AAABBBBBAAA ", " AAA AAA AAA AAA ", " ", " HHH HHH ", " HHHHH HHHHH ", " HH HH HH HH ", " HH HH J HH HH ", " HH HH J HH HH ", " HH HH J HH HH ", " HH HH HH HH ", " KK KK KK KK ", " HH HH HH HH ", " HH HH HH HH ", " HH HH HH HH ", " HH HH HH HH ", " HHHHHHH HHHHHHH ", " HHHHH HHHHH ", " ", " ", " O O ", " ", " ") - .aisle(" EEEEEEEEEEEEEEEEEEEEEEEEEEE ", " AAAAAAAAAAA LLL AAAAAAAAAAA ", " GABBBBBAG LLL GABBBBBAG ", " GAHHHHHAG LLL GAHHHHHAG ", " GA AG LLL GA AG ", " GAAAAAAAG LLL GAAAAAAAG ", " G G LLL G G ", " I I LLL I I ", " HHH LLL HHH ", " HHHHH LLL HHHHH ", " HHHHH LLL HHHHH ", " HHHHH LLL HHHHH ", " HHHHH LJL HHHHH ", " HHHHH LLL HHHHH ", " KKKKK LLL KKKKK ", " HHHHH LLL HHHHH ", " HHHHH LLL HHHHH ", " HHHHH LLL HHHHH ", " HHHHH LLL HHHHH ", " HHHHH HHHHH ", " ", " ", " ", " O O ", " ", " ") + .aisle(" EEEEEEEEEEEEEEEEEEEEEEEEEEE ", " AAAAAAAAAAA LLL AAAAAAAAAAA ", " GABBBBBAG LLL GABBBBBAG ", " GAHHHHHAG LLL GAHHHHHAG ", " GA AG LLL GA AG ", " GAAAAAAAG LLL GAAAAAAAG ", " G G LLL G G ", " I I LLL I I ", " HHH LLL HHH ", " HHHHH LLL HHHHH ", " HHHHH LLL HHHHH ", " HHHHH LLL HHHHH ", " HHHHH LLL HHHHH ", " HHHHH LLL HHHHH ", " KKKKK LLL KKKKK ", " HHHHH LLL HHHHH ", " HHHHH LLL HHHHH ", " HHHHH LLL HHHHH ", " HHHHH LLL HHHHH ", " HHHHH HHHHH ", " ", " ", " ", " O O ", " ", " ") .aisle(" EEEEEEEEEE E EEEEEEEEEE ", " AAAAAAAAA LLBLL AAAAAAAAAE ", " A A LLFLL A A E ", " AAAAAAA LLFLL AAAAAAA ", " A A LLFLL A A ", " LLFLL ", " LLFLL ", " LLFLL ", " LLFLL ", " LLFLL ", " LLFLL ", " LLFLL ", " LLFLL ", " LLFLL ", " LLFLL ", " LLFLL ", " LLFLL ", " LLFLL ", " LLFLL ", " LLL ", " ", " ", " ", " O O ", " ", " ") .aisle(" EEEEEEEE E E E EEEEEEEE ", " EEEEEEEE LLBBBLL EEEEEEEE ", " EEEEEEEE LL LL EEEEEEEE ", " EEEEEEE LL LL EEEEEEE ", " EEEEEEE LL LL EEEEEEE ", " EEEEEE LL LL EEEEEE ", " EEEEEE LL LL EEEEEE ", " EEEEE LL LL EEEEE ", " EEEEE LL LL EEEEE ", " LL LL ", " LL LL ", " LL LL ", " LL LL ", " LL LL ", " LL LL ", " LL LL ", " LL LL ", " LL LL ", " LL F LL ", " LLLLL ", " KKK ", " ", " ", " O O ", " ", " ") .aisle("CE E E E E E EC", "CCCCCCCCE LLBBBBBLL ECCCCCCCC", "CCCCCCCCE LL N LL ECCCCCCCC", "CCCCCCCCE LL N LL ECCCCCCCC", "CCCCCCCCE LL N LL ECCCCCCCC", "CCCCCCCCE LL N LL ECCCCCCCC", "CCCCCCCCE LL I LL ECCCCCCCC", " CCCCE LL I LL ECCCC ", " CCCCE LL I LL ECCCC ", " CCCC LL LL CCCC ", " LL LL ", " LL LL ", " LL LL ", " LL LL ", " LL LL ", " LL LL ", " LL LL ", " LL LL ", " LL F LL ", " LLKKKLL ", " KPPPK ", " P P ", " P P ", " O P P O ", " KKK ", " ") @@ -225,7 +230,7 @@ public static void init() {} .where("C", Predicates.blocks(GTBlocks.CASING_PTFE_INERT.get()) .or(Predicates.autoAbilities(definition.getRecipeTypes())) .or(Predicates.autoAbilities(true, false, true)) - .or(Predicates.abilities(PartAbility.INPUT_LASER).setExactLimit(1))) + .or(Predicates.abilities(PartAbility.INPUT_LASER).setMaxGlobalLimited(1))) .where("D", Predicates.blocks(GTBlocks.HIGH_POWER_CASING.get())) .where("E", Predicates.blocks(GTBlocks.CASING_HSSE_STURDY.get())) .where("F", Predicates.blocks(GTBlocks.CASING_POLYTETRAFLUOROETHYLENE_PIPE.get())) @@ -258,10 +263,9 @@ public static void init() {} .tooltipBuilder(GTLMachines.GTL_ADD) .recipeTypes(GTLRecipeTypes.NEUTRON_ACTIVATOR_RECIPES) .recipeModifiers(((machine, recipe, params, result) -> { - long eu = recipe.data.getInt("evt") * 2000; + long eu = recipe.data.getInt("evt") * 2000L; RecipeHelper.setInputEUt(recipe, eu); - GTRecipe recipe1 = GTRecipeModifiers.hatchParallel(machine, recipe, false, params, result); - return recipe1; + return GTRecipeModifiers.hatchParallel(machine, recipe, false, params, result); })) .appearanceBlock(GTLBlocks.SPS_CASING) .pattern(definition -> FactoryBlockPattern.start() @@ -299,4 +303,79 @@ public static void init() {} .build()) .workableCasingRenderer(GTLCore.id("block/casings/sps_casing"), GTCEu.id("block/multiblock/fusion_reactor")) .register(); + + public final static MultiblockMachineDefinition MOLECULAR_ASSEMBLER_MATRIX = REGISTRATE.multiblock("molecular_assembler_matrix", MolecularAssemblerMultiblockMachine::new) + .rotationState(RotationState.ALL) + .recipeType(GTLRecipeTypes.MOLECULAR_ASSEMBLER) + .tooltips(Component.translatable("gtceu.machine.molecular_assembler_matrix.tooltip.0"), + Component.translatable("gtceu.machine.molecular_assembler_matrix.tooltip.1"), + Component.translatable("gtceu.machine.molecular_assembler_matrix.tooltip.2"), + Component.translatable("gtceu.machine.molecular_assembler_matrix.tooltip.3"), + Component.translatable("gtceu.machine.molecular_assembler_matrix.tooltip.4"), + Component.translatable("gtceu.machine.molecular_assembler_matrix.tooltip.5"), + Component.translatable("gtceu.machine.molecular_assembler_matrix.tooltip.6")) + .tooltipBuilder(GTLMachines.GTL_ADD) + .recipeModifiers((machine, recipe, params, result) -> GTRecipeModifiers.accurateParallel(machine, recipe, (int) Math.pow(4, (((WorkableElectricMultiblockMachine) machine).getTier() - 4)), false).getFirst(), GTRecipeModifiers.ELECTRIC_OVERCLOCK.apply(OverclockingLogic.NON_PERFECT_OVERCLOCK_SUBTICK)) + .appearanceBlock(GCyMBlocks.CASING_LARGE_SCALE_ASSEMBLING) + .pattern(definition -> FactoryBlockPattern.start() + .aisle(" ", " ", " ", " ", " ", " ", " ", " A ", " B ", " B ", " B ", " A ", " ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " A ", " A ", " ACA ", " DED ", " DDDDEDDDD ", " DED ", " ACA ", " A ", " A ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " ", " A ", " AAAAAAAAA ", " DDDFFFFFFFFFDDD ", " AAAAAAAAA ", " A ", " ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " ", " ", " AAAA AAAA ", " DDFFFGHHHHHHHGFFFDD ", " AAAA AAAA ", " ", " ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " ", " ", " AAA AAA ", " DFFGHHH HHHGFFD ", " AAA AAA ", " ", " ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " ", " I I ", " BAAA AAAB ", " BDFGHH HHGFDB ", " BAAA AAAB ", " I I ", " ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " G G ", " J J ", " J J ", " ICI ICI ", " BEAA AAEB ", " BEFGH HGFEB ", " BEAA AAEB ", " ICI ICI ", " J J ", " J J ", " G G ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " ", " I KLL LLK I ", " AAA AAA ", " DFGH HGFD ", " AAA AAA ", " I KLL LLK I ", " ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " ", " K K ", " AA LL LL AA ", " DFGH K K HGFD ", " AA LL LL AA ", " K K ", " ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " ", " ", " AA D D AA ", " DFGH KLLK KLLK HGFD ", " AA D D AA ", " ", " ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " D D ", " D D ", " A DFD DFD A ", " DFH KFG GFK HFD ", " A DFD DFD A ", " D D ", " D D ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " ", " ", " AA D D AA ", " DFGH LG GL HGFD ", " AA D D AA ", " ", " ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " ", " ", " A A ", " DFH KL LK HFD ", " A A ", " ", " ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " ", " ", " A L L A ", " DFH K K HFD ", " A L L A ", " ", " ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " D D ", " D D ", " K M N M K ", " AA L ENE L AA ", " DFGH NNNNN HGFD ", " AA L ENE L AA ", " K M N M K ", " D D ", " D D ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " D D ", " DDDNNNDDD ", " LK DGDDDGD KL ", " A MDCCCDM A ", " DFH MDCCCDM HFD ", " A MDCCCDM A ", " LK DGDDDGD KL ", " DDDNNNDDD ", " D D ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " O ", " DGDDDGD ", " L GDPPPDG L ", " A DPQQQPD A ", " DFH NDPQQQPDN HFD ", " A DPQQQPD A ", " L GDPPPDG L ", " DGDDDGD ", " O ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " O ", " O ", " EOE ", " NDCCCDN ", " A DPQQQPD A ", " DA ECQQQQQCE AD ", " DFH NCQQQQQCN HFD ", " DA ECQQQQQCE AD ", " A DPQQQPD A ", " NDCCCDN ", " EOE ", " O ", " O ", " ", " ", " ") + .aisle(" G ", " D ", " D ", " ODO ", " ODO ", " A OODOO A ", " A NDCCCDN A ", "ACA NDPQQQPDN ACA", "BEA NCQQQQQCN AEB", "BEFH NCQQQQQCN HFEB", "BEA NCQQQQQCN AEB", "ACA NDPQQQPDN ACA", " A NDCCCDN A ", " A OODOO A ", " ODO ", " ODO ", " D ", " D ", " G ") + .aisle(" ", " ", " ", " O ", " O ", " EOE ", " NDCCCDN ", " A DPQQQPD A ", " DA ECQQQQQCE AD ", " DFH NCQQQQQCN HFD ", " DA ECQQQQQCE AD ", " A DPQQQPD A ", " NDCCCDN ", " EOE ", " O ", " O ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " O ", " DGDDDGD ", " L GDPPPDG L ", " A DPQQQPD A ", " DFH NDPQQQPDN HFD ", " A DPQQQPD A ", " L GDPPPDG L ", " DGDDDGD ", " O ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " D D ", " DDDNNNDDD ", " LK DGDDDGD KL ", " A MDCCCDM A ", " DFH MDCCCDM HFD ", " A MDCCCDM A ", " LK DGDDDGD KL ", " DDDNNNDDD ", " D D ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " D D ", " D D ", " K M N M K ", " AA L ENE L AA ", " DFGH NNRNN HGFD ", " AA L ENE L AA ", " K M N M K ", " D D ", " D D ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " ", " ", " A L L A ", " DFH K K HFD ", " A L L A ", " ", " ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " ", " ", " A A ", " DFH KL LK HFD ", " A A ", " ", " ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " ", " ", " AA D D AA ", " DFGH LG GL HGFD ", " AA D D AA ", " ", " ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " D D ", " D D ", " A DFD DFD A ", " DFH KFG GFK HFD ", " A DFD DFD A ", " D D ", " D D ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " ", " ", " AA D D AA ", " DFGH KLLK KLLK HGFD ", " AA D D AA ", " ", " ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " ", " K K ", " AA LL LL AA ", " DFGH K K HGFD ", " AA LL LL AA ", " K K ", " ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " ", " I KLL LLK I ", " AAA AAA ", " DFGH HGFD ", " AAA AAA ", " I KLL LLK I ", " ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " G G ", " J J ", " J J ", " ICI ICI ", " BEAA AAEB ", " BEFGH HGFEB ", " BEAA AAEB ", " ICI ICI ", " J J ", " J J ", " G G ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " ", " I I ", " BAAA AAAB ", " BDFGHH HHGFDB ", " BAAA AAAB ", " I I ", " ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " ", " ", " AAA AAA ", " DFFGHHH HHHGFFD ", " AAA AAA ", " ", " ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " ", " ", " AAAA AAAA ", " DDFFFGHHHHHHHGFFFDD ", " AAAA AAAA ", " ", " ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " ", " A ", " AAAAAAAAA ", " DDDFFFFFFFFFDDD ", " AAAAAAAAA ", " A ", " ", " ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " A ", " A ", " ACA ", " DED ", " DDDDEDDDD ", " DED ", " ACA ", " A ", " A ", " ", " ", " ", " ", " ") + .aisle(" ", " ", " ", " ", " ", " ", " ", " A ", " B ", " B ", " B ", " A ", " ", " ", " ", " ", " ", " ", " ") + .where("R", Predicates.controller(Predicates.blocks(definition.get()))) + .where("H", Predicates.blocks(GTLBlocks.MOLECULAR_CASING.get())) + .where("Q", abilities(GTLPartAbility.MOLECULAR_ASSEMBLER_MATRIX) + .or(Predicates.blocks(GTLBlocks.MOLECULAR_CASING.get()))) + .where("D", Predicates.blocks(Registries.getBlock("gtceu:high_power_casing"))) + .where("N", Predicates.blocks(Registries.getBlock("gtceu:large_scale_assembler_casing")) + .or(blocks(GTLMachines.GTAEMachines.ME_MOLECULAR_ASSEMBLER_IO.get()).setExactLimit(1))) + .where("K", Predicates.blocks(Registries.getBlock("gtceu:naquadah_alloy_frame"))) + .where("M", Predicates.blocks(Registries.getBlock("gtceu:hsse_frame"))) + .where("O", Predicates.blocks(Registries.getBlock("gtceu:large_scale_assembler_casing"))) + .where("I", Predicates.blocks(Registries.getBlock("gtceu:assembly_line_grating"))) + .where("P", Predicates.blocks(Registries.getBlock("gtceu:europium_frame"))) + .where("L", Predicates.blocks(Registries.getBlock("gtlcore:hyper_mechanical_casing"))) + .where("B", Predicates.blocks(Registries.getBlock("gtceu:fusion_glass"))) + .where("C", Predicates.blocks(Registries.getBlock("gtlcore:advanced_assembly_line_unit"))) + .where("J", Predicates.blocks(Registries.getBlock("gtceu:assembly_line_casing"))) + .where("A", Predicates.blocks(Registries.getBlock("gtlcore:iridium_casing"))) + .where("F", Predicates.blocks(Registries.getBlock("gtceu:superconducting_coil"))) + .where("E", Predicates.blocks(Registries.getBlock("gtceu:advanced_computer_casing"))) + .where("G", Predicates.blocks(Registries.getBlock("gtceu:trinium_frame"))) + .build()) + .workableCasingRenderer(GTCEu.id("block/casings/gcym/large_scale_assembling_casing"), GTCEu.id("block/multiblock/research_station")) + .register(); } diff --git a/src/main/java/org/gtlcore/gtlcore/common/data/machines/AdvancedMultiBlockMachine.java b/src/main/java/org/gtlcore/gtlcore/common/data/machines/AdvancedMultiBlockMachine.java index f8366c243..8eb1189bf 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/data/machines/AdvancedMultiBlockMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/data/machines/AdvancedMultiBlockMachine.java @@ -3,8 +3,10 @@ import org.gtlcore.gtlcore.GTLCore; import org.gtlcore.gtlcore.api.machine.multiblock.GTLPartAbility; import org.gtlcore.gtlcore.api.pattern.GTLPredicates; +import org.gtlcore.gtlcore.api.recipe.RecipeResult; import org.gtlcore.gtlcore.client.renderer.machine.EyeOfHarmonyRenderer; import org.gtlcore.gtlcore.client.renderer.machine.SpaceElevatorRenderer; +import org.gtlcore.gtlcore.common.block.BlockMap; import org.gtlcore.gtlcore.common.block.GTLFusionCasingBlock; import org.gtlcore.gtlcore.common.data.*; import org.gtlcore.gtlcore.common.machine.multiblock.SimulationMachine; @@ -56,6 +58,7 @@ import net.minecraft.world.phys.AABB; import com.hepdd.gtmthings.data.CustomMachines; +import org.jetbrains.annotations.Nullable; import java.util.*; @@ -128,6 +131,8 @@ public static void init() {} .tooltips(Component.translatable("gtceu.machine.eye_of_harmony.tooltip.4")) .tooltips(Component.translatable("gtceu.machine.eye_of_harmony.tooltip.5")) .tooltips(Component.translatable("gtceu.machine.eye_of_harmony.tooltip.6")) + .tooltips(Component.translatable("gtceu.machine.eye_of_harmony.tooltip.7")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.cosmos_simulation"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -192,6 +197,7 @@ public static void init() {} .tooltips(Component.translatable("gtceu.machine.perfect_oc")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.space_probe_surface_reception"))) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GCY")) .tooltipBuilder(GTLMachines.GTL_ADD) .recipeModifier(GTRecipeModifiers.ELECTRIC_OVERCLOCK.apply(OverclockingLogic.PERFECT_OVERCLOCK_SUBTICK)) .appearanceBlock(GCyMBlocks.CASING_ATOMIC) @@ -225,7 +231,31 @@ public static void init() {} .workableCasingRenderer(GTCEu.id("block/casings/gcym/atomic_casing"), GTCEu.id("block/multiblock/data_bank")) .register(); - public final static MultiblockMachineDefinition SPACE_COSMIC_PROBE_RECEIVERS = REGISTRATE.multiblock("space_cosmic_probe_receivers", WorkableElectricMultiblockMachine::new) + public final static MultiblockMachineDefinition SPACE_COSMIC_PROBE_RECEIVERS = REGISTRATE.multiblock("space_cosmic_probe_receivers", holder -> new SpaceProbeSurfaceReceptionMachine(holder) { + + @Override + @Nullable + protected BlockPos findTopBlock() { + Level level = getLevel(); + if (level == null) return null; + + BlockPos pos = getPos(); + BlockPos[] coordinates = new BlockPos[] { + pos.offset(9, 20, 0), + pos.offset(-9, 20, 0), + pos.offset(0, 20, 9), + pos.offset(0, 20, -9) + }; + + for (BlockPos checkPos : coordinates) { + if (level.getBlockState(checkPos) + .is(ChemicalHelper.getBlock(TagPrefix.frameGt, GTLMaterials.Vibranium))) { + return checkPos.offset(0, 1, 0); + } + } + return null; + } + }) .rotationState(RotationState.NON_Y_AXIS) .allowExtendedFacing(false) .recipeType(GTLRecipeTypes.SPACE_COSMIC_PROBE_RECEIVERS_RECIPES) @@ -257,9 +287,9 @@ public static void init() {} .aisle(" B B ", " B B ", " EEJJJJJJJJJJJEE ", " E E ", " E E ", " EE EE ", " GGGEEEEEGGG ", " E ", " FFFDDDDDDDDDDDFFF ", " FFFDDDDDDDDDDDFFF ", " CCCCCCCCCCCCCCCCC ", " AAA AAA ", " AA AA ", " A A ", " A A ", "AAA AAA", " ", " ", " ", " ", " ", " CCC ", " CCKKKCC ", " ", " ", " ", " ", " ", " ", " ", " ") .aisle(" ", " ", " EHJJJJJJJJJJJHE ", " E E ", " B E E ", " EE EE ", " EGG GGE ", " GGEEEGG ", " FFFIDDFFFFFFFDDIFFF ", " FFFIDDFFFFFFFDDIFFF ", " CCCCCCCCCCCCCCCCCCC ", " AA AA ", " AA AA ", " A A ", " A A ", "AAA AAA", " ", " ", " ", " ", " ", " CCDCC ", " CCKKKKKCC ", " ", " ", " ", " ", " ", " ", " ", " ") .aisle(" ", " ", " EHJJJJJJJJJJJHE ", " EE EE ", " BD E E DB ", " D EEE EEE D ", " EE EE ", " GEEEEEG ", " FFFIDDFFFFFFFDDIFFF ", " FFFIDDFFFFFFFDDIFFF ", " CCCCCCCCCCCCCCCCCCC ", " AA AA ", " AA AA ", " A A ", " A A ", "AAA AAA", " ", " ", " ", " ", " C ", " CCDDDCC ", " CCKKKKKKKCC ", " ", " ", " ", " ", " ", " ", " ", " ") - .aisle(" B ", " ", " EHJJJJJJJJJJJHE ", " EE EE ", " B E E B ", " D EE EE D ", " D EEE EEE D ", " EEEEEEE D ", " FFFIDDFFFFFFFDDIFFF ", " FFFIDDFFFFFFFDDIFFF ", " CCCCCCCCCCCCCCCCCCC ", " AA AA ", " AA AA ", " A A ", " A A ", "AAA AAA", " ", " ", " ", " ", " CCC ", " CCDDDDDCC ", " CKKKKKKKKKC ", " L ", " L ", " ", " ", " ", " ", " ", " ") + .aisle(" B B ", " ", " EHJJJJJJJJJJJHE ", " EE EE ", " B E E B ", " D EE EE D ", " D EEE EEE D ", " EEEEEEE D ", " FFFIDDFFFFFFFDDIFFF ", " FFFIDDFFFFFFFDDIFFF ", " CCCCCCCCCCCCCCCCCCC ", " AA AA ", " AA AA ", " A A ", " A A ", "AAA AAA", " ", " ", " ", " ", " CCC ", " CCDDDDDCC ", " CKKKKKKKKKC ", " L ", " L ", " ", " ", " ", " ", " ", " ") .aisle(" BB BB ", " BB BB ", " BB EEHJJJJJJJJJJJHEE BB ", " BB EE M EE BB ", " B E M E B ", " BBBEEE M EEEBBB ", " D EEEE M EEEE D ", " D EEEEMEEEE D ", " DFFFIDDFFFMFFFDDIFFFD ", " FFFIDDFFFMFFFDDIFFF ", " CCCCCCCCCCCCCCCCCCC ", " AACC CCAA ", " AACCC CCCAA ", " ACC CCA ", " AC CA ", "AAAC C C CAAA", " C C ", " C C ", " C C ", " C C ", " C CCCCC C ", " C CDDDDDDDC C ", " CCKKKKKKKKKCC ", " LLL ", " LLL ", " L ", " L ", " L ", " L ", " L ", " ") - .aisle(" B ", " ", " EHJJJJJJJJJJJHE ", " EE EE ", " B E E B ", " D EE EE D ", " D EEE EEE D ", " EEEEEEE D ", " FFFIDDFFFFFFFDDIFFF ", " FFFIDDFFFFFFFDDIFFF ", " CCCCCCCCCCCCCCCCCCC ", " AA AA ", " AA AA ", " A A ", " A A ", "AAA AAA", " ", " ", " ", " ", " CCC ", " CCDDDDDCC ", " CKKKKKKKKKC ", " L ", " L ", " ", " ", " ", " ", " ", " ") + .aisle(" B B ", " ", " EHJJJJJJJJJJJHE ", " EE EE ", " B E E B ", " D EE EE D ", " D EEE EEE D ", " EEEEEEE D ", " FFFIDDFFFFFFFDDIFFF ", " FFFIDDFFFFFFFDDIFFF ", " CCCCCCCCCCCCCCCCCCC ", " AA AA ", " AA AA ", " A A ", " A A ", "AAA AAA", " ", " ", " ", " ", " CCC ", " CCDDDDDCC ", " CKKKKKKKKKC ", " L ", " L ", " ", " ", " ", " ", " ", " ") .aisle(" ", " ", " EHJJJJJJJJJJJHE ", " EE EE ", " BD E E DB ", " D EEE EEE D ", " EE EE ", " GEEEEEG ", " FFFIDDFFFFFFFDDIFFF ", " FFFIDDFFFFFFFDDIFFF ", " CCCCCCCCCCCCCCCCCCC ", " AA AA ", " AA AA ", " A A ", " A A ", "AAA AAA", " ", " ", " ", " ", " C ", " CCDDDDC ", " CCKKKKKKKCC ", " ", " ", " ", " ", " ", " ", " ", " ") .aisle(" ", " ", " EHJJJJJJJJJJJHE ", " E E ", " B E E ", " EE EE ", " EGG GGE ", " GGEEEGG ", " FFFIDDFFFFFFFDDIFFF ", " FFFIDDFFFFFFFDDIFFF ", " CCCCCCCCCCCCCCCCCCC ", " AA AA ", " AA AA ", " A A ", " A A ", "AAA AAA", " ", " ", " ", " ", " ", " CCDCC ", " CCKKKKKCC ", " ", " ", " ", " ", " ", " ", " ", " ") .aisle(" B B ", " B B ", " EEJJJJJJJJJJJEE ", " E E ", " E E ", " EE EE ", " GGGEEEEEGGG ", " E ", " FFFDDDDDDDDDDDFFF ", " FFFDDDDDDDDDDDFFF ", " CCCCCCCCCCCCCCCCC ", " AAA AAA ", " AA AA ", " A A ", " A A ", "AAA AAA", " ", " ", " ", " ", " ", " CCC ", " CCKKKCC ", " ", " ", " ", " ", " ", " ", " ", " ") @@ -310,6 +340,7 @@ public static void init() {} .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_2.tooltip", Component.translatable("gtceu.dimensionally_transcendent_plasma_forge"), Component.translatable("gtceu.stellar_forge"))) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltipBuilder(GTLMachines.GTL_ADD) .recipeModifiers(GTRecipeModifiers.PARALLEL_HATCH, GTRecipeModifiers.ELECTRIC_OVERCLOCK.apply(OverclockingLogic.PERFECT_OVERCLOCK_SUBTICK)) .appearanceBlock(GTLBlocks.DIMENSIONALLY_TRANSCENDENT_CASING) @@ -348,7 +379,7 @@ public static void init() {} int temp = machine.getCoilType().getCoilTemperature(); components.add(Component.translatable("gtceu.multiblock.blast_furnace.max_temperature", Component.literal(FormattingUtil.formatNumbers((temp == 273 ? 32000 : temp)) + "K").withStyle(ChatFormatting.BLUE))); if (machine.getRecipeType() == GTLRecipeTypes.STELLAR_FORGE_RECIPES && temp != 273) { - components.add(Component.literal("当前配方模式无法使用该线圈").withStyle(ChatFormatting.RED)); + components.add(Component.translatable("message.gtlcore.coil_incompatible_recipe_mode").withStyle(ChatFormatting.RED)); } } }) @@ -399,8 +430,8 @@ public static void init() {} .where("c", Predicates.blocks(GTBlocks.CASING_LAMINATED_GLASS.get())) .where("d", Predicates.blocks(GTMachines.ITEM_IMPORT_BUS[0].get()).or(Predicates.blocks(CustomMachines.HUGE_ITEM_IMPORT_BUS[0].get()))) .where("e", Predicates.blocks(Registries.getBlock("kubejs:machine_casing_circuit_assembly_line"))) - .where("f", Predicates.abilities(PartAbility.EXPORT_ITEMS)) - .where("g", Predicates.abilities(PartAbility.IMPORT_FLUIDS_4X)) + .where("f", GTLPredicates.diffAbilities(List.of(PartAbility.EXPORT_ITEMS), List.of(PartAbility.IMPORT_ITEMS, PartAbility.IMPORT_FLUIDS))) + .where("g", Predicates.abilities(PartAbility.IMPORT_FLUIDS_4X).or(Predicates.blocks(GTLMachines.HUGE_FLUID_IMPORT_HATCH[4].get()))) .build()) .workableCasingRenderer(GTLCore.id("block/casings/pikyonium_machine_casing"), GTCEu.id("block/multiblock/assembly_line")) .register(); @@ -452,32 +483,16 @@ public static void init() {} .workableCasingRenderer(GTLCore.id("block/space_elevator_mechanical_casing"), GTCEu.id("block/multiblock/gcym/large_assembler")) .register(); - private static final List poses1 = new ArrayList<>(); - private static final List poses2 = new ArrayList<>(); - private static final Map covRecipe = new HashMap<>(); + private static final Map COV_RECIPE = new HashMap<>(); static { - for (int i = -2; i <= 2; i++) { - for (int j = -1; j >= -5; j--) { - for (int k = -2; k <= 2; k++) { - poses1.add(new int[] { i, j, k }); - } - } - } - for (int i = -4; i <= 4; i++) { - for (int j = -1; j >= -7; j--) { - for (int k = -4; k <= 4; k++) { - poses2.add(new int[] { i, j, k }); - } - } - } - covRecipe.put("minecraft:bone_block", "kubejs:essence_block"); - covRecipe.put("minecraft:oak_log", "minecraft:crimson_stem"); - covRecipe.put("minecraft:birch_log", "minecraft:warped_stem"); - covRecipe.put("gtceu:calcium_block", "minecraft:bone_block"); - covRecipe.put("minecraft:moss_block", "minecraft:sculk"); - covRecipe.put("minecraft:grass_block", "minecraft:moss_block"); - covRecipe.put("kubejs:infused_obsidian", "kubejs:draconium_block_charged"); + COV_RECIPE.put("minecraft:bone_block", "kubejs:essence_block"); + COV_RECIPE.put("minecraft:oak_log", "minecraft:crimson_stem"); + COV_RECIPE.put("minecraft:birch_log", "minecraft:warped_stem"); + COV_RECIPE.put("gtceu:calcium_block", "minecraft:bone_block"); + COV_RECIPE.put("minecraft:moss_block", "minecraft:sculk"); + COV_RECIPE.put("minecraft:grass_block", "minecraft:moss_block"); + COV_RECIPE.put("kubejs:infused_obsidian", "kubejs:draconium_block_charged"); } private static boolean blockConversionRoom(List poses, IRecipeLogicMachine machine, int tier) { @@ -493,8 +508,8 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin pos = pos_0; BlockPos blockPos = machine.self().getPos().offset(pos[0], pos[1], pos[2]); String block = level.getBlockState(blockPos).getBlock().kjs$getId(); - if (covRecipe.containsKey(block)) { - level.setBlockAndUpdate(blockPos, Registries.getBlock(covRecipe.get(block)).defaultBlockState()); + if (COV_RECIPE.containsKey(block)) { + level.setBlockAndUpdate(blockPos, Registries.getBlock(COV_RECIPE.get(block)).defaultBlockState()); } } else { i--; @@ -586,6 +601,7 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin .tooltips(Component.translatable("gtceu.machine.pcb_factory.tooltip.2")) .tooltips(Component.translatable("gtceu.machine.perfect_oc")) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.pcb_factory"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -636,6 +652,7 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin .tooltips(Component.translatable("gtceu.machine.electric_blast_furnace.tooltip.0")) .tooltips(Component.translatable("gtceu.machine.perfect_oc")) .tooltips(Component.translatable("gtceu.machine.electric_blast_furnace.tooltip.2")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GT++")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.electric_blast_furnace"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -688,6 +705,7 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin .tooltips(Component.translatable("gtceu.machine.duration_multiplier.tooltip", 0.5)) .tooltips(Component.translatable("gtceu.machine.cold_ice_freezer.tooltip.0")) .tooltips(Component.translatable("gtceu.machine.blaze_blast_furnace.tooltip.1")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GT++")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.vacuum_freezer"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -794,7 +812,7 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin if (Objects.equals(player.getArmorSlots().toString(), "[1 magnetohydrodynamicallyconstrainedstarmatter_boots, 1 magnetohydrodynamicallyconstrainedstarmatter_leggings, 1 magnetohydrodynamicallyconstrainedstarmatter_chestplate, 1 magnetohydrodynamicallyconstrainedstarmatter_helmet]")) { player.getServer().kjs$runCommandSilent("execute in kubejs:create as " + entity.getName().getString() + " run tp 0 1 0"); } else { - player.kjs$setStatusMessage(Component.literal("你的装备无法适应目标维度的环境")); + player.kjs$setStatusMessage(Component.translatable("message.gtlcore.equipment_incompatible_dimension")); } } if (entity instanceof ItemEntity item && Objects.equals(item.getItem().kjs$getId(), "gtceu:magnetohydrodynamicallyconstrainedstarmatter_block")) { @@ -813,7 +831,7 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin .workableCasingRenderer(GTLCore.id("block/dimension_connection_casing"), GTCEu.id("block/multiblock/door_of_create")) .register(); - public final static MultiblockMachineDefinition BEDROCK_DRILLING_RIG = REGISTRATE.multiblock("bedrock_drilling_rig", WorkableElectricMultiblockMachine::new) + public final static MultiblockMachineDefinition BEDROCK_DRILLING_RIG = REGISTRATE.multiblock("bedrock_drilling_rig", BedrockDrillingRig::new) .rotationState(RotationState.NONE) .allowExtendedFacing(false) .allowFlip(false) @@ -851,16 +869,6 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin .where("g", Predicates.blocks(Registries.getBlock("kubejs:machine_casing_grinding_head"))) .where(" ", Predicates.any()) .build()) - .beforeWorking((machine, recipe) -> { - Level level = machine.self().getLevel(); - if (level != null) { - if (Math.random() < 0.1) { - level.setBlockAndUpdate(machine.self().getPos().offset(0, -9, 0), Blocks.AIR.defaultBlockState()); - } - return Objects.equals(level.getBlockState(machine.self().getPos().offset(0, -9, 0)).getBlock().kjs$getId(), "minecraft:bedrock"); - } - return false; - }) .workableCasingRenderer(GTLCore.id("block/casings/echo_casing"), GTCEu.id("block/multiblock/cleanroom")) .register(); @@ -933,7 +941,7 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin .workableCasingRenderer(GTLCore.id("block/dimension_connection_casing"), GTCEu.id("block/multiblock/create_aggregation")) .register(); - public final static MultiblockMachineDefinition SUPRACHRONAL_ASSEMBLY_LINE = REGISTRATE.multiblock("suprachronal_assembly_line", (holder) -> new SuprachronalAssemblyLineMachine(holder, false)) + public final static MultiblockMachineDefinition SUPRACHRONAL_ASSEMBLY_LINE = REGISTRATE.multiblock("suprachronal_assembly_line", SuprachronalAssemblyLineMachine::new) .rotationState(RotationState.ALL) .recipeType(GTLRecipeTypes.SUPRACHRONAL_ASSEMBLY_LINE_RECIPES) .recipeType(GTRecipeTypes.ASSEMBLY_LINE_RECIPES) @@ -944,6 +952,7 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) .tooltips(Component.translatable("gtceu.machine.perfect_oc")) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "TST")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_3.tooltip", Component.translatable("gtceu.suprachronal_assembly_line"), Component.translatable("gtceu.assembly_line"), Component.translatable("gtceu.circuit_assembly_line"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -1011,17 +1020,18 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin .workableCasingRenderer(GTLCore.id("block/molecular_casing"), GTCEu.id("block/multiblock/fusion_reactor")) .register(); - public final static MultiblockMachineDefinition SUPRACHRONAL_ASSEMBLY_LINE_MODULE = REGISTRATE.multiblock("suprachronal_assembly_line_module", (holder) -> new SuprachronalAssemblyLineMachine(holder, true)) + public final static MultiblockMachineDefinition SUPRACHRONAL_ASSEMBLY_LINE_MODULE = REGISTRATE.multiblock("suprachronal_assembly_line_module", SuprachronalAssemblyLineModuleMachine::new) .rotationState(RotationState.ALL) .recipeType(GTRecipeTypes.ASSEMBLY_LINE_RECIPES) .recipeType(GTLRecipeTypes.CIRCUIT_ASSEMBLY_LINE_RECIPES) .tooltips(Component.translatable("gtceu.machine.duration_multiplier.tooltip", 0.4)) .tooltips(Component.translatable("gtceu.machine.suprachronal_assembly_line_module.tooltip.0")) .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "TST")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_2.tooltip", Component.translatable("gtceu.assembly_line"), Component.translatable("gtceu.circuit_assembly_line"))) .tooltipBuilder(GTLMachines.GTL_ADD) - .recipeModifiers((machine, recipe, params, result) -> GTLRecipeModifiers.reduction(machine, recipe, 1, 0.4), (machine, recipe, params, result) -> GTRecipeModifiers.accurateParallel(machine, recipe, ((SuprachronalAssemblyLineMachine) machine).getParallel(), false).getFirst(), GTRecipeModifiers.ELECTRIC_OVERCLOCK.apply(OverclockingLogic.NON_PERFECT_OVERCLOCK_SUBTICK)) + .recipeModifiers((machine, recipe, params, result) -> GTLRecipeModifiers.reduction(machine, recipe, 1, 0.4), (machine, recipe, params, result) -> GTRecipeModifiers.accurateParallel(machine, recipe, ((SuprachronalAssemblyLineModuleMachine) machine).getParallel(), false).getFirst(), GTRecipeModifiers.ELECTRIC_OVERCLOCK.apply(OverclockingLogic.NON_PERFECT_OVERCLOCK_SUBTICK)) .appearanceBlock(GTLBlocks.MOLECULAR_CASING) .pattern(definition -> FactoryBlockPattern.start() .aisle(" D ", " E ", " D ") @@ -1030,7 +1040,6 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin .aisle(" C ", " C ", " C ") .aisle(" B ", " B ", " B ") .aisle("AAA", "A~A", "AAA") - .aisle(" ", " - ", " ") .where("~", Predicates.controller(Predicates.blocks(definition.get()))) .where("B", Predicates.blocks(GTLBlocks.DIMENSIONALLY_TRANSCENDENT_CASING.get())) .where("C", Predicates.blocks(Registries.getBlock("kubejs:molecular_coil"))) @@ -1044,7 +1053,6 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin .or(Predicates.abilities(PartAbility.INPUT_ENERGY).setMaxGlobalLimited(2)) .or(Predicates.abilities(PartAbility.INPUT_LASER).setMaxGlobalLimited(1))) .where(" ", Predicates.any()) - .where("-", Predicates.air()) .build()) .workableCasingRenderer(GTLCore.id("block/molecular_casing"), GTCEu.id("block/multiblock/fusion_reactor")) .register(); @@ -1066,6 +1074,7 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin .tooltips(Component.translatable("gtceu.machine.duration_multiplier.tooltip", 0.6)) .tooltips(Component.translatable("gtceu.machine.processing_plant.tooltip.0")) .tooltips(Component.translatable("gtceu.machine.processing_plant.tooltip.1")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_11.tooltip", Component.translatable("gtceu.bender"), Component.translatable("gtceu.compressor"), @@ -1097,6 +1106,10 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin if (machine instanceof StorageMachine storageMachine) { int tier = storageMachine.getTier(); GTRecipeType recipeType = storageMachine.getRecipeType(); + if (storageMachine.getMachineStorageItem().isEmpty()) { + RecipeResult.of(machine, RecipeResult.FAIL_PROCESSING_PLANT_NO_INPUT); + return false; + } if (recipeType.equals(GTRecipeTypes.BENDER_RECIPES)) { isrecipe = Objects.equals(storageMachine.getMachineStorageItem().kjs$getId(), "gtceu:" + GTValues.VN[tier].toLowerCase() + "_bender"); } else if (recipeType.equals(GTRecipeTypes.COMPRESSOR_RECIPES)) { @@ -1121,6 +1134,7 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin isrecipe = Objects.equals(storageMachine.getMachineStorageItem().kjs$getId(), "gtceu:" + GTValues.VN[tier].toLowerCase() + "_laser_engraver"); } if (!isrecipe) { + RecipeResult.of(machine, RecipeResult.FAIL_PROCESSING_PLANT_WRONG_INPUT); machine.getRecipeLogic().interruptRecipe(); } } @@ -1138,6 +1152,7 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin .tooltips(Component.translatable("gtceu.machine.duration_multiplier.tooltip", 0.6)) .tooltips(Component.translatable("gtceu.machine.processing_plant.tooltip.0")) .tooltips(Component.translatable("gtceu.machine.processing_plant.tooltip.1")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_2.tooltip", Component.translatable("gtceu.assembler"), Component.translatable("gtceu.circuit_assembler"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -1159,12 +1174,17 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin if (machine instanceof StorageMachine storageMachine) { int tier = storageMachine.getTier(); GTRecipeType recipeType = storageMachine.getRecipeType(); + if (storageMachine.getMachineStorageItem().isEmpty()) { + RecipeResult.of(machine, RecipeResult.FAIL_PROCESSING_PLANT_NO_INPUT); + return false; + } if (recipeType.equals(GTRecipeTypes.ASSEMBLER_RECIPES)) { isrecipe = Objects.equals(storageMachine.getMachineStorageItem().kjs$getId(), "gtceu:" + GTValues.VN[tier].toLowerCase() + "_assembler"); } else if (recipeType.equals(GTRecipeTypes.CIRCUIT_ASSEMBLER_RECIPES)) { isrecipe = Objects.equals(storageMachine.getMachineStorageItem().kjs$getId(), "gtceu:" + GTValues.VN[tier].toLowerCase() + "_circuit_assembler"); } if (!isrecipe) { + RecipeResult.of(machine, RecipeResult.FAIL_PROCESSING_PLANT_WRONG_INPUT); machine.getRecipeLogic().interruptRecipe(); } } @@ -1187,6 +1207,7 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin .tooltips(Component.translatable("gtceu.machine.duration_multiplier.tooltip", 0.6)) .tooltips(Component.translatable("gtceu.machine.processing_plant.tooltip.0")) .tooltips(Component.translatable("gtceu.machine.processing_plant.tooltip.1")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_7.tooltip", Component.translatable("gtceu.centrifuge"), Component.translatable("gtceu.thermal_centrifuge"), @@ -1214,6 +1235,10 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin if (machine instanceof StorageMachine storageMachine) { int tier = storageMachine.getTier(); GTRecipeType recipeType = storageMachine.getRecipeType(); + if (storageMachine.getMachineStorageItem().isEmpty()) { + RecipeResult.of(machine, RecipeResult.FAIL_PROCESSING_PLANT_NO_INPUT); + return false; + } if (recipeType.equals(GTRecipeTypes.CENTRIFUGE_RECIPES)) { isrecipe = Objects.equals(storageMachine.getMachineStorageItem().kjs$getId(), "gtceu:" + GTValues.VN[tier].toLowerCase() + "_centrifuge"); } else if (recipeType.equals(GTRecipeTypes.THERMAL_CENTRIFUGE_RECIPES)) { @@ -1230,6 +1255,7 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin isrecipe = Objects.equals(storageMachine.getMachineStorageItem().kjs$getId(), "gtceu:" + GTValues.VN[tier].toLowerCase() + "_dehydrator"); } if (!isrecipe) { + RecipeResult.of(machine, RecipeResult.FAIL_PROCESSING_PLANT_WRONG_INPUT); machine.getRecipeLogic().interruptRecipe(); } } @@ -1249,6 +1275,7 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin .tooltips(Component.translatable("gtceu.machine.duration_multiplier.tooltip", 0.6)) .tooltips(Component.translatable("gtceu.machine.processing_plant.tooltip.0")) .tooltips(Component.translatable("gtceu.machine.processing_plant.tooltip.1")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_4.tooltip", Component.translatable("gtceu.chemical_reactor"), Component.translatable("gtceu.mixer"), @@ -1273,6 +1300,10 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin if (machine instanceof StorageMachine storageMachine) { int tier = storageMachine.getTier(); GTRecipeType recipeType = storageMachine.getRecipeType(); + if (storageMachine.getMachineStorageItem().isEmpty()) { + RecipeResult.of(machine, RecipeResult.FAIL_PROCESSING_PLANT_NO_INPUT); + return false; + } if (recipeType.equals(GTRecipeTypes.CHEMICAL_RECIPES)) { isrecipe = Objects.equals(storageMachine.getMachineStorageItem().kjs$getId(), "gtceu:" + GTValues.VN[tier].toLowerCase() + "_chemical_reactor"); } else if (recipeType.equals(GTRecipeTypes.MIXER_RECIPES)) { @@ -1283,6 +1314,7 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin isrecipe = Objects.equals(storageMachine.getMachineStorageItem().kjs$getId(), "gtceu:" + GTValues.VN[tier].toLowerCase() + "_ore_washer"); } if (!isrecipe) { + RecipeResult.of(machine, RecipeResult.FAIL_PROCESSING_PLANT_WRONG_INPUT); machine.getRecipeLogic().interruptRecipe(); } } @@ -1349,7 +1381,8 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin .recipeType(GTLRecipeTypes.NANO_FORGE_RECIPES) .tooltips(Component.translatable("gtceu.machine.nano_forge.tooltip.0")) .tooltips(Component.translatable("gtceu.machine.nano_forge_1.tooltip.0")) - .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) + .tooltips(Component.translatable("gtceu.multiblock.only.laser.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.nano_forge"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -1380,7 +1413,7 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin if (Objects.equals(machine.getMachineStorageItem().kjs$getId(), "gtceu:carbon_nanoswarm")) { components.add(Component.translatable("gtceu.multiblock.parallel", Component.literal(String.valueOf(machine.getMachineStorageItem().getCount())).withStyle(ChatFormatting.DARK_PURPLE)).withStyle(ChatFormatting.GRAY)); } else { - components.add(Component.literal("需要放入碳纳米蜂群").withStyle(ChatFormatting.RED)); + components.add(Component.translatable("message.gtlcore.need_carbon_nano_swarm_red").withStyle(ChatFormatting.RED)); } } }) @@ -1394,7 +1427,8 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin .tooltips(Component.translatable("gtceu.machine.nano_forge.tooltip.0")) .tooltips(Component.translatable("gtceu.machine.nano_forge_2.tooltip.0")) .tooltips(Component.translatable("gtceu.machine.nano_forge_2.tooltip.1")) - .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) + .tooltips(Component.translatable("gtceu.multiblock.only.laser.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.nano_forge"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -1429,7 +1463,7 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin if (Objects.equals(machine.getMachineStorageItem().kjs$getId(), "gtceu:neutronium_nanoswarm")) { components.add(Component.translatable("gtceu.multiblock.parallel", Component.literal(String.valueOf(machine.getMachineStorageItem().getCount())).withStyle(ChatFormatting.DARK_PURPLE)).withStyle(ChatFormatting.GRAY)); } else { - components.add(Component.literal("需要放入中子素纳米蜂群").withStyle(ChatFormatting.RED)); + components.add(Component.translatable("message.gtlcore.need_neutronium_nano_swarm_red").withStyle(ChatFormatting.RED)); } } }) @@ -1444,7 +1478,8 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin .tooltips(Component.translatable("gtceu.machine.nano_forge_3.tooltip.0")) .tooltips(Component.translatable("gtceu.machine.nano_forge_3.tooltip.1")) .tooltips(Component.translatable("gtceu.machine.nano_forge_3.tooltip.2")) - .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) + .tooltips(Component.translatable("gtceu.multiblock.only.laser.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.nano_forge"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -1480,7 +1515,7 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin if (Objects.equals(machine.getMachineStorageItem().kjs$getId(), "gtceu:draconium_nanoswarm")) { components.add(Component.translatable("gtceu.multiblock.parallel", Component.literal(String.valueOf(machine.getMachineStorageItem().getCount())).withStyle(ChatFormatting.DARK_PURPLE)).withStyle(ChatFormatting.GRAY)); } else { - components.add(Component.literal("需要放入龙纳米蜂群").withStyle(ChatFormatting.RED)); + components.add(Component.translatable("message.gtlcore.need_dragon_nano_swarm_red").withStyle(ChatFormatting.RED)); } } }) @@ -1492,6 +1527,7 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin .recipeType(GTLRecipeTypes.ISA_MILL_RECIPES) .tooltips(Component.translatable("gtceu.machine.isa_mill.tooltip.0")) .tooltips(Component.translatable("gtceu.machine.perfect_oc")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.isa_mill"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -1548,6 +1584,7 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin .tooltips(Component.translatable("gtceu.machine.neutron_activator.tooltip.3")) .tooltips(Component.translatable("gtceu.machine.neutron_activator.tooltip.4")) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.neutron_activator"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -1586,6 +1623,7 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin .langValue("Heat Exchanger") .tooltips(Component.translatable("gtceu.machine.heat_exchanger.tooltip.0")) .tooltips(Component.translatable("gtceu.machine.heat_exchanger.tooltip.1")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.heat_exchanger"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -1743,6 +1781,7 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin .tooltips(Component.translatable("gtceu.machine.space_elevator.tooltip.0")) .tooltips(Component.translatable("gtceu.machine.space_elevator.tooltip.1")) .tooltips(Component.translatable("gtceu.machine.space_elevator.tooltip.2")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.space_elevator"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -1793,7 +1832,7 @@ private static boolean blockConversionRoom(List poses, IRecipeLogicMachin .where("E", Predicates.blocks(GTLBlocks.SPACE_ELEVATOR_SUPPORT.get())) .where("H", Predicates.blocks(ChemicalHelper.getBlock(TagPrefix.frameGt, GTMaterials.Neutronium))) .where("F", Predicates.blocks(Registries.getBlock("kubejs:space_elevator_internal_support"))) - .where("C", GTLPredicates.tierActiveCasings(GTLBlocks.sepmmap, "SEPMTier")) + .where("C", GTLPredicates.tierCasings(BlockMap.sepmMap, "SEPMTier")) .where("A", Predicates.blocks(Registries.getBlock("kubejs:high_strength_concrete"))) .where("D", Predicates.blocks(GTLBlocks.SPACE_ELEVATOR_MECHANICAL_CASING.get())) .where("M", Predicates.blocks(GTLBlocks.POWER_CORE.get())) @@ -1955,6 +1994,7 @@ public long getMaxVoltage() { Component.translatable("gtceu.machine.fusion_reactor.overclocking")) .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltipBuilder(GTLMachines.GTL_ADD) .appearanceBlock(() -> GTLFusionCasingBlock.getCasingState(tier)) .pattern((definition) -> { diff --git a/src/main/java/org/gtlcore/gtlcore/common/data/machines/GeneratorMachine.java b/src/main/java/org/gtlcore/gtlcore/common/data/machines/GeneratorMachine.java index 3d0ea76d2..82e6324c1 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/data/machines/GeneratorMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/data/machines/GeneratorMachine.java @@ -103,9 +103,10 @@ public static MultiblockMachineDefinition registerMegaTurbine(String name, int t .rotationState(RotationState.ALL) .recipeType(recipeType) .generator(true) - .tooltips(Component.literal("可使用变电动力仓")) + .tooltips(Component.translatable("tooltip.gtlcore.can_use_transformer_hatch")) .tooltips(Component.translatable("gtceu.universal.tooltip.base_production_eut", FormattingUtil.formatNumbers(GTValues.V[tier] * value))) .tooltips(Component.translatable("gtceu.multiblock.turbine.efficiency_tooltip", GTValues.VNF[tier])) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltipBuilder(GTLMachines.GTL_ADD) .recipeModifier(MegaTurbineMachine::recipeModifier) .appearanceBlock(casing) @@ -168,6 +169,7 @@ public static MultiblockMachineDefinition registerMegaTurbine(String name, int t .tooltips(Component.translatable("gtceu.machine.dyson_sphere.tooltip.4")) .tooltips(Component.translatable("gtceu.machine.dyson_sphere.tooltip.5")) .tooltips(Component.translatable("gtceu.machine.dyson_sphere.tooltip.6")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.dyson_sphere"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -243,8 +245,10 @@ public static MultiblockMachineDefinition registerMegaTurbine(String name, int t .allowExtendedFacing(false) .recipeType(GTLRecipeTypes.LARGE_NAQUADAH_REACTOR_RECIPES) .tooltips(Component.translatable("gtceu.machine.perfect_oc")) + .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.large_naquadah_reactor"))) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltipBuilder(GTLMachines.GTL_ADD) .generator(true) .recipeModifier((machine, recipe, params, result) -> GTLRecipeModifiers.standardOverclocking((WorkableElectricMultiblockMachine) machine, recipe)) @@ -277,6 +281,7 @@ public static MultiblockMachineDefinition registerMegaTurbine(String name, int t .tooltips(Component.translatable("gtceu.machine.advanced_hyper_reactor.tooltip.0")) .tooltips(Component.translatable("gtceu.machine.advanced_hyper_reactor.tooltip.1")) .tooltips(Component.translatable("gtceu.machine.perfect_oc")) + .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.advanced_hyper_reactor"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -328,6 +333,7 @@ public static MultiblockMachineDefinition registerMegaTurbine(String name, int t .tooltips(Component.translatable("gtceu.machine.hyper_reactor.tooltip.1")) .tooltips(Component.translatable("gtceu.machine.hyper_reactor.tooltip.2")) .tooltips(Component.translatable("gtceu.machine.perfect_oc")) + .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.hyper_reactor"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -413,6 +419,7 @@ public static MultiblockMachineDefinition registerMegaTurbine(String name, int t .rotationState(RotationState.ALL) .recipeType(GTLRecipeTypes.ANNIHILATE_GENERATOR_RECIPES) .tooltips(Component.translatable("gtceu.machine.perfect_oc")) + .tooltips(Component.translatable("gtceu.multiblock.only.laser.tooltip")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.annihilate_generator"))) .tooltipBuilder(GTLMachines.GTL_ADD) diff --git a/src/main/java/org/gtlcore/gtlcore/common/data/machines/MultiBlockMachineA.java b/src/main/java/org/gtlcore/gtlcore/common/data/machines/MultiBlockMachineA.java index 6ad35625f..c2c80c07b 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/data/machines/MultiBlockMachineA.java +++ b/src/main/java/org/gtlcore/gtlcore/common/data/machines/MultiBlockMachineA.java @@ -4,9 +4,12 @@ import org.gtlcore.gtlcore.api.machine.multiblock.CoilWorkableElectricMultipleRecipesMachine; import org.gtlcore.gtlcore.api.machine.multiblock.NoEnergyMultiblockMachine; import org.gtlcore.gtlcore.api.pattern.GTLPredicates; +import org.gtlcore.gtlcore.common.block.BlockMap; import org.gtlcore.gtlcore.common.data.*; import org.gtlcore.gtlcore.common.machine.multiblock.electric.*; import org.gtlcore.gtlcore.common.machine.multiblock.steam.LargeSteamParallelMultiblockMachine; +import org.gtlcore.gtlcore.common.machine.trait.MultipleRecipesLogic; +import org.gtlcore.gtlcore.utils.NumberUtils; import org.gtlcore.gtlcore.utils.Registries; import com.gregtechceu.gtceu.GTCEu; @@ -19,6 +22,7 @@ import com.gregtechceu.gtceu.api.machine.multiblock.CoilWorkableElectricMultiblockMachine; import com.gregtechceu.gtceu.api.machine.multiblock.PartAbility; import com.gregtechceu.gtceu.api.machine.multiblock.WorkableElectricMultiblockMachine; +import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; import com.gregtechceu.gtceu.api.pattern.FactoryBlockPattern; import com.gregtechceu.gtceu.api.pattern.MultiblockShapeInfo; import com.gregtechceu.gtceu.api.pattern.Predicates; @@ -39,6 +43,10 @@ import net.minecraft.world.level.block.DirectionalBlock; import net.minecraft.world.level.material.Fluids; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + import static com.gregtechceu.gtceu.api.pattern.Predicates.*; import static com.gregtechceu.gtceu.common.data.GTRecipeModifiers.ELECTRIC_OVERCLOCK; import static com.gregtechceu.gtceu.common.registry.GTRegistration.REGISTRATE; @@ -303,6 +311,7 @@ public static void init() { .recipeType(GTLRecipeTypes.RANDOM_ORE_RECIPES) .tooltips(Component.translatable("gtceu.machine.large_void_miner.tooltip.0")) .tooltips(Component.translatable("gtceu.machine.large_void_miner.tooltip.1")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GCY")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_2.tooltip", Component.translatable("gtceu.large_void_miner"), Component.translatable("gtceu.random_ore"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -338,6 +347,7 @@ public static void init() { .tooltips(Component.translatable("gtceu.machine.chemical_plant.tooltip.0")) .tooltips(Component.translatable("gtceu.machine.perfect_oc")) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GCY")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.large_chemical_reactor"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -423,6 +433,7 @@ public static void init() { .recipeType(GTLRecipeTypes.MASS_FABRICATOR_RECIPES) .tooltips(Component.translatable("gtceu.machine.perfect_oc")) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GCY")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.mass_fabricator"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -503,6 +514,7 @@ public static void init() { .recipeType(GTRecipeTypes.ASSEMBLER_RECIPES) .tooltips(Component.translatable("gtceu.machine.perfect_oc")) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_2.tooltip", Component.translatable("gtceu.precision_assembler"), Component.translatable("gtceu.assembler"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -702,7 +714,7 @@ public static void init() { .where("e", Predicates.blocks(Registries.getBlock("kubejs:aggregatione_core"))) .where("a", Predicates.blocks(ChemicalHelper.getBlock(TagPrefix.frameGt, GTMaterials.NaquadahEnriched))) .where("i", Predicates.blocks(GTMachines.ITEM_IMPORT_BUS[0].get())) - .where("g", Predicates.abilities(PartAbility.EXPORT_ITEMS)) + .where("g", GTLPredicates.diffAbilities(List.of(PartAbility.EXPORT_ITEMS), List.of(PartAbility.IMPORT_ITEMS, PartAbility.IMPORT_FLUIDS))) .where(" ", Predicates.any()) .build()) .additionalDisplay((controller, components) -> { @@ -718,6 +730,7 @@ public static void init() { .recipeType(GTLRecipeTypes.SUPER_PARTICLE_COLLIDER_RECIPES) .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "TST")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.super_particle_collider"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -795,6 +808,7 @@ public static void init() { .tooltips(Component.translatable("gtceu.machine.engraving_laser_plant.tooltip.0")) .tooltips(Component.translatable("gtceu.machine.perfect_oc")) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "TST")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_2.tooltip", Component.translatable("gtceu.precision_laser_engraver"), Component.translatable("gtceu.laser_engraver"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -846,6 +860,7 @@ public static void init() { .tooltips(Component.translatable("gtceu.machine.perfect_oc")) .tooltips(Component.translatable("gtceu.machine.electric_blast_furnace.tooltip.2")) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GT++")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.alloy_blast_smelter"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -931,6 +946,7 @@ public static void init() { .recipeType(GTLRecipeTypes.QFT_RECIPES) .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GT++")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.qft"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -967,10 +983,30 @@ public static void init() { .workableCasingRenderer(GTLCore.id("block/manipulator"), GTCEu.id("block/multiblock/fusion_reactor")) .register(); - public final static MultiblockMachineDefinition SUPER_BLAST_SMELTER = REGISTRATE.multiblock("super_blast_smelter", CoilWorkableElectricMultipleRecipesMachine::new) + public final static MultiblockMachineDefinition SUPER_BLAST_SMELTER = REGISTRATE.multiblock("super_blast_smelter", holder -> new CoilWorkableElectricMultipleRecipesMachine(holder, 1, 0.2) { + + @Override + protected @NotNull RecipeLogic createRecipeLogic(Object @NotNull... args) { + return new MultipleRecipesLogic(this, EBF_CHECK) { + + @Override + protected double getTotalEuOfRecipe(GTRecipe recipe) { + double eu = super.getTotalEuOfRecipe(recipe); + + if (recipe.data.contains("ebf_temp")) { + final var coilMachine = (CoilWorkableElectricMultiblockMachine) getMachine(); + int requiredTemp = recipe.data.getInt("ebf_temp"); + int blastFurnaceTemperature = coilMachine.getCoilType().getCoilTemperature() + 100 * Math.max(0, coilMachine.getTier() - 2); + eu *= Math.max(0.5, (double) requiredTemp / blastFurnaceTemperature) * Math.min(1, NumberUtils.pow95(Math.max(0, (blastFurnaceTemperature - requiredTemp) / 900))); + } + + return eu; + } + }; + } + }) .rotationState(RotationState.NON_Y_AXIS) .allowExtendedFacing(false) - .recipeModifiers((machine, recipe, params, result) -> GTLRecipeModifiers.reduction(machine, recipe, 1, 0.2), GTRecipeModifiers.PARALLEL_HATCH, GTRecipeModifiers::ebfOverclock) .appearanceBlock(GCyMBlocks.CASING_HIGH_TEMPERATURE_SMELTING) .recipeType(GTRecipeTypes.BLAST_RECIPES) .recipeType(GTRecipeTypes.ALLOY_SMELTER_RECIPES) @@ -978,7 +1014,6 @@ public static void init() { .tooltips(Component.translatable("gtceu.machine.duration_multiplier.tooltip", 0.2)) .tooltips(Component.translatable("gtceu.machine.electric_blast_furnace.tooltip.a")) .tooltips(Component.translatable("gtceu.machine.electric_blast_furnace.tooltip.0")) - .tooltips(Component.translatable("gtceu.machine.perfect_oc")) .tooltips(Component.translatable("gtceu.machine.electric_blast_furnace.tooltip.2")) .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) .tooltips(Component.translatable("gtceu.machine.multiple_recipes.tooltip")) @@ -1099,7 +1134,7 @@ public static void init() { .tooltips(Component.translatable("gtceu.machine.integrated_ore_processor.tooltip.6")) .tooltips(Component.translatable("gtceu.machine.integrated_ore_processor.tooltip.7")) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) - .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.integrated_ore_processor"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -1159,6 +1194,7 @@ public static void init() { .rotationState(RotationState.ALL) .recipeType(GTRecipeTypes.CRACKING_RECIPES) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.cracker"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -1203,6 +1239,7 @@ public static void init() { .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) .tooltips(Component.translatable("gtceu.machine.perfect_oc")) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_3.tooltip", Component.translatable("gtceu.assembler"), Component.translatable("gtceu.precision_assembler"), Component.translatable("gtceu.circuit_assembler"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -1308,7 +1345,6 @@ public static void init() { .tooltips(Component.translatable("gtceu.machine.available_recipe_map_2.tooltip", Component.translatable("gtceu.vacuum_freezer"), Component.translatable("gtceu.plasma_condenser"))) .tooltipBuilder(GTLMachines.GTL_ADD) - .recipeModifiers(GTRecipeModifiers.PARALLEL_HATCH, GTRecipeModifiers.ELECTRIC_OVERCLOCK.apply(OverclockingLogic.PERFECT_OVERCLOCK_SUBTICK)) .appearanceBlock(GTBlocks.CASING_ALUMINIUM_FROSTPROOF) .pattern(definition -> FactoryBlockPattern.start() .aisle(" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ") @@ -1413,10 +1449,9 @@ public static void init() { .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) .tooltips(Component.translatable("gtceu.machine.multiple_recipes.tooltip")) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) - .tooltips(Component.translatable("gtceu.machine.available_recipe_map_2.tooltip", - Component.translatable("gtceu.distillation_tower"), Component.translatable("gtceu.evaporation"))) + .tooltips(Component.translatable("gtceu.machine.available_recipe_map_3.tooltip", + Component.translatable("gtceu.distillation_tower"), Component.translatable("gtceu.evaporation"), Component.translatable("gtceu.distillery"))) .tooltipBuilder(GTLMachines.GTL_ADD) - .recipeModifiers(GTRecipeModifiers.PARALLEL_HATCH, GTRecipeModifiers.ELECTRIC_OVERCLOCK.apply(OverclockingLogic.PERFECT_OVERCLOCK_SUBTICK)) .appearanceBlock(GTBlocks.CASING_STAINLESS_CLEAN) .pattern(definition -> FactoryBlockPattern.start() .aisle(" aaaaaaa ", " aaaaaaa ", " bbbbbbb ", " bbbbbbb ", " bbbbbbb ", " bbbbbbb ", " bbbbbbb ", " bbbbbbb ", " ccccccc ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ") @@ -1522,6 +1557,7 @@ public static void init() { .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) .tooltips(Component.translatable("gtceu.machine.perfect_oc")) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "TST")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_2.tooltip", Component.translatable("gtceu.autoclave"), Component.translatable("gtceu.chemical_bath"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -2108,6 +2144,7 @@ public static void init() { .rotationState(RotationState.ALL) .recipeType(GTRecipeTypes.PRIMITIVE_BLAST_FURNACE_RECIPES) .tooltips(Component.translatable("gtceu.machine.dimensionally_transcendent_dirt_forge.tooltip.0")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.primitive_blast_furnace"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -2145,6 +2182,7 @@ public static void init() { .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) .tooltips(Component.translatable("gtceu.machine.multiple_recipes.tooltip")) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "TST")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_4.tooltip", Component.translatable("gtceu.cutter"), Component.translatable("gtceu.lathe"), Component.translatable("gtceu.macerator"), Component.translatable("gtceu.centrifuge"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -2305,6 +2343,7 @@ public static void init() { .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) .tooltips(Component.translatable("gtceu.machine.multiple_recipes.tooltip")) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "TST")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_2.tooltip", Component.translatable("gtceu.extruder"), Component.translatable("gtceu.compressor"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -2397,7 +2436,8 @@ public static void init() { .recipeType(GTLRecipeTypes.DISTORT_RECIPES) .tooltips(Component.translatable("gtceu.machine.chemical_distort.tooltip.0")) .tooltips(Component.translatable("gtceu.machine.chemical_distort.tooltip.1")) - .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) + .tooltips(Component.translatable("gtceu.multiblock.only.laser.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "TST")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.distort"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -2564,12 +2604,13 @@ public static void init() { .workableCasingRenderer(GTCEu.id("block/casings/gcym/laser_safe_engraving_casing"), GTCEu.id("block/multiblock/fusion_reactor")) .register(); - public final static MultiblockMachineDefinition MEGA_WIREMILL = REGISTRATE.multiblock("mega_wiremill", (holder) -> new CoilWorkableElectricMultipleRecipesMultiblockMachine(holder)) + public final static MultiblockMachineDefinition MEGA_WIREMILL = REGISTRATE.multiblock("mega_wiremill", CoilWorkableElectricMultipleRecipesMultiblockMachine::new) .rotationState(RotationState.ALL) .recipeType(GTRecipeTypes.WIREMILL_RECIPES) .tooltips(Component.translatable("gtceu.multiblock.coil_parallel")) .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) .tooltips(Component.translatable("gtceu.machine.multiple_recipes.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "TST")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.wiremill"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -2623,7 +2664,7 @@ public static void init() { .workableCasingRenderer(GTLCore.id("block/casings/oxidation_resistant_hastelloy_n_mechanical_casing"), GTCEu.id("block/multiblock/gcym/large_wiremill")) .register(); - public final static MultiblockMachineDefinition MEGA_PRESSER = REGISTRATE.multiblock("mega_presser", (holder) -> new CoilWorkableElectricMultipleRecipesMultiblockMachine(holder)) + public final static MultiblockMachineDefinition MEGA_PRESSER = REGISTRATE.multiblock("mega_presser", CoilWorkableElectricMultipleRecipesMultiblockMachine::new) .rotationState(RotationState.ALL) .recipeType(GTRecipeTypes.BENDER_RECIPES) .recipeType(GTRecipeTypes.FORGE_HAMMER_RECIPES) @@ -2631,6 +2672,7 @@ public static void init() { .tooltips(Component.translatable("gtceu.multiblock.coil_parallel")) .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) .tooltips(Component.translatable("gtceu.machine.multiple_recipes.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "TST")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_3.tooltip", Component.translatable("gtceu.bender"), Component.translatable("gtceu.forge_hammer"), Component.translatable("gtceu.forming_press"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -2680,13 +2722,14 @@ public static void init() { .workableCasingRenderer(GTLCore.id("block/molecular_casing"), GTCEu.id("block/multiblock/fusion_reactor")) .register(); - public final static MultiblockMachineDefinition MEGA_EXTRACTOR = REGISTRATE.multiblock("mega_extractor", (holder) -> new CoilWorkableElectricMultipleRecipesMultiblockMachine(holder)) + public final static MultiblockMachineDefinition MEGA_EXTRACTOR = REGISTRATE.multiblock("mega_extractor", CoilWorkableElectricMultipleRecipesMultiblockMachine::new) .rotationState(RotationState.ALL) .recipeType(GTRecipeTypes.EXTRACTOR_RECIPES) .recipeType(GTRecipeTypes.FLUID_SOLIDFICATION_RECIPES) .tooltips(Component.translatable("gtceu.multiblock.coil_parallel")) .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) .tooltips(Component.translatable("gtceu.machine.multiple_recipes.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "TST")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_2.tooltip", Component.translatable("gtceu.extractor"), Component.translatable("gtceu.fluid_solidifier"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -2793,6 +2836,7 @@ public static void init() { .recipeType(GTLRecipeTypes.ELEMENT_COPYING_RECIPES) .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GCY")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.element_copying"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -2888,6 +2932,7 @@ public static void init() { .tooltips(Component.translatable("gtceu.machine.flotation_cell_regulator.tooltip.0")) .tooltips(Component.translatable("gtceu.machine.perfect_oc")) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GT++")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.flotating_beneficiation"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -2932,6 +2977,7 @@ public static void init() { .tooltips(Component.translatable("gtceu.machine.electric_blast_furnace.tooltip.2")) .tooltips(Component.translatable("gtceu.machine.vacuum_drying_furnace.tooltip.1")) .tooltips(Component.translatable("gtceu.multiblock.coil_parallel")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GT++")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_2.tooltip", Component.translatable("gtceu.vacuum_drying"), Component.translatable("gtceu.dehydrator"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -2983,6 +3029,7 @@ public static void init() { .tooltips(Component.translatable("gtceu.multiblock.coil_parallel")) .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) .tooltips(Component.translatable("gtceu.machine.multiple_recipes.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "TST")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.fluid_heater"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -3023,6 +3070,7 @@ public static void init() { .tooltips(Component.translatable("gtceu.machine.eut_multiplier.tooltip", 0.8)) .tooltips(Component.translatable("gtceu.machine.duration_multiplier.tooltip", 0.6)) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GCY")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.electric_implosion_compressor"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -3058,6 +3106,7 @@ public static void init() { .allowExtendedFacing(false) .recipeType(GTLRecipeTypes.STELLAR_FORGE_RECIPES) .tooltips(Component.translatable("gtceu.machine.perfect_oc")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GCY")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.stellar_forge"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -3085,7 +3134,7 @@ public static void init() { .or(Predicates.autoAbilities(definition.getRecipeTypes())) .or(Predicates.abilities(PartAbility.MAINTENANCE).setExactLimit(1))) .where("c", Predicates.blocks(GTBlocks.FUSION_COIL.get())) - .where("d", GTLPredicates.tierCasings(GTLBlocks.scmap, "SCTier")) + .where("d", GTLPredicates.tierCasings(BlockMap.scMap, "SCTier")) .where(" ", Predicates.any()) .build()) .workableCasingRenderer(GTCEu.id("block/casings/gcym/atomic_casing"), GTCEu.id("block/multiblock/electric_blast_furnace")) @@ -3096,6 +3145,7 @@ public static void init() { .recipeType(GTLRecipeTypes.COMPONENT_ASSEMBLY_LINE_RECIPES) .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.component_assembly_line"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -3153,7 +3203,7 @@ public static void init() { .or(Predicates.abilities(PartAbility.INPUT_LASER).setMaxGlobalLimited(1))) .where("I", Predicates.blocks(ChemicalHelper.getBlock(TagPrefix.frameGt, GTLMaterials.HastelloyN))) .where("J", Predicates.blocks(GTLBlocks.ADVANCED_ASSEMBLY_LINE_UNIT.get())) - .where("K", GTLPredicates.tierCasings(GTLBlocks.calmap, "CATier")) + .where("K", GTLPredicates.tierCasings(BlockMap.calMap, "CATier")) .where("L", Predicates.blocks(GTBlocks.CASING_POLYTETRAFLUOROETHYLENE_PIPE.get())) .where("M", Predicates.blocks(ChemicalHelper.getBlock(TagPrefix.frameGt, GTMaterials.TungstenSteel))) .where("N", Predicates.blocks(GTLBlocks.IRIDIUM_CASING.get()) @@ -3168,8 +3218,9 @@ public static void init() { .recipeType(GTLRecipeTypes.INTEGRATED_ORE_PROCESSOR) .tooltips(Component.translatable("gtceu.machine.integrated_ore_processor.tooltip.0")) .tooltips(Component.translatable("gtceu.machine.advanced_integrated_ore_processor.tooltip.0")) - .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) + .tooltips(Component.translatable("gtceu.multiblock.only.laser.tooltip")) .tooltips(Component.translatable("gtceu.machine.multiple_recipes.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "TST")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.integrated_ore_processor"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -3231,6 +3282,7 @@ public static void init() { .recipeType(GTRecipeTypes.LARGE_BOILER_RECIPES) .tooltips(Component.translatable("gtceu.multiblock.large_boiler.max_temperature", 4096000 + 274.15, 4096000)) .tooltips(Component.translatable("gtceu.multiblock.large_boiler.heat_time_tooltip", 4096000 / 32 / 20)) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.large_boiler"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -3254,6 +3306,7 @@ public static void init() { .rotationState(RotationState.ALL) .recipeType(GTRecipeTypes.FURNACE_RECIPES) .tooltips(Component.translatable("gtceu.machine.dimensionally_transcendent_dirt_forge.tooltip.0")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("compass.node.gtceu.steam/steam_furnace"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -3313,8 +3366,10 @@ public static void init() { .recipeType(GTLRecipeTypes.NANO_FORGE_RECIPES) .tooltips(Component.translatable("gtceu.machine.nano_core.tooltip.0")) .tooltips(Component.translatable("gtceu.machine.nano_core.tooltip.1")) - .tooltips(Component.translatable("gtceu.multiblock.laser.tooltip")) + .tooltips(Component.translatable("gtceu.machine.nano_core.tooltip.2")) + .tooltips(Component.translatable("gtceu.multiblock.only.laser.tooltip")) .tooltips(Component.translatable("gtceu.machine.perfect_oc")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "TST")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.nano_forge"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -3417,7 +3472,7 @@ public static void init() { .where(" ", Predicates.any()) .build()) .additionalDisplay((controller, components) -> { - if (controller.isFormed() && controller instanceof StorageMachine machine) { + if (controller.isFormed()) { components.add(Component.translatable("gtceu.multiblock.parallel", Component.literal(String.valueOf(8192)).withStyle(ChatFormatting.DARK_PURPLE)).withStyle(ChatFormatting.GRAY)); } }) diff --git a/src/main/java/org/gtlcore/gtlcore/common/data/machines/MultiBlockMachineB.java b/src/main/java/org/gtlcore/gtlcore/common/data/machines/MultiBlockMachineB.java index 58aef4745..d91002dda 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/data/machines/MultiBlockMachineB.java +++ b/src/main/java/org/gtlcore/gtlcore/common/data/machines/MultiBlockMachineB.java @@ -1,10 +1,7 @@ package org.gtlcore.gtlcore.common.data.machines; import org.gtlcore.gtlcore.GTLCore; -import org.gtlcore.gtlcore.common.data.GTLBlocks; -import org.gtlcore.gtlcore.common.data.GTLMachines; -import org.gtlcore.gtlcore.common.data.GTLRecipeModifiers; -import org.gtlcore.gtlcore.common.data.GTLRecipeTypes; +import org.gtlcore.gtlcore.common.data.*; import org.gtlcore.gtlcore.common.machine.multiblock.electric.WorkableElectricParallelHatchMultipleRecipesMachine; import org.gtlcore.gtlcore.common.machine.multiblock.noenergy.PrimitiveOreMachine; import org.gtlcore.gtlcore.config.ConfigHolder; @@ -29,8 +26,6 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.Blocks; -import committee.nova.mods.avaritia.init.registry.ModBlocks; - import static com.gregtechceu.gtceu.api.pattern.Predicates.blocks; import static com.gregtechceu.gtceu.api.pattern.Predicates.controller; import static com.gregtechceu.gtceu.common.registry.GTRegistration.REGISTRATE; @@ -68,7 +63,7 @@ public static void init() {} .or(Predicates.abilities(PartAbility.PARALLEL_HATCH).setExactLimit(1)) .or(Predicates.abilities(PartAbility.INPUT_LASER).setMaxGlobalLimited(1))) .where("b", Predicates.blocks(GTLBlocks.INFINITY_GLASS.get())) - .where("c", Predicates.blocks(ModBlocks.infinity.get())) + .where("c", Predicates.blocks(ChemicalHelper.getBlock(TagPrefix.block, GTLMaterials.Infinity))) .where("~", Predicates.controller(Predicates.blocks(definition.get()))) .where(" ", Predicates.any()) .build()) @@ -79,6 +74,7 @@ public static void init() {} .langValue("Dissolving Tank") .tooltips(Component.translatable("gtceu.multiblock.dissolving_tank.tooltip.0")) .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.dissolution_treatment"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -108,6 +104,7 @@ public static void init() {} public final static MultiblockMachineDefinition DIGESTION_TANK = REGISTRATE.multiblock("digestion_tank", CoilWorkableElectricMultiblockMachine::new) .langValue("Digestion Tank") .tooltips(Component.translatable("gtceu.multiblock.parallelizable.tooltip")) + .tooltips(Component.translatable("tooltip.gtlcore.structure.source", "GTNH")) .tooltips(Component.translatable("gtceu.machine.available_recipe_map_1.tooltip", Component.translatable("gtceu.digestion_treatment"))) .tooltipBuilder(GTLMachines.GTL_ADD) @@ -210,7 +207,7 @@ public static void init() {} .where("P", Predicates.blocks(GTBlocks.CASING_BRONZE_PIPE.get())) .where("G", Predicates.blocks(GTBlocks.HERMETIC_CASING_MV.get())) .where("D", Predicates.blocks(GTBlocks.CASING_STAINLESS_CLEAN.get()) - .setMinGlobalLimited(40) + .setMinGlobalLimited(36) .or(Predicates.autoAbilities(definition.getRecipeTypes())) .or(Predicates.autoAbilities(true, false, true))) .where("L", Predicates.blocks(GTBlocks.COIL_KANTHAL.get())) @@ -223,8 +220,8 @@ public static void init() {} public final static MultiblockMachineDefinition PRIMITIVE_VOID_ORE = ConfigHolder.INSTANCE.enablePrimitiveVoidOre ? REGISTRATE.multiblock("primitive_void_ore", PrimitiveOreMachine::new) .langValue("Primitive Void Ore") - .tooltips(Component.literal("运行时根据维度每tick随机产出一组任意粗矿")) - .tooltips(Component.literal("支持主世界,下界,末地")) + .tooltips(Component.translatable("tooltip.gtlcore.primitive_void_ore_random_output")) + .tooltips(Component.translatable("tooltip.gtlcore.supports_dimensions")) .tooltipBuilder(GTLMachines.GTL_ADD) .rotationState(RotationState.ALL) .recipeType(GTLRecipeTypes.PRIMITIVE_VOID_ORE_RECIPES) diff --git a/src/main/java/org/gtlcore/gtlcore/common/item/ConfigurationCopyBehavior.java b/src/main/java/org/gtlcore/gtlcore/common/item/ConfigurationCopyBehavior.java index 6f5cee556..1832ab09e 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/item/ConfigurationCopyBehavior.java +++ b/src/main/java/org/gtlcore/gtlcore/common/item/ConfigurationCopyBehavior.java @@ -1,5 +1,9 @@ package org.gtlcore.gtlcore.common.item; +import org.gtlcore.gtlcore.common.machine.multiblock.part.maintenance.IAutoConfiguratioGravityPart; +import org.gtlcore.gtlcore.common.machine.multiblock.part.maintenance.IAutoConfigurationMaintenanceHatch; +import org.gtlcore.gtlcore.common.machine.multiblock.part.maintenance.IGravityPartMachine; + import com.gregtechceu.gtceu.api.blockentity.MetaMachineBlockEntity; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.item.component.IInteractionItem; @@ -33,25 +37,66 @@ public InteractionResult onItemUseFirst(ItemStack stack, UseOnContext context) { if (metaMachine instanceof SimpleTieredMachine simpleTieredMachine) { if (Objects.requireNonNull(context.getPlayer()).isShiftKeyDown()) { getSTMCfg(tags, simpleTieredMachine); - context.getPlayer().displayClientMessage(Component.literal("已复制机器数据"), true); + context.getPlayer().displayClientMessage(Component.translatable("message.gtlcore.machine_data_copied"), true); } else { if (tags.getBoolean("Configuration")) { setSTMCfg(tags, simpleTieredMachine); - context.getPlayer().displayClientMessage(Component.literal("已粘贴机器数据"), true); + context.getPlayer().displayClientMessage(Component.translatable("message.gtlcore.machine_data_pasted"), true); } else { - context.getPlayer().displayClientMessage(Component.literal("未找到机器数据"), true); + context.getPlayer().displayClientMessage(Component.translatable("message.gtlcore.machine_data_not_found"), true); } } } else if (metaMachine instanceof ItemBusPartMachine itemBusPartMachine) { if (Objects.requireNonNull(context.getPlayer()).isShiftKeyDown()) { getBusCfg(tags, itemBusPartMachine); - context.getPlayer().displayClientMessage(Component.literal("已复制机器数据"), true); + context.getPlayer().displayClientMessage(Component.translatable("message.gtlcore.machine_data_copied"), true); } else { if (tags.getBoolean("Configuration")) { setBusCfg(tags, itemBusPartMachine); - context.getPlayer().displayClientMessage(Component.literal("已粘贴机器数据"), true); + context.getPlayer().displayClientMessage(Component.translatable("message.gtlcore.machine_data_pasted"), true); + } else { + context.getPlayer().displayClientMessage(Component.translatable("message.gtlcore.machine_data_not_found"), true); + } + } + } else if (metaMachine instanceof IAutoConfiguratioGravityPart acgp) { + if (Objects.requireNonNull(context.getPlayer()).isShiftKeyDown()) { + tags.putBoolean("Configuration", true); + tags.putFloat("durationMultiplier", acgp.getDurationMultiplier()); + tags.putInt("currentGravity", acgp.getCurrentGravity()); + context.getPlayer().displayClientMessage(Component.translatable("message.gtlcore.machine_data_copied"), true); + } else { + if (tags.getBoolean("Configuration")) { + acgp.setDurationMultiplier(tags.getFloat("durationMultiplier")); + acgp.setCurrentGravity(tags.getInt("currentGravity")); + context.getPlayer().displayClientMessage(Component.translatable("message.gtlcore.machine_data_pasted"), true); + } else { + context.getPlayer().displayClientMessage(Component.translatable("message.gtlcore.machine_data_not_found"), true); + } + } + } else if (metaMachine instanceof IAutoConfigurationMaintenanceHatch acmh) { + if (Objects.requireNonNull(context.getPlayer()).isShiftKeyDown()) { + tags.putBoolean("Configuration", true); + tags.putFloat("durationMultiplier", acmh.getDurationMultiplier()); + context.getPlayer().displayClientMessage(Component.translatable("message.gtlcore.machine_data_copied"), true); + } else { + if (tags.getBoolean("Configuration")) { + acmh.setDurationMultiplier(tags.getFloat("durationMultiplier")); + context.getPlayer().displayClientMessage(Component.translatable("message.gtlcore.machine_data_pasted"), true); + } else { + context.getPlayer().displayClientMessage(Component.translatable("message.gtlcore.machine_data_not_found"), true); + } + } + } else if (metaMachine instanceof IGravityPartMachine gpm) { + if (Objects.requireNonNull(context.getPlayer()).isShiftKeyDown()) { + tags.putBoolean("Configuration", true); + tags.putInt("currentGravity", gpm.getCurrentGravity()); + context.getPlayer().displayClientMessage(Component.translatable("message.gtlcore.machine_data_copied"), true); + } else { + if (tags.getBoolean("Configuration")) { + gpm.setCurrentGravity(tags.getInt("currentGravity")); + context.getPlayer().displayClientMessage(Component.translatable("message.gtlcore.machine_data_pasted"), true); } else { - context.getPlayer().displayClientMessage(Component.literal("未找到机器数据"), true); + context.getPlayer().displayClientMessage(Component.translatable("message.gtlcore.machine_data_not_found"), true); } } } diff --git a/src/main/java/org/gtlcore/gtlcore/common/item/MEPatternBufferCopyBehavior.java b/src/main/java/org/gtlcore/gtlcore/common/item/MEPatternBufferCopyBehavior.java new file mode 100644 index 000000000..2ff150792 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/item/MEPatternBufferCopyBehavior.java @@ -0,0 +1,52 @@ +package org.gtlcore.gtlcore.common.item; + +import org.gtlcore.gtlcore.common.machine.multiblock.part.ae.MEPatternBufferPartMachine; + +import com.gregtechceu.gtceu.api.blockentity.MetaMachineBlockEntity; +import com.gregtechceu.gtceu.api.item.component.IInteractionItem; +import com.gregtechceu.gtceu.common.item.TooltipBehavior; + +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.UseOnContext; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.function.Consumer; + +import static com.gregtechceu.gtceu.api.item.tool.ToolHelper.getBehaviorsTag; + +public class MEPatternBufferCopyBehavior extends TooltipBehavior implements IInteractionItem { + + public static final MEPatternBufferCopyBehavior INSTANCE = new MEPatternBufferCopyBehavior((list -> { + list.add(Component.translatable("tooltip.gtlcore.copy_pattern_buffer_sneak_right_click")); + list.add(Component.translatable("tooltip.gtlcore.apply_pattern_buffer_right_click")); + })); + + public MEPatternBufferCopyBehavior(@NotNull Consumer> tooltips) { + super(tooltips); + } + + @Override + public InteractionResult onItemUseFirst(ItemStack itemStack, UseOnContext context) { + if (context.getLevel().getBlockEntity(context.getClickedPos()) instanceof MetaMachineBlockEntity machineBlock) { + if (machineBlock.getMetaMachine() instanceof MEPatternBufferPartMachine partMachine) { + if (context.getPlayer() instanceof ServerPlayer serverPlayer) { + var tags = getBehaviorsTag(itemStack); + if (!serverPlayer.isShiftKeyDown()) { + if (tags.isEmpty()) serverPlayer.displayClientMessage(Component.translatable("message.gtlcore.pattern_buffer_not_copied"), true); + else partMachine.copyFromTag(tags.getCompound("tag"), serverPlayer); + } else { + tags = partMachine.copyToTag(tags); + if (tags.isEmpty()) serverPlayer.displayClientMessage(Component.translatable("message.gtlcore.pattern_buffer_copy_failed"), true); + else serverPlayer.displayClientMessage(Component.translatable("message.gtlcore.pattern_buffer_copied"), true); + } + } + } + } + return InteractionResult.PASS; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/item/PatternModifier.java b/src/main/java/org/gtlcore/gtlcore/common/item/PatternModifier.java index 932200e2f..c6c911733 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/item/PatternModifier.java +++ b/src/main/java/org/gtlcore/gtlcore/common/item/PatternModifier.java @@ -1,7 +1,9 @@ package org.gtlcore.gtlcore.common.item; import org.gtlcore.gtlcore.api.item.tool.ae2.patternTool.Ae2BaseProcessingPattern; +import org.gtlcore.gtlcore.common.machine.multiblock.part.ae.MEPatternBufferPartMachine; +import com.gregtechceu.gtceu.api.blockentity.MetaMachineBlockEntity; import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.item.component.IItemUIFactory; import com.gregtechceu.gtceu.integration.ae2.gui.widget.AETextInputButtonWidget; @@ -28,13 +30,11 @@ import appeng.api.inventories.InternalInventory; import appeng.api.parts.IPart; -import appeng.blockentity.crafting.PatternProviderBlockEntity; import appeng.blockentity.networking.CableBusBlockEntity; -import appeng.parts.crafting.PatternProviderPart; +import appeng.helpers.patternprovider.PatternProviderLogicHost; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import lombok.Setter; -import java.util.HashMap; - @Setter public class PatternModifier implements IItemUIFactory { @@ -57,23 +57,23 @@ public ModularUI createUI(HeldItemUIFactory.HeldItemHolder heldItemHolder, Playe .addWidget(new AETextInputButtonWidget(120, 46 + 4, 72, 12) .setText(String.valueOf(Ae2PatternGeneratorScale)) .setOnConfirm(this::setAe2PatternGeneratorScale) - .setButtonTooltips(Component.literal("设置模板乘数"))) + .setButtonTooltips(Component.translatable("tooltip.gtlcore.pattern_multiplier_scale"))) .addWidget(new AETextInputButtonWidget(120, 60 + 4, 72, 12) .setText(String.valueOf(Ae2PatternGeneratorDivScale)) .setOnConfirm(this::setAe2PatternGeneratorDivScale) - .setButtonTooltips(Component.literal("设置模板除数"))) + .setButtonTooltips(Component.translatable("tooltip.gtlcore.pattern_divider_scale"))) .addWidget(new AETextInputButtonWidget(120, 74 + 4, 72, 12) .setText(String.valueOf(Ae2PatternGeneratorMaxItemStack)) .setOnConfirm(this::setAe2PatternGeneratorMaxItemStack) - .setButtonTooltips(Component.literal("设置乘法后最大物品/个"))) + .setButtonTooltips(Component.translatable("tooltip.gtlcore.pattern_max_item_stack"))) .addWidget(new AETextInputButtonWidget(120, 88 + 4, 72, 12) .setText(String.valueOf(Ae2PatternGeneratorMaxFluidStack)) .setOnConfirm(this::setAe2PatternGeneratorMaxFluidStack) - .setButtonTooltips(Component.literal("设置乘法后最大流体/桶"))) + .setButtonTooltips(Component.translatable("tooltip.gtlcore.pattern_max_fluid_stack"))) .addWidget(new AETextInputButtonWidget(120, 102 + 4, 72, 12) .setText(String.valueOf(Ae2PatternGeneratorAppliedNumber)) .setOnConfirm(this::setAe2PatternGeneratorAppliedNumber) - .setButtonTooltips(Component.literal("一次使用的应用次数(<=16)")))) + .setButtonTooltips(Component.translatable("tooltip.gtlcore.pattern_applied_number")))) .background(GuiTextures.BACKGROUND); } @@ -82,11 +82,11 @@ private void setAe2PatternGeneratorAppliedNumber(String s) { } private void setAe2PatternGeneratorMaxFluidStack(String s) { - Ae2PatternGeneratorMaxFluidStack = Integer.parseInt(s); + Ae2PatternGeneratorMaxFluidStack = Long.parseLong(s); } private void setAe2PatternGeneratorMaxItemStack(String s) { - Ae2PatternGeneratorMaxItemStack = Integer.parseInt(s); + Ae2PatternGeneratorMaxItemStack = Long.parseLong(s); } private void setAe2PatternGeneratorDivScale(String s) { @@ -123,38 +123,43 @@ public InteractionResult useOn(UseOnContext context) { // 计算具体点击位置 Vec3 hitInBlock = new Vec3(hitVec.x - (double) pos.getX(), hitVec.y - (double) pos.getY(), hitVec.z - (double) pos.getZ()); IPart part = cable.getCableBus().selectPartLocal(hitInBlock).part; - internalInventory = (part instanceof PatternProviderPart providerPart) ? + internalInventory = (part instanceof PatternProviderLogicHost providerPart) ? providerPart.getLogic().getPatternInv() : null; + + } else if (tile instanceof PatternProviderLogicHost providerBlock) { + internalInventory = providerBlock.getLogic().getPatternInv(); + } else if (tile instanceof MetaMachineBlockEntity mmbe && mmbe.getMetaMachine() instanceof MEPatternBufferPartMachine me) { + internalInventory = me.getTerminalPatternInventory(); } else { - internalInventory = (tile instanceof PatternProviderBlockEntity providerBlock) ? - providerBlock.getLogic().getPatternInv() : null; + internalInventory = null; } if (internalInventory == null) { - serverPlayer.displayClientMessage(Component.literal("只能对着样板供应器使用") + serverPlayer.displayClientMessage(Component.translatable("message.gtlcore.pattern_provider_only") .withStyle(), true); return InteractionResult.FAIL; } for (int i = 0; i < Ae2PatternGeneratorAppliedNumber; ++i) { - HashMap newItemStackHashMap = new HashMap<>(); + var newItemStackHashMap = new Int2ObjectOpenHashMap(); for (int slot = 0; slot < internalInventory.size(); slot++) { ItemStack itemStack = internalInventory.getStackInSlot(slot); if (!itemStack.isEmpty()) { ItemStack patternItemStack = getNewPatternItemStack(serverPlayer, itemStack); newItemStackHashMap.put(slot, patternItemStack); } - } - newItemStackHashMap.forEach((slot, itemStack) -> { + newItemStackHashMap.int2ObjectEntrySet().fastForEach((entry) -> { + int slot = entry.getIntKey(); + var itemStack = entry.getValue(); internalInventory.extractItem(slot, 1, false); internalInventory.insertItem(slot, itemStack, false); }); } - serverPlayer.displayClientMessage(Component.literal("已更新内部的样板,应用了%s次".formatted(Ae2PatternGeneratorAppliedNumber)), true); + serverPlayer.displayClientMessage(Component.translatable("message.gtlcore.pattern_updated", Ae2PatternGeneratorAppliedNumber), true); } if (!serverPlayer.isShiftKeyDown()) { - serverPlayer.displayClientMessage(Component.literal("右键空气打开GUI"), true); + serverPlayer.displayClientMessage(Component.translatable("message.gtlcore.right_click_air_gui"), true); } } return InteractionResult.SUCCESS; diff --git a/src/main/java/org/gtlcore/gtlcore/common/item/PatternTestBehavior.java b/src/main/java/org/gtlcore/gtlcore/common/item/PatternTestBehavior.java index e86babe9a..edce01a3f 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/item/PatternTestBehavior.java +++ b/src/main/java/org/gtlcore/gtlcore/common/item/PatternTestBehavior.java @@ -71,15 +71,15 @@ public ModularUI createUI(HeldItemUIFactory.HeldItemHolder heldItemHolder, Playe .addWidget(new AETextInputButtonWidget(82, 6, 72, 12) .setText(ConflictAnalysisType) .setOnConfirm(this::setConflictAnalysisType) - .setButtonTooltips(Component.literal("设置配方类型"))) + .setButtonTooltips(Component.translatable("tooltip.gtlcore.set_recipe_type"))) .addWidget(new AETextInputButtonWidget(82, 20, 72, 12) .setText(String.valueOf(ConflictAnalysisCircuit)) .setOnConfirm(s -> setConflictAnalysisCircuit(Integer.parseInt(s))) - .setButtonTooltips(Component.literal("设置编程电路"))) + .setButtonTooltips(Component.translatable("tooltip.gtlcore.set_programmed_circuit"))) .addWidget(new ButtonWidget(6, 24, 64, 20, new GuiTextureGroup(GuiTextures.BUTTON, new TextTexture("开始分析")), clickData -> useAnalysisRecipesBaby(heldItemHolder)) - .setHoverTooltips(Component.literal("当前配方类型:") + .setHoverTooltips(Component.translatable("tooltip.gtlcore.current_recipe_type") .append(Component.translatable("gtceu." + ConflictAnalysisType)).append(" 电路:" + ConflictAnalysisCircuit))); var containerPatternGenerator = new WidgetGroup(8, 58, 160, 50) @@ -88,38 +88,38 @@ public ModularUI createUI(HeldItemUIFactory.HeldItemHolder heldItemHolder, Playe .addWidget(new ButtonWidget(6, 24, 64, 20, new GuiTextureGroup(GuiTextures.BUTTON, new TextTexture("获取样板")), clickData -> useAe2PatternGenerator(heldItemHolder)) - .setHoverTooltips(Component.literal("当前配方类型:") + .setHoverTooltips(Component.translatable("tooltip.gtlcore.current_recipe_type") .append(Component.translatable("gtceu." + Ae2PatternGeneratorType)) .append(" 电路:" + Ae2PatternGeneratorCircuit) .append(" 尺寸:" + Ae2PatternGeneratorScale))) .addWidget(new AETextInputButtonWidget(82, 6, 72, 12) .setText(Ae2PatternGeneratorType) .setOnConfirm(this::setAe2PatternGeneratorType) - .setButtonTooltips(Component.literal("设置配方类型"))) + .setButtonTooltips(Component.translatable("tooltip.gtlcore.set_recipe_type"))) .addWidget(new AETextInputButtonWidget(82, 20, 72, 12) .setText(String.valueOf(Ae2PatternGeneratorCircuit)) .setOnConfirm(s -> setAe2PatternGeneratorCircuit(Integer.parseInt(s))) - .setButtonTooltips(Component.literal("设置编程电路"))) + .setButtonTooltips(Component.translatable("tooltip.gtlcore.set_programmed_circuit"))) .addWidget(new AETextInputButtonWidget(82, 34, 72, 12) .setText(String.valueOf(Ae2PatternGeneratorScale)) .setOnConfirm(this::setAe2PatternGeneratorScale) - .setButtonTooltips(Component.literal("设置模板倍数"))) + .setButtonTooltips(Component.translatable("tooltip.gtlcore.set_pattern_multiplier"))) .addWidget(new AETextInputButtonWidget(82, 48, 72, 12) .setText(String.valueOf(Ae2PatternGeneratorInputsWhiteKey)) .setOnConfirm(this::setAe2PatternGeneratorInputsWhiteKey) - .setButtonTooltips(Component.literal("输入白名单ID关键词,以空格分割 匹配1个即通过"))) + .setButtonTooltips(Component.translatable("tooltip.gtlcore.input_whitelist_keywords"))) .addWidget(new AETextInputButtonWidget(82, 62, 72, 12) .setText(String.valueOf(Ae2PatternGeneratorInputsBlackKey)) .setOnConfirm(this::setAe2PatternGeneratorInputsBlackKey) - .setButtonTooltips(Component.literal("输入黑名单ID关键词,以空格分割 匹配1个即否决"))) + .setButtonTooltips(Component.translatable("tooltip.gtlcore.input_blacklist_keywords"))) .addWidget(new AETextInputButtonWidget(82, 76, 72, 12) .setText(String.valueOf(Ae2PatternGeneratorOutputsWhiteKey)) .setOnConfirm(this::setAe2PatternGeneratorOutputsWhiteKey) - .setButtonTooltips(Component.literal("输出白名单ID关键词,以空格分割 匹配1个即通过"))) + .setButtonTooltips(Component.translatable("tooltip.gtlcore.output_whitelist_keywords"))) .addWidget(new AETextInputButtonWidget(82, 90, 72, 12) .setText(String.valueOf(Ae2PatternGeneratorOutputsBlackKey)) .setOnConfirm(this::setAe2PatternGeneratorOutputsBlackKey) - .setButtonTooltips(Component.literal("输出黑名单ID关键词,以空格分割 匹配1个即否决"))); + .setButtonTooltips(Component.translatable("tooltip.gtlcore.output_blacklist_keywords"))); return new ModularUI(176, 124, heldItemHolder, player) .widget(containerPatternAnalysis) @@ -133,17 +133,6 @@ private void setAe2PatternGeneratorScale(String s) { public void useAe2PatternGenerator(HeldItemUIFactory.HeldItemHolder playerInventoryHolder) { if (playerInventoryHolder.getPlayer() instanceof ServerPlayer serverPlayer) { - boolean allowUsing = switch (playerInventoryHolder.getPlayer().getName().getString()) { - case "xinxinsuried", "Dev" -> true; - default -> false; - /* - * 仅测试使用 - */ - }; - - if (!allowUsing) { - return; - } /* * 获取样板 入口 */ @@ -164,8 +153,8 @@ public void useAe2PatternGenerator(HeldItemUIFactory.HeldItemHolder playerInvent Ae2GtmProcessingPattern ae2GtmProcessingPattern = Ae2GtmProcessingPattern.of(recipe, serverPlayer); ae2GtmProcessingPattern.setScale(Ae2PatternGeneratorScale, false); ae2GtmProcessingPattern.setDefaultFilter(); - ae2GtmProcessingPattern.setLore(Component.literal("机器:").append(Component.translatable("gtceu." + Ae2PatternGeneratorType).append(Component.literal(" 电路%s".formatted(Ae2PatternGeneratorCircuit))))); - ae2GtmProcessingPattern.setLore(Component.literal("电压:%s(%s)".formatted(recipeTier, inputEUt))); + ae2GtmProcessingPattern.setLore(Component.translatable("gui.gtlcore.machine_colon").append(Component.translatable("gtceu." + Ae2PatternGeneratorType).append(Component.translatable("gui.gtlcore.circuit_format", Ae2PatternGeneratorCircuit)))); + ae2GtmProcessingPattern.setLore(Component.translatable("gui.gtlcore.voltage_colon", recipeTier, inputEUt)); ItemStack patternItemStack = ae2GtmProcessingPattern.getPatternItemStack(); serverPlayer.kjs$give(patternItemStack); } @@ -212,7 +201,7 @@ public InteractionResultHolder use(Item item, Level level, Player pla @Override public InteractionResult useOn(UseOnContext context) { if (context.getPlayer() instanceof ServerPlayer serverPlayer) { - serverPlayer.displayClientMessage(Component.literal("右键空气打开GUI"), true); + serverPlayer.displayClientMessage(Component.translatable("message.gtlcore.right_click_air_gui"), true); } return InteractionResult.SUCCESS; } diff --git a/src/main/java/org/gtlcore/gtlcore/common/item/StructureDetectBehavior.java b/src/main/java/org/gtlcore/gtlcore/common/item/StructureDetectBehavior.java index 2eca9ad2a..fbc71ddda 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/item/StructureDetectBehavior.java +++ b/src/main/java/org/gtlcore/gtlcore/common/item/StructureDetectBehavior.java @@ -6,27 +6,26 @@ import com.gregtechceu.gtceu.api.item.tool.behavior.IToolBehavior; import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; -import com.gregtechceu.gtceu.api.pattern.BlockPattern; -import com.gregtechceu.gtceu.api.pattern.MultiblockState; -import com.gregtechceu.gtceu.api.pattern.error.PatternError; -import com.gregtechceu.gtceu.api.pattern.error.PatternStringError; -import com.gregtechceu.gtceu.api.pattern.error.SinglePredicateError; +import com.gregtechceu.gtceu.api.pattern.*; +import com.gregtechceu.gtceu.api.pattern.error.*; import com.gregtechceu.gtceu.common.item.TooltipBehavior; import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.Level; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; @@ -53,73 +52,61 @@ public StructureDetectBehavior(@NotNull Consumer> tooltips) { @Override public InteractionResult onItemUseFirst(ItemStack stack, UseOnContext context) { Player player = context.getPlayer(); + var tag = stack.getTag(); + if (tag == null) { + tag = new CompoundTag(); + tag.putBoolean("isFlipped", false); + stack.setTag(tag); + } if (player != null) { Level level = context.getLevel(); if (level.isClientSide) return InteractionResult.PASS; BlockPos blockPos = context.getClickedPos(); if (MetaMachine.getMachine(level, blockPos) instanceof IMultiController controller) { if (controller.isFormed()) { - player.sendSystemMessage(Component.literal("已成型").withStyle(ChatFormatting.GREEN)); + player.sendSystemMessage(Component.translatable("message.gtlcore.structure_formed").withStyle(ChatFormatting.GREEN)); } else { - if (!controller.self().allowFlip()) { - MultiblockState multiblockState = controller.getMultiblockState(); - PatternError error = multiblockState.error; - if (error != null) { - showError(player, error, false); - } - } else { - ((ServerLevel) level).getServer().execute(() -> { - BlockPattern pattern = controller.getPattern(); - LOCK.lock(); - if (LOCK.tryLock()) { - var result = check(controller, pattern); - for (int i = 0; i < result.size(); i++) { - showError(player, result.get(i), (i == 1)); - } - LOCK.unlock(); - } else { - LOCK.unlock(); - } - }); - } + boolean isFlipped = !tag.isEmpty() && tag.getBoolean("isFlipped"); + ((ServerLevel) level).getServer().execute(() -> { + var pattern = controller.getPattern(); + LOCK.lock(); + if (LOCK.tryLock()) { + var result = check(controller, pattern, isFlipped); + for (var patternError : result) showError(player, patternError, isFlipped); + LOCK.unlock(); + } else LOCK.unlock(); + }); return InteractionResult.SUCCESS; } + } else if (player instanceof ServerPlayer serverPlayer) { + tag.putBoolean("isFlipped", !tag.getBoolean("isFlipped")); + serverPlayer.displayClientMessage(Component.translatable(!tag.getBoolean("isFlipped") ? "message.gtlcore.detection_mode_normal" : "message.gtlcore.detection_mode_mirrored"), true); } } return InteractionResult.PASS; } - private List check(IMultiController controller, BlockPattern pattern) { - List errors = new ArrayList<>(); + private List check(IMultiController controller, BlockPattern pattern, boolean isFlipped) { + var errors = new ObjectArrayList(); if (controller == null) { errors.add(new PatternStringError("no controller found")); return errors; } - BlockPos centerPos = controller.self().getPos(); - Direction frontFacing = controller.self().getFrontFacing(); - Direction[] facings = controller.hasFrontFacing() ? new Direction[] { frontFacing } : + var centerPos = controller.self().getPos(); + var frontFacing = controller.self().getFrontFacing(); + var facings = controller.hasFrontFacing() ? new Direction[] { frontFacing } : new Direction[] { Direction.SOUTH, Direction.NORTH, Direction.EAST, Direction.WEST }; - Direction upwardsFacing = controller.self().getUpwardsFacing(); - boolean allowsFlip = controller.self().allowFlip(); - MultiblockState worldState = new MultiblockState(controller.self().getLevel(), controller.self().getPos()); - for (Direction direction : facings) { - pattern.checkPatternAt(worldState, centerPos, direction, upwardsFacing, false, false); - if (worldState.hasError()) { - errors.add(worldState.error); - } - if (allowsFlip) { - worldState = new MultiblockState(worldState.getWorld(), worldState.getPos()); - pattern.checkPatternAt(worldState, centerPos, direction, upwardsFacing, true, false); - if (worldState.hasError()) { - errors.add(worldState.error); - } - } + var upwardsFacing = controller.self().getUpwardsFacing(); + var worldState = new MultiblockState(controller.self().getLevel(), controller.self().getPos()); + for (var direction : facings) { + pattern.checkPatternAt(worldState, centerPos, direction, upwardsFacing, isFlipped, false); + if (worldState.hasError()) errors.add(worldState.error); } return errors; } private void showError(Player player, PatternError error, boolean flip) { - List show = new ArrayList<>(); + var show = new ObjectArrayList(); if (error instanceof PatternStringError pe) { player.sendSystemMessage(pe.getErrorInfo()); return; @@ -128,15 +115,14 @@ private void showError(Player player, PatternError error, boolean flip) { var posComponent = Component.translatable("item.gtlcore.structure_detect.error.2", pos.getX(), pos.getY(), pos.getZ(), flip ? Component.translatable("item.gtlcore.structure_detect.error.3").withStyle(ChatFormatting.GREEN) : Component.translatable("item.gtlcore.structure_detect.error.4").withStyle(ChatFormatting.YELLOW)); + var candidates = error.getCandidates(); if (error instanceof SinglePredicateError) { - List> candidates = error.getCandidates(); var root = candidates.get(0).get(0).getHoverName(); show.add(Component.translatable("item.gtlcore.structure_detect.error.1", posComponent)); show.add(Component.literal(" - ").append(root).append(error.getErrorInfo())); } else { show.add(Component.translatable("item.gtlcore.structure_detect.error.0", posComponent)); - List> candidates = error.getCandidates(); - for (List candidate : candidates) { + for (var candidate : candidates) { if (!candidate.isEmpty()) { show.add(Component.literal(" - ").append(candidate.get(0).getDisplayName())); } diff --git a/src/main/java/org/gtlcore/gtlcore/common/item/UltimateTerminalBehavior.java b/src/main/java/org/gtlcore/gtlcore/common/item/UltimateTerminalBehavior.java new file mode 100644 index 000000000..ef92f289c --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/item/UltimateTerminalBehavior.java @@ -0,0 +1,264 @@ +package org.gtlcore.gtlcore.common.item; + +import org.gtlcore.gtlcore.api.gui.BlockMapSelectorWidget; +import org.gtlcore.gtlcore.api.gui.ExtendLabelWidget; + +import com.gregtechceu.gtceu.api.block.MetaMachineBlock; +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.api.item.component.IItemUIFactory; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.MultiblockMachineDefinition; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.*; +import com.gregtechceu.gtceu.api.machine.multiblock.WorkableMultiblockMachine; +import com.gregtechceu.gtceu.api.machine.multiblock.part.*; +import com.gregtechceu.gtceu.api.registry.GTRegistries; + +import com.lowdragmc.lowdraglib.gui.editor.ColorPattern; +import com.lowdragmc.lowdraglib.gui.factory.HeldItemUIFactory; +import com.lowdragmc.lowdraglib.gui.modular.ModularUI; +import com.lowdragmc.lowdraglib.gui.texture.GuiTextureGroup; +import com.lowdragmc.lowdraglib.gui.texture.TextTexture; +import com.lowdragmc.lowdraglib.gui.widget.*; +import com.lowdragmc.lowdraglib.utils.BlockInfo; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.block.*; + +import com.hepdd.gtmthings.api.gui.widget.TerminalInputWidget; +import it.unimi.dsi.fastutil.objects.*; +import lombok.Getter; +import lombok.Setter; + +import java.util.*; + +import static net.minecraft.network.chat.Component.translatable; +import static org.gtlcore.gtlcore.api.gui.BlockMapSelectorWidget.*; +import static org.gtlcore.gtlcore.api.pattern.AdvancedBlockPattern.getAdvancedBlockPattern; +import static org.gtlcore.gtlcore.common.block.BlockMap.*; + +/** + * 代码参考自gtmthings + * @line ... + */ + +public class UltimateTerminalBehavior implements IItemUIFactory { + + public UltimateTerminalBehavior() {} + + @Override + public InteractionResult useOn(UseOnContext context) { + if (context.getPlayer() != null && context.getPlayer().isShiftKeyDown()) { + var level = context.getLevel(); + var blockPos = context.getClickedPos(); + var metaMachine = MetaMachine.getMachine(level, blockPos); + if (context.getPlayer() != null && !level.isClientSide() && + metaMachine instanceof IMultiController controller) { + var autoBuildSetting = getAutoBuildSetting(context.getPlayer().getMainHandItem()); + + if (!controller.isFormed()) { + getAdvancedBlockPattern(controller.getPattern()).autoBuild(context.getPlayer(), controller.getMultiblockState(), autoBuildSetting); + } else if (metaMachine instanceof WorkableMultiblockMachine machine && autoBuildSetting.isReplaceMode()) { + getAdvancedBlockPattern(controller.getPattern()).autoBuild(context.getPlayer(), controller.getMultiblockState(), autoBuildSetting); + machine.onPartUnload(); + } + } + return InteractionResult.sidedSuccess(level.isClientSide); + } + return InteractionResult.PASS; + } + + private AutoBuildSetting getAutoBuildSetting(ItemStack mainHandItem) { + var autoBuildSetting = new AutoBuildSetting(); + var tag = mainHandItem.getOrCreateTag(); + if (!tag.isEmpty()) { + autoBuildSetting.setTier(tag.getInt("Tier")); + autoBuildSetting.setRepeatCount(tag.getInt("RepeatCount")); + autoBuildSetting.setNoHatchMode(tag.getBoolean("NoHatchMode")); + autoBuildSetting.setReplaceMode(tag.getBoolean("ReplaceMode")); + autoBuildSetting.setFlipped(tag.getBoolean("IsFlipped")); + String block = tag.getString("blocks"); + if (!block.isEmpty()) { + autoBuildSetting.tierBlock = tierBlockMap.get(block).get(); + autoBuildSetting.blocks = new ObjectOpenHashSet<>(autoBuildSetting.tierBlock); + } + } + return autoBuildSetting; + } + + @Override + public ModularUI createUI(HeldItemUIFactory.HeldItemHolder heldItemHolder, Player player) { + return (new ModularUI(166, 166, heldItemHolder, player)).widget(this.createWidget(player)); + } + + private Widget createWidget(Player player) { + final var handItem = player.getMainHandItem(); + WidgetGroup group = new WidgetGroup(0, 0, 200, 136); + var contain = new DraggableScrollableWidgetGroup(4, 4, 192, 128) + .setBackground(GuiTextures.DISPLAY).setYScrollBarWidth(2) + .setYBarStyle(null, ColorPattern.T_WHITE.rectTexture().setRadius(1.0F)); + contain.addWidget(new ExtendLabelWidget(65, 8, translatable("gui.gtlcore.ultimate_terminal_settings"))) + .addWidget(new ExtendLabelWidget(14, 26, translatable("gui.gtlcore.tier_blocks"))) + .addWidget(new ExtendLabelWidget(14, 46, translatable("gui.gtlcore.repeat_count"))) + .addWidget(new ExtendLabelWidget(14, 66, translatable("gui.gtlcore.no_hatch_mode")) + .setHoverTooltips(translatable("tooltip.gtlcore.no_hatch_mode"))) + .addWidget(new ExtendLabelWidget(14, 86, translatable("gui.gtlcore.replace_mode")) + .setHoverTooltips(translatable("tooltip.gtlcore.replace_mode"))) + .addWidget(new ExtendLabelWidget(14, 106, translatable("gui.gtlcore.mirror_mode")) + .setHoverTooltips(translatable("tooltip.gtlcore.mirror_mode"))) + .addWidget(new TerminalInputWidget(140, 45, 36, 12, + () -> getRepeatCount(handItem), (v) -> setRepeatCount(v, handItem)).setMax(648).setMin(0)) + .addWidget(new SwitchWidget(140, 63, 36, 14, + (c, b) -> setIsBuildHatches(!getIsBuildHatches(handItem), handItem)).setPressed(getIsBuildHatches(handItem)) + .setTexture(new GuiTextureGroup(GuiTextures.BUTTON, new TextTexture("OFF")), + new GuiTextureGroup(GuiTextures.BUTTON, new TextTexture("ON")))) + .addWidget(new SwitchWidget(140, 83, 36, 14, + (c, b) -> setReplaceMode(!getReplaceMode(handItem), handItem)).setPressed(getReplaceMode(handItem)) + .setTexture(new GuiTextureGroup(GuiTextures.BUTTON, new TextTexture("OFF")), + new GuiTextureGroup(GuiTextures.BUTTON, new TextTexture("ON")))) + .addWidget(new SwitchWidget(140, 103, 36, 14, + (c, b) -> setIsFlip(!getIsFlip(handItem), handItem)).setPressed(getIsFlip(handItem)) + .setTexture(new GuiTextureGroup(GuiTextures.BUTTON, new TextTexture("OFF")), + new GuiTextureGroup(GuiTextures.BUTTON, new TextTexture("ON")))); + var blockLabel = new ExtendLabelWidget(47, 26, getBlockComponent(handItem)); + var blockMap = new BlockMapSelectorWidget(group.getSizeHeight() + 4, contain.getSizeWidth(), (s, i) -> { + if (s != null && i != null) { + CompoundTag tag = handItem.getOrCreateTag(); + tag.putString("blocks", s); + tag.putInt("Tier", i); + handItem.setTag(tag); + blockLabel.setComponent(Component.literal("(").append(getBlock(s)) + .append(Component.literal(" : ")) + .append(tierBlockMap.get(s).get()[i].getName()) + .append(Component.literal(")"))); + } + }); + blockMap.setInit(handItem); + var open = new SwitchWidget(14, 26, 30, 16, (c, f) -> blockMap.showType(f)) + .setHoverTooltips(Component.translatable("gui.gtlcore.open.config.map")); + contain.addWidget(open).addWidget(blockLabel); + group.addWidget(contain).addWidget(blockMap).setBackground(GuiTextures.BACKGROUND_INVERSE); + return group; + } + + private static Component getBlockComponent(ItemStack itemStack) { + var tag = itemStack.getOrCreateTag(); + if (!tag.isEmpty()) { + var block = tag.getString("blocks"); + if (!block.isEmpty()) { + int tier = tag.getInt("Tier"); + return Component.literal("(").append(getBlock(block)) + .append(Component.literal(" : ")) + .append(tierBlockMap.get(block).get()[tier].getName()) + .append(Component.literal(")")); + } + } + return Component.literal(""); + } + + private static int getRepeatCount(ItemStack itemStack) { + CompoundTag tag = itemStack.getOrCreateTag(); + return !tag.isEmpty() ? tag.getInt("RepeatCount") : 0; + } + + private static void setRepeatCount(int repeatCount, ItemStack itemStack) { + CompoundTag tag = itemStack.getOrCreateTag(); + tag.putInt("RepeatCount", repeatCount); + itemStack.setTag(tag); + } + + private static boolean getIsBuildHatches(ItemStack itemStack) { + CompoundTag tag = itemStack.getOrCreateTag(); + return tag.isEmpty() || tag.getBoolean("NoHatchMode"); + } + + private static void setIsBuildHatches(boolean isBuildHatches, ItemStack itemStack) { + CompoundTag tag = itemStack.getOrCreateTag(); + tag.putBoolean("NoHatchMode", isBuildHatches); + itemStack.setTag(tag); + } + + private static boolean getReplaceMode(ItemStack itemStack) { + CompoundTag tag = itemStack.getOrCreateTag(); + return !tag.isEmpty() && tag.getBoolean("ReplaceMode"); + } + + private static void setReplaceMode(boolean isReplace, ItemStack itemStack) { + CompoundTag tag = itemStack.getOrCreateTag(); + tag.putBoolean("ReplaceMode", isReplace); + itemStack.setTag(tag); + } + + private static boolean getIsFlip(ItemStack itemStack) { + CompoundTag tag = itemStack.getOrCreateTag(); + return !tag.isEmpty() && tag.getBoolean("IsFlipped"); + } + + private static void setIsFlip(boolean isFlip, ItemStack itemStack) { + CompoundTag tag = itemStack.getOrCreateTag(); + tag.putBoolean("IsFlipped", isFlip); + itemStack.setTag(tag); + } + + @Setter + @Getter + public static class AutoBuildSetting { + + Block[] tierBlock; + Set blocks = Collections.emptySet(); + private int tier, repeatCount; + private boolean noHatchMode, replaceMode, isFlipped; + + public AutoBuildSetting() { + this.tier = 0; + this.repeatCount = 0; + this.noHatchMode = true; + this.replaceMode = false; + this.isFlipped = false; + } + + public List apply(BlockInfo[] blockInfos) { + List candidates = new ObjectArrayList<>(); + if (blockInfos != null) { + for (var info : blockInfos) { + if (this.tierBlock != null && this.tier >= 0 && blockInfos.length > 1 && + this.blocks.contains(info.getBlockState().getBlock())) { + candidates.add(tierBlock[Math.min(this.tier, blockInfos.length - 1)].asItem().getDefaultInstance()); + return candidates; + } + if (info.getBlockState().getBlock() != Blocks.AIR) candidates.add(info.getItemStackForm()); + } + } + return candidates; + } + + public boolean isPlaceHatch(BlockInfo[] blockInfos) { + if (!this.noHatchMode) return true; + if (blockInfos != null && blockInfos.length > 0) { + var blockInfo = blockInfos[0]; + return !(blockInfo.getBlockState().getBlock() instanceof MetaMachineBlock machineBlock) || + !Hatch.Set.contains(machineBlock); + } + return true; + } + + private static final class Hatch { + + public static final Set Set = new ObjectOpenHashSet<>(); + + static { + GTRegistries.MACHINES.forEach(d -> { + if (d.getRecipeTypes() != null || d instanceof MultiblockMachineDefinition) return; + var block = d.getBlock(); + if (d.createMetaMachine((IMachineBlockEntity) d.getBlockEntityType().create(BlockPos.ZERO, block.defaultBlockState())) instanceof MultiblockPartMachine) Set.add(block); + }); + } + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/AdvancedInfiniteDrillMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/AdvancedInfiniteDrillMachine.java index a6b033325..8889b4e1c 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/AdvancedInfiniteDrillMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/AdvancedInfiniteDrillMachine.java @@ -3,7 +3,7 @@ import org.gtlcore.gtlcore.common.data.GTLMaterials; import org.gtlcore.gtlcore.common.machine.multiblock.part.TemperatureSensorPartMachine; import org.gtlcore.gtlcore.common.machine.trait.AdvancedInfiniteDrillLogic; -import org.gtlcore.gtlcore.utils.MachineIO; +import org.gtlcore.gtlcore.utils.MachineUtil; import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.block.ICoilType; @@ -79,7 +79,7 @@ public class AdvancedInfiniteDrillMachine extends StorageMachine { public static final double RUNNING_HEAT = 2000; - public static int maxHeat = -1; + public int maxHeat = -1; @Persisted @Getter @@ -117,13 +117,13 @@ protected void heatUpdate() { } if (getRecipeLogic().isWorking() || process > 0) { - if (MachineIO.inputFluid(this, DISTILLED_WATER)) { + if (MachineUtil.inputFluid(this, DISTILLED_WATER)) { heat--; - } else if (MachineIO.inputFluid(this, OXYGEN)) { + } else if (MachineUtil.inputFluid(this, OXYGEN)) { heat -= 2; - } else if (MachineIO.inputFluid(this, HELIUM)) { + } else if (MachineUtil.inputFluid(this, HELIUM)) { heat -= 4; - } else if (MachineIO.inputFluid(this, NITROGEN)) { + } else if (MachineUtil.inputFluid(this, NITROGEN)) { heat -= 8; } } @@ -241,7 +241,7 @@ public void addDisplayText(List tooltips) { tooltips.add(Component.translatable("gtceu.machine.advanced_infinite_driller.heat", maxHeat, RUNNING_HEAT)); tooltips.add(Component.translatable("gtceu.machine.advanced_infinite_driller.current_heat", currentHeat)); tooltips.add(Component.translatable("gtceu.machine.advanced_infinite_driller.fast", - ComponentPanelWidget.withButton(Component.literal(fast ? "[开启]" : "[关闭]"), + ComponentPanelWidget.withButton(Component.translatable(fast ? "gui.gtlcore.status_on" : "gui.gtlcore.status_off"), "fast_mode"))); tooltips.add(Component.translatable("gtceu.machine.advanced_infinite_driller.process", FormattingUtil.formatNumber2Places(process / 300F * 100)) @@ -291,7 +291,7 @@ public int getDrillHeadRate() { } protected boolean inputBlast() { - return MachineIO.inputFluid(this, GTMaterials.Blaze.getFluid(getFluidConsume())); + return MachineUtil.inputFluid(this, GTMaterials.Blaze.getFluid(getFluidConsume())); } protected long getFluidConsume() { diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/BedrockDrillingRig.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/BedrockDrillingRig.java new file mode 100644 index 000000000..e26da2148 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/BedrockDrillingRig.java @@ -0,0 +1,97 @@ +package org.gtlcore.gtlcore.common.machine.multiblock.electric; + +import org.gtlcore.gtlcore.common.util.BlockStateWatcher; + +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.feature.IMachineLife; +import com.gregtechceu.gtceu.api.machine.multiblock.WorkableElectricMultiblockMachine; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; + +import org.jetbrains.annotations.Nullable; + +import java.util.concurrent.ThreadLocalRandom; + +public class BedrockDrillingRig extends WorkableElectricMultiblockMachine implements IMachineLife { + + protected BlockStateWatcher.WatcherHandle watcherHandle; + protected BlockPos targetPos; + protected boolean hasBedrockAtTarget; + + public BedrockDrillingRig(IMachineBlockEntity holder, Object... args) { + super(holder, args); + targetPos = getPos().offset(0, -9, 0); + } + + @Override + public void onUnload() { + super.onUnload(); + if (!isRemote()) unregisterBlockWatcher(); + } + + @Override + public void onStructureFormed() { + super.onStructureFormed(); + if (getLevel() instanceof ServerLevel level) { + registerBlockWatcher(level); + } + } + + @Override + public void onStructureInvalid() { + super.onStructureInvalid(); + if (!isRemote()) unregisterBlockWatcher(); + } + + @Override + public void onMachinePlaced(@Nullable LivingEntity player, ItemStack stack) { + targetPos = getPos().offset(0, -9, 0); + } + + @Override + public void onMachineRemoved() { + if (!isRemote()) unregisterBlockWatcher(); + } + + protected void registerBlockWatcher(ServerLevel level) { + unregisterBlockWatcher(); + watcherHandle = BlockStateWatcher.addWatcher(getLevel(), targetPos, this::onBlockStateChanged); + + BlockState currentState = level.getBlockState(targetPos); + hasBedrockAtTarget = currentState.getBlock().kjs$getId().equals("minecraft:bedrock"); + if (hasBedrockAtTarget) this.recipeLogic.updateTickSubscription(); + } + + protected void unregisterBlockWatcher() { + if (watcherHandle != null) { + watcherHandle.remove(); + watcherHandle = null; + } + } + + protected void onBlockStateChanged(BlockState newState) { + hasBedrockAtTarget = newState != null && newState.getBlock().kjs$getId().equals("minecraft:bedrock"); + if (hasBedrockAtTarget) this.recipeLogic.updateTickSubscription(); + } + + @Override + public boolean beforeWorking(@Nullable GTRecipe recipe) { + return hasBedrockAtTarget; + } + + @Override + public void afterWorking() { + super.afterWorking(); + Level level = getLevel(); + if (level != null && ThreadLocalRandom.current().nextInt(10) == 0) { + level.setBlockAndUpdate(targetPos, Blocks.AIR.defaultBlockState()); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/FissionReactorMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/FissionReactorMachine.java index 2dc8e4af2..5747c8e77 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/FissionReactorMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/FissionReactorMachine.java @@ -153,7 +153,7 @@ public void doExplosion(BlockPos pos, float explosionPower) { } private boolean inputWater(long amount) { - boolean value = MachineIO.inputFluid(this, GTMaterials.DistilledWater.getFluid(amount * 800)); + boolean value = MachineUtil.inputFluid(this, GTMaterials.DistilledWater.getFluid(amount * 800)); double steamMultiplier = heat > 373 ? 160 : 160 / Math.pow(1.4, 373 - heat); if (value) value = MachineIO.outputFluid(this, GTMaterials.Steam.getFluid((long) (amount * 800 * steamMultiplier))); @@ -161,7 +161,7 @@ private boolean inputWater(long amount) { } private boolean inputSodiumPotassium(long amount) { - boolean value = MachineIO.inputFluid(this, GTMaterials.SodiumPotassium.getFluid(amount * 20)); + boolean value = MachineUtil.inputFluid(this, GTMaterials.SodiumPotassium.getFluid(amount * 20)); if (heat > 825) { if (value) value = MachineIO.outputFluid(this, GTLMaterials.SupercriticalSodiumPotassium.getFluid(amount * 20)); diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/GreenhouseMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/GreenhouseMachine.java index 34322bcf6..2375299c6 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/GreenhouseMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/GreenhouseMachine.java @@ -1,5 +1,7 @@ package org.gtlcore.gtlcore.common.machine.multiblock.electric; +import org.gtlcore.gtlcore.api.recipe.RecipeResult; + import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.multiblock.WorkableElectricMultiblockMachine; import com.gregtechceu.gtceu.api.recipe.GTRecipe; @@ -32,7 +34,39 @@ private void getGreenhouseLight() { Level level = getLevel(); BlockPos pos = getPos(); SkyLight = 15; - BlockPos[] coordinates = new BlockPos[] { pos.offset(1, 2, 0), pos.offset(1, 2, 1), pos.offset(1, 2, -1), pos.offset(0, 2, 1), pos.offset(0, 2, -1), pos.offset(-1, 2, 0), pos.offset(-1, 2, 1), pos.offset(-1, 2, -1), pos.offset(2, 2, 0), pos.offset(2, 2, -1), pos.offset(2, 2, 1), pos.offset(3, 2, 0), pos.offset(3, 2, -1), pos.offset(3, 2, 1), pos.offset(-2, 2, 0), pos.offset(-2, 2, -1), pos.offset(-2, 2, 1), pos.offset(-3, 2, 0), pos.offset(-3, 2, -1), pos.offset(-3, 2, 1), pos.offset(-1, 2, 2), pos.offset(0, 2, 2), pos.offset(1, 2, 2), pos.offset(-1, 2, 3), pos.offset(0, 2, 3), pos.offset(1, 2, 3), pos.offset(-1, 2, -2), pos.offset(0, 2, -2), pos.offset(1, 2, -2), pos.offset(-1, 2, -3), pos.offset(0, 2, -3), pos.offset(1, 2, -3) }; + BlockPos[] coordinates = new BlockPos[] { + pos.offset(1, 2, 0), + pos.offset(1, 2, 1), + pos.offset(1, 2, -1), + pos.offset(0, 2, 1), + pos.offset(0, 2, -1), + pos.offset(-1, 2, 0), + pos.offset(-1, 2, 1), + pos.offset(-1, 2, -1), + pos.offset(2, 2, 0), + pos.offset(2, 2, -1), + pos.offset(2, 2, 1), + pos.offset(3, 2, 0), + pos.offset(3, 2, -1), + pos.offset(3, 2, 1), + pos.offset(-2, 2, 0), + pos.offset(-2, 2, -1), + pos.offset(-2, 2, 1), + pos.offset(-3, 2, 0), + pos.offset(-3, 2, -1), + pos.offset(-3, 2, 1), + pos.offset(-1, 2, 2), + pos.offset(0, 2, 2), + pos.offset(1, 2, 2), + pos.offset(-1, 2, 3), + pos.offset(0, 2, 3), + pos.offset(1, 2, 3), + pos.offset(-1, 2, -2), + pos.offset(0, 2, -2), + pos.offset(1, 2, -2), + pos.offset(-1, 2, -3), + pos.offset(0, 2, -3), + pos.offset(1, 2, -3) }; for (BlockPos i : coordinates) { if (level != null && level.getBlockState(i).getBlock() == GTBlocks.CASING_TEMPERED_GLASS.get()) { int l = level.getBrightness(LightLayer.SKY, i.offset(0, 1, 0)) - level.getSkyDarken(); @@ -48,6 +82,7 @@ public boolean beforeWorking(@Nullable GTRecipe recipe) { getGreenhouseLight(); if (SkyLight == 0) { getRecipeLogic().interruptRecipe(); + RecipeResult.of(this, RecipeResult.FAIL_NO_SKYLIGHT); return false; } return super.beforeWorking(recipe); @@ -59,9 +94,12 @@ public boolean onWorking() { if (getOffsetTimer() % 20 == 0) { getGreenhouseLight(); if (SkyLight == 0) { + RecipeResult.of(this, RecipeResult.FAIL_NO_SKYLIGHT); getRecipeLogic().setProgress(0); } if (SkyLight < 13) { + RecipeResult.of(this, + RecipeResult.fail(Component.translatable("gtceu.recipe.fail.no.enough.skylight"))); getRecipeLogic().setProgress(getRecipeLogic().getProgress() - 10); } } @@ -75,6 +113,6 @@ public void addDisplayText(@NotNull List textList) { if (getOffsetTimer() % 10 == 0) { getGreenhouseLight(); } - textList.add(Component.literal("当前光照:" + SkyLight)); + textList.add(Component.translatable("tooltip.gtlcore.current_light_level", SkyLight)); } } diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/HarmonyMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/HarmonyMachine.java index aa793a782..995db7fa2 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/HarmonyMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/HarmonyMachine.java @@ -6,9 +6,11 @@ import com.gregtechceu.gtceu.api.machine.ConditionalSubscriptionHandler; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.feature.IMachineLife; import com.gregtechceu.gtceu.api.recipe.GTRecipe; import com.gregtechceu.gtceu.api.recipe.logic.OCParams; import com.gregtechceu.gtceu.api.recipe.logic.OCResult; +import com.gregtechceu.gtceu.common.data.GTItems; import com.gregtechceu.gtceu.common.data.GTMaterials; import com.gregtechceu.gtceu.utils.FormattingUtil; @@ -16,9 +18,15 @@ import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; import net.minecraft.MethodsReturnNonnullByDefault; +import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; import com.hepdd.gtmthings.api.misc.WirelessEnergyManager; @@ -33,7 +41,7 @@ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -public class HarmonyMachine extends NoEnergyMultiblockMachine { +public class HarmonyMachine extends NoEnergyMultiblockMachine implements IMachineLife { public static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( HarmonyMachine.class, NoEnergyMultiblockMachine.MANAGED_FIELD_HOLDER); @@ -91,6 +99,13 @@ public void onStructureFormed() { StartupSubs.initialize(getLevel()); } + @Override + public void onMachinePlaced(@Nullable LivingEntity player, ItemStack stack) { + if (player != null) { + this.userid = player.getUUID(); + } + } + @Nullable public static GTRecipe recipeModifier(MetaMachine machine, @NotNull GTRecipe recipe, @NotNull OCParams params, @NotNull OCResult result) { @@ -114,6 +129,21 @@ public boolean shouldOpenUI(Player player, InteractionHand hand, BlockHitResult return true; } + @Override + public InteractionResult onUse(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + if (player.getItemInHand(hand).is(GTItems.TOOL_DATA_STICK.asItem())) { + this.userid = player.getUUID(); + if (isRemote()) { + player.sendSystemMessage( + Component.translatable( + "gtmthings.machine.wireless_energy_hatch.tooltip.bind", + TeamUtil.GetName(player))); + } + return InteractionResult.sidedSuccess(isRemote()); + } + return super.onUse(state, world, pos, player, hand, hit); + } + @Override public void addDisplayText(List textList) { super.addDisplayText(textList); @@ -124,9 +154,9 @@ public void addDisplayText(List textList) { textList.add(Component.translatable("gtmthings.machine.wireless_energy_monitor.tooltip.1", FormattingUtil.formatNumbers(WirelessEnergyManager.getUserEU(userid)))); } - textList.add(Component.literal("启动耗能:" + FormattingUtil.formatNumbers(getStartupEnergy()) + "EU")); - textList.add(Component.literal("氢储量:" + FormattingUtil.formatNumbers(hydrogen) + "mb")); - textList.add(Component.literal("氦储量:" + FormattingUtil.formatNumbers(helium) + "mb")); + textList.add(Component.translatable("tooltip.gtlcore.startup_energy_cost", FormattingUtil.formatNumbers(getStartupEnergy()))); + textList.add(Component.translatable("tooltip.gtlcore.hydrogen_storage", FormattingUtil.formatNumbers(hydrogen))); + textList.add(Component.translatable("tooltip.gtlcore.helium_storage", FormattingUtil.formatNumbers(helium))); } } } diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/SpaceElevatorMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/SpaceElevatorMachine.java index 36a181055..0a2f3f46f 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/SpaceElevatorMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/SpaceElevatorMachine.java @@ -1,32 +1,91 @@ package org.gtlcore.gtlcore.common.machine.multiblock.electric; +import org.gtlcore.gtlcore.api.machine.multiblock.IModularMachineHost; +import org.gtlcore.gtlcore.api.machine.multiblock.IModularMachineModule; +import org.gtlcore.gtlcore.api.recipe.RecipeResult; +import org.gtlcore.gtlcore.client.gui.widget.IExtendedClickData; +import org.gtlcore.gtlcore.utils.MachineUtil; + +import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; -import com.gregtechceu.gtceu.api.machine.MetaMachine; -import com.gregtechceu.gtceu.api.machine.multiblock.WorkableElectricMultiblockMachine; +import com.gregtechceu.gtceu.api.machine.feature.IMachineLife; import com.lowdragmc.lowdraglib.gui.util.ClickData; -import com.lowdragmc.lowdraglib.gui.widget.ComponentPanelWidget; +import com.lowdragmc.lowdraglib.gui.widget.*; +import net.minecraft.Util; +import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.Level; -import net.minecraft.world.phys.AABB; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; import earth.terrarium.adastra.common.menus.base.PlanetsMenuProvider; import earth.terrarium.botarium.common.menu.MenuHooks; +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +public class SpaceElevatorMachine extends TierCasingMachine + implements IModularMachineHost, IMachineLife { -public class SpaceElevatorMachine extends TierCasingMachine { + private final Set> modules = new ReferenceOpenHashSet<>(); + private int mam = 0; public SpaceElevatorMachine(IMachineBlockEntity holder) { super(holder, "SEPMTier"); } - private int mam = 0; + @Override + public @NotNull Set> getModuleSet() { + return modules; + } + + @Override + public void onStructureInvalid() { + super.onStructureInvalid(); + safeClearModules(); + } + + @Override + public void onMachineRemoved() { + safeClearModules(); + } + + @Override + public void onStructureFormed() { + super.onStructureFormed(); + safeClearModules(); + scanAndConnectModules(); + } + + @Override + public BlockPos[] getModuleScanPositions() { + final Level level = getLevel(); + final BlockPos powerCore = getPowerCore(getPos(), level); + if (powerCore != null) { + return new BlockPos[] { + powerCore.offset(8, 2, 3), + powerCore.offset(8, 2, -3), + powerCore.offset(-8, 2, 3), + powerCore.offset(-8, 2, -3), + powerCore.offset(3, 2, 8), + powerCore.offset(-3, 2, 8), + powerCore.offset(3, 2, -8), + powerCore.offset(-3, 2, -8) + }; + } + return MachineUtil.EMPTY_POS_ARRAY; + } private BlockPos getPowerCore(BlockPos pos, Level level) { BlockPos[] coordinates = new BlockPos[] { pos.offset(3, -2, 0), @@ -43,30 +102,7 @@ private BlockPos getPowerCore(BlockPos pos, Level level) { private int getMAM() { if (getOffsetTimer() % 20 == 0) { - final Level level = getLevel(); - final BlockPos blockPos = getPowerCore(getPos(), level); - if (blockPos != null) { - BlockPos[] coordinatess = new BlockPos[] { blockPos.offset(8, 2, 3), - blockPos.offset(8, 2, -3), - blockPos.offset(-8, 2, 3), - blockPos.offset(-8, 2, -3), - blockPos.offset(3, 2, 8), - blockPos.offset(-3, 2, 8), - blockPos.offset(3, 2, -8), - blockPos.offset(-3, 2, -8) }; - mam = 0; - for (BlockPos blockPoss : coordinatess) { - MetaMachine metaMachine = MetaMachine.getMachine(level, blockPoss); - if (metaMachine instanceof WorkableElectricMultiblockMachine mbmachine && - mbmachine.isFormed()) { - String bid = mbmachine.getBlockState().getBlock().kjs$getId(); - if (bid.equals("gtceu:assembler_module") || bid.equals("gtceu:resource_collection")) { - mam++; - } - } - } - return mam; - } + mam = getFormedModuleCount(); } return mam; } @@ -76,36 +112,92 @@ public boolean onWorking() { boolean value = super.onWorking(); if (getOffsetTimer() % 20 == 0) { if (getRecipeLogic().getProgress() > 240) { + RecipeResult.of(this, RecipeResult.SUCCESS); getRecipeLogic().setProgress(120); } } return value; } + @Override + public @NotNull Widget createUIWidget() { + WidgetGroup group = new WidgetGroup(0, 0, 190, 125); + group.addWidget((new DraggableScrollableWidgetGroup(4, 4, 182, 117)) + .setBackground(this.getScreenTexture()) + .addWidget(new LabelWidget(4, 5, this.self().getBlockState().getBlock().getDescriptionId())) + .addWidget((new ComponentPanelWidget(4, 17, this::addDisplayText) { + + @Override + @OnlyIn(Dist.CLIENT) + public boolean mouseClicked(double mouseX, double mouseY, int button) { + var style = getStyleUnderMouse(mouseX, mouseY); + if (style != null) { + if (style.getClickEvent() != null) { + ClickEvent clickEvent = style.getClickEvent(); + String componentText = clickEvent.getValue(); + if (clickEvent.getAction() == ClickEvent.Action.OPEN_URL) { + if (componentText.startsWith("@!")) { + String rawText = componentText.substring(2); + ClickData clickData = new ClickData(); + if (clickHandler != null) { + clickHandler.accept(rawText, clickData); + } + writeClientAction(1, buf -> { + clickData.writeToBuf(buf); + buf.writeUtf(rawText); + if (Minecraft.getInstance().player != null) buf.writeUUID(Minecraft.getInstance().player.getUUID()); + }); + } else if (componentText.startsWith("@#")) { + String rawText = componentText.substring(2); + Util.getPlatform().openUri(rawText); + } + playButtonClickSound(); + return true; + } + } + } + return super.mouseClicked(mouseX, mouseY, button); + } + + @Override + public void handleClientAction(int id, FriendlyByteBuf buffer) { + if (id == 1) { + ClickData clickData = ClickData.readFromBuf(buffer); + String componentData = buffer.readUtf(); + ((IExtendedClickData) clickData).setUUID(buffer.readUUID()); + if (clickHandler != null) { + clickHandler.accept(componentData, clickData); + } + } else { + super.handleClientAction(id, buffer); + } + } + }) + .textSupplier(Objects.requireNonNull(this.getLevel()).isClientSide ? null : this::addDisplayText).setMaxWidthLimit(150) + .clickHandler(this::handleDisplayClick))); + group.setBackground(GuiTextures.BACKGROUND_INVERSE); + return group; + } + @Override public void addDisplayText(@NotNull List textList) { super.addDisplayText(textList); if (!this.isFormed) return; textList.add(Component.translatable("gtceu.machine.module", getMAM())); - textList.add( - ComponentPanelWidget.withButton(Component.translatable("gtceu.machine.space_elevator.set_out"), - "set_out")); + textList.add(ComponentPanelWidget.withButton(Component.translatable("gtceu.machine.space_elevator.set_out"), + "set_out")); } @Override public void handleDisplayClick(String componentData, ClickData clickData) { + if (!(getLevel() instanceof ServerLevel serverLevel)) return; if (componentData.equals("set_out") && getRecipeLogic().isWorking()) { - final BlockPos pos = getPos(); - List entities = Objects.requireNonNull(getLevel()).getEntitiesOfClass(ServerPlayer.class, new AABB(pos.getX() - 2, - pos.getY() - 2, - pos.getZ() - 2, - pos.getX() + 2, - pos.getY() + 2, - pos.getZ() + 2)); - for (ServerPlayer pr : entities) { - if (pr != null) { - pr.addTag("spaceelevatorst"); - MenuHooks.openMenu(pr, new PlanetsMenuProvider()); + UUID playerUUID = ((IExtendedClickData) clickData).getUUID(); + if (playerUUID != null) { + ServerPlayer player = serverLevel.getServer().getPlayerList().getPlayer(playerUUID); + if (player != null) { + player.addTag("spaceelevatorst"); + MenuHooks.openMenu(player, new PlanetsMenuProvider()); } } } diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/SpaceElevatorModuleMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/SpaceElevatorModuleMachine.java index 9a464e2f5..4ba872648 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/SpaceElevatorModuleMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/SpaceElevatorModuleMachine.java @@ -1,13 +1,14 @@ package org.gtlcore.gtlcore.common.machine.multiblock.electric; +import org.gtlcore.gtlcore.api.machine.multiblock.IModularMachineModule; import org.gtlcore.gtlcore.common.data.GTLBlocks; import org.gtlcore.gtlcore.common.data.GTLRecipeModifiers; -import org.gtlcore.gtlcore.common.data.machines.AdvancedMultiBlockMachine; +import org.gtlcore.gtlcore.utils.MachineUtil; import com.gregtechceu.gtceu.api.GTValues; -import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.feature.IMachineLife; import com.gregtechceu.gtceu.api.machine.multiblock.WorkableElectricMultiblockMachine; import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; import com.gregtechceu.gtceu.api.recipe.GTRecipe; @@ -18,12 +19,18 @@ import com.gregtechceu.gtceu.common.data.GTRecipeModifiers; import com.gregtechceu.gtceu.utils.FormattingUtil; +import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; +import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; +import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; + import net.minecraft.ChatFormatting; import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; -import net.minecraft.world.level.Level; +import net.minecraft.server.level.ServerLevel; +import lombok.Getter; +import lombok.Setter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -33,65 +40,142 @@ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -public class SpaceElevatorModuleMachine extends WorkableElectricMultiblockMachine { +public class SpaceElevatorModuleMachine extends WorkableElectricMultiblockMachine + implements IModularMachineModule, IMachineLife { + + protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(SpaceElevatorModuleMachine.class, WorkableElectricMultiblockMachine.MANAGED_FIELD_HOLDER); - public SpaceElevatorModuleMachine(IMachineBlockEntity holder, boolean SEPMTier, Object... args) { + public SpaceElevatorModuleMachine(IMachineBlockEntity holder, boolean sepmTier, Object... args) { super(holder, args); - this.SEPMTier = SEPMTier; + this.sepmTier = sepmTier; } - private int SpaceElevatorTier = 0, ModuleTier = 0; + @DescSynced + private int spaceElevatorTier = 0; + private int moduleTier = 0; - private final boolean SEPMTier; + private final boolean sepmTier; - private void getSpaceElevatorTier() { - Level level = getLevel(); - BlockPos pos = getPos(); - BlockPos[] coordinates = new BlockPos[] { pos.offset(8, -2, 3), + @Persisted + @Nullable + @Getter + @Setter + private BlockPos hostPosition; + + @Nullable + @Getter + @Setter + private SpaceElevatorMachine host; + + @Override + public @NotNull Class getHostType() { + return SpaceElevatorMachine.class; + } + + @Override + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } + + // ======================================== + // Elevator connection + // ======================================== + + @Override + public void onConnected(@NotNull SpaceElevatorMachine host) { + getSpaceElevatorTier(); + recipeLogic.updateTickSubscription(); + } + + @Override + public BlockPos[] getHostScanPositions() { + final BlockPos pos = getPos(); + BlockPos[] powerCorePositions = new BlockPos[] { + pos.offset(8, -2, 3), pos.offset(8, -2, -3), pos.offset(-8, -2, 3), pos.offset(-8, -2, -3), pos.offset(3, -2, 8), pos.offset(-3, -2, 8), pos.offset(3, -2, -8), - pos.offset(-3, -2, -8) }; - for (BlockPos i : coordinates) { - if (level != null && level.getBlockState(i).getBlock() == GTLBlocks.POWER_CORE.get()) { - BlockPos[] coordinatess = new BlockPos[] { i.offset(3, 2, 0), - i.offset(-3, 2, 0), - i.offset(0, 2, 3), - i.offset(0, 2, -3) }; - for (BlockPos j : coordinatess) { - RecipeLogic logic = GTCapabilityHelper.getRecipeLogic(level, j, null); - if (logic != null && logic.getMachine().getDefinition() == AdvancedMultiBlockMachine.SPACE_ELEVATOR) { - if (logic.isWorking() && logic.getProgress() > 80) { - SpaceElevatorTier = ((SpaceElevatorMachine) logic.machine).getTier() - GTValues.ZPM; - ModuleTier = ((SpaceElevatorMachine) logic.machine).getCasingTier(); - } else if (!logic.isWorking()) { - SpaceElevatorTier = 0; - ModuleTier = 0; - } - } + pos.offset(-3, -2, -8) + }; + + if (getLevel() instanceof ServerLevel serverLevel) { + for (BlockPos i : powerCorePositions) { + if (serverLevel.getBlockState(i).getBlock() == GTLBlocks.POWER_CORE.get()) { + return new BlockPos[] { + i.offset(3, 2, 0), + i.offset(-3, 2, 0), + i.offset(0, 2, 3), + i.offset(0, 2, -3) + }; } } } + return MachineUtil.EMPTY_POS_ARRAY; + } + + @Override + public void onStructureFormed() { + super.onStructureFormed(); + if (!findAndConnectToHost()) { + removeFromHost(this.host); + } + } + + @Override + public void onStructureInvalid() { + super.onStructureInvalid(); + removeFromHost(this.host); + } + + @Override + public void onPartUnload() { + super.onPartUnload(); + removeFromHost(this.host); + } + + @Override + public void onMachineRemoved() { + removeFromHost(this.host); + } + + // ======================================== + // Recipe Tier + // ======================================== + + private void getSpaceElevatorTier() { + if (this.host != null) { + final RecipeLogic logic = host.getRecipeLogic(); + if (logic.isWorking() && logic.getProgress() > 80) { + spaceElevatorTier = host.getTier() - GTValues.ZPM; + moduleTier = host.getCasingTier(); + } else if (!logic.isWorking()) { + spaceElevatorTier = 0; + moduleTier = 0; + } + } else { + spaceElevatorTier = 0; + moduleTier = 0; + } } @Nullable public static GTRecipe recipeModifier(MetaMachine machine, @NotNull GTRecipe recipe, @NotNull OCParams params, @NotNull OCResult result) { - if (machine instanceof SpaceElevatorModuleMachine spaceElevatorModuleMachine) { - spaceElevatorModuleMachine.getSpaceElevatorTier(); - if (spaceElevatorModuleMachine.SpaceElevatorTier < 1) { + if (machine instanceof SpaceElevatorModuleMachine moduleMachine) { + moduleMachine.getSpaceElevatorTier(); + if (moduleMachine.spaceElevatorTier < 1) { return null; } - if (spaceElevatorModuleMachine.SEPMTier && recipe.data.getInt("SEPMTier") > spaceElevatorModuleMachine.ModuleTier) { + if (moduleMachine.sepmTier && recipe.data.getInt("SEPMTier") > moduleMachine.moduleTier) { return null; } - GTRecipe recipe1 = GTLRecipeModifiers.reduction(machine, recipe, 1, Math.pow(0.8, spaceElevatorModuleMachine.SpaceElevatorTier - 1)); + GTRecipe recipe1 = GTLRecipeModifiers.reduction(machine, recipe, 1, Math.pow(0.8, moduleMachine.spaceElevatorTier - 1)); if (recipe1 != null) { - recipe1 = GTRecipeModifiers.accurateParallel(machine, recipe1, (int) Math.pow(4, spaceElevatorModuleMachine.ModuleTier - 1), false).getFirst(); - if (recipe1 != null) return RecipeHelper.applyOverclock(OverclockingLogic.NON_PERFECT_OVERCLOCK_SUBTICK, recipe1, spaceElevatorModuleMachine.getOverclockVoltage(), params, result); + recipe1 = GTRecipeModifiers.accurateParallel(machine, recipe1, (int) Math.pow(4, moduleMachine.moduleTier - 1), false).getFirst(); + if (recipe1 != null) return RecipeHelper.applyOverclock(OverclockingLogic.NON_PERFECT_OVERCLOCK_SUBTICK, recipe1, moduleMachine.getOverclockVoltage(), params, result); } } return null; @@ -102,9 +186,8 @@ public boolean onWorking() { boolean value = super.onWorking(); if (getOffsetTimer() % 20 == 0) { getSpaceElevatorTier(); - if (SpaceElevatorTier < 1) { - getRecipeLogic().interruptRecipe(); - return false; + if (spaceElevatorTier < 1) { + getRecipeLogic().setProgress(0); } } return value; @@ -117,8 +200,8 @@ public void addDisplayText(@NotNull List textList) { if (getOffsetTimer() % 10 == 0) { getSpaceElevatorTier(); } - textList.add(Component.translatable("gtceu.multiblock.parallel", Component.literal(FormattingUtil.formatNumbers(Math.pow(4, ModuleTier - 1))).withStyle(ChatFormatting.DARK_PURPLE)).withStyle(ChatFormatting.GRAY)); - textList.add(Component.literal((SpaceElevatorTier < 1 ? "未" : "已") + "连接正在运行的太空电梯")); - textList.add(Component.translatable("gtceu.machine.duration_multiplier.tooltip", Math.pow(0.8, SpaceElevatorTier - 1))); + textList.add(Component.translatable("gtceu.multiblock.parallel", Component.literal(FormattingUtil.formatNumbers(Math.pow(4, moduleTier - 1))).withStyle(ChatFormatting.DARK_PURPLE)).withStyle(ChatFormatting.GRAY)); + textList.add(Component.translatable(spaceElevatorTier < 1 ? "tooltip.gtlcore.space_elevator_not_connected" : "tooltip.gtlcore.space_elevator_connected")); + textList.add(Component.translatable("gtceu.machine.duration_multiplier.tooltip", FormattingUtil.formatPercent(Math.pow(0.8, spaceElevatorTier - 1)))); } } diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/SpaceProbeSurfaceReceptionMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/SpaceProbeSurfaceReceptionMachine.java index e2eca78cd..e3f2bdf78 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/SpaceProbeSurfaceReceptionMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/SpaceProbeSurfaceReceptionMachine.java @@ -1,61 +1,74 @@ package org.gtlcore.gtlcore.common.machine.multiblock.electric; +import org.gtlcore.gtlcore.api.recipe.RecipeResult; import org.gtlcore.gtlcore.common.data.GTLMaterials; import com.gregtechceu.gtceu.api.data.chemical.ChemicalHelper; import com.gregtechceu.gtceu.api.data.tag.TagPrefix; -import com.gregtechceu.gtceu.api.machine.ConditionalSubscriptionHandler; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.multiblock.WorkableElectricMultiblockMachine; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; import net.minecraft.world.level.Level; -import net.minecraft.world.level.LightLayer; + +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; /** * @author EasterFG on 2024/12/2 */ public class SpaceProbeSurfaceReceptionMachine extends WorkableElectricMultiblockMachine { - protected ConditionalSubscriptionHandler checkSub; - private int time; + @Nullable + private BlockPos cachedCheckPos; public SpaceProbeSurfaceReceptionMachine(IMachineBlockEntity holder, Object... args) { super(holder, args); - checkSub = new ConditionalSubscriptionHandler(this, this::check, this::isFormed); } @Override public void onStructureFormed() { super.onStructureFormed(); - checkSub.initialize(getLevel()); + cachedCheckPos = findTopBlock(); } - private void check() { - if (getOffsetTimer() % 20 == 0) { - time++; - } + @Override + public void onStructureInvalid() { + super.onStructureInvalid(); + cachedCheckPos = null; + } - if (time < 30) { - return; - } - time = 0; + @Nullable + protected BlockPos findTopBlock() { Level level = getLevel(); + if (level == null) return null; + BlockPos pos = getPos(); - BlockPos[] coordinates = new BlockPos[] { pos.offset(4, 8, 0), pos.offset(-4, 8, 0), pos.offset(0, 8, 4), pos.offset(0, 8, -4) }; - for (BlockPos a : coordinates) { - if (level != null && level.getBlockState(a).is(ChemicalHelper.getBlock(TagPrefix.frameGt, GTLMaterials.BlackTitanium))) { - for (int i = -6; i < 7; i++) { - for (int j = -6; j < 7; j++) { - if (level.getBrightness(LightLayer.SKY, a.offset(0, 1, 0)) == 0) { - getRecipeLogic().interruptRecipe(); - return; - } - } - } - return; + BlockPos[] coordinates = new BlockPos[] { + pos.offset(4, 8, 0), + pos.offset(-4, 8, 0), + pos.offset(0, 8, 4), + pos.offset(0, 8, -4) + }; + + for (BlockPos checkPos : coordinates) { + if (level.getBlockState(checkPos).is(ChemicalHelper.getBlock(TagPrefix.frameGt, GTLMaterials.BlackTitanium))) { + return checkPos.offset(0, 1, 0); } } - getRecipeLogic().interruptRecipe(); + return null; + } + + @Override + public boolean beforeWorking(@Nullable GTRecipe recipe) { + if (cachedCheckPos == null) return false; + if (!Objects.requireNonNull(getLevel()).canSeeSky(cachedCheckPos)) { + RecipeResult.of(this, RecipeResult.fail(Component.translatable("gtceu.recipe.fail.block"))); + return false; + } + return super.beforeWorking(recipe); } } diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/SuprachronalAssemblyLineMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/SuprachronalAssemblyLineMachine.java index 7450c43af..f8196869c 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/SuprachronalAssemblyLineMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/SuprachronalAssemblyLineMachine.java @@ -1,94 +1,84 @@ package org.gtlcore.gtlcore.common.machine.multiblock.electric; -import org.gtlcore.gtlcore.common.data.GTLRecipeModifiers; +import org.gtlcore.gtlcore.api.machine.multiblock.IModularMachineHost; +import org.gtlcore.gtlcore.api.machine.multiblock.IModularMachineModule; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; -import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.machine.multiblock.WorkableElectricMultiblockMachine; import com.gregtechceu.gtceu.api.recipe.GTRecipe; -import com.gregtechceu.gtceu.utils.FormattingUtil; -import net.minecraft.ChatFormatting; import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; -import net.minecraft.world.level.Level; +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; -import java.util.Objects; +import java.util.Set; import javax.annotation.ParametersAreNonnullByDefault; @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -public class SuprachronalAssemblyLineMachine extends WorkableElectricMultiblockMachine { +public class SuprachronalAssemblyLineMachine extends WorkableElectricMultiblockMachine + implements IModularMachineHost { - private final boolean isModule; + private final Set> modules = new ReferenceOpenHashSet<>(); + private int mam = 0; - private int module = 0; - private WorkableElectricMultiblockMachine machine = null; - - public SuprachronalAssemblyLineMachine(IMachineBlockEntity holder, boolean isModule, Object... args) { + public SuprachronalAssemblyLineMachine(IMachineBlockEntity holder, Object... args) { super(holder, args); - this.isModule = isModule; } - private void getSuprachronalAssemblyLineModule() { - Level level = getLevel(); - if (level != null) { - BlockPos pos = getPos(); - module = 0; - BlockPos[] coordinates = new BlockPos[] { pos.offset(3, 0, 0), - pos.offset(-3, 0, 0), - pos.offset(0, 0, 3), - pos.offset(0, 0, -3) }; - for (BlockPos i : coordinates) { - MetaMachine metaMachine = MetaMachine.getMachine(level, i); - if (metaMachine != null && Objects.equals(metaMachine.getBlockState().getBlock().kjs$getId(), "gtceu:suprachronal_assembly_line_module") && ((WorkableElectricMultiblockMachine) metaMachine).isFormed()) { - module++; - } - } + private int getMAM() { + if (getOffsetTimer() % 20 == 0) { + mam = getFormedModuleCount(); } + return mam; } - private void getSuprachronalAssemblyLine() { - Level level = getLevel(); - if (level != null) { - BlockPos pos = getPos(); - BlockPos[] coordinates = new BlockPos[] { pos.offset(3, 0, 0), - pos.offset(-3, 0, 0), - pos.offset(0, 0, 3), - pos.offset(0, 0, -3) }; - for (BlockPos i : coordinates) { - MetaMachine metaMachine = MetaMachine.getMachine(level, i); - if (metaMachine != null && Objects.equals(metaMachine.getBlockState().getBlock().kjs$getId(), "gtceu:suprachronal_assembly_line") && ((WorkableElectricMultiblockMachine) metaMachine).isFormed()) { - machine = (WorkableElectricMultiblockMachine) metaMachine; - } - } - } + @Override + public @NotNull Set> getModuleSet() { + return modules; } - public int getParallel() { - return GTLRecipeModifiers.getHatchParallel(machine); + @Override + public BlockPos[] getModuleScanPositions() { + final BlockPos pos = getPos(); + return new BlockPos[] { + pos.offset(3, 0, 0), + pos.offset(-3, 0, 0), + pos.offset(0, 0, 3), + pos.offset(0, 0, -3) + }; + } + + @Override + public int getMaxModuleCount() { + return 2; // 最多连接2个模块 + } + + @Override + public void onStructureFormed() { + super.onStructureFormed(); + safeClearModules(); + scanAndConnectModules(); + } + + @Override + public void onStructureInvalid() { + super.onStructureInvalid(); + safeClearModules(); } @Override public boolean onWorking() { boolean value = super.onWorking(); if (getOffsetTimer() % 20 == 0) { - if (isModule) { - getSuprachronalAssemblyLine(); - if (machine == null) { - getRecipeLogic().setProgress(0); - } - } else { - getSuprachronalAssemblyLineModule(); - if (module > 2) { - getRecipeLogic().setProgress(0); - } + if (exceedsModuleLimit()) { + getRecipeLogic().setProgress(0); } } return value; @@ -96,18 +86,9 @@ public boolean onWorking() { @Override public boolean beforeWorking(@Nullable GTRecipe recipe) { - if (isModule) { - getSuprachronalAssemblyLine(); - if (machine == null) { - getRecipeLogic().interruptRecipe(); - return false; - } - } else { - getSuprachronalAssemblyLineModule(); - if (module > 2) { - getRecipeLogic().interruptRecipe(); - return false; - } + if (exceedsModuleLimit()) { + getRecipeLogic().interruptRecipe(); + return false; } return super.beforeWorking(recipe); } @@ -116,17 +97,6 @@ public boolean beforeWorking(@Nullable GTRecipe recipe) { public void addDisplayText(@NotNull List textList) { super.addDisplayText(textList); if (!this.isFormed) return; - if (isModule) { - if (getOffsetTimer() % 10 == 0) { - getSuprachronalAssemblyLine(); - } - textList.add(Component.translatable("gtceu.multiblock.parallel", Component.literal(FormattingUtil.formatNumbers(getParallel())).withStyle(ChatFormatting.DARK_PURPLE)).withStyle(ChatFormatting.GRAY)); - textList.add(Component.literal("该模块" + (machine != null ? "已" : "未") + "成功安装")); - } else { - if (getOffsetTimer() % 10 == 0) { - getSuprachronalAssemblyLineModule(); - } - textList.add(Component.literal("已安装的模块数:" + module)); - } + textList.add(Component.translatable("tooltip.gtlcore.installed_module_count", getMAM())); } } diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/SuprachronalAssemblyLineModuleMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/SuprachronalAssemblyLineModuleMachine.java new file mode 100644 index 000000000..993b46065 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/SuprachronalAssemblyLineModuleMachine.java @@ -0,0 +1,142 @@ +package org.gtlcore.gtlcore.common.machine.multiblock.electric; + +import org.gtlcore.gtlcore.api.machine.multiblock.IModularMachineModule; +import org.gtlcore.gtlcore.common.data.GTLRecipeModifiers; + +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.feature.IMachineLife; +import com.gregtechceu.gtceu.api.machine.multiblock.WorkableElectricMultiblockMachine; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.utils.FormattingUtil; + +import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; +import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; + +import net.minecraft.ChatFormatting; +import net.minecraft.MethodsReturnNonnullByDefault; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; + +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class SuprachronalAssemblyLineModuleMachine extends WorkableElectricMultiblockMachine + implements IModularMachineModule, IMachineLife { + + protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( + SuprachronalAssemblyLineModuleMachine.class, + WorkableElectricMultiblockMachine.MANAGED_FIELD_HOLDER); + + @Persisted + @Nullable + @Getter + @Setter + private BlockPos hostPosition; + + @Nullable + @Getter + @Setter + private SuprachronalAssemblyLineMachine host; + + public SuprachronalAssemblyLineModuleMachine(IMachineBlockEntity holder, Object... args) { + super(holder, args); + } + + @Override + public @NotNull Class getHostType() { + return SuprachronalAssemblyLineMachine.class; + } + + @Override + public BlockPos[] getHostScanPositions() { + final BlockPos pos = getPos(); + return new BlockPos[] { + pos.offset(3, 0, 0), + pos.offset(-3, 0, 0), + pos.offset(0, 0, 3), + pos.offset(0, 0, -3) + }; + } + + @Override + public void onConnected(@NotNull SuprachronalAssemblyLineMachine host) { + getRecipeLogic().updateTickSubscription(); + } + + public int getParallel() { + if (host != null) { + return GTLRecipeModifiers.getHatchParallel(host); + } + return 0; + } + + @Override + public boolean onWorking() { + boolean value = super.onWorking(); + if (getOffsetTimer() % 20 == 0) { + if (!isConnectedToHost()) { + getRecipeLogic().setProgress(0); + } + } + return value; + } + + @Override + public boolean beforeWorking(@Nullable GTRecipe recipe) { + if (!isConnectedToHost()) { + getRecipeLogic().interruptRecipe(); + return false; + } + return super.beforeWorking(recipe); + } + + @Override + public void onStructureFormed() { + super.onStructureFormed(); + if (!findAndConnectToHost()) { + removeFromHost(this.host); + } + } + + @Override + public void onStructureInvalid() { + super.onStructureInvalid(); + removeFromHost(this.host); + } + + @Override + public void onPartUnload() { + super.onPartUnload(); + removeFromHost(this.host); + } + + @Override + public void onMachineRemoved() { + removeFromHost(this.host); + } + + @Override + public void addDisplayText(@NotNull List textList) { + super.addDisplayText(textList); + if (!this.isFormed) return; + textList.add(Component.translatable("gtceu.multiblock.parallel", + Component.literal(FormattingUtil.formatNumbers(getParallel())) + .withStyle(ChatFormatting.DARK_PURPLE)) + .withStyle(ChatFormatting.GRAY)); + textList.add(Component.translatable( + isConnectedToHost() ? "tooltip.gtlcore.module_installed" : "tooltip.gtlcore.module_not_installed")); + } + + @Override + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/TierCasingMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/TierCasingMachine.java index ae66216f5..3bc16de51 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/TierCasingMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/electric/TierCasingMachine.java @@ -1,16 +1,15 @@ package org.gtlcore.gtlcore.common.machine.multiblock.electric; +import org.gtlcore.gtlcore.api.recipe.RecipeResult; + import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.multiblock.WorkableElectricMultiblockMachine; -import com.gregtechceu.gtceu.api.machine.multiblock.WorkableMultiblockMachine; import com.gregtechceu.gtceu.api.recipe.GTRecipe; -import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; -import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; - import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.network.chat.Component; +import lombok.Getter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -22,38 +21,33 @@ @MethodsReturnNonnullByDefault public class TierCasingMachine extends WorkableElectricMultiblockMachine { - public static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( - TierCasingMachine.class, WorkableMultiblockMachine.MANAGED_FIELD_HOLDER); + @Getter + protected final String tierType; - private final String tierType; - - @Persisted - private int tier = 0; + @Getter + protected int casingTier = 0; public TierCasingMachine(IMachineBlockEntity holder, String tierType, Object... args) { super(holder, args); this.tierType = tierType; } - public int getCasingTier() { - return tier; - } - @Override public void onStructureFormed() { super.onStructureFormed(); - tier = getMultiblockState().getMatchContext().get(tierType); + casingTier = getMultiblockState().getMatchContext().get(tierType); } @Override public void onStructureInvalid() { super.onStructureInvalid(); - tier = 0; + casingTier = 0; } @Override public boolean beforeWorking(@Nullable GTRecipe recipe) { - if (recipe != null && recipe.data.contains(tierType) && recipe.data.getInt(tierType) > tier) { + if (recipe != null && recipe.data.contains(tierType) && recipe.data.getInt(tierType) > casingTier) { + RecipeResult.of(this, RecipeResult.FAIL_NO_ENOUGH_TIER); getRecipeLogic().interruptRecipe(); return false; } @@ -64,11 +58,6 @@ public boolean beforeWorking(@Nullable GTRecipe recipe) { public void addDisplayText(@NotNull List textList) { super.addDisplayText(textList); if (!this.isFormed) return; - textList.add(Component.translatable("gtceu.casings.tier", tier)); - } - - @Override - public @NotNull ManagedFieldHolder getFieldHolder() { - return MANAGED_FIELD_HOLDER; + textList.add(Component.translatable("gtceu.casings.tier", casingTier)); } } diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/generator/ChemicalEnergyDevourerMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/generator/ChemicalEnergyDevourerMachine.java index c484910a0..d466ab386 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/generator/ChemicalEnergyDevourerMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/generator/ChemicalEnergyDevourerMachine.java @@ -2,7 +2,6 @@ import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.capability.recipe.EURecipeCapability; -import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.fluids.store.FluidStorageKeys; import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.gui.fancy.TooltipsPanel; @@ -37,6 +36,8 @@ import javax.annotation.ParametersAreNonnullByDefault; +import static org.gtlcore.gtlcore.api.recipe.RecipeRunnerHelper.*; + @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault public class ChemicalEnergyDevourerMachine extends WorkableElectricMultiblockMachine implements ITieredMachine { @@ -106,7 +107,7 @@ public static GTRecipe recipeModifier(MetaMachine machine, @NotNull GTRecipe rec @NotNull OCResult result) { if (machine instanceof ChemicalEnergyDevourerMachine engineMachine) { var EUt = RecipeHelper.getOutputEUt(recipe); - if (EUt > 0 && engineMachine.getLubricantRecipe().matchRecipe(engineMachine).isSuccess() && + if (EUt > 0 && matchRecipe(engineMachine, engineMachine.getLubricantRecipe()) && !engineMachine.isIntakesObstructed()) { var maxParallel = (int) (engineMachine.getOverclockVoltage() / EUt); var parallelResult = GTRecipeModifiers.fastParallel(engineMachine, recipe, maxParallel, false); @@ -134,7 +135,7 @@ public boolean onWorking() { boolean value = super.onWorking(); val totalContinuousRunningTime = recipeLogic.getTotalContinuousRunningTime(); if ((totalContinuousRunningTime == 1 || totalContinuousRunningTime % 72 == 0)) { - if (!getLubricantRecipe().handleRecipeIO(IO.IN, this, this.recipeLogic.getChanceCaches())) { + if (!handleRecipeInput(this, getLubricantRecipe())) { recipeLogic.interruptRecipe(); return false; } @@ -142,10 +143,10 @@ public boolean onWorking() { if ((totalContinuousRunningTime == 1 || totalContinuousRunningTime % 20 == 0) && isBoostAllowed()) { var boosterRecipe = getBoostRecipe(); var boosterRecipea = getBoostRecipea(); - this.isOxygenBoosted = boosterRecipe.matchRecipe(this).isSuccess() && - boosterRecipe.handleRecipeIO(IO.IN, this, this.recipeLogic.getChanceCaches()); - this.isDinitrogenTetroxideBoosted = boosterRecipea.matchRecipe(this).isSuccess() && - boosterRecipea.handleRecipeIO(IO.IN, this, this.recipeLogic.getChanceCaches()); + this.isOxygenBoosted = matchRecipe(this, boosterRecipe) && + handleRecipeInput(this, boosterRecipe); + this.isDinitrogenTetroxideBoosted = matchRecipe(this, boosterRecipea) && + handleRecipeInput(this, boosterRecipea); } return value; } diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/generator/DysonSphereMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/generator/DysonSphereMachine.java index 9ce39354d..f8cd69c8a 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/generator/DysonSphereMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/generator/DysonSphereMachine.java @@ -108,7 +108,7 @@ public boolean beforeWorking(@Nullable GTRecipe recipe) { } } if (recipe != null && isLaunch(recipe)) { - return getDysonSphereData() < 10000; + return getDysonSphereData() <= 10000; } else { return getDysonSphereData() > 0; } diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/generator/GTLLargeCombustionEngineMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/generator/GTLLargeCombustionEngineMachine.java index f5778a220..17b69d1af 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/generator/GTLLargeCombustionEngineMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/generator/GTLLargeCombustionEngineMachine.java @@ -2,7 +2,6 @@ import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.capability.recipe.EURecipeCapability; -import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.fluids.store.FluidStorageKeys; import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.gui.fancy.TooltipsPanel; @@ -37,6 +36,8 @@ import javax.annotation.ParametersAreNonnullByDefault; +import static org.gtlcore.gtlcore.api.recipe.RecipeRunnerHelper.*; + /** * @author KilaBash * @date 2023/7/9 @@ -111,7 +112,7 @@ public static GTRecipe recipeModifier(MetaMachine machine, @NotNull GTRecipe rec @NotNull OCResult result) { if (machine instanceof GTLLargeCombustionEngineMachine engineMachine) { var EUt = RecipeHelper.getOutputEUt(recipe); - if (EUt > 0 && engineMachine.getLubricantRecipe().matchRecipe(engineMachine).isSuccess() && + if (EUt > 0 && matchRecipe(engineMachine, engineMachine.getLubricantRecipe()) && !engineMachine.isIntakesObstructed()) { var maxParallel = (int) (engineMachine.getOverclockVoltage() / EUt); GTRecipe parallelResult = GTRecipeModifiers.fastParallel(engineMachine, recipe, maxParallel, false).getFirst(); @@ -133,7 +134,7 @@ public boolean onWorking() { val totalContinuousRunningTime = recipeLogic.getTotalContinuousRunningTime(); if ((totalContinuousRunningTime == 1 || totalContinuousRunningTime % 72 == 0)) { // insufficient lubricant - if (!getLubricantRecipe().handleRecipeIO(IO.IN, this, this.recipeLogic.getChanceCaches())) { + if (!handleRecipeInput(this, getLubricantRecipe())) { recipeLogic.interruptRecipe(); return false; } @@ -141,8 +142,7 @@ public boolean onWorking() { // check boost fluid if ((totalContinuousRunningTime == 1 || totalContinuousRunningTime % 20 == 0) && isBoostAllowed()) { var boosterRecipe = getBoostRecipe(); - this.isOxygenBoosted = boosterRecipe.matchRecipe(this).isSuccess() && - boosterRecipe.handleRecipeIO(IO.IN, this, this.recipeLogic.getChanceCaches()); + this.isOxygenBoosted = matchRecipe(this, boosterRecipe) && handleRecipeInput(this, boosterRecipe); } return value; } diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/generator/MegaTurbineMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/generator/MegaTurbineMachine.java index 811c00698..1d4d01a41 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/generator/MegaTurbineMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/generator/MegaTurbineMachine.java @@ -1,5 +1,6 @@ package org.gtlcore.gtlcore.common.machine.multiblock.generator; +import org.gtlcore.gtlcore.api.recipe.RecipeResult; import org.gtlcore.gtlcore.common.machine.multiblock.part.RotorHatchPartMachine; import com.gregtechceu.gtceu.api.GTValues; @@ -31,14 +32,12 @@ import net.minecraft.network.chat.Style; import net.minecraft.world.item.ItemStack; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import lombok.Getter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; +import java.util.*; import javax.annotation.ParametersAreNonnullByDefault; @@ -90,7 +89,7 @@ public void onStructureFormed() { super.onStructureFormed(); for (IMultiPart part : getParts()) { if (part instanceof RotorHolderPartMachine rotorHolderPartMachine) { - rotorHolderMachines = Objects.requireNonNullElseGet(rotorHolderMachines, HashSet::new); + rotorHolderMachines = Objects.requireNonNullElseGet(rotorHolderMachines, ObjectOpenHashSet::new); rotorHolderMachines.add(rotorHolderPartMachine); } @@ -108,6 +107,12 @@ public void onStructureInvalid() { rotorHatchPartMachine = null; } + @Override + public boolean onWorking() { + for (var part : this.rotorHolderMachines) if (!part.onWorking(this)) return false; + return super.onWorking(); + } + @Nullable private RotorHolderPartMachine getRotorHolder() { if (rotorHolderMachines != null) { @@ -121,8 +126,11 @@ private RotorHolderPartMachine getRotorHolder() { @Override public long getOverclockVoltage() { var rotorHolder = getRotorHolder(); - if (rotorHolder != null && rotorHolder.hasRotor()) - return (long) baseEuOutput * rotorHolder.getTotalPower() / 100; + if (rotorHolder != null && rotorHolder.hasRotor()) { + long eu = (long) baseEuOutput * rotorHolder.getTotalPower() / 100; + if (eu < 0) RecipeResult.of(this, RecipeResult.fail(Component.translatable("gtceu.recipe.fail.rotor_holder.tier"))); + return eu; + } return 0; } @@ -145,6 +153,15 @@ protected long boostProduction(long production) { public static GTRecipe recipeModifier(MetaMachine machine, @NotNull GTRecipe recipe, @NotNull OCParams params, @NotNull OCResult result) { if (machine instanceof MegaTurbineMachine turbineMachine) { + String rotor = ""; + for (var part : turbineMachine.rotorHolderMachines) { + String partRotor = part.getRotorStack().getHoverName().getString(); + if (rotor.isEmpty()) rotor = partRotor; + else if (!rotor.equals(partRotor)) { + RecipeResult.of(turbineMachine, RecipeResult.fail(Component.translatable("gtceu.recipe.fail.rotor.different"))); + return null; + } + } RotorHolderPartMachine rotorHolder = turbineMachine.getRotorHolder(); long EUt = RecipeHelper.getOutputEUt(recipe); if (rotorHolder == null || EUt <= 0) return null; diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/noenergy/HeatExchangerMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/noenergy/HeatExchangerMachine.java index 8f9867b88..f0b19021b 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/noenergy/HeatExchangerMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/noenergy/HeatExchangerMachine.java @@ -2,57 +2,62 @@ import org.gtlcore.gtlcore.GTLCore; import org.gtlcore.gtlcore.api.machine.multiblock.NoEnergyMultiblockMachine; -import org.gtlcore.gtlcore.common.data.GTLMaterials; +import org.gtlcore.gtlcore.api.recipe.RecipeResult; +import org.gtlcore.gtlcore.api.recipe.RecipeRunnerHelper; import org.gtlcore.gtlcore.utils.MachineIO; import com.gregtechceu.gtceu.api.capability.recipe.FluidRecipeCapability; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; import com.gregtechceu.gtceu.common.data.GTMaterials; import com.gregtechceu.gtceu.common.data.GTRecipeModifiers; import com.gregtechceu.gtceu.common.data.GTRecipeTypes; import com.gregtechceu.gtceu.data.recipe.builder.GTRecipeBuilder; +import net.minecraft.network.chat.Component; +import net.minecraftforge.registries.ForgeRegistries; + import com.mojang.datafixers.util.Pair; import org.jetbrains.annotations.NotNull; +import java.util.Objects; + public class HeatExchangerMachine extends NoEnergyMultiblockMachine { public HeatExchangerMachine(IMachineBlockEntity holder, Object... args) { super(holder, args); } - private long count = 0; - public static GTRecipe recipeModifier(MetaMachine machine, @NotNull GTRecipe recipe) { if (machine instanceof HeatExchangerMachine hMachine) { if (FluidRecipeCapability.CAP.of(recipe.inputs.get(FluidRecipeCapability.CAP) .get(1).getContent()).getStacks()[0].getFluid() == GTMaterials.Water.getFluid()) { return GTRecipeModifiers.accurateParallel(machine, recipe, Integer.MAX_VALUE, false).getFirst(); } - final Pair result = GTRecipeModifiers.accurateParallel(machine, new GTRecipeBuilder(GTLCore.id("heat_exchanger"), GTRecipeTypes.DUMMY_RECIPES) - .inputFluids(FluidRecipeCapability.CAP.of(recipe.inputs - .get(FluidRecipeCapability.CAP).get(0).getContent())) - .outputFluids(FluidRecipeCapability.CAP.of(recipe.outputs - .get(FluidRecipeCapability.CAP).get(0).getContent())) + + // for recipe cache + final var fluidIngredient = FluidRecipeCapability.CAP.of(recipe.inputs.get(FluidRecipeCapability.CAP).get(0).getContent()); + final var prefix = Objects.requireNonNull(ForgeRegistries.FLUID_TYPES.get().getKey(fluidIngredient.getStacks()[0].getFluid().getFluidType())).getPath(); + final GTRecipe plasmaRecipe = new GTRecipeBuilder(GTLCore.id("heat_exchanger_" + prefix), GTRecipeTypes.DUMMY_RECIPES) + .inputFluids(fluidIngredient) + .outputFluids(recipe.outputs.get(FluidRecipeCapability.CAP).stream().map(c -> FluidRecipeCapability.CAP.of(c.getContent())).toArray((FluidIngredient[]::new))) .duration(200) - .buildRawRecipe(), Integer.MAX_VALUE, false); - long count = result.getSecond() * recipe.data.getLong("eu"); - if (MachineIO.inputFluid(hMachine, GTMaterials.DistilledWater.getFluid(count / 160))) { - hMachine.count = count; + .buildRawRecipe(); + + if (!RecipeRunnerHelper.matchRecipeInputNoMEInnerCache(hMachine, plasmaRecipe)) return null; + + final Pair result = GTRecipeModifiers.accurateParallel(machine, plasmaRecipe, Integer.MAX_VALUE, false); + + long count = result.getSecond() * FluidRecipeCapability.CAP.of(recipe.inputs.get(FluidRecipeCapability.CAP) + .get(1).getContent()).getStacks()[0].getAmount(); + + if (MachineIO.inputFluid(hMachine, GTMaterials.DistilledWater.getFluid(count))) { return result.getFirst(); - } + } else RecipeResult.of((IRecipeLogicMachine) machine, RecipeResult.fail(Component.translatable("gtceu.recipe.fail.no.enough.distilledwater"))); } return null; } - - @Override - public void afterWorking() { - if (count != 0) { - MachineIO.outputFluid(this, GTLMaterials.SupercriticalSteam - .getFluid(count)); - } - count = 0; - } } diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/noenergy/NeutronActivatorMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/noenergy/NeutronActivatorMachine.java index 150100d7f..9bdf22101 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/noenergy/NeutronActivatorMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/noenergy/NeutronActivatorMachine.java @@ -2,6 +2,7 @@ import org.gtlcore.gtlcore.api.machine.multiblock.NoEnergyMultiblockMachine; import org.gtlcore.gtlcore.api.pattern.util.IValueContainer; +import org.gtlcore.gtlcore.api.recipe.RecipeResult; import org.gtlcore.gtlcore.common.data.GTLRecipeModifiers; import org.gtlcore.gtlcore.common.machine.multiblock.part.NeutronAcceleratorPartMachine; import org.gtlcore.gtlcore.common.machine.multiblock.part.NeutronSensorPartMachine; @@ -12,6 +13,7 @@ import com.gregtechceu.gtceu.api.machine.ConditionalSubscriptionHandler; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiPart; import com.gregtechceu.gtceu.api.recipe.GTRecipe; import com.gregtechceu.gtceu.common.data.GTMaterials; @@ -113,13 +115,18 @@ private double getEfficiencyFactor() { @Nullable public static GTRecipe recipeModifier(MetaMachine machine, @NotNull GTRecipe recipe) { - if (machine instanceof NeutronActivatorMachine nMachine && - (nMachine.eV > recipe.data.getInt("ev_min") * 1000000 && - nMachine.eV < recipe.data.getInt("ev_max") * 1000000)) { - GTRecipe recipe1 = GTRecipeModifiers.accurateParallel(machine, recipe, GTLRecipeModifiers.getHatchParallel(nMachine), false) - .getFirst(); - recipe1.duration = (int) Math.round(Math.max(recipe1.duration * nMachine.getEfficiencyFactor(), 1)); - return recipe1; + if (machine instanceof NeutronActivatorMachine nMachine) { + if (nMachine.eV > recipe.data.getInt("ev_min") * 1000000 && + nMachine.eV < recipe.data.getInt("ev_max") * 1000000) { + GTRecipe recipe1 = GTRecipeModifiers.accurateParallel(machine, recipe, GTLRecipeModifiers.getHatchParallel(nMachine), false) + .getFirst(); + recipe1.duration = (int) Math.round(Math.max(recipe1.duration * nMachine.getEfficiencyFactor(), 1)); + return recipe1; + } else if (nMachine.eV < recipe.data.getInt("ev_min") * 1000000) { + RecipeResult.of((IRecipeLogicMachine) machine, RecipeResult.fail(Component.translatable("gtceu.recipe.fail.no.enough.neutron"))); + } else if (nMachine.eV > recipe.data.getInt("ev_max") * 1000000) { + RecipeResult.of((IRecipeLogicMachine) machine, RecipeResult.fail(Component.translatable("gtceu.recipe.fail.too.much.neutron"))); + } } return null; } @@ -127,7 +134,11 @@ public static GTRecipe recipeModifier(MetaMachine machine, @NotNull GTRecipe rec @Override public boolean beforeWorking(@Nullable GTRecipe recipe) { if (recipe != null) { - return eV >= recipe.data.getInt("evt") * 1000 * getEVtMultiplier(); + if (eV >= recipe.data.getInt("evt") * 1000 * getEVtMultiplier()) return true; + else { + RecipeResult.of(this, RecipeResult.fail(Component.translatable("gtceu.recipe.fail.no.enough.eV.consumed"))); + return false; + } } return false; } @@ -139,8 +150,7 @@ public boolean onWorking() { int evt = (int) (getRecipeLogic().getLastRecipe().data.getInt("evt") * 1000 * getEVtMultiplier()); if (eV < evt) { - getRecipeLogic().interruptRecipe(); - return false; + getRecipeLogic().setProgress(0); } else { eV -= evt; } diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/MEDualHatchStockPartMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/MEDualHatchStockPartMachine.java index 7dde564f1..d151f1cd8 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/MEDualHatchStockPartMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/MEDualHatchStockPartMachine.java @@ -1,7 +1,14 @@ package org.gtlcore.gtlcore.common.machine.multiblock.part; import org.gtlcore.gtlcore.api.gui.TurnsConfiguratorButton; +import org.gtlcore.gtlcore.api.machine.trait.MEStock.ExportOnlyAEConfigureFluidSlot; +import org.gtlcore.gtlcore.api.machine.trait.MEStock.ExportOnlyAEConfigureItemSlot; +import org.gtlcore.gtlcore.api.machine.trait.MEStock.IMEPartMachine; +import org.gtlcore.gtlcore.api.machine.trait.MEStock.IMESlot; +import org.gtlcore.gtlcore.api.recipe.ingredient.LongIngredient; import org.gtlcore.gtlcore.client.gui.widget.AEDualConfigWidget; +import org.gtlcore.gtlcore.config.ConfigHolder; +import org.gtlcore.gtlcore.integration.ae2.AEUtils; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.gui.GuiTextures; @@ -10,20 +17,17 @@ import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.machine.feature.IDataStickInteractable; -import com.gregtechceu.gtceu.api.machine.trait.NotifiableFluidTank; -import com.gregtechceu.gtceu.api.machine.trait.NotifiableItemStackHandler; +import com.gregtechceu.gtceu.api.machine.trait.*; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.ingredient.*; import com.gregtechceu.gtceu.common.item.IntCircuitBehaviour; import com.gregtechceu.gtceu.integration.ae2.machine.MEBusPartMachine; import com.gregtechceu.gtceu.integration.ae2.slot.*; import com.gregtechceu.gtceu.integration.ae2.utils.AEUtil; import com.lowdragmc.lowdraglib.gui.modular.ModularUI; -import com.lowdragmc.lowdraglib.gui.texture.IGuiTexture; -import com.lowdragmc.lowdraglib.gui.texture.ItemStackTexture; -import com.lowdragmc.lowdraglib.gui.texture.TextTexture; -import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; -import com.lowdragmc.lowdraglib.gui.widget.Widget; -import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; +import com.lowdragmc.lowdraglib.gui.texture.*; +import com.lowdragmc.lowdraglib.gui.widget.*; import com.lowdragmc.lowdraglib.side.fluid.FluidStack; import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; @@ -33,25 +37,28 @@ import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; +import net.minecraft.server.TickTask; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; +import net.minecraft.world.item.crafting.Ingredient; import appeng.api.config.Actionable; import appeng.api.networking.IGrid; +import appeng.api.networking.IGridNodeListener; import appeng.api.networking.storage.IStorageService; -import appeng.api.stacks.AEFluidKey; -import appeng.api.stacks.AEItemKey; -import appeng.api.stacks.AEKey; -import appeng.api.stacks.GenericStack; +import appeng.api.stacks.*; import appeng.api.storage.MEStorage; -import it.unimi.dsi.fastutil.objects.Object2LongMap; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.objects.*; import lombok.Getter; import lombok.Setter; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.List; +import java.util.*; import javax.annotation.ParametersAreNonnullByDefault; @@ -75,6 +82,8 @@ public class MEDualHatchStockPartMachine extends MEBusPartMachine implements IDa private static final IGuiTexture AUTO_PULL_ITEM_ICON = new ItemStackTexture(Items.IRON_INGOT); private static final IGuiTexture AUTO_PULL_FLUID_ICON = new ItemStackTexture(Items.WATER_BUCKET); + private static final boolean ENABLE_ULTIMATE_ME_STOCKING = ConfigHolder.INSTANCE.enableUltimateMEStocking; + protected ExportOnlyAEItemList aeItemHandler; protected ExportOnlyAEFluidList aeFluidHandler; @@ -106,6 +115,12 @@ protected NotifiableFluidTank createTank() { return this.aeFluidHandler; } + @Override + public void onMainNodeStateChanged(IGridNodeListener.State reason) { + super.onMainNodeStateChanged(reason); + if (getMainNode().isOnline()) aeItemHandler.notifyListeners(); + } + @Override public ManagedFieldHolder getFieldHolder() { return MANAGED_FIELD_HOLDER; @@ -136,6 +151,9 @@ private void refreshList() { } IStorageService storageService = grid.getStorageService(); MEStorage networkStorage = storageService.getInventory(); + final var inventory = this.aeItemHandler.getInventory(); + final var fluidInventory = this.aeFluidHandler.getInventory(); + var counter = networkStorage.getAvailableStacks(); int index = 0; for (Object2LongMap.Entry entry : counter) { @@ -154,26 +172,29 @@ private void refreshList() { long request = networkStorage.extract(what, amount, Actionable.SIMULATE, actionSource); if (request == 0) continue; if (isItem) { - this.aeFluidHandler.getInventory()[index].setConfig(null); + ((IMESlot) fluidInventory[index]).setConfigWithoutNotify(null); } else { - this.aeItemHandler.getInventory()[index].setConfig(null); + ((IMESlot) inventory[index]).setConfigWithoutNotify(null); } - var itemSlot = this.aeItemHandler.getInventory()[index]; - var fluidSlot = this.aeFluidHandler.getInventory()[index]; + var itemSlot = inventory[index]; + var fluidSlot = fluidInventory[index]; var slot = isItem ? itemSlot : fluidSlot; if (isItem) { - fluidSlot.setConfig(null); + ((IMESlot) fluidSlot).setConfigWithoutNotify(null); fluidSlot.setStock(null); } else { - itemSlot.setConfig(null); + ((IMESlot) itemSlot).setConfigWithoutNotify(null); itemSlot.setStock(null); } - slot.setConfig(new GenericStack(what, 1)); + ((IMESlot) slot).setConfigWithoutNotify(new GenericStack(what, 1)); slot.setStock(new GenericStack(what, request)); index++; } aeItemHandler.clearInventory(index); aeFluidHandler.clearInventory(index); + + ((IMEPartMachine) aeItemHandler).onConfigChanged(); + ((IMEPartMachine) aeFluidHandler).onConfigChanged(); } protected void syncME() { @@ -186,18 +207,15 @@ protected void syncME() { ExportOnlyAEFluidSlot[] aeFluid = aeFluidHandler.getInventory(); ExportOnlyAESlot slot; for (int i = 0; i < aeItem.length; i++) { - boolean isFluid = false; slot = aeItem[i]; var config = slot.getConfig(); if (config == null) { slot = aeFluid[i]; - isFluid = true; config = slot.getConfig(); } if (config != null) { var key = config.what(); - long extracted = networkInv.extract(key, isFluid ? Long.MAX_VALUE : Integer.MAX_VALUE, - Actionable.SIMULATE, actionSource); + long extracted = networkInv.extract(key, Long.MAX_VALUE, Actionable.SIMULATE, actionSource); if (extracted > 0) { slot.setStock(new GenericStack(key, extracted)); continue; @@ -205,6 +223,19 @@ protected void syncME() { } slot.setStock(null); } + ((IMEPartMachine) aeItemHandler).setChanged(true); + ((IMEPartMachine) aeFluidHandler).setChanged(true); + } + + @Override + public void onLoad() { + super.onLoad(); + if (getLevel() instanceof ServerLevel serverLevel) { + serverLevel.getServer().tell(new TickTask(1, () -> { + ((IMEPartMachine) this.aeItemHandler).onConfigChanged(); + ((IMEPartMachine) this.aeFluidHandler).onConfigChanged(); + })); + } } @Override @@ -283,6 +314,9 @@ protected void readConfigFromTag(CompoundTag tag) { } if (tag.contains("ConfigStacks")) { + final var inventory = this.aeItemHandler.getInventory(); + final var fluidInventory = this.aeFluidHandler.getInventory(); + CompoundTag configStacks = tag.getCompound("ConfigStacks"); for (int i = 0; i < CONFIG_SIZE; i++) { String key = Integer.toString(i); @@ -291,16 +325,19 @@ protected void readConfigFromTag(CompoundTag tag) { var stack = GenericStack.readTag(configTag); if (stack != null) { if (stack.what() instanceof AEItemKey) { - this.aeItemHandler.getInventory()[i].setConfig(stack); + ((IMESlot) inventory[i]).setConfigWithoutNotify(stack); } else { - this.aeFluidHandler.getInventory()[i].setConfig(stack); + ((IMESlot) fluidInventory[i]).setConfigWithoutNotify(stack); } continue; } } - this.aeItemHandler.getInventory()[i].setConfig(null); - this.aeFluidHandler.getInventory()[i].setConfig(null); + ((IMESlot) inventory[i]).setConfigWithoutNotify(null); + ((IMESlot) fluidInventory[i]).setConfigWithoutNotify(null); } + + ((IMEPartMachine) aeItemHandler).onConfigChanged(); + ((IMEPartMachine) aeFluidHandler).onConfigChanged(); } if (tag.contains("GhostCircuit")) { @@ -335,10 +372,39 @@ public boolean onDataStickLeftClick(Player player, ItemStack dataStick) { return true; } - private class ExportOnlyAEStockingItemList extends ExportOnlyAEItemList { + private class ExportOnlyAEStockingItemList extends ExportOnlyAEItemList implements IMEPartMachine { + + protected ObjectArrayList configList = new ObjectArrayList<>(); + + protected IntArrayList configIndexList = new IntArrayList(); public ExportOnlyAEStockingItemList(MetaMachine holder, int slots) { super(holder, slots, ExportOnlyAEStockingItemSlot::new); + for (ExportOnlyAEItemSlot exportOnlyAEItemSlot : inventory) { + ((IMESlot) exportOnlyAEItemSlot).setOnConfigChanged(this::onConfigChanged); + } + } + + @Override + public void clearInventory(int startIndex) { + for (int i = startIndex; i < this.getConfigurableSlots(); ++i) { + IConfigurableSlot slot = this.getConfigurableSlot(i); + ((IMESlot) slot).setConfigWithoutNotify(null); + slot.setStock(null); + } + } + + @Override + public void onConfigChanged() { + configList.clear(); + configIndexList.clear(); + for (int i = 0, inventoryLength = inventory.length; i < inventoryLength; i++) { + final var config = inventory[i].getConfig(); + if (config != null && config.what() instanceof AEItemKey key) { + configList.add(key); + configIndexList.add(i); + } + } } @Override @@ -350,9 +416,83 @@ public boolean isAutoPull() { public boolean isStocking() { return true; } + + @Override + public List handleRecipeInner(IO io, GTRecipe recipe, List left, @Nullable String slotName, boolean simulate) { + if (io != IO.IN || left.isEmpty()) { + return left; + } + IGrid grid = getMainNode().getGrid(); + if (grid == null) { + return left; + } + + MEStorage aeNetwork = grid.getStorageService().getInventory(); + boolean changed = false; + var listIterator = left.listIterator(); + + while (listIterator.hasNext()) { + Ingredient ingredient = listIterator.next(); + if (ingredient.isEmpty()) { + listIterator.remove(); + } else { + long amount; + if (ingredient instanceof LongIngredient li) amount = li.getActualAmount(); + else if (ingredient instanceof SizedIngredient si) amount = si.getAmount(); + else amount = 1; + if (amount < 1) listIterator.remove(); + else { + for (int i = 0, configListSize = configList.size(); i < configListSize; i++) { + AEItemKey aeItemKey = configList.get(i); + if (aeItemKey.matches(ingredient)) { + long extracted = aeNetwork.extract(aeItemKey, amount, simulate ? Actionable.SIMULATE : Actionable.MODULATE, getActionSource()); + if (extracted > 0) { + changed = true; + amount -= extracted; + if (!simulate) { + var slot = this.inventory[configIndexList.getInt(i)]; + if (slot.getStock() != null) { + long amt = slot.getStock().amount() - extracted; + if (amt == 0) slot.setStock(null); + else slot.setStock(new GenericStack(aeItemKey, amt)); + } + } + } + } + if (amount <= 0L) { + listIterator.remove(); + break; + } + } + } + } + } + if (!simulate && changed) { + setChanged(true); + this.onContentsChanged(); + } + return left.isEmpty() ? null : left; + } + + @Override + public @Nullable Object2LongMap getMEItemMap() { + if (ENABLE_ULTIMATE_ME_STOCKING || getChanged()) { + setChanged(false); + final var itemMap = getItemMap(); + itemMap.clear(); + final MEStorage aeNetwork = Objects.requireNonNull(getMainNode().getGrid()).getStorageService().getInventory(); + for (var key : configList) { + long extracted = aeNetwork.extract(key, Long.MAX_VALUE, Actionable.SIMULATE, getActionSource()); + if (extracted > 0) { + itemMap.addTo(key.toStack(), extracted); + } + } + } + return getItemMap().isEmpty() ? null : getItemMap(); + } } - private class ExportOnlyAEStockingItemSlot extends ExportOnlyAEItemSlot { + private class ExportOnlyAEStockingItemSlot extends ExportOnlyAEConfigureItemSlot { public ExportOnlyAEStockingItemSlot() { super(); @@ -398,10 +538,39 @@ public ExportOnlyAEStockingItemSlot copy() { } } - private class ExportOnlyAEStockingFluidList extends ExportOnlyAEFluidList { + private class ExportOnlyAEStockingFluidList extends ExportOnlyAEFluidList implements IMEPartMachine { + + protected ObjectArrayList configList = new ObjectArrayList<>(); + + protected IntArrayList configIndexList = new IntArrayList(); public ExportOnlyAEStockingFluidList(MetaMachine holder, int slots) { super(holder, slots, ExportOnlyAEStockingFluidSlot::new); + for (ExportOnlyAEFluidSlot exportOnlyAEFluidSlot : inventory) { + ((IMESlot) exportOnlyAEFluidSlot).setOnConfigChanged(this::onConfigChanged); + } + } + + @Override + public void clearInventory(int startIndex) { + for (int i = startIndex; i < this.getConfigurableSlots(); ++i) { + IConfigurableSlot slot = this.getConfigurableSlot(i); + ((IMESlot) slot).setConfigWithoutNotify(null); + slot.setStock(null); + } + } + + @Override + public void onConfigChanged() { + configList.clear(); + configIndexList.clear(); + for (int i = 0, inventoryLength = inventory.length; i < inventoryLength; i++) { + final var config = inventory[i].getConfig(); + if (config != null && config.what() instanceof AEFluidKey key) { + configList.add(key); + configIndexList.add(i); + } + } } @Override @@ -413,9 +582,80 @@ public boolean isAutoPull() { public boolean isStocking() { return true; } + + @Override + public List handleRecipeInner(IO io, GTRecipe recipe, List left, @Nullable String slotName, boolean simulate) { + if (io != IO.IN || left.isEmpty()) { + return left; + } + IGrid grid = getMainNode().getGrid(); + if (grid == null) { + return left; + } + + MEStorage aeNetwork = grid.getStorageService().getInventory(); + boolean changed = false; + var listIterator = left.listIterator(); + + while (listIterator.hasNext()) { + FluidIngredient ingredient = listIterator.next(); + if (ingredient.isEmpty()) { + listIterator.remove(); + } else { + long amount = ingredient.getAmount(); + if (amount < 1) listIterator.remove(); + else { + for (int i = 0, configListSize = configList.size(); i < configListSize; i++) { + AEFluidKey aeFluidKey = configList.get(i); + if (AEUtils.testFluidIngredient(ingredient, aeFluidKey)) { + long extracted = aeNetwork.extract(aeFluidKey, amount, simulate ? Actionable.SIMULATE : Actionable.MODULATE, getActionSource()); + if (extracted > 0) { + changed = true; + amount -= extracted; + if (!simulate) { + var slot = this.inventory[configIndexList.getInt(i)]; + if (slot.getStock() != null) { + long amt = slot.getStock().amount() - extracted; + if (amt == 0) slot.setStock(null); + else slot.setStock(new GenericStack(aeFluidKey, amt)); + } + } + } + } + if (amount <= 0L) { + listIterator.remove(); + break; + } + } + } + } + } + if (!simulate && changed) { + setChanged(true); + this.onContentsChanged(); + } + return left.isEmpty() ? null : left; + } + + @Override + public @NotNull List getMEFluidList() { + if (ENABLE_ULTIMATE_ME_STOCKING || getChanged()) { + setChanged(false); + final var fluidList = getFluidList(); + fluidList.clear(); + final MEStorage aeNetwork = Objects.requireNonNull(getMainNode().getGrid()).getStorageService().getInventory(); + for (var key : configList) { + long extracted = aeNetwork.extract(key, Long.MAX_VALUE, Actionable.SIMULATE, getActionSource()); + if (extracted > 0) { + fluidList.add(FluidStack.create(key.getFluid(), extracted)); + } + } + } + return getFluidList(); + } } - private class ExportOnlyAEStockingFluidSlot extends ExportOnlyAEFluidSlot { + private class ExportOnlyAEStockingFluidSlot extends ExportOnlyAEConfigureFluidSlot { public ExportOnlyAEStockingFluidSlot() { super(); diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/PaginationUIManager.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/PaginationUIManager.java new file mode 100644 index 000000000..3cdec0c19 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/PaginationUIManager.java @@ -0,0 +1,198 @@ +package org.gtlcore.gtlcore.common.machine.multiblock.part; + +import org.gtlcore.gtlcore.integration.ae2.widget.AEPatternViewExtendSlotWidget; + +import com.gregtechceu.gtceu.api.gui.GuiTextures; + +import com.lowdragmc.lowdraglib.gui.texture.GuiTextureGroup; +import com.lowdragmc.lowdraglib.gui.texture.TextTexture; +import com.lowdragmc.lowdraglib.gui.widget.ButtonWidget; +import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; +import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; +import com.lowdragmc.lowdraglib.side.item.IItemTransfer; +import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; + +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; + +import appeng.crafting.pattern.EncodedPatternItem; +import lombok.Getter; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Function; +import java.util.function.IntConsumer; + +/** + * 管理翻页UI的创建和状态 + */ +public class PaginationUIManager { + + // Getters for configuration + // UI配置 + @Getter + private final int uiWidth; + @Getter + private final int uiHeight; + @Getter + private final int patternsPerRow; + @Getter + private final int rowsPerPage; + @Getter + private final int maxPages; + @Getter + private final int maxPatternCount; + + private final IItemTransfer patternInventory; + + @DescSynced + private int currentPageIndex; + + private WidgetGroup paginationUI; + + // 回调函数 + private final @Nullable Function isCached; + private final IntConsumer onPatternChange; + private @Nullable IntConsumer onMiddleClicked; + + public PaginationUIManager(int patternsPerRow, int rowsPerPage, int maxPages, + int uiWidth, int uiHeight, + IntConsumer onPatternChange, + @Nullable Function isCached, + IItemTransfer patternInventory) { + this.patternsPerRow = patternsPerRow; + this.rowsPerPage = rowsPerPage; + this.maxPages = maxPages; + this.maxPatternCount = patternsPerRow * rowsPerPage * maxPages; + this.uiWidth = uiWidth; + this.uiHeight = uiHeight; + + this.onPatternChange = onPatternChange; + this.isCached = isCached; + this.patternInventory = patternInventory; + } + + public PaginationUIManager(int patternsPerRow, int rowsPerPage, int totalCount, + int uiWidth, int uiHeight, + IntConsumer onPatternChange, + IItemTransfer patternInventory) { + this.patternsPerRow = patternsPerRow; + this.rowsPerPage = rowsPerPage; + this.maxPatternCount = totalCount; + this.maxPages = (int) Math.ceil((double) totalCount / (rowsPerPage * patternsPerRow)); + this.uiWidth = uiWidth; + this.uiHeight = uiHeight; + + this.onPatternChange = onPatternChange; + isCached = null; + this.patternInventory = patternInventory; + } + + /** + * 重建完整的翻页UI + */ + public WidgetGroup createPaginationUI(@Nullable IntConsumer onMiddleClicked) { + this.onMiddleClicked = onMiddleClicked; + + var basePage = new WidgetGroup(0, 0, uiWidth, uiHeight); + // 默认重建当前页面的slot组 + this.paginationUI = rebuildPatternSlots(currentPageIndex); + basePage.addWidget(this.paginationUI); + // 重建翻页控制按钮 + createPageControls(basePage); + + return basePage; + } + + /** + * 重建所有页面的pattern slots + */ + protected WidgetGroup rebuildPatternSlots(int pageIndex) { + final int startY = 16; + final int patternAreaHeight = rowsPerPage * 18; + final int startSlot = pageIndex * (rowsPerPage * patternsPerRow); + final int endSlot = Math.min(startSlot + (rowsPerPage * patternsPerRow), maxPatternCount); + + var pageGroup = new WidgetGroup(0, startY, uiWidth, patternAreaHeight); + recreatePatternSlots(pageGroup, startSlot, endSlot); + return pageGroup; + } + + private void recreatePatternSlots(WidgetGroup pageGroup, int startSlot, int endSlot) { + for (int i = startSlot; i < endSlot; i++) { + int finalI = i; + int slotInPage = i - startSlot; + int row = slotInPage / patternsPerRow; + int col = slotInPage % patternsPerRow; + + int x = uiWidth == 106 ? (106 - patternsPerRow * 18) / 2 + col * 18 : 8 + col * 18; + int y = row * 18; + + AEPatternViewExtendSlotWidget slot = (AEPatternViewExtendSlotWidget) new AEPatternViewExtendSlotWidget(patternInventory, i, x, y) + .setOnPatternSlotChanged(() -> onPatternChange.accept(finalI)) + .setOccupiedTexture(GuiTextures.SLOT) + .setItemHook(stack -> { + if (!stack.isEmpty() && stack.getItem() instanceof EncodedPatternItem iep) { + final ItemStack out = iep.getOutput(stack); + if (!out.isEmpty()) return out; + } + return stack; + }) + .setBackground(GuiTextures.SLOT, GuiTextures.PATTERN_OVERLAY); + + if (onMiddleClicked != null) { + slot.setOnMiddleClick(() -> onMiddleClicked.accept(finalI)); + } + if (isCached != null) { + slot.setOnAddedTooltips((s, l) -> { + if (isCached.apply(finalI)) l.add(Component.translatable("gtceu.machine.pattern.recipe.cache")); + }); + } + + pageGroup.addWidget(slot); + } + } + + /** + * 重建翻页控制按钮 + */ + protected void createPageControls(WidgetGroup parentGroup) { + int startY = 16; + int patternAreaHeight = rowsPerPage * 18; + int pageControlY = startY + patternAreaHeight + 4; + + // 上一页按钮 + parentGroup.addWidget(new ButtonWidget(8, pageControlY, 30, 12, + new GuiTextureGroup(GuiTextures.BUTTON, new TextTexture("<<")), + clickData -> { + if (currentPageIndex > 0) { + currentPageIndex--; + refreshPage(currentPageIndex); + } + })); + + // 页面指示器(居中) + int pageIndicatorWidth = 12; // 估计页面文本宽度 + int pageIndicatorX = (uiWidth - pageIndicatorWidth) / 2; // 居中文本块 + parentGroup.addWidget(new LabelWidget(pageIndicatorX, pageControlY + 2, + () -> (currentPageIndex + 1) + " / " + maxPages)); + + // 下一页按钮 + parentGroup.addWidget(new ButtonWidget(uiWidth - 38, pageControlY, 30, 12, + new GuiTextureGroup(GuiTextures.BUTTON, new TextTexture(">>")), + clickData -> { + if (currentPageIndex < maxPages - 1) { + currentPageIndex++; + refreshPage(currentPageIndex); + } + })); + } + + public void refreshPage(int pageIndex) { + this.paginationUI.clearAllWidgets(); + + final int startSlot = pageIndex * (rowsPerPage * patternsPerRow); + final int endSlot = Math.min(startSlot + (rowsPerPage * patternsPerRow), maxPatternCount); + + recreatePatternSlots(this.paginationUI, startSlot, endSlot); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/TagFilterMEStockBusPartMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/TagFilterMEStockBusPartMachine.java index 84030977d..e58df7a6b 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/TagFilterMEStockBusPartMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/TagFilterMEStockBusPartMachine.java @@ -1,57 +1,51 @@ package org.gtlcore.gtlcore.common.machine.multiblock.part; +import org.gtlcore.gtlcore.api.machine.trait.MEStock.ExportOnlyAEConfigureItemSlot; +import org.gtlcore.gtlcore.api.machine.trait.MEStock.IMEPartMachine; +import org.gtlcore.gtlcore.api.machine.trait.MEStock.IMESlot; +import org.gtlcore.gtlcore.api.recipe.ingredient.LongIngredient; +import org.gtlcore.gtlcore.config.ConfigHolder; + +import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.gui.GuiTextures; -import com.gregtechceu.gtceu.api.gui.fancy.ConfiguratorPanel; -import com.gregtechceu.gtceu.api.gui.fancy.IFancyConfigurator; -import com.gregtechceu.gtceu.api.gui.fancy.IFancyConfiguratorButton; +import com.gregtechceu.gtceu.api.gui.fancy.*; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.machine.trait.NotifiableItemStackHandler; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.ingredient.SizedIngredient; import com.gregtechceu.gtceu.common.item.IntCircuitBehaviour; import com.gregtechceu.gtceu.integration.ae2.machine.MEInputBusPartMachine; -import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEItemList; -import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEItemSlot; -import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAESlot; +import com.gregtechceu.gtceu.integration.ae2.slot.*; import com.lowdragmc.lowdraglib.gui.texture.IGuiTexture; import com.lowdragmc.lowdraglib.gui.texture.TextTexture; -import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; -import com.lowdragmc.lowdraglib.gui.widget.TextFieldWidget; -import com.lowdragmc.lowdraglib.gui.widget.Widget; -import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; +import com.lowdragmc.lowdraglib.gui.widget.*; import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.core.Holder; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; -import net.minecraft.tags.TagKey; -import net.minecraft.world.item.Item; +import net.minecraft.server.TickTask; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.material.Fluid; -import net.minecraftforge.registries.ForgeRegistries; +import net.minecraft.world.item.crafting.Ingredient; import appeng.api.config.Actionable; import appeng.api.networking.IGrid; import appeng.api.networking.storage.IStorageService; -import appeng.api.stacks.AEItemKey; -import appeng.api.stacks.AEKey; -import appeng.api.stacks.GenericStack; +import appeng.api.stacks.*; import appeng.api.storage.MEStorage; import appeng.util.prioritylist.IPartitionList; -import com.glodblock.github.extendedae.common.me.taglist.TagExpParser; -import it.unimi.dsi.fastutil.objects.Object2LongMap; -import it.unimi.dsi.fastutil.objects.Reference2BooleanMap; -import it.unimi.dsi.fastutil.objects.Reference2BooleanOpenHashMap; +import com.glodblock.github.extendedae.common.me.taglist.TagPriorityList; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.objects.*; import lombok.Getter; import lombok.Setter; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; +import java.util.*; import javax.annotation.ParametersAreNonnullByDefault; @@ -66,6 +60,8 @@ public class TagFilterMEStockBusPartMachine extends MEInputBusPartMachine { protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(TagFilterMEStockBusPartMachine.class, MEInputBusPartMachine.MANAGED_FIELD_HOLDER); + private static final boolean ENABLE_ULTIMATE_ME_STOCKING = ConfigHolder.INSTANCE.enableUltimateMEStocking; + @Persisted protected String tagWhite = ""; @@ -95,7 +91,7 @@ public void attachConfigurators(ConfiguratorPanel configuratorPanel) { new TextTexture("数量▼"), this::isCountSort, (clickData, pressed) -> setCountSort(pressed)) - .setTooltipsSupplier(pressed -> List.of(Component.literal("自动拉取排序方式")))); + .setTooltipsSupplier(pressed -> List.of(Component.translatable("tooltip.gtlcore.auto_pull_sort_mode")))); configuratorPanel.attachConfigurators(new FilterIFancyConfigurator()); } @@ -120,8 +116,8 @@ protected void syncME() { if (config != null) { // Try to fill the slot var key = config.what(); - // try max fill Integer.MAX_VALUE - long extracted = networkInv.extract(key, Integer.MAX_VALUE, Actionable.SIMULATE, actionSource); + // try max fill Long.MAX_VALUE + long extracted = networkInv.extract(key, Long.MAX_VALUE, Actionable.SIMULATE, actionSource); if (extracted > 0) { slot.setStock(new GenericStack(key, extracted)); continue; @@ -139,9 +135,11 @@ private void refreshList() { } IStorageService storageService = grid.getStorageService(); MEStorage networkStorage = storageService.getInventory(); - IPartitionList filter = new ItemTagPriority(TagExpParser.getMatchingOre(this.tagWhite), - TagExpParser.getMatchingOre(this.tagBlack), this.tagWhite + this.tagBlack); - List order = new ArrayList<>(); + IPartitionList filter = new TagPriorityList(this.tagWhite, this.tagBlack); + + List order = new ObjectArrayList<>(); + final var inventory = this.aeItemHandler.getInventory(); + var counter = networkStorage.getAvailableStacks(); int index = 0; for (Object2LongMap.Entry entry : counter) { @@ -159,8 +157,8 @@ private void refreshList() { long request = networkStorage.extract(what, amount, Actionable.SIMULATE, actionSource); if (request == 0) continue; // Ensure that it is valid to configure with this stack - var slot = this.aeItemHandler.getInventory()[index]; - slot.setConfig(new GenericStack(what, 1)); + var slot = inventory[index]; + ((IMESlot) slot).setConfigWithoutNotify(new GenericStack(what, 1)); slot.setStock(new GenericStack(what, request)); index++; } @@ -173,13 +171,23 @@ private void refreshList() { long request = networkStorage.extract(stack.what(), stack.amount(), Actionable.SIMULATE, actionSource); if (request == 0) continue; // Ensure that it is valid to configure with this stack - var slot = this.aeItemHandler.getInventory()[index]; - slot.setConfig(new GenericStack(stack.what(), 1)); + var slot = inventory[index]; + ((IMESlot) slot).setConfigWithoutNotify(new GenericStack(stack.what(), 1)); slot.setStock(new GenericStack(stack.what(), request)); index++; } } aeItemHandler.clearInventory(index); + + ((IMEPartMachine) aeItemHandler).onConfigChanged(); + } + + @Override + public void onLoad() { + super.onLoad(); + if (getLevel() instanceof ServerLevel serverLevel) { + serverLevel.getServer().tell(new TickTask(1, () -> ((IMEPartMachine) this.aeItemHandler).onConfigChanged())); + } } @Override @@ -216,7 +224,7 @@ private class FilterIFancyConfigurator implements IFancyConfigurator { @Override public Component getTitle() { - return Component.literal("标签过滤配置"); + return Component.translatable("gui.gtlcore.tag_filter_config"); } @Override @@ -228,26 +236,55 @@ public IGuiTexture getIcon() { public Widget createConfigurator() { return new WidgetGroup(0, 0, 132, 100) .addWidget(new LabelWidget(9, 4, - () -> "标签白名单")) + () -> Component.translatable("gui.gtlcore.tag_whitelist").getString())) .addWidget(new TextFieldWidget(9, 16, 114, 16, () -> tagWhite, v -> tagWhite = v)) .addWidget(new LabelWidget(9, 36, - () -> "标签黑名单")) + () -> Component.translatable("gui.gtlcore.tag_blacklist").getString())) .addWidget(new TextFieldWidget(9, 48, 114, 16, () -> tagBlack, v -> tagBlack = v)) .addWidget(new LabelWidget(0, 68, - () -> "* 表示通配符 ()表示优先")) + () -> Component.translatable("gui.gtlcore.wildcard_info").getString())) .addWidget(new LabelWidget(0, 84, - () -> "& = 逻辑与 | = 逻辑或 ^ = 逻辑异或")); + () -> Component.translatable("gui.gtlcore.logic_operators").getString())); } } - private class ExportOnlyAEStockingItemList extends ExportOnlyAEItemList { + private class ExportOnlyAEStockingItemList extends ExportOnlyAEItemList implements IMEPartMachine { + + protected ObjectArrayList configList = new ObjectArrayList<>(); + + protected IntArrayList configIndexList = new IntArrayList(); public ExportOnlyAEStockingItemList(MetaMachine holder, int slots) { super(holder, slots, ExportOnlyAEStockingItemSlot::new); + for (ExportOnlyAEItemSlot exportOnlyAEItemSlot : inventory) { + ((IMESlot) exportOnlyAEItemSlot).setOnConfigChanged(this::onConfigChanged); + } + } + + @Override + public void clearInventory(int startIndex) { + for (int i = startIndex; i < this.getConfigurableSlots(); ++i) { + IConfigurableSlot slot = this.getConfigurableSlot(i); + ((IMESlot) slot).setConfigWithoutNotify(null); + slot.setStock(null); + } + } + + @Override + public void onConfigChanged() { + configList.clear(); + configIndexList.clear(); + for (int i = 0, inventoryLength = inventory.length; i < inventoryLength; i++) { + final var config = inventory[i].getConfig(); + if (config != null && config.what() instanceof AEItemKey key) { + configList.add(key); + configIndexList.add(i); + } + } } @Override @@ -260,9 +297,84 @@ public boolean isAutoPull() { // only read from the network, cant config this slot return true; } + + @Override + public List handleRecipeInner(IO io, GTRecipe recipe, List left, @Nullable String slotName, boolean simulate) { + if (io != IO.IN || left.isEmpty()) { + return left; + } + IGrid grid = getMainNode().getGrid(); + if (grid == null) { + return left; + } + + MEStorage aeNetwork = grid.getStorageService().getInventory(); + boolean changed = false; + var listIterator = left.listIterator(); + + while (listIterator.hasNext()) { + Ingredient ingredient = listIterator.next(); + if (ingredient.isEmpty()) { + listIterator.remove(); + } else { + long amount; + if (ingredient instanceof LongIngredient li) amount = li.getActualAmount(); + else if (ingredient instanceof SizedIngredient si) amount = si.getAmount(); + else amount = 1; + if (amount < 1) listIterator.remove(); + else { + for (int i = 0, configListSize = configList.size(); i < configListSize; i++) { + AEItemKey aeItemKey = configList.get(i); + if (aeItemKey.matches(ingredient)) { + long extracted = aeNetwork.extract(aeItemKey, amount, simulate ? Actionable.SIMULATE : Actionable.MODULATE, getActionSource()); + if (extracted > 0) { + changed = true; + amount -= extracted; + if (!simulate) { + var slot = this.inventory[configIndexList.getInt(i)]; + if (slot.getStock() != null) { + long amt = slot.getStock().amount() - extracted; + if (amt == 0) slot.setStock(null); + else slot.setStock(new GenericStack(aeItemKey, amt)); + } + } + } + } + if (amount <= 0L) { + listIterator.remove(); + break; + } + } + } + } + } + if (!simulate && changed) { + setChanged(true); + this.onContentsChanged(); + } + + return left.isEmpty() ? null : left; + } + + @Override + public @Nullable Object2LongMap getMEItemMap() { + if (ENABLE_ULTIMATE_ME_STOCKING || getChanged()) { + setChanged(false); + final var itemMap = getItemMap(); + itemMap.clear(); + final MEStorage aeNetwork = Objects.requireNonNull(getMainNode().getGrid()).getStorageService().getInventory(); + for (var key : configList) { + long extracted = aeNetwork.extract(key, Long.MAX_VALUE, Actionable.SIMULATE, getActionSource()); + if (extracted > 0) { + itemMap.addTo(key.toStack(), extracted); + } + } + } + return getItemMap().isEmpty() ? null : getItemMap(); + } } - private class ExportOnlyAEStockingItemSlot extends ExportOnlyAEItemSlot { + private class ExportOnlyAEStockingItemSlot extends ExportOnlyAEConfigureItemSlot { public ExportOnlyAEStockingItemSlot() { super(); @@ -309,58 +421,4 @@ public ExportOnlyAEStockingItemSlot copy() { return new ExportOnlyAEStockingItemSlot(this.config == null ? null : copy(this.config), this.stock == null ? null : copy(this.stock)); } } - - private static class ItemTagPriority implements IPartitionList { - - private final Set> whiteSet; - private final Set> blackSet; - private final String tagExp; - private final Reference2BooleanMap memory = new Reference2BooleanOpenHashMap<>(); - - public ItemTagPriority(Set> whiteSet, Set> blackSet, String tagExp) { - this.whiteSet = whiteSet; - this.blackSet = blackSet; - this.tagExp = tagExp; - } - - @Override - public boolean isListed(AEKey aeKey) { - Object key = aeKey.getPrimaryKey(); - return this.memory.computeIfAbsent(key, this::eval); - } - - @Override - public boolean isEmpty() { - return tagExp.isEmpty(); - } - - @Override - public Iterable getItems() { - return List.of(); - } - - private boolean eval(@NotNull Object obj) { - Holder refer = null; - if (obj instanceof Item item) { - refer = ForgeRegistries.ITEMS.getHolder(item).orElse(null); - } else if (obj instanceof Fluid) { - return false; - } - - if (refer != null) { - if (this.whiteSet.isEmpty()) { - return false; - } - - boolean pass = refer.tags().anyMatch(whiteSet::contains); - if (pass) { - if (!this.blackSet.isEmpty()) { - return refer.tags().noneMatch(blackSet::contains); - } - return true; - } - } - return false; - } - } } diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/WirelessOpticalDataHatchMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/WirelessOpticalDataHatchMachine.java index 96e601e82..dab4366a2 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/WirelessOpticalDataHatchMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/WirelessOpticalDataHatchMachine.java @@ -1,6 +1,7 @@ package org.gtlcore.gtlcore.common.machine.multiblock.part; import org.gtlcore.gtlcore.api.capability.BindCapability; +import org.gtlcore.gtlcore.api.recipe.RecipeResult; import com.gregtechceu.gtceu.api.capability.IDataAccessHatch; import com.gregtechceu.gtceu.api.capability.IOpticalDataAccessHatch; @@ -8,6 +9,7 @@ import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.machine.feature.IInteractedMachine; +import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; import com.gregtechceu.gtceu.api.machine.multiblock.PartAbility; import com.gregtechceu.gtceu.api.machine.multiblock.part.MultiblockPartMachine; @@ -97,9 +99,13 @@ public boolean isRecipeAvailable(GTRecipe recipe, Collection s return isRecipeAvailable(dataAccesses, seen, recipe) || isRecipeAvailable(transmitters, seen, recipe); } else { Level level = getLevel(); - if (level == null || transmitterPos == null) return false; + if (level == null || transmitterPos == null) { + RecipeResult.of((IRecipeLogicMachine) getControllers().get(0), RecipeResult.fail(Component.translatable("gtceu.machine.wireless_data_receiver_hatch.unbind"))); + return false; + } if (MetaMachine.getMachine(level, transmitterPos) instanceof WirelessOpticalDataHatchMachine machine) { - return machine.isRecipeAvailable(recipe, seen); + if (machine.isRecipeAvailable(recipe, seen)) return true; + else RecipeResult.of((IRecipeLogicMachine) getControllers().get(0), RecipeResult.FAIL_NO_FIND_RESEARCHED); } } } @@ -108,8 +114,8 @@ public boolean isRecipeAvailable(GTRecipe recipe, Collection s private static boolean isRecipeAvailable(@NotNull Iterable hatches, @NotNull Collection seen, @NotNull GTRecipe recipe) { for (IDataAccessHatch hatch : hatches) { - if (seen.contains(hatch)) continue; - if (hatch.isRecipeAvailable(recipe, seen)) { + if (hatch.isCreative()) return true; + if (!seen.contains(hatch) && hatch.isRecipeAvailable(recipe, seen)) { return true; } } diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MECraftHandler.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MECraftHandler.java new file mode 100644 index 000000000..370f75f8f --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MECraftHandler.java @@ -0,0 +1,73 @@ +package org.gtlcore.gtlcore.common.machine.multiblock.part.ae; + +import org.gtlcore.gtlcore.api.recipe.ingredient.LongIngredient; + +import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.chance.logic.ChanceLogic; +import com.gregtechceu.gtceu.api.recipe.content.Content; +import com.gregtechceu.gtceu.data.recipe.builder.GTRecipeBuilder; + +import net.minecraft.world.item.Item; +import net.minecraft.world.item.crafting.Ingredient; + +import appeng.api.stacks.AEItemKey; +import it.unimi.dsi.fastutil.objects.Object2LongMaps; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; + +import java.util.Collections; +import java.util.List; + +public class MECraftHandler extends NotifiableMAHandlerTrait { + + public MECraftHandler(MEMolecularAssemblerIOPartMachine machine) { + super(machine); + } + + public MEMolecularAssemblerIOPartMachine getMachine() { + return (MEMolecularAssemblerIOPartMachine) this.machine; + } + + @Override + public void handleRecipeOutput(GTRecipe recipe) { + final var buffer = getMachine().getBuffer(); + for (Content content : recipe.outputs.getOrDefault(ItemRecipeCapability.CAP, Collections.emptyList())) { + if (content.content instanceof LongIngredient longIngredient) { + buffer.addTo(AEItemKey.of(longIngredient.getItems()[0]), longIngredient.getActualAmount()); + } + } + getMachine().getMETrait().notifySelfIO(); + } + + @Override + public GTRecipe extractGTRecipe(long parallelAmount, int tickDuration) { + GTRecipe output = GTRecipeBuilder.ofRaw().buildRawRecipe(); + List outputList = output.outputs.computeIfAbsent(ItemRecipeCapability.CAP, cap -> new ObjectArrayList<>()); + long remain = parallelAmount; + for (var it = Object2LongMaps.fastIterator(getMachine().getOutputItems()); it.hasNext() && remain > 0;) { + var entry = it.next(); + var key = entry.getKey(); + if (!(key.what() instanceof AEItemKey aeItemKey)) { + it.remove(); + continue; + } + Item item = aeItemKey.getItem(); + long multiply = entry.getLongValue(); + + long extract = Math.min(multiply, remain); + + var cont = new Content(LongIngredient.create(Ingredient.of(item), extract * key.amount()), ChanceLogic.getMaxChancedValue(), ChanceLogic.getMaxChancedValue(), 0, null, null); + outputList.add(cont); + + remain -= extract; + multiply -= extract; + if (multiply == 0) it.remove(); + else entry.setValue(multiply); + } + if (outputList.isEmpty()) return null; + else { + output.duration = tickDuration; + return output; + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MECraftParallelCorePartMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MECraftParallelCorePartMachine.java new file mode 100644 index 000000000..571bc4d99 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MECraftParallelCorePartMachine.java @@ -0,0 +1,29 @@ +package org.gtlcore.gtlcore.common.machine.multiblock.part.ae; + +import org.gtlcore.gtlcore.api.machine.trait.AECraft.IMECraftParallelCore; + +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.multiblock.part.MultiblockPartMachine; + +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.phys.BlockHitResult; + +public class MECraftParallelCorePartMachine extends MultiblockPartMachine implements IMECraftParallelCore { + + public final static int PARALLEL = 4194304; + + public MECraftParallelCorePartMachine(IMachineBlockEntity holder) { + super(holder); + } + + @Override + public int getParallel() { + return PARALLEL; + } + + @Override + public boolean shouldOpenUI(Player player, InteractionHand hand, BlockHitResult hit) { + return false; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MECraftPatternContainerPartMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MECraftPatternContainerPartMachine.java new file mode 100644 index 000000000..9370feed3 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MECraftPatternContainerPartMachine.java @@ -0,0 +1,118 @@ +package org.gtlcore.gtlcore.common.machine.multiblock.part.ae; + +import org.gtlcore.gtlcore.api.machine.trait.AECraft.IMECraftPatternContainer; +import org.gtlcore.gtlcore.integration.ae2.AEUtils; +import org.gtlcore.gtlcore.integration.ae2.widget.AEPatternViewExtendSlotWidget; + +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.feature.IMachineLife; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; +import com.gregtechceu.gtceu.api.machine.multiblock.part.MultiblockPartMachine; + +import com.lowdragmc.lowdraglib.gui.widget.Widget; +import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; +import com.lowdragmc.lowdraglib.misc.ItemStackTransfer; +import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; +import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; +import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.BlockHitResult; + +import appeng.crafting.pattern.EncodedPatternItem; +import lombok.Getter; +import org.jetbrains.annotations.NotNull; + +public class MECraftPatternContainerPartMachine extends MultiblockPartMachine implements IMECraftPatternContainer, IMachineLife { + + protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( + MECraftPatternContainerPartMachine.class, MultiblockPartMachine.MANAGED_FIELD_HOLDER); + + private static final int PATTERNS_PER_ROW = 9; + + @Getter + @Persisted + protected final ItemStackTransfer patternInventory; + + @DescSynced + @Persisted + private boolean shouldOpen = true; + + public MECraftPatternContainerPartMachine(IMachineBlockEntity holder) { + super(holder); + patternInventory = new ItemStackTransfer(12 * PATTERNS_PER_ROW); + patternInventory.setFilter(itemStack -> AEUtils.molecularFilter(itemStack, getLevel())); + } + + @Override + public @NotNull ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } + + @Override + public BlockPos getBlockPos() { + return getPos(); + } + + @Override + public int getSlots() { + return this.patternInventory.getSlots(); + } + + @Override + public void onMachineRemoved() { + clearInventory(patternInventory); + } + + @Override + public void removedFromController(@NotNull IMultiController controller) { + super.removedFromController(controller); + shouldOpen = true; + } + + public void addedToController(@NotNull IMultiController controller) { + super.addedToController(controller); + shouldOpen = false; + } + + @Override + public @NotNull Widget createUIWidget() { + final int cowSize = 14; + final int totalCount = patternInventory.getSlots(); + final int rowSize = (int) Math.ceil((double) totalCount / cowSize); + var group = new WidgetGroup(0, 0, 18 * cowSize + 16, 18 * rowSize + 16); + + int index = 0; + for (int y = 0; y < rowSize; ++y) { + for (int x = 0; x < cowSize; ++x) { + if (index >= totalCount) break; + var slot = new AEPatternViewExtendSlotWidget(patternInventory, index++, x * 18 + 8, y * 18 + 14) + .setOccupiedTexture(GuiTextures.SLOT) + .setItemHook(stack -> { + if (!stack.isEmpty() && stack.getItem() instanceof EncodedPatternItem iep) { + final ItemStack out = iep.getOutput(stack); + if (!out.isEmpty()) return out; + } + return stack; + }) + .setBackground(GuiTextures.SLOT, GuiTextures.PATTERN_OVERLAY); + group.addWidget(slot); + } + } + return group; + } + + @Override + public boolean shouldOpenUI(Player player, InteractionHand hand, BlockHitResult hit) { + return shouldOpen; + } + + @Override + public boolean canShared() { + return false; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MECraftSpeedCorePartMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MECraftSpeedCorePartMachine.java new file mode 100644 index 000000000..76ff40e1c --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MECraftSpeedCorePartMachine.java @@ -0,0 +1,27 @@ +package org.gtlcore.gtlcore.common.machine.multiblock.part.ae; + +import org.gtlcore.gtlcore.api.machine.trait.AECraft.IMECraftSpeedCore; + +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.multiblock.part.MultiblockPartMachine; + +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.phys.BlockHitResult; + +public class MECraftSpeedCorePartMachine extends MultiblockPartMachine implements IMECraftSpeedCore { + + public MECraftSpeedCorePartMachine(IMachineBlockEntity holder) { + super(holder); + } + + @Override + public int getSpeedTier() { + return 8; + } + + @Override + public boolean shouldOpenUI(Player player, InteractionHand hand, BlockHitResult hit) { + return false; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEExtendedAsyncOutputPartMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEExtendedAsyncOutputPartMachine.java new file mode 100644 index 000000000..f322f3de0 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEExtendedAsyncOutputPartMachine.java @@ -0,0 +1,194 @@ +package org.gtlcore.gtlcore.common.machine.multiblock.part.ae; + +import org.gtlcore.gtlcore.api.machine.trait.MEPart.IMEFilterIOTrait; +import org.gtlcore.gtlcore.integration.ae2.AEUtils; +import org.gtlcore.gtlcore.integration.ae2.async.AEAccumulator; +import org.gtlcore.gtlcore.integration.ae2.async.AEWriteService; + +import com.gregtechceu.gtceu.api.gui.widget.IntInputWidget; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; + +import com.lowdragmc.lowdraglib.gui.modular.ModularUI; +import com.lowdragmc.lowdraglib.gui.widget.Widget; +import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; +import com.lowdragmc.lowdraglib.side.fluid.FluidStack; + +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + +import appeng.api.networking.IGridNode; +import appeng.api.networking.ticking.IGridTickable; +import appeng.api.networking.ticking.TickRateModulation; +import appeng.api.networking.ticking.TickingRequest; +import appeng.api.stacks.AEKey; +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; +import org.jetbrains.annotations.NotNull; + +import java.lang.ref.WeakReference; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +public class MEExtendedAsyncOutputPartMachine extends MEExtendedOutputPartMachineBase { + + private final AEAccumulator accumulator = new AEAccumulator(); + private final WeakReference accRef = new WeakReference<>(accumulator); + private final AtomicReference> pendingData = new AtomicReference<>(); + private final AtomicBoolean drainRequested = new AtomicBoolean(false); + + public MEExtendedAsyncOutputPartMachine(IMachineBlockEntity holder) { + super(holder); + } + + private void requestAsyncDrain() { + if (pendingData.get() == null && drainRequested.compareAndSet(false, true)) { + AEWriteService.INSTANCE.prepareDrainedData(accRef, pendingData, drainRequested); + } + } + + private boolean mergeFromPendingData() { + Object2LongOpenHashMap data = pendingData.getAndSet(null); + if (data != null && !data.isEmpty()) { + data.object2LongEntrySet().fastForEach(e -> buffer.addTo(e.getKey(), e.getLongValue())); + return true; + } + return false; + } + + @Override + public void onMachineRemoved() { + accumulator.clear(); + super.onMachineRemoved(); + } + + // ======================================== + // GUI SYSTEM + // ======================================== + + @Override + public ModularUI createUI(Player entityPlayer) { + final var ui = super.createUI(entityPlayer); + ui.registerCloseListener(this::updatePriority); + return ui; + } + + @Override + public @NotNull Widget createUIWidget() { + WidgetGroup group = (WidgetGroup) super.createUIWidget(); + group.addWidget(new IntInputWidget(90, 0, 80, 10, this::getPriority, this::setPriority).setMin(10).setMax(100000)); + return group; + } + + // ======================================== + // ME Output Handlers && Tick Service + // ======================================== + + @Override + protected NotifiableMERecipeHandlerTrait createItemOutputHandler() { + return new MEItemOutputHandler(this) { + + public MEExtendedAsyncOutputPartMachine getMachine() { + return (MEExtendedAsyncOutputPartMachine) this.machine; + } + + @Override + public List meHandleRecipeOutputInner(List left, boolean simulate) { + if (simulate) return List.of(); + AEWriteService.INSTANCE.submitIngredientLeft(accRef, left); + return List.of(); + } + }; + } + + @Override + protected NotifiableMERecipeHandlerTrait createFluidOutputHandler() { + return new MEFluidOutputHandler(this) { + + public MEExtendedAsyncOutputPartMachine getMachine() { + return (MEExtendedAsyncOutputPartMachine) this.machine; + } + + @Override + public List meHandleRecipeOutputInner(List left, boolean simulate) { + if (simulate) return List.of(); + AEWriteService.INSTANCE.submitFluidIngredientLeft(accRef, left); + return List.of(); + } + }; + } + + @Override + protected @NotNull IMEFilterIOTrait createMETrait() { + return new MEAsyncFilterIOTrait(this); + } + + @Override + public @NotNull IMEFilterIOTrait getMETrait() { + return (IMEFilterIOTrait) meTrait; + } + + @Override + protected void registerDefaultServices() { + getMainNode().addService(IGridTickable.class, new Ticker()); + } + + protected class Ticker implements IGridTickable { + + @Override + public TickingRequest getTickingRequest(IGridNode node) { + return new TickingRequest(MIN_FREQUENCY, MAX_FREQUENCY, false, true); + } + + @Override + public TickRateModulation tickingRequest(IGridNode node, int ticksSinceLastCall) { + final boolean isActive = getMainNode().isActive(); + final boolean dataMerged = mergeFromPendingData(); + final boolean hasPendingWork = pendingData.get() != null || !accumulator.isEmpty(); + + if (hasPendingWork) { + requestAsyncDrain(); + } + + if (!isActive) { + if (hasPendingWork) { + return TickRateModulation.FASTER; + } else { + if (ticksSinceLastCall >= MAX_FREQUENCY) { + isSleeping = true; + return TickRateModulation.SLEEP; + } else return TickRateModulation.SLOWER; + } + } + + if (buffer.isEmpty()) { + if (hasPendingWork) { + return TickRateModulation.FASTER; + } + if (ticksSinceLastCall >= MAX_FREQUENCY) { + isSleeping = true; + return TickRateModulation.SLEEP; + } else return TickRateModulation.SLOWER; + } else { + if (AEUtils.reFunds(buffer, getMainNode().getGrid(), actionSource) || dataMerged) { + return TickRateModulation.URGENT; + } else { + return TickRateModulation.SLOWER; + } + } + } + } + + protected class MEAsyncFilterIOTrait extends MEIOTrait implements IMEFilterIOTrait { + + public MEAsyncFilterIOTrait(MEExtendedAsyncOutputPartMachine machine) { + super(machine); + } + + @Override + public MEExtendedAsyncOutputPartMachine getMachine() { + return (MEExtendedAsyncOutputPartMachine) machine; + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEExtendedOutputPartMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEExtendedOutputPartMachine.java new file mode 100644 index 000000000..e809b9eed --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEExtendedOutputPartMachine.java @@ -0,0 +1,232 @@ +package org.gtlcore.gtlcore.common.machine.multiblock.part.ae; + +import org.gtlcore.gtlcore.api.machine.MEExtendedOutputFancyConfigurator; +import org.gtlcore.gtlcore.api.machine.trait.MEPart.IMEFilterIOTrait; +import org.gtlcore.gtlcore.api.recipe.ingredient.LongIngredient; + +import com.gregtechceu.gtceu.api.gui.fancy.TabsWidget; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.recipe.ingredient.*; + +import com.lowdragmc.lowdraglib.gui.modular.ModularUI; +import com.lowdragmc.lowdraglib.side.fluid.FluidStack; +import com.lowdragmc.lowdraglib.syncdata.annotation.LazyManaged; +import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; +import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + +import appeng.api.networking.IGridNode; +import appeng.api.networking.ticking.*; +import appeng.api.stacks.*; +import org.jetbrains.annotations.NotNull; + +import java.util.Iterator; +import java.util.List; + +import static org.gtlcore.gtlcore.integration.ae2.AEUtils.reFunds; + +public class MEExtendedOutputPartMachine extends MEExtendedOutputPartMachineBase { + + @Override + public @NotNull ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } + + protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( + MEExtendedOutputPartMachine.class, MEExtendedOutputPartMachineBase.MANAGED_FIELD_HOLDER); + + protected final static int FILTER_ROW = 3; + protected final static int FILTER_COL = 9; + + @Persisted + @LazyManaged + protected final MEOutputFilterHandler filterHandler; + + public MEExtendedOutputPartMachine(IMachineBlockEntity holder) { + super(holder); + filterHandler = new MEOutputFilterHandler(FILTER_ROW, FILTER_COL, this::onFilterChanged, this::updatePriority, this::getPriority, this::setPriority); + } + + private void onFilterChanged() { + markDirty(); + notifyHandlers(); + } + + // ======================================== + // GUI SYSTEM + // ======================================== + + @Override + public ModularUI createUI(Player entityPlayer) { + final var ui = super.createUI(entityPlayer); + ui.registerCloseListener(filterHandler::onUIClosed); + return ui; + } + + @Override + public void attachSideTabs(TabsWidget sideTabs) { + super.attachSideTabs(sideTabs); + sideTabs.attachSubTab(new MEExtendedOutputFancyConfigurator(filterHandler::createMainWidgetGroup)); + } + + // ======================================== + // DataStick Copy + // ======================================== + + protected CompoundTag writeConfigToTag() { + var tag = super.writeConfigToTag(); + tag.put("meOutputFilterHandler", filterHandler.serializeNBT()); + return tag; + } + + protected void readConfigFromTag(CompoundTag tag) { + super.readConfigFromTag(tag); + if (tag.contains("meOutputFilterHandler")) { + filterHandler.deserializeNBT(tag.getCompound("meOutputFilterHandler")); + onFilterChanged(); + } + } + + // ======================================== + // ME Output Handlers && Tick Service + // ======================================== + + @Override + protected NotifiableMERecipeHandlerTrait createItemOutputHandler() { + return new MEExtendedOutputPartMachineBase.MEItemOutputHandler(this) { + + @Override + public MEExtendedOutputPartMachine getMachine() { + return (MEExtendedOutputPartMachine) this.machine; + } + + @Override + public boolean outputHasFilter() { + return filterHandler.isHasItemFilter(); + } + + @Override + public List meHandleRecipeOutputInner(List left, boolean simulate) { + if (simulate) return filterHandler.testIngredient(left); + for (Iterator it = left.iterator(); it.hasNext();) { + Ingredient ingredient = it.next(); + if (ingredient instanceof IntProviderIngredient intProvider) { + intProvider.setItemStacks(null); + intProvider.setSampledCount(null); + } + + ItemStack[] items = ingredient.getItems(); + if (items.length != 0) { + ItemStack output = items[0]; + if (output.isEmpty()) it.remove(); + else if (filterHandler.test(output)) { + buffer.addTo(AEItemKey.of(output), ingredient instanceof LongIngredient longIngredient ? longIngredient.getActualAmount() : output.getCount()); + it.remove(); + } + } else it.remove(); + } + return left; + } + }; + } + + @Override + protected NotifiableMERecipeHandlerTrait createFluidOutputHandler() { + return new MEExtendedOutputPartMachineBase.MEFluidOutputHandler(this) { + + @Override + public MEExtendedOutputPartMachine getMachine() { + return (MEExtendedOutputPartMachine) this.machine; + } + + @Override + public boolean outputHasFilter() { + return filterHandler.isHasFluidFilter(); + } + + @Override + public List meHandleRecipeOutputInner(List left, boolean simulate) { + if (simulate) return filterHandler.testFluidIngredient(left); + for (Iterator it = left.iterator(); it.hasNext();) { + FluidIngredient fluidIngredient = it.next(); + if (!fluidIngredient.isEmpty()) { + FluidStack[] fluids = fluidIngredient.getStacks(); + if (fluids.length != 0) { + FluidStack output = fluids[0]; + if (output.isEmpty()) it.remove(); + else if (filterHandler.test(output)) { + buffer.addTo(AEFluidKey.of(output.getFluid()), output.getAmount()); + it.remove(); + } + } else it.remove(); + } else it.remove(); + } + + return left; + } + }; + } + + @Override + protected @NotNull IMEFilterIOTrait createMETrait() { + return new MEFilterIOTrait(this); + } + + @Override + public @NotNull IMEFilterIOTrait getMETrait() { + return (IMEFilterIOTrait) meTrait; + } + + @Override + protected void registerDefaultServices() { + getMainNode().addService(IGridTickable.class, new Ticker()); + } + + protected class Ticker implements IGridTickable { + + @Override + public TickingRequest getTickingRequest(IGridNode node) { + return new TickingRequest(MIN_FREQUENCY, MAX_FREQUENCY, false, true); + } + + @Override + public TickRateModulation tickingRequest(IGridNode node, int ticksSinceLastCall) { + if (!getMainNode().isActive()) { + return TickRateModulation.SLEEP; + } + + if (buffer.isEmpty()) { + if (ticksSinceLastCall >= MAX_FREQUENCY) { + isSleeping = true; + return TickRateModulation.SLEEP; + } else return TickRateModulation.SLOWER; + } else return reFunds(buffer, getMainNode().getGrid(), actionSource) ? TickRateModulation.URGENT : TickRateModulation.SLOWER; + } + } + + protected class MEFilterIOTrait extends MEIOTrait implements IMEFilterIOTrait { + + public MEFilterIOTrait(MEExtendedOutputPartMachine machine) { + super(machine); + } + + @Override + public MEExtendedOutputPartMachine getMachine() { + return (MEExtendedOutputPartMachine) machine; + } + + @Override + public boolean hasItemFilter() { + return filterHandler.isHasItemFilter(); + } + + @Override + public boolean hasFluidFilter() { + return filterHandler.isHasFluidFilter(); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEExtendedOutputPartMachineBase.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEExtendedOutputPartMachineBase.java new file mode 100644 index 000000000..d742fb14c --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEExtendedOutputPartMachineBase.java @@ -0,0 +1,310 @@ +package org.gtlcore.gtlcore.common.machine.multiblock.part.ae; + +import org.gtlcore.gtlcore.api.machine.trait.IMERecipeHandlerTrait; +import org.gtlcore.gtlcore.api.machine.trait.IRecipeCapabilityMachine; +import org.gtlcore.gtlcore.api.machine.trait.MEPart.IMEFilterIOPartMachine; +import org.gtlcore.gtlcore.api.machine.trait.MEPart.IMEFilterIOTrait; +import org.gtlcore.gtlcore.client.gui.widget.MEOutListGridWidget; +import org.gtlcore.gtlcore.config.ConfigHolder; +import org.gtlcore.gtlcore.integration.ae2.AEUtils; + +import com.gregtechceu.gtceu.api.capability.recipe.FluidRecipeCapability; +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; +import com.gregtechceu.gtceu.api.capability.recipe.RecipeCapability; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.feature.IDataStickInteractable; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; + +import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; +import com.lowdragmc.lowdraglib.gui.widget.Widget; +import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; +import com.lowdragmc.lowdraglib.side.fluid.FluidStack; +import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; +import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; +import com.lowdragmc.lowdraglib.utils.Position; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + +import appeng.api.stacks.AEKey; +import it.unimi.dsi.fastutil.Pair; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; +import it.unimi.dsi.fastutil.ints.IntCollection; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import it.unimi.dsi.fastutil.objects.Object2LongMaps; +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import static org.gtlcore.gtlcore.integration.ae2.AEUtils.loadInventory; + +public abstract class MEExtendedOutputPartMachineBase extends MEIOPartMachine implements IDataStickInteractable, IMEFilterIOPartMachine { + + @Override + public @NotNull ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } + + protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( + MEExtendedOutputPartMachineBase.class, MEIOPartMachine.MANAGED_FIELD_HOLDER); + + protected final static int MIN_FREQUENCY = ConfigHolder.INSTANCE.MEPatternOutputMin; + protected final static int MAX_FREQUENCY = ConfigHolder.INSTANCE.MEPatternOutputMax; + + @Getter + protected final Object2LongOpenHashMap buffer = new Object2LongOpenHashMap<>(); + + @Getter + protected final NotifiableMERecipeHandlerTrait itemOutputHandler; + + @Getter + protected final NotifiableMERecipeHandlerTrait fluidOutputHandler; + + @Persisted + @Getter + @Setter + protected int priority = 10; + + public MEExtendedOutputPartMachineBase(IMachineBlockEntity holder) { + super(holder, IO.OUT); + itemOutputHandler = createItemOutputHandler(); + fluidOutputHandler = createFluidOutputHandler(); + registerDefaultServices(); + } + + @Override + public @NotNull Widget createUIWidget() { + WidgetGroup group = new WidgetGroup(new Position(0, 0)); + // ME Network status + group.addWidget(new LabelWidget(0, 0, () -> this.isOnline ? + "gtceu.gui.me_network.online" : + "gtceu.gui.me_network.offline")); + + group.addWidget(new MEOutListGridWidget(5, 20, 7, this.buffer)); + return group; + } + + // ======================================== + // ME Output Handlers && Tick Service + // ======================================== + + @Override + public Pair, IMERecipeHandlerTrait> getMERecipeHandlerTraits() { + return Pair.of(itemOutputHandler, fluidOutputHandler); + } + + @Override + public abstract @NotNull IMEFilterIOTrait getMETrait(); + + protected abstract NotifiableMERecipeHandlerTrait createItemOutputHandler(); + + protected abstract NotifiableMERecipeHandlerTrait createFluidOutputHandler(); + + protected void registerDefaultServices() {} + + // ======================================== + // Filter && Priority SYSTEM + // ======================================== + + protected void notifyHandlers() { + itemOutputHandler.notifyListeners(); + fluidOutputHandler.notifyListeners(); + } + + protected void updatePriority() { + for (var controller : this.getControllers()) { + if (controller instanceof IRecipeCapabilityMachine machine) { + machine.sortMEOutput(); + } + } + } + + // ======================================== + // DataStick Copy + // ======================================== + + @Override + public InteractionResult onDataStickRightClick(Player player, ItemStack dataStick) { + CompoundTag tag = dataStick.getTag(); + if (tag == null || !tag.contains("MEExtendedExportBuffer")) { + return InteractionResult.PASS; + } + + if (!isRemote()) { + readConfigFromTag(tag.getCompound("MEExtendedExportBuffer")); + player.sendSystemMessage(Component.translatable("gtceu.machine.me.import_paste_settings")); + } + return InteractionResult.sidedSuccess(isRemote()); + } + + @Override + public boolean onDataStickLeftClick(Player player, ItemStack dataStick) { + if (!isRemote()) { + CompoundTag tag = new CompoundTag(); + tag.put("MEExtendedExportBuffer", writeConfigToTag()); + dataStick.setTag(tag); + dataStick.setHoverName(Component.translatable("gtceu.machine.me.me_extended_export_buffer.data_stick.name")); + player.sendSystemMessage(Component.translatable("gtceu.machine.me.import_copy_settings")); + } + return true; + } + + protected CompoundTag writeConfigToTag() { + CompoundTag tag = new CompoundTag(); + tag.putInt("priority", priority); + return tag; + } + + protected void readConfigFromTag(CompoundTag tag) { + if (tag.contains("priority")) { + this.priority = tag.getInt("priority"); + } + updatePriority(); + } + + // ======================================== + // Persist + // ======================================== + + @Override + public void saveCustomPersistedData(@NotNull CompoundTag tag, boolean forDrop) { + super.saveCustomPersistedData(tag, forDrop); + if (buffer.isEmpty()) return; + ListTag listTag = AEUtils.createListTag(AEKey::toTagGeneric, buffer); + if (!listTag.isEmpty()) tag.put("buffer", listTag); + } + + @Override + public void loadCustomPersistedData(@NotNull CompoundTag tag) { + super.loadCustomPersistedData(tag); + buffer.clear(); + ListTag listTag = tag.getList("buffer", Tag.TAG_COMPOUND); + loadInventory(listTag, AEKey::fromTagGeneric, buffer); + } + + protected abstract class MEItemOutputHandler extends NotifiableMERecipeHandlerTrait { + + public MEItemOutputHandler(MEExtendedOutputPartMachineBase machine) { + super(machine); + } + + public MEExtendedOutputPartMachineBase getMachine() { + return (MEExtendedOutputPartMachineBase) this.machine; + } + + @Override + public RecipeCapability getCapability() { + return ItemRecipeCapability.CAP; + } + + @Override + public IO getIo() { + return IO.OUT; + } + + // region Unused + @Override + public Set getActiveSlots() { + return Collections.emptySet(); + } + + @Override + public Int2ObjectMap> getActiveAndUnCachedSlotsLimitContentsMap() { + return Int2ObjectMaps.emptyMap(); + } + + @Override + public Object2LongMap getStackMapFromFirstAvailableSlot(IntCollection slots) { + return Object2LongMaps.emptyMap(); + } + + @Override + public boolean meHandleRecipeInner(GTRecipe recipe, Object2LongMap left, boolean simulate, int trySlot) { + return false; + } + + @Override + public void prepareMEHandleContents(GTRecipe recipe, List left, boolean simulate) {} + + @Override + public Object2LongMap getPreparedMEHandleContents() { + return Object2LongMaps.emptyMap(); + } + // endregion + + @Override + public int getPriority() { + return priority; + } + } + + public abstract class MEFluidOutputHandler extends NotifiableMERecipeHandlerTrait { + + public MEFluidOutputHandler(MEExtendedOutputPartMachineBase machine) { + super(machine); + } + + public MEExtendedOutputPartMachineBase getMachine() { + return (MEExtendedOutputPartMachineBase) this.machine; + } + + @Override + public RecipeCapability getCapability() { + return FluidRecipeCapability.CAP; + } + + @Override + public IO getIo() { + return IO.OUT; + } + + // region Unused + @Override + public Set getActiveSlots() { + return Collections.emptySet(); + } + + @Override + public Int2ObjectMap> getActiveAndUnCachedSlotsLimitContentsMap() { + return Int2ObjectMaps.emptyMap(); + } + + @Override + public Object2LongMap getStackMapFromFirstAvailableSlot(IntCollection slots) { + return Object2LongMaps.emptyMap(); + } + + @Override + public boolean meHandleRecipeInner(GTRecipe recipe, Object2LongMap left, boolean simulate, int trySlot) { + return false; + } + + @Override + public void prepareMEHandleContents(GTRecipe recipe, List left, boolean simulate) {} + + @Override + public Object2LongMap getPreparedMEHandleContents() { + return Object2LongMaps.emptyMap(); + } + // endregion + + @Override + public int getPriority() { + return priority; + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEIOPartMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEIOPartMachine.java new file mode 100644 index 000000000..cb305c3ee --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEIOPartMachine.java @@ -0,0 +1,144 @@ +package org.gtlcore.gtlcore.common.machine.multiblock.part.ae; + +import org.gtlcore.gtlcore.api.machine.trait.MEPart.IMEIOPartMachine; +import org.gtlcore.gtlcore.api.machine.trait.MEPart.IMEIOTrait; + +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.gui.fancy.IFancyUIProvider; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.feature.IMachineLife; +import com.gregtechceu.gtceu.api.machine.multiblock.part.MultiblockPartMachine; +import com.gregtechceu.gtceu.api.machine.trait.MachineTrait; +import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.trait.GridNodeHolder; + +import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; +import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; +import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; + +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; + +import appeng.api.networking.IManagedGridNode; +import appeng.api.networking.security.IActionSource; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.EnumSet; + +public abstract class MEIOPartMachine extends MultiblockPartMachine implements IMachineLife, IGridConnectedMachine, IMEIOPartMachine { + + protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(MEIOPartMachine.class, MultiblockPartMachine.MANAGED_FIELD_HOLDER); + + @Persisted + @Getter + protected final GridNodeHolder nodeHolder; + + @Getter + @Setter + @DescSynced + protected boolean isOnline; + + protected final IMEIOTrait meTrait; + + protected final IActionSource actionSource; + + protected final IO io; + + protected boolean isSleeping = true; + + public MEIOPartMachine(IMachineBlockEntity holder, IO io) { + super(holder); + this.nodeHolder = createNodeHolder(); + this.actionSource = IActionSource.ofMachine(nodeHolder.getMainNode()::getNode); + this.io = io; + this.meTrait = createMETrait(); + } + + protected @NotNull IMEIOTrait createMETrait() { + return new MEIOTrait(this); + } + + @Override + public @NotNull IMEIOTrait getMETrait() { + return meTrait; + } + + @Nullable + @Override + public IFancyUIProvider.@Nullable PageGroupingData getPageGroupingData() { + return switch (this.io) { + case IN -> new PageGroupingData("gtceu.multiblock.page_switcher.io.import", 1); + case OUT -> new PageGroupingData("gtceu.multiblock.page_switcher.io.export", 2); + case BOTH -> new PageGroupingData("gtceu.multiblock.page_switcher.io.both", 3); + case NONE -> null; + }; + } + + @Override + public void loadCustomPersistedData(@NotNull CompoundTag tag) { + super.loadCustomPersistedData(tag); + if (tag.getCompound("ForgeData").getBoolean("isAllFacing")) { + getMainNode().setExposedOnSides(EnumSet.allOf(Direction.class)); + } + } + + @Override + public IManagedGridNode getMainNode() { + return nodeHolder.getMainNode(); + } + + @Override + public void onRotated(@NotNull Direction oldFacing, @NotNull Direction newFacing) { + super.onRotated(oldFacing, newFacing); + this.getMainNode().setExposedOnSides(EnumSet.of(newFacing)); + } + + @Override + public @NotNull ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } + + protected GridNodeHolder createNodeHolder() { + return new GridNodeHolder(this); + } + + public class MEIOTrait extends MachineTrait implements IMEIOTrait { + + protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( + MEIOPartMachine.class); + + public MEIOTrait(MEIOPartMachine machine) { + super(machine); + } + + @Override + public MEIOPartMachine getMachine() { + return (MEIOPartMachine) machine; + } + + @Override + public void notifySelfIO() { + if (isSleeping) { + if (getMainNode().isActive()) { + getMainNode().ifPresent((grid, node) -> { + grid.getTickManager().wakeDevice(node); + isSleeping = false; + }); + } + } + } + + @Override + public IO getIO() { + return io; + } + + @Override + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEMolecularAssemblerIOPartMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEMolecularAssemblerIOPartMachine.java new file mode 100644 index 000000000..4e1c81ca6 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEMolecularAssemblerIOPartMachine.java @@ -0,0 +1,494 @@ +package org.gtlcore.gtlcore.common.machine.multiblock.part.ae; + +import org.gtlcore.gtlcore.api.machine.trait.AECraft.IMECraftIOPart; +import org.gtlcore.gtlcore.api.machine.trait.AECraft.IMECraftPatternContainer; +import org.gtlcore.gtlcore.common.data.GTLMachines; +import org.gtlcore.gtlcore.common.machine.multiblock.part.PaginationUIManager; +import org.gtlcore.gtlcore.integration.ae2.AEUtils; +import org.gtlcore.gtlcore.integration.lowdragmc.misc.MutableItemTransferList; + +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.gui.fancy.ConfiguratorPanel; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.MultiblockMachineDefinition; +import com.gregtechceu.gtceu.api.machine.TickableSubscription; +import com.gregtechceu.gtceu.api.machine.fancyconfigurator.FancyInvConfigurator; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; +import com.gregtechceu.gtceu.integration.ae2.gui.widget.AETextInputButtonWidget; + +import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; +import com.lowdragmc.lowdraglib.gui.widget.Widget; +import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; +import com.lowdragmc.lowdraglib.misc.ItemStackTransfer; +import com.lowdragmc.lowdraglib.side.item.IItemTransfer; +import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; +import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; +import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.BlockHitResult; + +import appeng.api.crafting.IPatternDetails; +import appeng.api.implementations.blockentities.PatternContainerGroup; +import appeng.api.inventories.InternalInventory; +import appeng.api.networking.IGrid; +import appeng.api.networking.IGridNode; +import appeng.api.networking.IGridNodeListener; +import appeng.api.networking.crafting.ICraftingProvider; +import appeng.api.networking.ticking.IGridTickable; +import appeng.api.networking.ticking.TickRateModulation; +import appeng.api.networking.ticking.TickingRequest; +import appeng.api.stacks.AEItemKey; +import appeng.api.stacks.AEKey; +import appeng.api.stacks.GenericStack; +import appeng.api.stacks.KeyCounter; +import appeng.blockentity.crafting.IMolecularAssemblerSupportedPattern; +import appeng.crafting.pattern.AEProcessingPattern; +import appeng.crafting.pattern.EncodedPatternItem; +import appeng.helpers.patternprovider.PatternContainer; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.*; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +import static org.gtlcore.gtlcore.integration.ae2.AEUtils.*; + +public class MEMolecularAssemblerIOPartMachine extends MEIOPartMachine implements PatternContainer, IMECraftIOPart { + + protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(MEMolecularAssemblerIOPartMachine.class, MEIOPartMachine.MANAGED_FIELD_HOLDER); + + private static final int ROWS_PER_PAGE = 8; + private static final int PATTERNS_PER_ROW = 9; + + private final InternalInventory internalPatternInventory = new InternalInventory() { + + @Override + public int size() { + return mutableItemTransferList.getSlots(); + } + + @Override + public ItemStack getStackInSlot(int slotIndex) { + return mutableItemTransferList.getStackInSlot(slotIndex); + } + + @Override + public void setItemDirect(int slotIndex, ItemStack stack) { + mutableItemTransferList.setStackInSlot(slotIndex, stack); + mutableItemTransferList.onContentsChanged(); + onPatternChange(slotIndex); + } + + @Override + public boolean isItemValid(int slot, ItemStack stack) { + return slot <= mutableItemTransferList.getSlots() && AEUtils.molecularFilter(stack, getLevel()); + } + }; + + // ======================================== + // Status + // ======================================== + + @DescSynced + @Persisted + @Setter + protected String customName = ""; + + private boolean needPatternSync; + + private boolean shouldOpen = false; + + // ======================================== + // Handlers + // ======================================== + + protected final NotifiableMAHandlerTrait maHandler; + + private final MutableItemTransferList mutableItemTransferList; + + protected PaginationUIManager paginationUIManager; + + // ======================================== + // Inventory + // ======================================== + + @Persisted + protected final ItemStackTransfer sharedToolsInventory; + + private final BiMap<@NotNull IPatternDetails, Integer> patternSlotMap; + + private final Int2ObjectMap<@NotNull ObjectSet<@NotNull Item>> toolsSlotMap; + + @Getter + private final Object2LongLinkedOpenHashMap outputItems; // must 1 count + + @Getter + private final Object2LongOpenHashMap buffer; + + @DescSynced + private final ObjectOpenHashSet proxies = new ObjectOpenHashSet<>(); + private final ReferenceSortedSet proxyStackTransfers = new ReferenceLinkedOpenHashSet<>(); + + public MEMolecularAssemblerIOPartMachine(IMachineBlockEntity holder) { + super(holder, IO.BOTH); + getMainNode().addService(IGridTickable.class, new Ticker()).addService(ICraftingProvider.class, this); + + patternSlotMap = HashBiMap.create(); + toolsSlotMap = new Int2ObjectOpenHashMap<>(); + outputItems = new Object2LongLinkedOpenHashMap<>(); + buffer = new Object2LongOpenHashMap<>(); + maHandler = new MECraftHandler(this); + sharedToolsInventory = new ItemStackTransfer(9); + + mutableItemTransferList = new MutableItemTransferList(); + } + + @Override + public boolean pushPattern(IPatternDetails iPatternDetails, KeyCounter[] keyCounters) { + if (!getMainNode().isActive() || !patternSlotMap.containsKey(iPatternDetails) || !(iPatternDetails instanceof AEProcessingPattern processingPattern)) { + return false; + } + + final GenericStack output = processingPattern.getOutputs()[0]; + if (!(output.what() instanceof AEItemKey)) return false; + + int slot = patternSlotMap.get(processingPattern); + if (toolsSlotMap.containsKey(slot)) { + var requiredTools = new ObjectOpenHashSet<>(toolsSlotMap.get(slot)); + + for (int i = 0; i < sharedToolsInventory.getSlots(); i++) { + final var stack = sharedToolsInventory.getStackInSlot(i); + if (!stack.isDamageableItem()) requiredTools.remove(stack.getItem()); + if (requiredTools.isEmpty()) break; + } + + if (!requiredTools.isEmpty()) { + AEUtils.pushInputsToMEPatternBufferInventory(keyCounters, this.buffer::addTo); + this.meTrait.notifySelfIO(); + return true; + } + } + + final GenericStack requireStack = processingPattern.getInputs()[0].getPossibleInputs()[0]; + long multiplier = 0; + for (var inputList : keyCounters) { + for (var input : inputList) { + if (requireStack.what().equals(input.getKey())) { + multiplier = input.getLongValue() / (requireStack.amount() * processingPattern.getInputs()[0].getMultiplier()); + break; + } + } + } + if (multiplier == 0) return false; + + outputItems.addTo(output, multiplier); + maHandler.notifyListeners(); + return true; + } + + @Override + public void init(@NotNull Set proxies) { + clear(); + + this.proxies.addAll(proxies); + mutableItemTransferList.addTransfers(getProxies()); + + if (!mutableItemTransferList.isEmpty()) { + for (int i = 0; i < mutableItemTransferList.getSlots(); i++) { + var pattern = getPatternDetailsAsProcessing(mutableItemTransferList.getStackInSlot(i), i); + if (pattern != null) patternSlotMap.forcePut(pattern, i); + } + shouldOpen = true; + } else { + shouldOpen = false; + } + needPatternSync = true; + } + + @Override + public @NotNull NotifiableMAHandlerTrait getNotifiableMAHandlerTrait() { + return this.maHandler; + } + + @Override + public boolean canShared() { + return false; + } + + protected void clear() { + this.mutableItemTransferList.clear(); + this.patternSlotMap.clear(); + this.toolsSlotMap.clear(); + this.proxies.clear(); + shouldOpen = false; + } + + private @Nullable IPatternDetails getPatternDetailsAsProcessing(ItemStack stack, int slotIndex) { + if (!stack.isEmpty()) { + if (stack.getItem() instanceof EncodedPatternItem encodedPatternItem) { + final var decodePattern = encodedPatternItem.decode(stack, getLevel(), false); + if (decodePattern instanceof IMolecularAssemblerSupportedPattern molecularAssemblerSupportedPattern) { + final var pair = AEUtils.createProcessingFromCraftPattern(molecularAssemblerSupportedPattern, getLevel()); + final var remainingStacks = pair.getRight(); + final var pattern = pair.getLeft(); + if (!(pattern instanceof AEProcessingPattern)) return null; + if (remainingStacks != null && !remainingStacks.isEmpty()) toolsSlotMap.put(slotIndex, remainingStacks); + else toolsSlotMap.remove(slotIndex); + return pattern; + } else return null; + } + } + return null; + } + + protected ReferenceSortedSet getProxies() { + if (proxyStackTransfers.size() != proxies.size()) { + proxyStackTransfers.clear(); + ObjectList patternInventories = new ObjectArrayList<>(); + for (var pos : proxies) { + if (MetaMachine.getMachine(Objects.requireNonNull(getLevel()), pos) instanceof MECraftPatternContainerPartMachine proxy) { + patternInventories.add(proxy.getPatternInventory()); + } + } + patternInventories.sort(Comparator.comparingInt(IMECraftPatternContainer::sumNonEmpty).reversed() + .thenComparingInt(IItemTransfer::getSlots)); + proxyStackTransfers.addAll(patternInventories); + } + return ReferenceSortedSets.unmodifiable(proxyStackTransfers); + } + + @Override + public @NotNull ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } + + // ======================================== + // LIFECYCLE & NETWORK MANAGEMENT + // ======================================== + + @Nullable + protected TickableSubscription updateSubs; + + @Override + public void onMainNodeStateChanged(IGridNodeListener.@NotNull State reason) { + super.onMainNodeStateChanged(reason); + this.updateSubscription(); + } + + @Override + public void onUnload() { + super.onUnload(); + clear(); + } + + @Override + public void removedFromController(@NotNull IMultiController controller) { + super.removedFromController(controller); + clear(); + needPatternSync = true; + } + + @Override + public void onMachineRemoved() { + clearInventory(sharedToolsInventory); + } + + protected void updateSubscription() { + if (getMainNode().isOnline()) { + updateSubs = subscribeServerTick(updateSubs, this::update); + } else if (updateSubs != null) { + updateSubs.unsubscribe(); + updateSubs = null; + } + } + + protected void update() { + if (needPatternSync) { + ICraftingProvider.requestUpdate(getMainNode()); + this.needPatternSync = false; + } + } + + protected void onPatternChange(int index) { + if (isRemote()) return; + + var newPattern = mutableItemTransferList.getStackInSlot(index); + var newPatternDetails = getPatternDetailsAsProcessing(newPattern, index); + + if (newPatternDetails != null) patternSlotMap.forcePut(newPatternDetails, index); + else patternSlotMap.inverse().remove(index); + + needPatternSync = true; + } + + // ======================================== + // GUI + // ======================================== + + @Override + public @NotNull Widget createUIWidget() { + var tempmutableItemTransferList = new MutableItemTransferList(getProxies()); + + final int totalCount = tempmutableItemTransferList.getSlots(); + final int colSize = 9; + final int uiWidth = Math.max(PATTERNS_PER_ROW * 18 + 16, 106); + final int uiHeight = ROWS_PER_PAGE * 18 + 28; + + this.paginationUIManager = new PaginationUIManager(9, ROWS_PER_PAGE, totalCount, + uiWidth, uiHeight, + this::onPatternChange, + tempmutableItemTransferList); + + var group = new WidgetGroup(0, 0, paginationUIManager.getUiWidth(), paginationUIManager.getUiHeight()); + + // ME Network status indicator + group.addWidget(new LabelWidget(8, 2, + () -> this.isOnline ? "gtceu.gui.me_network.online" : "gtceu.gui.me_network.offline")); + + // Custom name input widget + group.addWidget(new AETextInputButtonWidget(18 * colSize + 8 - 70, 2, 70, 10) + .setText(customName) + .setOnConfirm(this::setCustomName) + .setButtonTooltips(Component.translatable("gui.gtceu.rename.desc"))); + + group.addWidget(paginationUIManager.createPaginationUI(null)); + + return group; + } + + @Override + public void attachConfigurators(ConfiguratorPanel configuratorPanel) { + // Share inventory configurator + configuratorPanel.attachConfigurators(new FancyInvConfigurator( + sharedToolsInventory, Component.translatable("gui.gtceu.share_inventory.title")) + .setTooltips(List.of( + Component.translatable("gui.gtceu.share_inventory.desc.0"), + Component.translatable("gui.gtceu.share_inventory.desc.1")))); + } + + @Override + public boolean shouldOpenUI(Player player, InteractionHand hand, BlockHitResult hit) { + return shouldOpen; + } + + // ======================================== + // Persist + // ======================================== + + @Override + public void saveCustomPersistedData(@NotNull CompoundTag tag, boolean forDrop) { + super.saveCustomPersistedData(tag, forDrop); + if (buffer.isEmpty() && outputItems.isEmpty()) return; + ListTag bufferTag = AEUtils.createListTag(AEKey::toTagGeneric, buffer); + if (!bufferTag.isEmpty()) tag.put("buffer", bufferTag); + + ListTag outputTag = AEUtils.createListTag(GenericStack::writeTag, outputItems); + if (!outputTag.isEmpty()) tag.put("outputItems", outputTag); + } + + @Override + public void loadCustomPersistedData(@NotNull CompoundTag tag) { + super.loadCustomPersistedData(tag); + buffer.clear(); + ListTag bufferTag = tag.getList("buffer", Tag.TAG_COMPOUND); + AEUtils.loadInventory(bufferTag, AEKey::fromTagGeneric, buffer); + + ListTag outputTag = tag.getList("outputItems", Tag.TAG_COMPOUND); + AEUtils.loadInventory(outputTag, GenericStack::readTag, outputItems); + } + + // ======================================== + // Pattern + // ======================================== + + @Override + public List getAvailablePatterns() { + return new ObjectArrayList<>(patternSlotMap.keySet()); + } + + @Override + public boolean isBusy() { + return false; + } + + @Override + public @Nullable IGrid getGrid() { + return getMainNode().getGrid(); + } + + @Override + public InternalInventory getTerminalPatternInventory() { + return internalPatternInventory; + } + + @Override + public PatternContainerGroup getTerminalGroup() { + List controllers = getControllers(); + + // Handle multiblock controller grouping + if (!controllers.isEmpty()) { + IMultiController controller = controllers.get(0); + MultiblockMachineDefinition controllerDefinition = controller.self().getDefinition(); + + if (!customName.isEmpty()) { + return new PatternContainerGroup( + AEItemKey.of(controllerDefinition.asStack()), + Component.literal(customName), + Collections.emptyList()); + } else { + return new PatternContainerGroup( + AEItemKey.of(controllerDefinition.asStack()), Component.translatable(controllerDefinition.getDescriptionId()), Collections.emptyList()); + } + } else { + if (!customName.isEmpty()) { + return new PatternContainerGroup( + AEItemKey.of(GTLMachines.GTAEMachines.ME_EXTEND_PATTERN_BUFFER.getItem()), + Component.literal(customName), + Collections.emptyList()); + } else { + return new PatternContainerGroup( + AEItemKey.of(GTLMachines.GTAEMachines.ME_EXTEND_PATTERN_BUFFER.getItem()), + GTLMachines.GTAEMachines.ME_EXTEND_PATTERN_BUFFER.get().getDefinition().getItem().getDescription(), + Collections.emptyList()); + } + } + } + + protected class Ticker implements IGridTickable { + + @Override + public TickingRequest getTickingRequest(IGridNode node) { + return new TickingRequest(MEExtendedOutputPartMachineBase.MIN_FREQUENCY, MEExtendedOutputPartMachineBase.MAX_FREQUENCY, false, true); + } + + @Override + public TickRateModulation tickingRequest(IGridNode node, int ticksSinceLastCall) { + if (!getMainNode().isActive()) { + return TickRateModulation.SLEEP; + } + + if (buffer.isEmpty()) { + if (ticksSinceLastCall >= MEExtendedOutputPartMachineBase.MAX_FREQUENCY) { + isSleeping = true; + return TickRateModulation.SLEEP; + } else return TickRateModulation.SLOWER; + } else return reFunds(buffer, getMainNode().getGrid(), actionSource) ? TickRateModulation.URGENT : TickRateModulation.SLOWER; + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEOutputFilterHandler.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEOutputFilterHandler.java new file mode 100644 index 000000000..0b38c1022 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEOutputFilterHandler.java @@ -0,0 +1,262 @@ +package org.gtlcore.gtlcore.common.machine.multiblock.part.ae; + +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.api.gui.widget.IntInputWidget; +import com.gregtechceu.gtceu.api.gui.widget.ToggleButtonWidget; +import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; + +import com.lowdragmc.lowdraglib.gui.widget.PhantomFluidWidget; +import com.lowdragmc.lowdraglib.gui.widget.PhantomSlotWidget; +import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; +import com.lowdragmc.lowdraglib.misc.FluidStorage; +import com.lowdragmc.lowdraglib.misc.ItemStackTransfer; +import com.lowdragmc.lowdraglib.side.fluid.FluidHelper; +import com.lowdragmc.lowdraglib.side.fluid.FluidStack; +import com.lowdragmc.lowdraglib.syncdata.ITagSerializable; +import com.lowdragmc.lowdraglib.utils.Position; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + +import appeng.api.stacks.AEFluidKey; +import appeng.api.stacks.AEItemKey; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import lombok.Getter; +import lombok.Setter; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import static com.lowdragmc.lowdraglib.LDLib.isRemote; +import static org.gtlcore.gtlcore.integration.ae2.AEUtils.createListTag; +import static org.gtlcore.gtlcore.integration.ae2.AEUtils.loadInventory; + +public class MEOutputFilterHandler implements ITagSerializable { + + private final ObjectOpenHashSet itemFilterHashSet = new ObjectOpenHashSet<>(); + private final ObjectOpenHashSet fluidFilterHashSet = new ObjectOpenHashSet<>(); + private final Runnable markDirty; + private final Runnable updatePriority; + private final Supplier prioritySupplier; + private final Consumer onPriorityChanged; + + @Getter + @Setter + protected boolean isItemBlackList; + @Getter + @Setter + protected boolean ignoreItemNbt = true; + @Getter + @Setter + protected boolean isFluidBlackList; + @Getter + @Setter + protected boolean ignoreFluidNbt = true; + + private boolean hasItemFilterChange; + private boolean hasFluidFilterChange; + @Getter + private boolean hasItemFilter; + @Getter + private boolean hasFluidFilter; + private final int row; + private final int col; + + private final FluidStorage[] filterTanks; + private final ItemStackTransfer filterSlots; + + public MEOutputFilterHandler(int row, int col, Runnable markDirty, Runnable updatePriority, Supplier prioritySupplier, Consumer onPriorityChanged) { + this.row = row; + this.col = col; + this.markDirty = markDirty; + this.updatePriority = updatePriority; + this.prioritySupplier = prioritySupplier; + this.onPriorityChanged = onPriorityChanged; + filterTanks = new FluidStorage[row * col]; + filterSlots = new ItemStackTransfer(row * col); + Arrays.setAll(filterTanks, i -> new FluidStorage(FluidHelper.getBucket())); + } + + public WidgetGroup createMainWidgetGroup() { + final var itemFilter = openItemFilterConfigurator(20, 5, this.row, this.col); + int height = itemFilter.getSizeHeight() + 5; + final var fluidFilter = openFluidFilterConfigurator(20, height + 5, this.row, this.col); + height += fluidFilter.getSizeHeight() + 10; + final var priorityWidget = new IntInputWidget(new Position(20, height), prioritySupplier, onPriorityChanged) + .setMin(10) + .setMax(100000); + height += priorityWidget.getSizeHeight(); + WidgetGroup widgetGroup = new WidgetGroup(0, 0, itemFilter.getSizeWidth() + 6, height); + widgetGroup.addWidget(itemFilter); + widgetGroup.addWidget(fluidFilter); + widgetGroup.addWidget(priorityWidget); + return widgetGroup; + } + + protected WidgetGroup openItemFilterConfigurator(int x, int y, int row, int col) { + WidgetGroup group = new WidgetGroup(x, y, 18 * col + 25, 18 * row); + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + final int index = i * col + j; + + var slot = new PhantomSlotWidget(filterSlots, index, j * 18, i * 18) { + + @Override + public void updateScreen() { + super.updateScreen(); + setMaxStackSize(1); + } + + @Override + public void detectAndSendChanges() { + super.detectAndSendChanges(); + setMaxStackSize(1); + } + } + .setChangeListener(() -> hasItemFilterChange = true) + .setBackground(GuiTextures.SLOT); + + group.addWidget(slot); + } + } + group.addWidget(new ToggleButtonWidget(18 * col + 17, 9, 18, 18, + GuiTextures.BUTTON_BLACKLIST, this::isItemBlackList, this::setItemBlackList)); + group.addWidget(new ToggleButtonWidget(18 * col + 17, (18) + 9, 18, 18, + GuiTextures.BUTTON_FILTER_NBT, this::isIgnoreItemNbt, this::setIgnoreItemNbt)); + return group; + } + + protected WidgetGroup openFluidFilterConfigurator(int x, int y, int row, int col) { + WidgetGroup group = new WidgetGroup(x, y, 18 * col + 25, 18 * row); + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + final int index = i * col + j; + + var slot = new PhantomFluidWidget(filterTanks[index], 0, j * 18, i * 18, 18, 18, + () -> filterTanks[index].getFluid(), + fluidStack -> filterTanks[index].setFluid(fluidStack)) { + + @Override + public void updateScreen() { + super.updateScreen(); + setShowAmount(false); + } + + @Override + public void detectAndSendChanges() { + super.detectAndSendChanges(); + setShowAmount(false); + } + } + .setChangeListener(() -> hasFluidFilterChange = true) + .setBackground(GuiTextures.SLOT); + + group.addWidget(slot); + } + } + group.addWidget(new ToggleButtonWidget(18 * col + 17, 9, 18, 18, + GuiTextures.BUTTON_BLACKLIST, this::isFluidBlackList, this::setFluidBlackList)); + group.addWidget(new ToggleButtonWidget(18 * col + 17, (18) + 9, 18, 18, + GuiTextures.BUTTON_FILTER_NBT, this::isIgnoreFluidNbt, this::setIgnoreFluidNbt)); + return group; + } + + protected void onUIClosed() { + if (isRemote()) return; + if (hasItemFilterChange || hasFluidFilterChange) { + if (hasItemFilterChange) { + itemFilterHashSet.clear(); + for (int i = 0; i < filterSlots.getSlots(); i++) { + var itemStack = filterSlots.getStackInSlot(i); + if (!itemStack.isEmpty()) { + itemFilterHashSet.add(ignoreItemNbt ? AEItemKey.of(itemStack.getItem()) : AEItemKey.of(itemStack)); + } + } + hasItemFilter = !itemFilterHashSet.isEmpty(); + hasItemFilterChange = false; + } + if (hasFluidFilterChange) { + fluidFilterHashSet.clear(); + for (FluidStorage filterTank : filterTanks) { + var fluidStack = filterTank.getFluid(); + if (!fluidStack.isEmpty()) { + fluidFilterHashSet.add(ignoreFluidNbt ? AEFluidKey.of(fluidStack.getFluid()) : AEFluidKey.of(fluidStack.getFluid(), fluidStack.getTag())); + } + } + hasFluidFilter = !fluidFilterHashSet.isEmpty(); + hasFluidFilterChange = false; + } + this.markDirty.run(); + } + this.updatePriority.run(); + } + + public List testIngredient(List left) { + if (!hasItemFilter) return List.of(); + left.removeIf(ingredient -> test(ingredient.getItems()[0])); + return left; + } + + public List testFluidIngredient(List left) { + if (!hasFluidFilter) return List.of(); + left.removeIf(ingredient -> test(ingredient.getStacks()[0])); + return left; + } + + protected boolean test(ItemStack itemStack) { + if (!hasItemFilter) return true; + final var key = ignoreItemNbt ? AEItemKey.of(itemStack.getItem()) : AEItemKey.of(itemStack); + return isItemBlackList != itemFilterHashSet.contains(key); + } + + protected boolean test(FluidStack fluidStack) { + if (!hasFluidFilter) return true; + final var key = ignoreFluidNbt ? AEFluidKey.of(fluidStack.getFluid()) : AEFluidKey.of(fluidStack.getFluid(), fluidStack.getTag()); + return isFluidBlackList != fluidFilterHashSet.contains(key); + } + + @Override + public CompoundTag serializeNBT() { + CompoundTag tag = new CompoundTag(); + + ListTag itemsTag = createListTag(AEItemKey::toTag, itemFilterHashSet); + if (!itemsTag.isEmpty()) tag.put("items", itemsTag); + ListTag fluidsTag = createListTag(AEFluidKey::toTag, fluidFilterHashSet); + if (!fluidsTag.isEmpty()) tag.put("fluids", fluidsTag); + + tag.putBoolean("isItemBlackList", isItemBlackList); + tag.putBoolean("ignoreItemNbt", ignoreItemNbt); + tag.putBoolean("isFluidBlackList", isFluidBlackList); + tag.putBoolean("ignoreFluidNbt", ignoreFluidNbt); + tag.put("filterSlots", filterSlots.serializeNBT()); + + var list = new ListTag(); + Arrays.stream(filterTanks).map(FluidStorage::serializeNBT).forEach(list::add); + tag.put("filterTanks", list); + + return tag; + } + + @Override + public void deserializeNBT(CompoundTag tag) { + loadInventory(tag.getList("items", Tag.TAG_COMPOUND), AEItemKey::fromTag, itemFilterHashSet); + loadInventory(tag.getList("fluids", Tag.TAG_COMPOUND), AEFluidKey::fromTag, fluidFilterHashSet); + + isItemBlackList = tag.getBoolean("isItemBlackList"); + isFluidBlackList = tag.getBoolean("isFluidBlackList"); + ignoreItemNbt = tag.getBoolean("ignoreItemNbt"); + ignoreFluidNbt = tag.getBoolean("ignoreFluidNbt"); + filterSlots.deserializeNBT(tag.getCompound("filterSlots")); + + var list = tag.getList("filterTanks", Tag.TAG_COMPOUND); + for (int i = 0; i < list.size(); i++) filterTanks[i].deserializeNBT(list.getCompound(i)); + + hasItemFilter = !itemFilterHashSet.isEmpty(); + hasFluidFilter = !fluidFilterHashSet.isEmpty(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEPatternBufferPartMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEPatternBufferPartMachine.java new file mode 100644 index 000000000..2fad15aab --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEPatternBufferPartMachine.java @@ -0,0 +1,1166 @@ +package org.gtlcore.gtlcore.common.machine.multiblock.part.ae; + +import org.gtlcore.gtlcore.api.gui.MEPatternCatalystUIManager; +import org.gtlcore.gtlcore.api.machine.trait.*; +import org.gtlcore.gtlcore.api.machine.trait.MEPart.IMEPatternPartMachine; +import org.gtlcore.gtlcore.api.machine.trait.MEPart.IMEPatternTrait; +import org.gtlcore.gtlcore.common.data.GTLMachines; +import org.gtlcore.gtlcore.integration.ae2.AEUtils; +import org.gtlcore.gtlcore.integration.ae2.handler.PatternCircuitHandler; +import org.gtlcore.gtlcore.integration.ae2.handler.SlotCacheManager; +import org.gtlcore.gtlcore.integration.ae2.widget.AEPatternViewExtendSlotWidget; +import org.gtlcore.gtlcore.utils.GTLUtil; + +import com.gregtechceu.gtceu.api.capability.recipe.*; +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.api.gui.fancy.ConfiguratorPanel; +import com.gregtechceu.gtceu.api.machine.*; +import com.gregtechceu.gtceu.api.machine.fancyconfigurator.*; +import com.gregtechceu.gtceu.api.machine.feature.IInteractedMachine; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; +import com.gregtechceu.gtceu.api.machine.trait.NotifiableItemStackHandler; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.content.Content; +import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; +import com.gregtechceu.gtceu.common.data.GTItems; +import com.gregtechceu.gtceu.common.data.GTRecipeTypes; +import com.gregtechceu.gtceu.common.item.IntCircuitBehaviour; +import com.gregtechceu.gtceu.integration.ae2.gui.widget.AETextInputButtonWidget; +import com.gregtechceu.gtceu.utils.*; + +import com.lowdragmc.lowdraglib.gui.texture.GuiTextureGroup; +import com.lowdragmc.lowdraglib.gui.util.ClickData; +import com.lowdragmc.lowdraglib.gui.widget.*; +import com.lowdragmc.lowdraglib.misc.*; +import com.lowdragmc.lowdraglib.side.fluid.*; +import com.lowdragmc.lowdraglib.syncdata.IContentChangeAware; +import com.lowdragmc.lowdraglib.syncdata.ITagSerializable; +import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; +import com.lowdragmc.lowdraglib.syncdata.annotation.LazyManaged; +import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; +import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.*; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraftforge.server.ServerLifecycleHooks; + +import appeng.api.crafting.IPatternDetails; +import appeng.api.implementations.blockentities.PatternContainerGroup; +import appeng.api.inventories.InternalInventory; +import appeng.api.networking.IGrid; +import appeng.api.networking.IGridNode; +import appeng.api.networking.IGridNodeListener; +import appeng.api.networking.crafting.ICraftingProvider; +import appeng.api.networking.ticking.*; +import appeng.api.stacks.*; +import appeng.core.definitions.AEItems; +import appeng.crafting.pattern.EncodedPatternItem; +import appeng.crafting.pattern.ProcessingPatternItem; +import appeng.helpers.patternprovider.PatternContainer; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.primitives.Ints; +import com.hepdd.gtmthings.common.block.machine.trait.CatalystFluidStackHandler; +import com.hepdd.gtmthings.common.block.machine.trait.CatalystItemStackHandler; +import it.unimi.dsi.fastutil.Pair; +import it.unimi.dsi.fastutil.ints.*; +import it.unimi.dsi.fastutil.objects.*; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.stream.Stream; + +import static org.gtlcore.gtlcore.api.pattern.AdvancedBlockPattern.foundItem; + +public class MEPatternBufferPartMachine extends MEIOPartMachine implements IInteractedMachine, ICraftingProvider, PatternContainer, IMEPatternPartMachine { + + protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( + MEPatternBufferPartMachine.class, MEIOPartMachine.MANAGED_FIELD_HOLDER); + + private final InternalInventory internalPatternInventory = new InternalInventory() { + + @Override + public int size() { + return maxPatternCount; + } + + @Override + public ItemStack getStackInSlot(int slotIndex) { + return patternInventory.getStackInSlot(slotIndex); + } + + @Override + public void setItemDirect(int slotIndex, ItemStack stack) { + patternInventory.setStackInSlot(slotIndex, stack); + patternInventory.onContentsChanged(slotIndex); + onPatternChange(slotIndex); + } + + @Override + public boolean isItemValid(int slot, ItemStack stack) { + return slot <= maxPatternCount && AEUtils.PROCESS_FILTER.apply(stack); + } + }; + + // ======================================== + // Info + // ======================================== + + @DescSynced + @Persisted + @Setter + @Getter + protected String customName = ""; + protected final int maxPatternCount; + private final boolean[] hasPatternArray; + @DescSynced + protected final boolean[] cacheRecipe; + private boolean needPatternSync; + + // ======================================== + // Inventory + // ======================================== + + @Getter + @Persisted + private final ItemStackTransfer patternInventory; + + @Getter + @Persisted(key = "shareInventory") + protected final CatalystItemStackHandler sharedCatalystInventory; + + @Getter + @Persisted(key = "shareTank") + protected final CatalystFluidStackHandler sharedCatalystTank; + + @Getter + @Persisted(key = "mePatternCircuitInventory") + protected final NotifiableItemStackHandler sharedCircuitInventory; + + @Persisted + protected final ItemStackTransfer[] catalystItems; + + @Persisted + @LazyManaged + protected final FluidTransferList[] catalystFluids; + + @Getter + @Persisted + protected final InternalSlot[] internalInventory; + + @Getter + protected final Object2LongOpenHashMap buffer; + + // ======================================== + // Handlers + // ======================================== + + /** Pattern circuit handler for managing circuit logic */ + @Getter + protected final PatternCircuitHandler circuitHandler; + + /** Recipe handler trait for ME Pattern Buffer */ + protected final MEPatternBufferRecipeHandlerTrait recipeHandler; + + // ======================================== + // Cache Map + // ======================================== + + protected final Int2ReferenceMap> recipeMultipleCacheMap; + protected final byte[] cacheRecipeCount; + private final BiMap<@NotNull IPatternDetails, Integer> patternSlotMap; + private final Int2ObjectMap slot2PatternMap; + protected IntConsumer removeSlotFromMap = i -> {}; + + // ======================================== + // Proxy + // ======================================== + + @Persisted + private final ObjectOpenHashSet proxies; + private final Set proxyMachines; + + public MEPatternBufferPartMachine(IMachineBlockEntity holder, int maxPatternCount, IO io) { + super(holder, io); + + // Initialize UI configuration + this.maxPatternCount = maxPatternCount; + + // Initialize arrays with calculated size + this.hasPatternArray = new boolean[maxPatternCount]; + this.cacheRecipe = new boolean[maxPatternCount]; + this.internalInventory = new InternalSlot[maxPatternCount]; + this.catalystItems = new ItemStackTransfer[maxPatternCount]; + this.catalystFluids = new FluidTransferList[maxPatternCount]; + this.cacheRecipeCount = new byte[maxPatternCount]; + + // Initialize pattern cache + this.patternSlotMap = HashBiMap.create(); + this.slot2PatternMap = new Int2ObjectOpenHashMap<>(); + this.recipeMultipleCacheMap = new Int2ReferenceOpenHashMap<>(); + + // Initialize Proxy + this.proxies = new ObjectOpenHashSet<>(); + this.proxyMachines = new ReferenceOpenHashSet<>(); + + // Initialize inventories + this.buffer = new Object2LongOpenHashMap<>(); + this.patternInventory = new ItemStackTransfer(maxPatternCount); + this.patternInventory.setFilter(AEUtils.PROCESS_FILTER); + Arrays.setAll(internalInventory, InternalSlot::new); + Arrays.setAll(catalystItems, i -> { + var transfer = new ItemStackTransfer(9); + transfer.setFilter(stack -> !(stack.getItem() instanceof ProcessingPatternItem)); + return transfer; + }); + Arrays.setAll(catalystFluids, i -> new FluidTransferList(Stream.generate(() -> (IFluidTransfer) new FluidStorage(16 * FluidHelper.getBucket())) + .limit(9) + .toList())); + Arrays.fill(cacheRecipeCount, (byte) 1); + + // Initialize handlers + this.sharedCircuitInventory = new NotifiableCircuitItemStackHandler(this); + this.circuitHandler = new PatternCircuitHandler((NotifiableCircuitItemStackHandler) sharedCircuitInventory); + this.sharedCatalystInventory = new CatalystItemStackHandler(this, 9, IO.IN, IO.NONE); + this.sharedCatalystTank = new CatalystFluidStackHandler(this, 9, 16 * FluidHelper.getBucket(), IO.IN, IO.NONE); + this.recipeHandler = new MEPatternBufferRecipeHandlerTrait(this, io); + + // Initialize AE2 Service + getMainNode().addService(ICraftingProvider.class, this); + if (io == IO.BOTH) { + getMainNode().addService(IGridTickable.class, new Ticker()); + } + } + + // ======================================== + // LIFECYCLE & NETWORK MANAGEMENT + // ======================================== + + @Nullable + protected TickableSubscription updateSubs; + + @Override + public void onLoad() { + super.onLoad(); + if (getLevel() instanceof ServerLevel serverLevel) { + serverLevel.getServer().execute(() -> { + for (int i = 0; i < patternInventory.getSlots(); i++) { + var pattern = patternInventory.getStackInSlot(i); + var realPattern = getRealPattern(i, pattern); + if (realPattern != null) { + this.slot2PatternMap.put(i, realPattern); + hasPatternArray[i] = true; + } + } + reCalculatePatternSlotMap(); + needPatternSync = true; + }); + for (int i = 0; i < maxPatternCount; i++) { + final int index = i; + catalystItems[index].setOnContentsChanged(() -> reCalculateCatalystItemMap(index)); + for (IFluidTransfer transfer : catalystFluids[index].transfers) { + if (transfer instanceof FluidStorage storage) { + storage.setOnContentsChanged(() -> reCalculateCatalystFluidMap(index)); + } + } + } + } + } + + @Override + public void onMainNodeStateChanged(IGridNodeListener.@NotNull State reason) { + super.onMainNodeStateChanged(reason); + this.updateSubscription(); + } + + protected void updateSubscription() { + if (getMainNode().isOnline()) { + updateSubs = subscribeServerTick(updateSubs, this::update); + } else if (updateSubs != null) { + updateSubs.unsubscribe(); + updateSubs = null; + } + } + + protected void update() { + if (needPatternSync) { + ICraftingProvider.requestUpdate(getMainNode()); + this.needPatternSync = false; + } + } + + protected void onPatternChange(int index) { + if (isRemote()) return; + + var internalInv = internalInventory[index]; + var newPattern = patternInventory.getStackInSlot(index); + var newPatternDetailsWithOutCircuit = getRealPattern(index, newPattern); + var oldPatternDetails = slot2PatternMap.get(index); + + // Update pattern mapping and tracking + if (newPatternDetailsWithOutCircuit != null) { + slot2PatternMap.put(index, newPatternDetailsWithOutCircuit); + hasPatternArray[index] = true; + } else { + slot2PatternMap.remove(index); + hasPatternArray[index] = false; + } + + // remove old pattern cache + if (oldPatternDetails != null && !oldPatternDetails.equals(newPatternDetailsWithOutCircuit)) { + internalInv.cacheManager.clearAllCaches(); + removeSlotFromGTRecipeCache(index); + refundSlot(internalInv); + AEUtils.reFunds(buffer, getMainNode().getGrid(), actionSource); + } + + reCalculatePatternSlotMap(); + needPatternSync = true; + } + + @Override + public void onMachineRemoved() { + clearInventory(patternInventory); + clearInventory(sharedCatalystInventory); + for (ItemStackTransfer catalystItem : catalystItems) { + clearInventory(catalystItem); + } + for (MEPatternBufferProxyPartMachine proxy : this.getProxies()) { + proxy.setBuffer(null); + } + } + + private void reCalculateCatalystItemMap(int slot) { + final var itemCatalystInventory = internalInventory[slot].itemCatalystInventory; + itemCatalystInventory.clear(); + var catalystItem = catalystItems[slot]; + for (int i = 0; i < catalystItem.getSlots(); i++) { + ItemStack stack = catalystItem.getStackInSlot(i); + if (!stack.isEmpty()) { + itemCatalystInventory.mergeLong(AEItemKey.of(stack), stack.getCount(), Long::sum); + } + } + internalInventory[slot].onContentsChanged.run(); + } + + private void reCalculateCatalystFluidMap(int slot) { + final var fluidCatalystInventory = internalInventory[slot].fluidCatalystInventory; + fluidCatalystInventory.clear(); + var catalystFluid = catalystFluids[slot]; + for (int i = 0; i < catalystFluid.getTanks(); i++) { + FluidStack stack = catalystFluid.getFluidInTank(i); + if (!stack.isEmpty()) { + fluidCatalystInventory.mergeLong(AEFluidKey.of(stack.getFluid()), stack.getAmount(), Long::sum); + } + } + internalInventory[slot].onContentsChanged.run(); + } + + protected void reCalculatePatternSlotMap() { + patternSlotMap.clear(); + for (var entry : Int2ObjectMaps.fastIterable(slot2PatternMap)) { + int slot = entry.getIntKey(); + var pattern = entry.getValue(); + if (pattern != null) { + if (cacheRecipe[slot]) patternSlotMap.forcePut(pattern, slot); + else patternSlotMap.putIfAbsent(pattern, slot); + } + } + } + + protected void removeSlotFromGTRecipeCache(int slot) { + cacheRecipe[slot] = false; + recipeMultipleCacheMap.remove(slot); + removeSlotFromMap.accept(slot); + for (MEPatternBufferProxyPartMachine proxy : this.getProxies()) { + proxy.removeSlotFromMap.accept(slot); + } + } + + // ======================================== + // Persist + // ======================================== + + @Override + public void saveCustomPersistedData(@NotNull CompoundTag tag, boolean forDrop) { + super.saveCustomPersistedData(tag, forDrop); + if (!recipeMultipleCacheMap.isEmpty()) { + final CompoundTag recipeCacheTag = new CompoundTag(); + for (var entry : Int2ReferenceMaps.fastIterable(recipeMultipleCacheMap)) { + var recipeSet = entry.getValue(); + if (recipeSet.isEmpty()) continue; + + final ListTag list = new ListTag(); + for (GTRecipe recipe : recipeSet) { + list.add(GTLUtil.serializeNBT(recipe)); + } + + recipeCacheTag.put(Integer.toString(entry.getIntKey()), list); + } + tag.put("recipeMultipleCacheIdMap", recipeCacheTag); + } + + tag.putByteArray("cacheRecipeCount", cacheRecipeCount); + + ListTag bufferTag = AEUtils.createListTag(AEKey::toTagGeneric, buffer); + if (!bufferTag.isEmpty()) tag.put("buffer", bufferTag); + } + + @Override + public void loadCustomPersistedData(@NotNull CompoundTag tag) { + super.loadCustomPersistedData(tag); + + var byteArray = tag.getByteArray("cacheRecipeCount"); + System.arraycopy(byteArray, 0, cacheRecipeCount, 0, byteArray.length); + + recipeMultipleCacheMap.clear(); + var recipeManager = ServerLifecycleHooks.getCurrentServer().getRecipeManager(); + if (tag.contains("recipeMultipleCacheIdMap")) { + CompoundTag recipeCacheTag = tag.getCompound("recipeMultipleCacheIdMap"); + for (String key : recipeCacheTag.getAllKeys()) { + final int slotIndex = Integer.parseInt(key); + final ListTag recipeTags = recipeCacheTag.getList(key, Tag.TAG_COMPOUND); + for (Tag recipeTag : recipeTags) { + GTRecipe recipe = GTLUtil.deserializeNBT(recipeTag); + if (recipe != null && slotIndex >= 0 && slotIndex < maxPatternCount) { + var real = recipe.recipeType.getRecipe(recipeManager, recipe.id); + if (real != null) { + var set = recipeMultipleCacheMap.computeIfAbsent(slotIndex, integer -> new ObjectArraySet<>()); + set.add(real); + if (set.size() >= cacheRecipeCount[slotIndex]) cacheRecipe[slotIndex] = true; + } + } + } + } + } else if (tag.contains("gtRecipeCache")) { + CompoundTag oldRecipeCacheTag = tag.getCompound("gtRecipeCache"); + for (String key : oldRecipeCacheTag.getAllKeys()) { + int slotIndex = Integer.parseInt(key); + Tag recipeTag = oldRecipeCacheTag.get(key); + GTRecipe recipe = GTLUtil.deserializeNBT(recipeTag); + if (recipe != null && slotIndex >= 0 && slotIndex < maxPatternCount) { + var real = recipe.recipeType.getRecipe(recipeManager, recipe.id); + if (real != null) { + var set = recipeMultipleCacheMap.computeIfAbsent(slotIndex, integer -> new ObjectArraySet<>()); + set.add(real); + if (set.size() >= cacheRecipeCount[slotIndex]) cacheRecipe[slotIndex] = true; + } + } + } + } // Compatibility + + ListTag bufferTag = tag.getList("buffer", Tag.TAG_COMPOUND); + AEUtils.loadInventory(bufferTag, AEKey::fromTagGeneric, buffer); + } + + public void copyFromTag(CompoundTag tag, ServerPlayer serverPlayer) { + this.setCustomName(tag.getString("name")); + var list = tag.getList("patterns", Tag.TAG_COMPOUND); + + int listIndex = 0; + for (int index = 0; index < internalPatternInventory.size() && listIndex < list.size(); index++) { + if (!internalPatternInventory.getStackInSlot(index).isEmpty()) { + continue; + } + + var result = foundItem(serverPlayer, List.of(AEItems.BLANK_PATTERN.stack()), AEItems.BLANK_PATTERN.stack()::is); + if (result.getA() == null) break; + + CompoundTag patternData = list.getCompound(listIndex); + var patternTag = patternData.getCompound("pattern"); + var sourceCacheCount = patternData.getByte("cacheCount"); + if (sourceCacheCount <= 0) break; + + internalPatternInventory.setItemDirect(index, ItemStack.of(patternTag)); + this.cacheRecipeCount[index] = sourceCacheCount; + var handler = result.getB(); + if (handler != null) handler.extractItem(result.getC(), 1, false); + + listIndex++; + } + } + + public CompoundTag copyToTag(CompoundTag tags) { + var tag = new CompoundTag(); + tag.putString("name", customName); + + var listPattern = new ListTag(); + for (int slotIndex : patternSlotMap.values()) { + ItemStack stack = internalPatternInventory.getStackInSlot(slotIndex); + if (!stack.isEmpty()) { + CompoundTag patternData = new CompoundTag(); + patternData.put("pattern", stack.serializeNBT()); + patternData.putByte("cacheCount", cacheRecipeCount[slotIndex]); + listPattern.add(patternData); + } + } + tag.put("patterns", listPattern); + + tags.put("tag", tag); + return tags; + } + + @Override + @NotNull + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } + + // ======================================== + // PROXY MANAGEMENT SYSTEM + // ======================================== + + public void addProxy(MEPatternBufferProxyPartMachine proxy) { + proxies.add(proxy.getPos()); + proxyMachines.add(proxy); + } + + public void removeProxy(MEPatternBufferProxyPartMachine proxy) { + proxies.remove(proxy.getPos()); + proxyMachines.remove(proxy); + } + + public Set getProxies() { + if (proxyMachines.size() != proxies.size()) { + proxyMachines.clear(); + for (var pos : proxies) { + if (MetaMachine.getMachine(Objects.requireNonNull(getLevel()), pos) instanceof MEPatternBufferProxyPartMachine proxy) { + proxyMachines.add(proxy); + } + } + } + return Collections.unmodifiableSet(proxyMachines); + } + + // ======================================== + // REFUND SYSTEM + // ======================================== + + private void refundAll(ClickData clickData) { + if (!clickData.isRemote) { + // Move all slot contents to pending refund + Arrays.stream(internalInventory) + .filter(InternalSlot::isActive) + .forEach(this::refundSlot); + + // Immediately try to process pending refunds + AEUtils.reFunds(buffer, getMainNode().getGrid(), actionSource); + } + } + + public void refundSlot(InternalSlot slot) { + // Move all item contents to pending refund + for (var it = slot.itemInventory.object2LongEntrySet().fastIterator(); it.hasNext();) { + var entry = it.next(); + long amount = entry.getLongValue(); + if (amount > 0) { + buffer.addTo(entry.getKey(), amount); + it.remove(); + } + } + + // Move all fluid contents to pending refund + for (var it = slot.fluidInventory.object2LongEntrySet().fastIterator(); it.hasNext();) { + var entry = it.next(); + long amount = entry.getLongValue(); + if (amount > 0) { + buffer.addTo(entry.getKey(), amount); + it.remove(); + } + } + } + + // ======================================== + // DATA STICK INTERACTION + // ======================================== + + @Override + public InteractionResult onUse(BlockState state, Level world, BlockPos pos, Player player, + InteractionHand hand, BlockHitResult hit) { + ItemStack stack = player.getItemInHand(hand); + if (stack.isEmpty()) return InteractionResult.PASS; + + if (stack.is(GTItems.TOOL_DATA_STICK.asItem())) { + if (!world.isClientSide) { + // Check if it's research data - if so, pass to avoid conflicts + var researchData = ResearchManager.readResearchId(stack); + if (researchData != null) { + return InteractionResult.PASS; + } + + // Store this pattern buffer's position in the data stick + stack.getOrCreateTag().putIntArray("pos", new int[] { pos.getX(), pos.getY(), pos.getZ() }); + player.sendSystemMessage(Component.translatable("gtceu.machine.me.import_copy_settings")); + } + return InteractionResult.sidedSuccess(world.isClientSide); + } + + return InteractionResult.PASS; + } + + // ======================================== + // GUI SYSTEM + // ======================================== + + @Override + public void attachConfigurators(ConfiguratorPanel configuratorPanel) { + // Refund all button + configuratorPanel.attachConfigurators(new ButtonConfigurator( + new GuiTextureGroup(GuiTextures.BUTTON, GuiTextures.REFUND_OVERLAY), this::refundAll) + .setTooltips(List.of(Component.translatable("gui.gtceu.refund_all.desc")))); + + // Circuit configurator + configuratorPanel.attachConfigurators(new CircuitFancyConfigurator(sharedCircuitInventory.storage)); + + // Share inventory configurator + configuratorPanel.attachConfigurators(new FancyInvConfigurator( + sharedCatalystInventory.storage, Component.translatable("gui.gtceu.share_inventory.title")) + .setTooltips(List.of( + Component.translatable("gui.gtceu.share_inventory.desc.0"), + Component.translatable("gui.gtceu.share_inventory.desc.1")))); + + // Share tank configurator + configuratorPanel.attachConfigurators(new FancyTankConfigurator( + sharedCatalystTank.getStorages(), Component.translatable("gui.gtceu.share_tank.title")) + .setTooltips(List.of( + Component.translatable("gui.gtceu.share_tank.desc.0"), + Component.translatable("gui.gtceu.share_inventory.desc.1")))); + } + + @Override + public @NotNull Widget createUIWidget() { + int rowSize = 9; + int colSize = maxPatternCount / rowSize; + var group = new WidgetGroup(0, 0, 18 * rowSize + 16, 18 * colSize + 16); + + // ME Network status indicator + group.addWidget(new LabelWidget(8, 2, + () -> this.isOnline ? "gtceu.gui.me_network.online" : "gtceu.gui.me_network.offline")); + + // Custom name input widget + group.addWidget(new AETextInputButtonWidget(18 * rowSize + 8 - 70, 2, 70, 10) + .setText(customName) + .setOnConfirm(this::setCustomName) + .setButtonTooltips(Component.translatable("gui.gtceu.rename.desc"))); + + final var catalystUIManager = new MEPatternCatalystUIManager(group.getSizeWidth() + 4, catalystItems, catalystFluids, cacheRecipeCount, this::removeSlotFromGTRecipeCache); + group.waitToAdded(catalystUIManager); + + int index = 0; + for (int y = 0; y < colSize; ++y) { + for (int x = 0; x < rowSize; ++x) { + int finalI = index; + + var slot = new AEPatternViewExtendSlotWidget(patternInventory, index++, x * 18 + 8, y * 18 + 14) + .setOnMiddleClick(() -> catalystUIManager.toggleFor(finalI)) + .setOnPatternSlotChanged(() -> this.onPatternChange(finalI)) + .setOccupiedTexture(GuiTextures.SLOT) + .setItemHook(stack -> { + if (!stack.isEmpty() && stack.getItem() instanceof EncodedPatternItem iep) { + final ItemStack out = iep.getOutput(stack); + if (!out.isEmpty()) return out; + } + return stack; + }) + .setOnAddedTooltips((s, l) -> { + if (cacheRecipe[finalI]) + l.add(Component.translatable("gtceu.machine.pattern.recipe.cache")); + }) + .setBackground(GuiTextures.SLOT, GuiTextures.PATTERN_OVERLAY); + group.addWidget(slot); + } + } + return group; + } + + // ======================================== + // CIRCUIT HANDLING + // ======================================== + + private IPatternDetails getRealPattern(int slot, ItemStack stack) { + if (!stack.isEmpty()) { + var internalSlot = internalInventory[slot]; + return circuitHandler.processPatternWithCircuit( + stack, internalSlot.cacheManager::setCircuitCache, getLevel()); + } + return null; + } + + /** + * 获取用于配方的电路 + * + * @param slotIndex 槽位索引 + * @return 电路ItemStack,可能为空 + */ + public ItemStack getCircuitForRecipe(int slotIndex) { + return circuitHandler.getCircuitForRecipe(internalInventory[slotIndex].cacheManager.getCircuitStack()); + } + + // ======================================== + // AE2 CRAFTING + // ======================================== + + @Override + public List getAvailablePatterns() { + return patternSlotMap.keySet().stream().toList(); + } + + @Override + public boolean pushPattern(IPatternDetails patternDetails, KeyCounter[] inputHolder) { + if (!getMainNode().isActive() || !patternSlotMap.containsKey(patternDetails)) { + return false; + } + + var slotIndex = patternSlotMap.get(patternDetails); + if (slotIndex != null && slotIndex >= 0) { + internalInventory[slotIndex].pushPattern(inputHolder); + return true; + } + return false; + } + + @Override + public boolean isBusy() { + return false; + } + + // ======================================== + // PATTERN CONTAINER IMPLEMENTATION + // ======================================== + + @Override + public @Nullable IGrid getGrid() { + return getMainNode().getGrid(); + } + + @Override + public InternalInventory getTerminalPatternInventory() { + return internalPatternInventory; + } + + @Override + public PatternContainerGroup getTerminalGroup() { + List controllers = getControllers(); + + // Handle multiblock controller grouping + if (!controllers.isEmpty()) { + IMultiController controller = controllers.get(0); + MultiblockMachineDefinition controllerDefinition = controller.self().getDefinition(); + + if (!customName.isEmpty()) { + return new PatternContainerGroup( + AEItemKey.of(controllerDefinition.asStack()), + Component.literal(customName), + Collections.emptyList()); + } else { + ItemStack circuitStack = sharedCircuitInventory.storage.getStackInSlot(0); + int circuitConfiguration = circuitStack.isEmpty() ? -1 : + IntCircuitBehaviour.getCircuitConfiguration(circuitStack); + + Component groupName = circuitConfiguration != -1 ? + Component.translatable(controllerDefinition.getDescriptionId()) + .append(" - " + circuitConfiguration) : + Component.translatable(controllerDefinition.getDescriptionId()); + + return new PatternContainerGroup( + AEItemKey.of(controllerDefinition.asStack()), groupName, Collections.emptyList()); + } + } else { + if (!customName.isEmpty()) { + return new PatternContainerGroup( + AEItemKey.of(GTLMachines.GTAEMachines.ME_EXTEND_PATTERN_BUFFER.getItem()), + Component.literal(customName), + Collections.emptyList()); + } else { + return new PatternContainerGroup( + AEItemKey.of(GTLMachines.GTAEMachines.ME_EXTEND_PATTERN_BUFFER.getItem()), + GTLMachines.GTAEMachines.ME_EXTEND_PATTERN_BUFFER.get().getDefinition().getItem().getDescription(), + Collections.emptyList()); + } + } + } + + // ======================================== + // IMEPatternPartMachine + // ======================================== + + @Override + protected @NotNull IMEPatternTrait createMETrait() { + return new MEPatternTrait(this); + } + + @Override + public @NotNull IMEPatternTrait getMETrait() { + return (IMEPatternTrait) meTrait; + } + + @Override + public Pair, IMERecipeHandlerTrait> getMERecipeHandlerTraits() { + return Pair.of(recipeHandler.meItemHandler, recipeHandler.meFluidHandler); + } + + /** + * Internal Slot: Pattern-specific inventory management + * Each slot represents a single pattern's ingredient storage with separate item and fluid inventories. + * Provides optimized handling for recipe matching + * and ingredient consumption with efficient serialization. + * Features: + * - Separate inventories per pattern + * - Efficient recipe matching with early exit conditions + */ + public class InternalSlot implements ITagSerializable, IContentChangeAware { + + @Getter + @Setter + protected Runnable onContentsChanged = () -> { + recipeHandler.getMeFluidHandler().notifyListeners(); + recipeHandler.getMeItemHandler().notifyListeners(); + }; + + @Getter + private final Object2LongOpenHashMap itemInventory = new Object2LongOpenHashMap<>(); + + @Getter + private final Object2LongOpenHashMap fluidInventory = new Object2LongOpenHashMap<>(); + + private final Object2LongMap itemCatalystInventory = new Object2LongArrayMap<>(); + + private final Object2LongMap fluidCatalystInventory = new Object2LongArrayMap<>(); + + @Persisted + @Getter + private final SlotCacheManager cacheManager = new SlotCacheManager(); + + @Getter + private final int slotIndex; + + public InternalSlot(int slotIndex) { + this.slotIndex = slotIndex; + itemInventory.defaultReturnValue(0); + fluidInventory.defaultReturnValue(0); + itemCatalystInventory.defaultReturnValue(0); + fluidCatalystInventory.defaultReturnValue(0); + } + + public boolean isActive() { + return hasPatternArray[slotIndex] && (!itemInventory.isEmpty() || !fluidInventory.isEmpty()); + } + + public boolean isItemActive(boolean simulate) { + return hasPatternArray[slotIndex] && simulate ? (!itemInventory.isEmpty() || !sharedCatalystInventory.isEmpty() || !circuitHandler.getCircuitForRecipe(cacheManager.getCircuitStack()).isEmpty() || !itemCatalystInventory.isEmpty()) : !itemInventory.isEmpty(); + } + + public boolean isFluidActive(boolean simulate) { + return hasPatternArray[slotIndex] && simulate ? !fluidInventory.isEmpty() || !sharedCatalystTank.isEmpty() || !fluidCatalystInventory.isEmpty() : !fluidInventory.isEmpty(); + } + + private void add(AEKey what, long amount) { + if (amount <= 0L) return; + if (what instanceof AEItemKey itemKey) { + itemInventory.addTo(itemKey, amount); + } else if (what instanceof AEFluidKey fluidKey) { + fluidInventory.addTo(fluidKey, amount); + } + } + + public Object2LongMap getItemStackInputMap() { + var itemInputMap = new Object2LongOpenCustomHashMap<>(ItemStackHashStrategy.comparingAllButCount()); + for (Object2LongMap.Entry entry : Object2LongMaps.fastIterable(itemInventory)) { + AEItemKey key = entry.getKey(); + long amount = entry.getLongValue(); + if (amount <= 0) continue; + + ItemStack stack = key.toStack(1); + itemInputMap.addTo(stack, amount); + } + return itemInputMap; + } + + public Object2LongMap getFluidStackInputMap() { + var fluidInputMap = new Object2LongOpenCustomHashMap<>(FluidStackHashStrategy.comparingAllButAmount()); + for (Object2LongMap.Entry entry : Object2LongMaps.fastIterable(fluidInventory)) { + AEFluidKey key = entry.getKey(); + long amount = entry.getLongValue(); + if (amount <= 0) continue; + + FluidStack stack = FluidStack.create(key.getFluid(), 1); + fluidInputMap.addTo(stack, amount); + } + return fluidInputMap; + } + + public ObjectList getLimitItemStackInput() { + var limitInput = new ObjectArrayList(itemInventory.size()); + for (var it = Object2LongMaps.fastIterator(itemInventory); it.hasNext();) { + var entry = it.next(); + long amount = entry.getLongValue(); + if (amount <= 0) { + it.remove(); + continue; + } + limitInput.add(entry.getKey().toStack(Ints.saturatedCast(amount))); + } + for (Object2LongMap.Entry entry : Object2LongMaps.fastIterable(itemCatalystInventory)) { + limitInput.add(entry.getKey().toStack(Ints.saturatedCast(entry.getLongValue()))); + } + return limitInput; + } + + public ObjectList getLimitFluidStackInput() { + var limitInput = new ObjectArrayList(fluidInventory.size()); + for (var it = Object2LongMaps.fastIterator(fluidInventory); it.hasNext();) { + var entry = it.next(); + long amount = entry.getLongValue(); + if (amount <= 0) { + it.remove(); + continue; + } + limitInput.add(FluidStack.create(entry.getKey().getFluid(), amount)); + } + for (Object2LongMap.Entry entry : Object2LongMaps.fastIterable(fluidCatalystInventory)) { + limitInput.add(FluidStack.create(entry.getKey().getFluid(), entry.getLongValue())); + } + return limitInput; + } + + public void pushPattern(KeyCounter[] inputHolder) { + AEUtils.pushInputsToMEPatternBufferInventory(inputHolder, this::add); + onContentsChanged.run(); + } + + public boolean testCatalystItemInternal(GTRecipe recipe) { + for (Content content : recipe.getInputContents(ItemRecipeCapability.CAP)) { + if (content.chance <= 0) continue; + var ingredient = (Ingredient) content.getContent(); + for (ItemStack item : ingredient.getItems()) { + AEItemKey key = AEItemKey.of(item); + if (itemCatalystInventory.containsKey(key)) return false; + } + } + return true; + } + + public boolean testCatalystFluidInternal(GTRecipe recipe) { + for (Content content : recipe.getInputContents(FluidRecipeCapability.CAP)) { + if (content.chance <= 0) continue; + var fluidIngredient = (FluidIngredient) content.getContent(); + for (FluidStack stack : fluidIngredient.getStacks()) { + AEFluidKey key = AEFluidKey.of(stack.getFluid()); + if (fluidCatalystInventory.containsKey(key)) return false; + } + } + return true; + } + + public boolean handleItemInternal(Object2LongMap left, int leftCircuit, boolean simulate) { + if (left.isEmpty() && leftCircuit < 0) return true; + + if (simulate && leftCircuit > 0 && !(leftCircuit == cacheManager.getCircuitCache())) { + return false; + } + + for (var it = Object2LongMaps.fastIterator(left); it.hasNext();) { + var entry = it.next(); + var ingredient = entry.getKey(); + long needAmount = entry.getLongValue(); + if (needAmount <= 0) { + it.remove(); + continue; + } + + AEItemKey bestMatch = simulate ? cacheManager.getBestItemMatchSimulate(ingredient, itemInventory, itemCatalystInventory, needAmount) : + cacheManager.getBestItemMatch(ingredient, itemInventory, needAmount); + if (bestMatch == null) { + return false; + } + } + + if (!simulate) { + for (var it = Object2LongMaps.fastIterator(left); it.hasNext();) { + var entry = it.next(); + var ingredient = entry.getKey(); + long needAmount = entry.getLongValue(); + + var bestMatch = cacheManager.getBestItemMatch(ingredient, itemInventory, needAmount); + if (bestMatch != null) { + long amount = itemInventory.getLong(bestMatch); + long except = amount - needAmount; + if (except <= 0) { + itemInventory.removeLong(bestMatch); + } else { + itemInventory.put(bestMatch, except); + } + it.remove(); + } + } + } + + return true; + } + + public boolean handleFluidInternal(Object2LongMap left, boolean simulate) { + if (left.isEmpty()) return true; + + for (var it = Object2LongMaps.fastIterator(left); it.hasNext();) { + var entry = it.next(); + var ingredient = entry.getKey(); + long needAmount = entry.getLongValue(); + if (needAmount <= 0) { + it.remove(); + continue; + } + + AEFluidKey bestMatch = simulate ? cacheManager.getBestFluidMatchSimulate(ingredient, fluidInventory, fluidCatalystInventory, needAmount) : + cacheManager.getBestFluidMatch(ingredient, fluidInventory, needAmount); + if (bestMatch == null) { + return false; + } + } + + if (!simulate) { + for (var it = Object2LongMaps.fastIterator(left); it.hasNext();) { + var entry = it.next(); + var ingredient = entry.getKey(); + long needAmount = entry.getLongValue(); + + AEFluidKey bestMatch = cacheManager.getBestFluidMatch(ingredient, fluidInventory, needAmount); + if (bestMatch != null) { + long amount = fluidInventory.getLong(bestMatch); + long except = amount - needAmount; + if (except <= 0) { + fluidInventory.removeLong(bestMatch); + } else { + fluidInventory.put(bestMatch, except); + } + it.remove(); + } + } + } + + return true; + } + + @Override + public CompoundTag serializeNBT() { + CompoundTag tag = new CompoundTag(); + + ListTag itemsTag = AEUtils.createListTag(AEItemKey::toTag, itemInventory); + if (!itemsTag.isEmpty()) tag.put("inventory", itemsTag); + + ListTag itemCatalystTag = AEUtils.createListTag(AEItemKey::toTag, itemCatalystInventory); + if (!itemCatalystTag.isEmpty()) tag.put("catalystInventory", itemCatalystTag); + + ListTag fluidsTag = AEUtils.createListTag(AEFluidKey::toTag, fluidInventory); + if (!fluidsTag.isEmpty()) tag.put("fluidInventory", fluidsTag); + + ListTag fluidCatalystTag = AEUtils.createListTag(AEFluidKey::toTag, fluidCatalystInventory); + if (!fluidCatalystTag.isEmpty()) tag.put("catalystFluidInventory", fluidCatalystTag); + + return tag; + } + + @Override + public void deserializeNBT(CompoundTag tag) { + itemInventory.clear(); + itemCatalystInventory.clear(); + fluidInventory.clear(); + fluidCatalystInventory.clear(); + + ListTag items = tag.getList("inventory", Tag.TAG_COMPOUND); + AEUtils.loadInventory(items, AEItemKey::fromTag, itemInventory); + + ListTag catalystItems = tag.getList("catalystInventory", Tag.TAG_COMPOUND); + AEUtils.loadInventory(catalystItems, AEItemKey::fromTag, itemCatalystInventory); + + ListTag fluids = tag.getList("fluidInventory", Tag.TAG_COMPOUND); + AEUtils.loadInventory(fluids, AEFluidKey::fromTag, fluidInventory); + + ListTag catalystFluids = tag.getList("catalystFluidInventory", Tag.TAG_COMPOUND); + AEUtils.loadInventory(catalystFluids, AEFluidKey::fromTag, fluidCatalystInventory); + } + } + + protected class Ticker implements IGridTickable { + + @Override + public TickingRequest getTickingRequest(IGridNode node) { + return new TickingRequest(MEExtendedOutputPartMachineBase.MIN_FREQUENCY, MEExtendedOutputPartMachineBase.MAX_FREQUENCY, false, true); + } + + @Override + public TickRateModulation tickingRequest(IGridNode node, int ticksSinceLastCall) { + if (!getMainNode().isActive()) { + return TickRateModulation.SLEEP; + } + + if (buffer.isEmpty()) { + if (ticksSinceLastCall >= MEExtendedOutputPartMachineBase.MAX_FREQUENCY) { + isSleeping = true; + return TickRateModulation.SLEEP; + } else return TickRateModulation.SLOWER; + } else return AEUtils.reFunds(buffer, getMainNode().getGrid(), actionSource) ? TickRateModulation.URGENT : TickRateModulation.SLOWER; + } + } + + protected class MEPatternTrait extends MEIOTrait implements IMEPatternTrait { + + public MEPatternTrait(MEPatternBufferPartMachine machine) { + super(machine); + } + + @Override + public MEPatternBufferPartMachine getMachine() { + return (MEPatternBufferPartMachine) machine; + } + + @Override + public @NotNull ObjectSet<@NotNull GTRecipe> getCachedGTRecipe() { + ObjectSet recipes = new ObjectOpenHashSet<>(); + for (var it = Int2ReferenceMaps.fastIterator(recipeMultipleCacheMap); it.hasNext();) { + var entry = it.next(); + var recipeSet = entry.getValue(); + int slot = entry.getIntKey(); + if (recipeSet.isEmpty()) it.remove(); + else if (cacheRecipe[slot] && internalInventory[slot].isActive()) recipes.addAll(recipeSet); + } + return recipes; + } + + @Override + public void setSlotCacheRecipe(int index, GTRecipe recipe) { + if (recipe != null && recipe.recipeType != GTRecipeTypes.DUMMY_RECIPES) { + var set = recipeMultipleCacheMap.computeIfAbsent(index, integer -> new ObjectArraySet<>()); + if (set.add(recipe)) cacheRecipe[index] = set.size() >= cacheRecipeCount[index]; + } + } + + @Override + public @NotNull Int2ReferenceMap> getSlot2RecipesCache() { + return recipeMultipleCacheMap; + } + + @Override + public void setOnPatternChange(IntConsumer removeMapOnSlot) { + removeSlotFromMap = removeMapOnSlot; + } + + @Override + public boolean hasCacheInSlot(int slot) { + return cacheRecipe[slot]; + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEPatternBufferProxyPartMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEPatternBufferProxyPartMachine.java new file mode 100644 index 000000000..f491f480f --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEPatternBufferProxyPartMachine.java @@ -0,0 +1,293 @@ +package org.gtlcore.gtlcore.common.machine.multiblock.part.ae; + +import org.gtlcore.gtlcore.api.machine.trait.*; +import org.gtlcore.gtlcore.api.machine.trait.MEPart.IMEPatternPartMachine; +import org.gtlcore.gtlcore.api.machine.trait.MEPart.IMEPatternTrait; + +import com.gregtechceu.gtceu.api.capability.recipe.FluidRecipeCapability; +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.feature.IInteractedMachine; +import com.gregtechceu.gtceu.api.machine.feature.IMachineLife; +import com.gregtechceu.gtceu.api.machine.multiblock.part.MultiblockPartMachine; +import com.gregtechceu.gtceu.api.machine.trait.MachineTrait; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; +import com.gregtechceu.gtceu.common.data.GTItems; +import com.gregtechceu.gtceu.utils.ResearchManager; + +import com.lowdragmc.lowdraglib.gui.modular.ModularUI; +import com.lowdragmc.lowdraglib.side.fluid.FluidStack; +import com.lowdragmc.lowdraglib.syncdata.ISubscription; +import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; +import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; +import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; + +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.server.TickTask; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; + +import it.unimi.dsi.fastutil.Pair; +import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; +import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps; +import it.unimi.dsi.fastutil.ints.IntConsumer; +import it.unimi.dsi.fastutil.objects.ObjectSet; +import it.unimi.dsi.fastutil.objects.ObjectSets; +import lombok.Getter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class MEPatternBufferProxyPartMachine extends MultiblockPartMachine implements IMachineLife, IMEPatternPartMachine, IInteractedMachine { + + protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( + MEPatternBufferProxyPartMachine.class, MultiblockPartMachine.MANAGED_FIELD_HOLDER); + + protected final ISubscription[] handlerSubscriptions = new ISubscription[2]; + + protected final IMEPatternTrait meTrait; + protected final MEPatternBufferProxyRecipeHandler itemProxyHandler; + protected final MEPatternBufferProxyRecipeHandler fluidProxyHandler; + + protected IntConsumer removeSlotFromMap = i -> {}; + + @Persisted + @Getter + @DescSynced + private BlockPos bufferPos; + private @Nullable MEPatternBufferPartMachine buffer = null; + private @Nullable IMEPatternTrait bufferTrait = null; + private boolean bufferResolved = false; + + public MEPatternBufferProxyPartMachine(IMachineBlockEntity holder) { + super(holder); + this.itemProxyHandler = new MEPatternBufferProxyRecipeHandler<>(this, ItemRecipeCapability.CAP); + this.fluidProxyHandler = new MEPatternBufferProxyRecipeHandler<>(this, FluidRecipeCapability.CAP); + this.meTrait = new MEPatternProxyTrait(this); + } + + @Override + public MetaMachine self() { + var buffer = getBuffer(); + return buffer != null ? buffer.self() : super.self(); + } + + public void setBuffer(@Nullable BlockPos pos) { + bufferResolved = true; + var level = getLevel(); + releaseBuffer(); + if (level != null && pos != null) { + if (MetaMachine.getMachine(level, pos) instanceof MEPatternBufferPartMachine machine) { + bufferPos = pos; + buffer = machine; + machine.addProxy(this); + if (!isRemote()) { + var pair = machine.getMERecipeHandlerTraits(); + this.itemProxyHandler.setHandler(pair.left()); + this.fluidProxyHandler.setHandler(pair.right()); + this.bufferTrait = machine.getMETrait(); + handlerSubscriptions[0] = pair.left().addChangedListener(itemProxyHandler::notifyListeners); + handlerSubscriptions[1] = pair.right().addChangedListener(fluidProxyHandler::notifyListeners); + } + } + } + if (!isRemote()) updateIO(); + } + + @Nullable + public MEPatternBufferPartMachine getBuffer() { + if (!bufferResolved) setBuffer(bufferPos); + return buffer; + } + + protected void releaseBuffer() { + buffer = null; + bufferPos = null; + if (!isRemote()) { + this.itemProxyHandler.setHandler(null); + this.fluidProxyHandler.setHandler(null); + this.bufferTrait = null; + for (int i = 0; i < handlerSubscriptions.length; i++) { + if (handlerSubscriptions[i] != null) { + handlerSubscriptions[i].unsubscribe(); + handlerSubscriptions[i] = null; + } + } + } + } + + protected void updateIO() { + for (var controller : this.getControllers()) { + if (controller instanceof IRecipeCapabilityMachine machine) { + machine.upDate(); + } + } + itemProxyHandler.notifyListeners(); + fluidProxyHandler.notifyListeners(); + } + + // ======================================== + // GUI + // ======================================== + + @Override + public InteractionResult onUse(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + ItemStack stack = player.getItemInHand(hand); + if (stack.isEmpty()) return InteractionResult.PASS; + + if (stack.is(GTItems.TOOL_DATA_STICK.asItem())) { + if (!world.isClientSide) { + // Check if it's research data - if so, pass to avoid conflicts + var researchData = ResearchManager.readResearchId(stack); + if (researchData != null) { + return InteractionResult.PASS; + } + + // Read pattern buffer position from the data stick + var tag = stack.getTag(); + if (tag != null && tag.contains("pos")) { + int[] posArray = tag.getIntArray("pos"); + if (posArray.length == 3) { + BlockPos bufferPos = new BlockPos(posArray[0], posArray[1], posArray[2]); + player.sendSystemMessage(Component.translatable("gtceu.machine.me.import_paste_settings")); + setBuffer(bufferPos); + } + } + } + return InteractionResult.sidedSuccess(world.isClientSide); + } + + return InteractionResult.PASS; + } + + @Override + public boolean shouldOpenUI(Player player, InteractionHand hand, BlockHitResult hit) { + return getBuffer() != null; + } + + @Override + public ModularUI createUI(Player entityPlayer) { + assert getBuffer() != null; // UI should never be able to be opened when buffer is null + return getBuffer().createUI(entityPlayer); + } + + // ======================================== + // LIFECYCLE + // ======================================== + + @Override + public void onLoad() { + super.onLoad(); + if (getLevel() instanceof ServerLevel level) { + level.getServer().tell(new TickTask(0, () -> this.setBuffer(bufferPos))); + } + } + + @Override + public void onMachineRemoved() { + var buf = getBuffer(); + if (buf != null) { + buf.removeProxy(this); + } + for (int i = 0; i < handlerSubscriptions.length; i++) { + if (handlerSubscriptions[i] != null) { + handlerSubscriptions[i].unsubscribe(); + handlerSubscriptions[i] = null; + } + } + } + + @Override + @NotNull + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } + + // ======================================== + // IMEPatternPartMachine + // ======================================== + + @Override + public Pair, IMERecipeHandlerTrait> getMERecipeHandlerTraits() { + return Pair.of(itemProxyHandler, fluidProxyHandler); + } + + @Override + public @NotNull IMEPatternTrait getMETrait() { + return this.meTrait; + } + + protected class MEPatternProxyTrait extends MachineTrait implements IMEPatternTrait { + + protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( + MEPatternBufferProxyPartMachine.class); + + public MEPatternProxyTrait(MEPatternBufferProxyPartMachine machine) { + super(machine); + } + + @Override + public MEPatternBufferProxyPartMachine getMachine() { + return (MEPatternBufferProxyPartMachine) machine; + } + + @Override + public @NotNull ObjectSet<@NotNull GTRecipe> getCachedGTRecipe() { + if (bufferTrait == null) return ObjectSets.emptySet(); + return bufferTrait.getCachedGTRecipe(); + } + + @Override + public void setSlotCacheRecipe(int index, GTRecipe recipe) { + if (bufferTrait != null) { + bufferTrait.setSlotCacheRecipe(index, recipe); + } + } + + @Override + public @NotNull Int2ReferenceMap> getSlot2RecipesCache() { + return bufferTrait == null ? Int2ReferenceMaps.emptyMap() : bufferTrait.getSlot2RecipesCache(); + } + + @Override + public void setOnPatternChange(IntConsumer removeMapOnSlot) { + removeSlotFromMap = removeMapOnSlot; + } + + @Override + public boolean hasCacheInSlot(int slot) { + if (bufferTrait == null) return false; + return bufferTrait.hasCacheInSlot(slot); + } + + @Override + public void notifySelfIO() { + if (bufferTrait != null) { + bufferTrait.notifySelfIO(); + } + } + + @Override + public IO getIO() { + if (bufferTrait != null) { + return bufferTrait.getIO(); + } + return IO.NONE; + } + + @Override + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEPatternBufferProxyRecipeHandler.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEPatternBufferProxyRecipeHandler.java new file mode 100644 index 000000000..251c63180 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEPatternBufferProxyRecipeHandler.java @@ -0,0 +1,100 @@ +package org.gtlcore.gtlcore.common.machine.multiblock.part.ae; + +import org.gtlcore.gtlcore.api.machine.trait.IMERecipeHandlerTrait; + +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.capability.recipe.RecipeCapability; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; +import it.unimi.dsi.fastutil.ints.IntCollection; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import it.unimi.dsi.fastutil.objects.Object2LongMaps; +import lombok.Setter; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; + +public class MEPatternBufferProxyRecipeHandler, S> extends NotifiableMERecipeHandlerTrait { + + @Setter + private IMERecipeHandlerTrait handler; + private final RecipeCapability capability; + + public MEPatternBufferProxyRecipeHandler(MetaMachine machine, RecipeCapability capability) { + super(machine); + this.capability = capability; + } + + @Override + public RecipeCapability getCapability() { + return capability; + } + + @Override + public IO getIo() { + if (handler != null) { + return handler.getIo(); + } + return IO.NONE; + } + + @Override + public Set getActiveSlots() { + if (handler != null) { + return handler.getActiveSlots(); + } + return Collections.emptySet(); + } + + @Override + public Int2ObjectMap> getActiveAndUnCachedSlotsLimitContentsMap() { + if (handler != null) { + return handler.getActiveAndUnCachedSlotsLimitContentsMap(); + } + return Int2ObjectMaps.emptyMap(); + } + + @Override + public Object2LongMap getStackMapFromFirstAvailableSlot(IntCollection slots) { + if (handler != null) { + return handler.getStackMapFromFirstAvailableSlot(slots); + } + return Object2LongMaps.emptyMap(); + } + + @Override + public boolean meHandleRecipeInner(GTRecipe recipe, Object2LongMap left, boolean simulate, int trySlot) { + if (handler != null) { + return handler.meHandleRecipeInner(recipe, left, simulate, trySlot); + } + return false; + } + + @Override + public List meHandleRecipeOutputInner(List contents, boolean simulate) { + if (handler != null) { + return handler.meHandleRecipeOutputInner(contents, simulate); + } + return contents; + } + + @Override + public void prepareMEHandleContents(GTRecipe recipe, List left, boolean simulate) { + if (handler != null) { + handler.prepareMEHandleContents(recipe, left, simulate); + } + } + + @Override + public Object2LongMap getPreparedMEHandleContents() { + if (handler != null) { + return handler.getPreparedMEHandleContents(); + } + return Object2LongMaps.emptyMap(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEPatternBufferRecipeHandlerTrait.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEPatternBufferRecipeHandlerTrait.java new file mode 100644 index 000000000..26377246f --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/MEPatternBufferRecipeHandlerTrait.java @@ -0,0 +1,288 @@ +package org.gtlcore.gtlcore.common.machine.multiblock.part.ae; + +import org.gtlcore.gtlcore.api.recipe.ingredient.LongIngredient; +import org.gtlcore.gtlcore.integration.ae2.AEUtils; + +import com.gregtechceu.gtceu.api.capability.recipe.*; +import com.gregtechceu.gtceu.api.machine.trait.MachineTrait; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; +import com.gregtechceu.gtceu.api.recipe.ingredient.IntProviderIngredient; + +import com.lowdragmc.lowdraglib.side.fluid.FluidStack; +import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; + +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + +import appeng.api.stacks.AEFluidKey; +import appeng.api.stacks.AEItemKey; +import it.unimi.dsi.fastutil.ints.*; +import it.unimi.dsi.fastutil.objects.*; +import lombok.Getter; +import lombok.Setter; + +import java.util.*; +import java.util.stream.IntStream; + +public class MEPatternBufferRecipeHandlerTrait extends MachineTrait { + + protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( + MEPatternBufferPartMachine.class); + + @Getter + protected final MEItemInputHandler meItemHandler; + + @Getter + protected final MEFluidHandler meFluidHandler; + + public MEPatternBufferRecipeHandlerTrait(MEPatternBufferPartMachine ioBuffer, IO io) { + super(ioBuffer); + meItemHandler = new MEItemInputHandler(ioBuffer, io); + meFluidHandler = new MEFluidHandler(ioBuffer, io); + } + + @Override + public MEPatternBufferPartMachine getMachine() { + return (MEPatternBufferPartMachine) super.getMachine(); + } + + @Override + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } + + @Override + public void onChanged() {} + + private boolean handleItemInner(GTRecipe recipe, Object2LongMap left, int circuit, boolean simulate, int trySlot) { + var internalSlot = getMachine().getInternalInventory()[trySlot]; + if (internalSlot.isItemActive(simulate)) { + if (simulate) { + if (!internalSlot.testCatalystItemInternal(recipe)) return false; + } + return internalSlot.handleItemInternal(left, circuit, simulate); + } else return left.isEmpty() && circuit < 0; + } + + private boolean handleFluidInner(GTRecipe recipe, Object2LongMap left, boolean simulate, int trySlot) { + var internalSlot = getMachine().getInternalInventory()[trySlot]; + if (internalSlot.isFluidActive(simulate)) { + if (simulate) { + if (!internalSlot.testCatalystFluidInternal(recipe)) return false; + } + return internalSlot.handleFluidInternal(left, simulate); + } else return left.isEmpty(); + } + + private Set getActiveSlots(MEPatternBufferPartMachine.InternalSlot[] slots) { + final Set activeSlots = new IntOpenHashSet(); + for (int i = 0; i < slots.length; i++) { + if (slots[i].isActive()) activeSlots.add(i); + } + return activeSlots; + } + + private int[] getActiveAndUnCachedSlots(MEPatternBufferPartMachine.InternalSlot[] slots) { + final var machine = getMachine(); + return IntStream.range(0, slots.length) + .filter(i -> slots[i].isActive() && !machine.cacheRecipe[i]) + .toArray(); + } + + public class MEItemInputHandler extends NotifiableMERecipeHandlerTrait { + + @Getter + private final IO io; + + @Getter + @Setter + private Object2LongMap preparedMEHandleContents = new Object2LongOpenHashMap<>(); + + @Setter + private int preparedCircuitConfig = -1; + + public MEItemInputHandler(MEPatternBufferPartMachine machine, IO io) { + super(machine); + this.io = io; + } + + public MEPatternBufferPartMachine getMachine() { + return (MEPatternBufferPartMachine) this.machine; + } + + @Override + public RecipeCapability getCapability() { + return ItemRecipeCapability.CAP; + } + + @Override + public Set getActiveSlots() { + return MEPatternBufferRecipeHandlerTrait.this.getActiveSlots(getMachine().getInternalInventory()); + } + + @SuppressWarnings("unchecked") + @Override + public Int2ObjectMap> getActiveAndUnCachedSlotsLimitContentsMap() { + var map = new Int2ObjectArrayMap>(); + var machine = getMachine(); + var shared = (List) (Object) machine.getSharedCatalystInventory().getContents(); + for (int slot : MEPatternBufferRecipeHandlerTrait.this.getActiveAndUnCachedSlots(getMachine().getInternalInventory())) { + var inputs = machine.getInternalInventory()[slot].getLimitItemStackInput(); + + // 通过统一方法获取该槽位的电路(可能来自样板或总成配置) + ItemStack circuitForRecipe = machine.getCircuitForRecipe(slot); + if (!circuitForRecipe.isEmpty()) { + inputs.add(circuitForRecipe); + } + + inputs.addAll(shared); + map.put(slot, inputs); + } + return map; + } + + @Override + public Object2LongMap getStackMapFromFirstAvailableSlot(IntCollection slots) { + final var inventory = getMachine().getInternalInventory(); + for (int slot : slots) { + if (!inventory[slot].isActive()) continue; + Object2LongOpenHashMap map = new Object2LongOpenHashMap<>(); + for (var entry : Object2LongMaps.fastIterable(inventory[slot].getItemStackInputMap())) { + map.addTo(entry.getKey(), entry.getLongValue()); + } + return map; + } + return Object2LongMaps.emptyMap(); + } + + @Override + public boolean meHandleRecipeInner(GTRecipe recipe, Object2LongMap left, boolean simulate, int trySlot) { + return handleItemInner(recipe, left, preparedCircuitConfig, simulate, trySlot); + } + + @Override + public void prepareMEHandleContents(GTRecipe recipe, List left, boolean simulate) { + preparedCircuitConfig = -1; + if (simulate) { + // 处理总成配置的电路 + getMachine().getSharedCircuitInventory().handleRecipeInner(IO.IN, recipe, left, null, true); + + // 处理共享库存 + getMachine().getSharedCatalystInventory().handleRecipeInner(IO.IN, recipe, left, null, true); + + // simulate时left会包含Circuit + setPreparedMEHandleContents(AEUtils.ingredientsMapWithOutCircuit(left, this::setPreparedCircuitConfig)); + } else { + setPreparedMEHandleContents(AEUtils.ingredientsMap(left)); + } + } + + @Override + public List meHandleRecipeOutputInner(List left, boolean simulate) { + if (simulate) return List.of(); + final var buffer = getMachine().buffer; + for (Ingredient ingredient : left) { + if (ingredient instanceof IntProviderIngredient intProvider) { + intProvider.setItemStacks(null); + intProvider.setSampledCount(null); + } + + ItemStack[] items = ingredient.getItems(); + if (items.length != 0) { + ItemStack output = items[0]; + if (!output.isEmpty()) { + buffer.addTo(AEItemKey.of(output), ingredient instanceof LongIngredient longIngredient ? longIngredient.getActualAmount() : output.getCount()); + } + } + } + return List.of(); + } + } + + public class MEFluidHandler extends NotifiableMERecipeHandlerTrait { + + @Getter + private final IO io; + + @Getter + @Setter + private Object2LongMap preparedMEHandleContents = new Object2LongOpenHashMap<>(); + + public MEFluidHandler(MEPatternBufferPartMachine machine, IO io) { + super(machine); + this.io = io; + } + + public MEPatternBufferPartMachine getMachine() { + return (MEPatternBufferPartMachine) this.machine; + } + + @Override + public RecipeCapability getCapability() { + return FluidRecipeCapability.CAP; + } + + @Override + public Set getActiveSlots() { + return MEPatternBufferRecipeHandlerTrait.this.getActiveSlots(getMachine().getInternalInventory()); + } + + @SuppressWarnings("unchecked") + @Override + public Int2ObjectMap> getActiveAndUnCachedSlotsLimitContentsMap() { + var map = new Int2ObjectArrayMap>(); + var machine = getMachine(); + var shared = (List) (Object) machine.getSharedCatalystTank().getContents(); + for (int slot : MEPatternBufferRecipeHandlerTrait.this.getActiveAndUnCachedSlots(getMachine().getInternalInventory())) { + var inputs = machine.getInternalInventory()[slot].getLimitFluidStackInput(); + inputs.addAll(shared); + map.put(slot, inputs); + } + return map; + } + + @Override + public Object2LongMap getStackMapFromFirstAvailableSlot(IntCollection slots) { + final var inventory = getMachine().getInternalInventory(); + for (int slot : slots) { + if (!inventory[slot].isActive()) continue; + Object2LongOpenHashMap map = new Object2LongOpenHashMap<>(); + for (var entry : Object2LongMaps.fastIterable(inventory[slot].getFluidStackInputMap())) { + map.addTo(entry.getKey(), entry.getLongValue()); + } + return map; + } + return Object2LongMaps.emptyMap(); + } + + @Override + public boolean meHandleRecipeInner(GTRecipe recipe, Object2LongMap left, boolean simulate, int trySlot) { + return handleFluidInner(recipe, left, simulate, trySlot); + } + + @Override + public void prepareMEHandleContents(GTRecipe recipe, List left, boolean simulate) { + if (simulate) { + getMachine().getSharedCatalystTank().handleRecipeInner(IO.IN, recipe, left, null, true); + } + setPreparedMEHandleContents(AEUtils.fluidIngredientsMap(left)); + } + + @Override + public List meHandleRecipeOutputInner(List left, boolean simulate) { + if (simulate) return List.of(); + final var buffer = getMachine().buffer; + for (FluidIngredient fluidIngredient : left) { + if (!fluidIngredient.isEmpty()) { + FluidStack[] fluids = fluidIngredient.getStacks(); + if (fluids.length != 0) { + FluidStack output = fluids[0]; + buffer.addTo(AEFluidKey.of(output.getFluid()), output.getAmount()); + } + } + } + return List.of(); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/NotifiableMAHandlerTrait.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/NotifiableMAHandlerTrait.java new file mode 100644 index 000000000..2938ac915 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/NotifiableMAHandlerTrait.java @@ -0,0 +1,37 @@ +package org.gtlcore.gtlcore.common.machine.multiblock.part.ae; + +import org.gtlcore.gtlcore.api.machine.trait.AECraft.IMolecularAssemblerHandlerTrait; + +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.trait.MachineTrait; + +import com.lowdragmc.lowdraglib.syncdata.ISubscription; +import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; + +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; + +import java.util.List; + +public abstract class NotifiableMAHandlerTrait extends MachineTrait implements IMolecularAssemblerHandlerTrait { + + public static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(NotifiableMAHandlerTrait.class); + protected List listeners = new ReferenceArrayList<>(); + + public NotifiableMAHandlerTrait(MetaMachine machine) { + super(machine); + } + + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } + + @Override + public ISubscription addChangedListener(Runnable listener) { + this.listeners.add(listener); + return () -> this.listeners.remove(listener); + } + + public void notifyListeners() { + this.listeners.forEach(Runnable::run); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/NotifiableMERecipeHandlerTrait.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/NotifiableMERecipeHandlerTrait.java new file mode 100644 index 000000000..1e8ff4a2c --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/ae/NotifiableMERecipeHandlerTrait.java @@ -0,0 +1,42 @@ +package org.gtlcore.gtlcore.common.machine.multiblock.part.ae; + +import org.gtlcore.gtlcore.api.machine.trait.IMERecipeHandlerTrait; + +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.trait.MachineTrait; + +import com.lowdragmc.lowdraglib.syncdata.ISubscription; +import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; + +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; + +import java.util.List; +import java.util.function.Predicate; + +public abstract class NotifiableMERecipeHandlerTrait, S> extends MachineTrait implements IMERecipeHandlerTrait { + + public static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(NotifiableMERecipeHandlerTrait.class); + protected List listeners = new ReferenceArrayList<>(); + + public NotifiableMERecipeHandlerTrait(MetaMachine machine) { + super(machine); + } + + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } + + public ISubscription addChangedListener(Runnable listener) { + this.listeners.add(listener); + return () -> this.listeners.remove(listener); + } + + public ISubscription addBufferChangedListener(Runnable mainListener) { + this.listeners.add(0, mainListener); + return () -> this.listeners.remove(mainListener); + } + + public void notifyListeners() { + this.listeners.forEach(Runnable::run); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/maintenance/AutoConfigurationMaintenanceHatchPartMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/maintenance/AutoConfigurationMaintenanceHatchPartMachine.java index d4ab73489..92f77f677 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/maintenance/AutoConfigurationMaintenanceHatchPartMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/maintenance/AutoConfigurationMaintenanceHatchPartMachine.java @@ -3,8 +3,11 @@ import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMaintenanceMachine; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; import com.gregtechceu.gtceu.api.machine.multiblock.part.MultiblockPartMachine; import com.gregtechceu.gtceu.api.machine.multiblock.part.TieredPartMachine; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.config.ConfigHolder; import com.lowdragmc.lowdraglib.gui.widget.ComponentPanelWidget; import com.lowdragmc.lowdraglib.gui.widget.DraggableScrollableWidgetGroup; @@ -19,6 +22,7 @@ import net.minecraft.network.chat.Style; import lombok.Getter; +import org.jetbrains.annotations.NotNull; import java.math.BigDecimal; import java.math.RoundingMode; @@ -29,7 +33,8 @@ @Getter @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -public class AutoConfigurationMaintenanceHatchPartMachine extends TieredPartMachine implements IMaintenanceMachine { +public class AutoConfigurationMaintenanceHatchPartMachine extends TieredPartMachine + implements IMaintenanceMachine, IAutoConfigurationMaintenanceHatch { protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( AutoConfigurationMaintenanceHatchPartMachine.class, MultiblockPartMachine.MANAGED_FIELD_HOLDER); @@ -38,6 +43,7 @@ public class AutoConfigurationMaintenanceHatchPartMachine extends TieredPartMach private static final float MIN_DURATION_MULTIPLIER = 0.2f; private static final float DURATION_ACTION_AMOUNT = 0.01f; @Persisted + @Getter private float durationMultiplier = 1f; public AutoConfigurationMaintenanceHatchPartMachine(IMachineBlockEntity metaTileEntityId) { @@ -95,6 +101,21 @@ public float getTimeMultiplier() { .floatValue(); } + @Override + public GTRecipe modifyRecipe(GTRecipe recipe) { + if (ConfigHolder.INSTANCE.machines.enableMaintenance) { + if (this.hasMaintenanceProblems()) { + return null; + } + float durationMultiplier = this.getDurationMultiplier(); + if (durationMultiplier != 1.0F) { + recipe = recipe.copy(); + recipe.duration = (int) Math.max(1, recipe.duration * durationMultiplier); + } + } + return recipe; + } + private void incInternalMultiplier(int multiplier) { float newDurationMultiplier = durationMultiplier + DURATION_ACTION_AMOUNT * multiplier; if (newDurationMultiplier >= MAX_DURATION_MULTIPLIER) { @@ -113,6 +134,18 @@ private void decInternalMultiplier(int multiplier) { durationMultiplier = newDurationMultiplier; } + @Override + public void addedToController(@NotNull IMultiController controller) { + super.addedToController(controller); + ICleaningRoom.addedToController(controller, null); + } + + @Override + public void removedFromController(@NotNull IMultiController controller) { + super.removedFromController(controller); + ICleaningRoom.removedFromController(controller, null); + } + @Override public Widget createUIWidget() { WidgetGroup group; @@ -151,4 +184,11 @@ private static Component getTextWidgetText(Supplier multiplier) { return Component.translatable("gtceu.maintenance.configurable_" + "duration", multiplier.get()) .setStyle(Style.EMPTY.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, tooltip))); } + + @Override + public void setDurationMultiplier(float count) { + if (count > MAX_DURATION_MULTIPLIER) durationMultiplier = MAX_DURATION_MULTIPLIER; + else if (count < MIN_DURATION_MULTIPLIER) durationMultiplier = MIN_DURATION_MULTIPLIER; + else durationMultiplier = count; + } } diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/maintenance/GravityCleaningConfigurationMaintenancePartMachine.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/maintenance/GravityCleaningConfigurationMaintenancePartMachine.java index fcba2973f..24486c313 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/maintenance/GravityCleaningConfigurationMaintenancePartMachine.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/maintenance/GravityCleaningConfigurationMaintenancePartMachine.java @@ -20,7 +20,8 @@ * @author EasterFG on 2024/10/17 */ @MethodsReturnNonnullByDefault -public class GravityCleaningConfigurationMaintenancePartMachine extends AutoConfigurationMaintenanceHatchPartMachine implements IGravityPartMachine { +public class GravityCleaningConfigurationMaintenancePartMachine extends AutoConfigurationMaintenanceHatchPartMachine + implements IAutoConfiguratioGravityPart { @Persisted private int gravity = 0; diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/maintenance/IAutoConfiguratioGravityPart.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/maintenance/IAutoConfiguratioGravityPart.java new file mode 100644 index 000000000..61692c8ef --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/maintenance/IAutoConfiguratioGravityPart.java @@ -0,0 +1,3 @@ +package org.gtlcore.gtlcore.common.machine.multiblock.part.maintenance; + +public interface IAutoConfiguratioGravityPart extends IAutoConfigurationMaintenanceHatch, IGravityPartMachine {} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/maintenance/IAutoConfigurationMaintenanceHatch.java b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/maintenance/IAutoConfigurationMaintenanceHatch.java new file mode 100644 index 000000000..1d08b3656 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/multiblock/part/maintenance/IAutoConfigurationMaintenanceHatch.java @@ -0,0 +1,8 @@ +package org.gtlcore.gtlcore.common.machine.multiblock.part.maintenance; + +public interface IAutoConfigurationMaintenanceHatch { + + float getDurationMultiplier(); + + void setDurationMultiplier(float count); +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/trait/AdvancedInfiniteDrillLogic.java b/src/main/java/org/gtlcore/gtlcore/common/machine/trait/AdvancedInfiniteDrillLogic.java index 9793814d5..37151abc6 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/trait/AdvancedInfiniteDrillLogic.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/trait/AdvancedInfiniteDrillLogic.java @@ -3,7 +3,6 @@ import org.gtlcore.gtlcore.common.machine.multiblock.electric.AdvancedInfiniteDrillMachine; import com.gregtechceu.gtceu.api.GTValues; -import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.data.worldgen.bedrockfluid.BedrockFluidVeinSavedData; import com.gregtechceu.gtceu.api.data.worldgen.bedrockfluid.FluidVeinWorldEntry; import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; @@ -27,6 +26,8 @@ import java.util.HashMap; import java.util.Map; +import static org.gtlcore.gtlcore.api.recipe.RecipeRunnerHelper.*; + /** * @author EasterFG on 2024/10/27 */ @@ -70,7 +71,7 @@ public void findAndHandleRecipe() { var match = getFluidDrillRecipe(); if (match != null) { var copied = match.copy(new ContentModifier(match.duration, 0)); - if (match.matchRecipe(this.machine).isSuccess() && copied.matchTickRecipe(this.machine).isSuccess()) { + if (matchRecipe(this.machine, match) && copied.matchTickRecipe(this.machine).isSuccess()) { setupRecipe(match); } } @@ -89,7 +90,7 @@ private GTRecipe getFluidDrillRecipe() { .toArray(FluidStack[]::new)) .buildRawRecipe(); recipe = recipe.copy(ContentModifier.multiplier(getMachine().getRate()), false); - if (recipe.matchRecipe(getMachine()).isSuccess() && recipe.matchTickRecipe(getMachine()).isSuccess()) { + if (matchRecipe(getMachine(), recipe) && recipe.matchTickRecipe(getMachine()).isSuccess()) { return recipe; } } @@ -135,16 +136,12 @@ public Map getVeinFluids() { @Override public void onRecipeFinish() { - machine.afterWorking(); - if (lastRecipe != null) { - lastRecipe.postWorking(this.machine); - lastRecipe.handleRecipeIO(IO.OUT, this.machine, this.chanceCaches); - } + if (lastRecipe != null) handleRecipeOutput(this.machine, lastRecipe); // try it again var match = getFluidDrillRecipe(); if (match != null) { var copied = match.copy(new ContentModifier(match.duration, 0)); - if (match.matchRecipe(this.machine).isSuccess() && copied.matchTickRecipe(this.machine).isSuccess()) { + if (matchRecipe(this.machine, match) && copied.matchTickRecipe(this.machine).isSuccess()) { setupRecipe(match); return; } diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/trait/INFFluidDrillLogic.java b/src/main/java/org/gtlcore/gtlcore/common/machine/trait/INFFluidDrillLogic.java index 47e5dc189..43e4f3d06 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/trait/INFFluidDrillLogic.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/trait/INFFluidDrillLogic.java @@ -3,12 +3,10 @@ import org.gtlcore.gtlcore.common.machine.multiblock.electric.INFFluidDrillMachine; import com.gregtechceu.gtceu.api.GTValues; -import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.data.worldgen.bedrockfluid.BedrockFluidVeinSavedData; import com.gregtechceu.gtceu.api.data.worldgen.bedrockfluid.FluidVeinWorldEntry; import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; import com.gregtechceu.gtceu.api.recipe.GTRecipe; -import com.gregtechceu.gtceu.api.recipe.content.ContentModifier; import com.gregtechceu.gtceu.data.recipe.builder.GTRecipeBuilder; import com.lowdragmc.lowdraglib.side.fluid.FluidHelper; @@ -21,6 +19,8 @@ import lombok.Getter; import org.jetbrains.annotations.Nullable; +import static org.gtlcore.gtlcore.api.recipe.RecipeRunnerHelper.*; + @Getter public class INFFluidDrillLogic extends RecipeLogic { @@ -55,10 +55,7 @@ public void findAndHandleRecipe() { } var match = getFluidDrillRecipe(); if (match != null) { - var copied = match.copy(new ContentModifier(match.duration, 0)); - if (match.matchRecipe(this.machine).isSuccess() && copied.matchTickRecipe(this.machine).isSuccess()) { - setupRecipe(match); - } + setupRecipe(match); } } } @@ -73,7 +70,7 @@ private GTRecipe getFluidDrillRecipe() { .outputFluids(FluidStack.create(veinFluid, getFluidToProduce(data.getFluidVeinWorldEntry(getChunkX(), getChunkZ())))) .buildRawRecipe(); - if (recipe.matchRecipe(getMachine()).isSuccess() && recipe.matchTickRecipe(getMachine()).isSuccess()) { + if (matchRecipeOutput(getMachine(), recipe) && recipe.matchTickRecipe(getMachine()).isSuccess()) { return recipe; } } @@ -111,18 +108,12 @@ private long getFluidToProduce(FluidVeinWorldEntry entry) { @Override public void onRecipeFinish() { machine.afterWorking(); - if (lastRecipe != null) { - lastRecipe.postWorking(this.machine); - lastRecipe.handleRecipeIO(IO.OUT, this.machine, this.chanceCaches); - } + if (lastRecipe != null) handleRecipeOutput(getMachine(), lastRecipe); // try it again var match = getFluidDrillRecipe(); if (match != null) { - var copied = match.copy(new ContentModifier(match.duration, 0)); - if (match.matchRecipe(this.machine).isSuccess() && copied.matchTickRecipe(this.machine).isSuccess()) { - setupRecipe(match); - return; - } + setupRecipe(match); + return; } setStatus(Status.IDLE); progress = 0; diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/trait/MolecularAssemblerRecipesLogic.java b/src/main/java/org/gtlcore/gtlcore/common/machine/trait/MolecularAssemblerRecipesLogic.java new file mode 100644 index 000000000..7e9cbafa1 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/trait/MolecularAssemblerRecipesLogic.java @@ -0,0 +1,113 @@ +package org.gtlcore.gtlcore.common.machine.trait; + +import org.gtlcore.gtlcore.api.machine.multiblock.MolecularAssemblerMultiblockMachine; +import org.gtlcore.gtlcore.api.machine.multiblock.ParallelMachine; +import org.gtlcore.gtlcore.mixin.gtm.api.recipe.RecipeLogicAccessor; + +import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; +import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; + +import org.jetbrains.annotations.Nullable; + +public class MolecularAssemblerRecipesLogic extends RecipeLogic { + + private final ParallelMachine parallel; + + public MolecularAssemblerRecipesLogic(ParallelMachine machine) { + super((IRecipeLogicMachine) machine); + this.parallel = machine; + } + + @Override + public MolecularAssemblerMultiblockMachine getMachine() { + return (MolecularAssemblerMultiblockMachine) super.getMachine(); + } + + private @Nullable GTRecipe getRecipe() { + var handler = getMachine().getMAHandler(); + if (handler != null) return handler.extractGTRecipe(parallel.getMaxParallel(), getMachine().getTickDuration()); + else return null; + } + + @Override + public void updateTickSubscription() { + if (!this.isSuspend() && this.machine.isRecipeLogicAvailable()) { + this.subscription = this.getMachine().subscribeServerTick(this.subscription, this::serverTick); + } else if (this.subscription != null) { + this.subscription.unsubscribe(); + this.subscription = null; + } + } + + @Override + public void serverTick() { + if (!this.isSuspend()) { + if (!this.isIdle() && this.lastRecipe != null) { + if (this.progress < this.duration) { + this.handleRecipeWorking(); + } + if (this.progress >= this.duration) { + this.onRecipeFinish(); + } + } else if (this.lastRecipe != null || !this.machine.keepSubscribing() || this.getMachine().getOffsetTimer() % 5L == 0L) { + this.findAndHandleRecipe(); + } + } + + boolean unsubscribe = false; + if (this.isSuspend()) { + unsubscribe = true; + } else if (this.lastRecipe == null && this.isIdle() && !this.machine.keepSubscribing()) { + unsubscribe = true; + } + + if (unsubscribe && this.subscription != null) { + this.subscription.unsubscribe(); + this.subscription = null; + } + } + + @Override + public void handleRecipeWorking() { + ++this.progress; + ++this.totalContinuousRunningTime; + } + + @Override + public void findAndHandleRecipe() { + lastRecipe = null; + var match = getRecipe(); + if (match != null) { + setupRecipe(match); + } + } + + @Override + public void setupRecipe(GTRecipe recipe) { + this.lastRecipe = recipe; + this.setStatus(RecipeLogic.Status.WORKING); + this.progress = 0; + this.duration = recipe.duration; + ((RecipeLogicAccessor) this).setIsActive(true); + } + + @Override + public void onRecipeFinish() { + if (this.lastRecipe != null) { + var handler = getMachine().getMAHandler(); + if (handler != null) handler.handleRecipeOutput(this.lastRecipe); + + var match = getRecipe(); + if (match != null) { + setupRecipe(match); + } else { + lastRecipe = null; + this.setStatus(RecipeLogic.Status.IDLE); + this.progress = 0; + this.duration = 0; + ((RecipeLogicAccessor) this).setIsActive(false); + } + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/common/machine/trait/MultipleRecipesLogic.java b/src/main/java/org/gtlcore/gtlcore/common/machine/trait/MultipleRecipesLogic.java index 175ff9baa..4f9d56471 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/machine/trait/MultipleRecipesLogic.java +++ b/src/main/java/org/gtlcore/gtlcore/common/machine/trait/MultipleRecipesLogic.java @@ -1,7 +1,11 @@ package org.gtlcore.gtlcore.common.machine.trait; import org.gtlcore.gtlcore.api.machine.multiblock.ParallelMachine; -import org.gtlcore.gtlcore.api.recipe.RecipeRunnerHelper; +import org.gtlcore.gtlcore.api.machine.trait.ILockRecipe; +import org.gtlcore.gtlcore.api.machine.trait.IRecipeCapabilityMachine; +import org.gtlcore.gtlcore.api.machine.trait.IRecipeStatus; +import org.gtlcore.gtlcore.api.recipe.IGTRecipe; +import org.gtlcore.gtlcore.api.recipe.RecipeResult; import com.gregtechceu.gtceu.api.capability.recipe.*; import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; @@ -9,35 +13,49 @@ import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; import com.gregtechceu.gtceu.api.recipe.GTRecipe; import com.gregtechceu.gtceu.api.recipe.RecipeHelper; -import com.gregtechceu.gtceu.api.recipe.chance.logic.ChanceLogic; import com.gregtechceu.gtceu.api.recipe.content.Content; import com.gregtechceu.gtceu.api.recipe.content.ContentModifier; import com.gregtechceu.gtceu.data.recipe.builder.GTRecipeBuilder; import net.minecraft.nbt.CompoundTag; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import lombok.Getter; -import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import java.util.function.BiPredicate; +import static org.gtlcore.gtlcore.api.recipe.IParallelLogic.*; +import static org.gtlcore.gtlcore.api.recipe.RecipeRunnerHelper.*; + @Getter -public class MultipleRecipesLogic extends RecipeLogic { +public class MultipleRecipesLogic extends RecipeLogic implements ILockRecipe, IRecipeStatus { private final ParallelMachine parallel; private final BiPredicate dataCheck; + private static final int MAX_THREADS = 64; + + private double reductionRatio; + public MultipleRecipesLogic(ParallelMachine machine) { this(machine, null); } public MultipleRecipesLogic(ParallelMachine machine, BiPredicate dataCheck) { + this(machine, dataCheck, 1.0, 1.0); + } + + public MultipleRecipesLogic(ParallelMachine machine, BiPredicate dataCheck, double reductionEUt, double reductionDuration) { super((IRecipeLogicMachine) machine); this.parallel = machine; this.dataCheck = dataCheck; + this.reductionRatio = reductionEUt * reductionDuration; + } + + public void setReduction(double reductionEUt, double reductionDuration) { + this.reductionRatio = reductionEUt * reductionDuration; } @Override @@ -48,95 +66,97 @@ public WorkableElectricMultiblockMachine getMachine() { @Override public void findAndHandleRecipe() { lastRecipe = null; + setRecipeStatus(null); var match = getRecipe(); if (match != null) { - if (RecipeRunnerHelper.matchRecipeOutput(machine, match)) { + RecipeResult.of(machine, RecipeResult.SUCCESS); + if (matchRecipeOutput(machine, match)) { setupRecipe(match); } } } - @Nullable + protected double getTotalEuOfRecipe(GTRecipe recipe) { + return RecipeHelper.getInputEUt(recipe) * recipe.duration; + } + + protected double getEuMultiplier() { + var maintenanceMachine = ((IRecipeCapabilityMachine) parallel).getMaintenanceMachine(); + return maintenanceMachine != null ? + maintenanceMachine.getDurationMultiplier() * this.reductionRatio : + this.reductionRatio; + } + private GTRecipe getRecipe() { if (!machine.hasProxies()) return null; - GTRecipe match = lookupRecipe(); - if (match == null) return null; - GTRecipe recipe = buildEmptyRecipe(); - recipe.outputs.put(ItemRecipeCapability.CAP, new ArrayList<>()); - recipe.outputs.put(FluidRecipeCapability.CAP, new ArrayList<>()); long maxEUt = getMachine().getOverclockVoltage(); - long totalEu = 0; - int parallel = this.parallel.getMaxParallel(); - for (int i = 0; i < 64; i++) { - match = parallelRecipe(match, parallel); - GTRecipe input = buildEmptyRecipe(); - long inputEUt = RecipeHelper.getInputEUt(match); - if (inputEUt > maxEUt) { - continue; - } - // 需要在此check data - if (dataCheck != null && !dataCheck.test(match.data, machine)) { - continue; - } - input.inputs.putAll(match.inputs); - input.setId(match.id); - if (RecipeRunnerHelper.matchRecipeInput(machine, input) && RecipeRunnerHelper.handleRecipeInput(machine, input)) { - totalEu += match.duration * inputEUt; - List item = match.outputs.get(ItemRecipeCapability.CAP); - if (item != null) { - recipe.outputs.get(ItemRecipeCapability.CAP).addAll(item); - } - List fluid = match.outputs.get(FluidRecipeCapability.CAP); - if (fluid != null) { - recipe.outputs.get(FluidRecipeCapability.CAP).addAll(fluid); - } + if (maxEUt <= 0) return null; + var iterator = lookupRecipeIterator(); + GTRecipe output = GTRecipeBuilder.ofRaw().buildRawRecipe(); + output.outputs.put(ItemRecipeCapability.CAP, new ObjectArrayList<>()); + output.outputs.put(FluidRecipeCapability.CAP, new ObjectArrayList<>()); + double totalEu = 0; + long remain = (long) this.parallel.getMaxParallel() * MAX_THREADS; + double euMultiplier = getEuMultiplier(); + + while (remain > 0 && iterator.hasNext()) { + var match = iterator.next(); + if (match == null) continue; + var p = getMaxParallel(machine, match, remain); + if (p <= 0) continue; + else if (p > 1) match = match.copy(ContentModifier.multiplier(p), false); + ((IGTRecipe) match).setRealParallels(p); + match = getRecipeOutputChance(machine, match); + if (handleRecipeInput(machine, match)) { + remain -= p; + totalEu += getTotalEuOfRecipe(match) * euMultiplier; + var item = match.outputs.get(ItemRecipeCapability.CAP); + if (item != null) output.outputs.get(ItemRecipeCapability.CAP).addAll(item); + var fluid = match.outputs.get(FluidRecipeCapability.CAP); + if (fluid != null) output.outputs.get(FluidRecipeCapability.CAP).addAll(fluid); } - match = lookupRecipe(); - if (match == null) break; + if (totalEu / maxEUt > 20 * 500) break; } - if (recipe.outputs.get(ItemRecipeCapability.CAP).isEmpty() && recipe.outputs.get(FluidRecipeCapability.CAP).isEmpty()) + if (output.outputs.get(ItemRecipeCapability.CAP).isEmpty() && + output.outputs.get(FluidRecipeCapability.CAP).isEmpty()) { + if (getRecipeStatus() == null || getRecipeStatus().isSuccess()) RecipeResult.of(this.machine, RecipeResult.FAIL_FIND); return null; - double d = (double) totalEu / maxEUt; + } + var d = totalEu / maxEUt; long eut = d > 20 ? maxEUt : (long) (maxEUt * d / 20); - recipe.tickInputs.put(EURecipeCapability.CAP, List.of(new Content(eut, ChanceLogic.getMaxChancedValue(), ChanceLogic.getMaxChancedValue(), 0, null, null))); - recipe.duration = (int) Math.max(d, 20); - return recipe; + output.tickInputs.put(EURecipeCapability.CAP, + List.of(new Content(eut, 10000, 10000, 0, null, null))); + output.duration = (int) Math.max(d, 20); + IGTRecipe.of(output).setHasTick(true); + return output; } - private GTRecipe lookupRecipe() { - return machine.getRecipeType().getLookup().findRecipe(machine); + private Iterator lookupRecipeIterator() { + if (this.isLock()) { + if (this.getLockRecipe() == null) { + this.setLockRecipe(machine.getRecipeType().getLookup() + .find(machine, this::checkRecipe)); + } else if (!checkRecipe(this.getLockRecipe())) return Collections.emptyIterator(); + return Collections.singleton(this.getLockRecipe()).iterator(); + } else return machine.getRecipeType().getLookup().getRecipeIterator(machine, this::checkRecipe); } - private GTRecipe buildEmptyRecipe() { - return GTRecipeBuilder.ofRaw().buildRawRecipe(); - } - - private GTRecipe parallelRecipe(GTRecipe recipe, int max) { - int maxMultipliers = Integer.MAX_VALUE; - for (RecipeCapability cap : recipe.inputs.keySet()) { - if (cap.doMatchInRecipe()) { - int currentMultiplier = cap.getMaxParallelRatio(machine, recipe, max); - if (currentMultiplier < maxMultipliers) { - maxMultipliers = currentMultiplier; - } - } - } - if (maxMultipliers > 0) { - recipe = recipe.copy(ContentModifier.multiplier(maxMultipliers), false); - } - return recipe; + private boolean checkRecipe(GTRecipe recipe) { + return matchRecipe(machine, recipe) && + IGTRecipe.of(recipe).getEuTier() <= getMachine().getTier() && + recipe.checkConditions(this).isSuccess() && + (dataCheck == null || dataCheck.test(recipe.data, machine)); } @Override public void onRecipeFinish() { machine.afterWorking(); if (lastRecipe != null) { - lastRecipe.postWorking(this.machine); - RecipeRunnerHelper.handleRecipeOutput(this.machine, lastRecipe); + handleRecipeOutput(this.machine, lastRecipe); } var match = getRecipe(); if (match != null) { - if (RecipeRunnerHelper.matchRecipeOutput(machine, match)) { + if (matchRecipeOutput(machine, match)) { setupRecipe(match); return; } diff --git a/src/main/java/org/gtlcore/gtlcore/common/recipe/condition/GravityCondition.java b/src/main/java/org/gtlcore/gtlcore/common/recipe/condition/GravityCondition.java index 8ffe2bd61..68daafc8c 100644 --- a/src/main/java/org/gtlcore/gtlcore/common/recipe/condition/GravityCondition.java +++ b/src/main/java/org/gtlcore/gtlcore/common/recipe/condition/GravityCondition.java @@ -1,9 +1,11 @@ package org.gtlcore.gtlcore.common.recipe.condition; +import org.gtlcore.gtlcore.api.recipe.RecipeResult; import org.gtlcore.gtlcore.common.data.GTLRecipeConditions; import org.gtlcore.gtlcore.common.machine.multiblock.part.maintenance.IGravityPartMachine; import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiPart; import com.gregtechceu.gtceu.api.machine.multiblock.MultiblockControllerMachine; import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; @@ -61,7 +63,9 @@ public boolean test(@NotNull GTRecipe recipe, @NotNull RecipeLogic recipeLogic) if (machine instanceof MultiblockControllerMachine controllerMachine) { for (IMultiPart part : controllerMachine.self().getParts()) { if (part instanceof IGravityPartMachine gravityPart) { - return gravityPart.getCurrentGravity() == (zero ? 0 : 100); + if (gravityPart.getCurrentGravity() == (zero ? 0 : 100)) return true; + else RecipeResult.of((IRecipeLogicMachine) machine, + RecipeResult.fail(Component.translatable("gtceu.recipe.fail.gravity" + (zero ? ".low" : ".power")))); } } } diff --git a/src/main/java/org/gtlcore/gtlcore/common/util/BlockStateWatcher.java b/src/main/java/org/gtlcore/gtlcore/common/util/BlockStateWatcher.java new file mode 100644 index 000000000..91a657f01 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/common/util/BlockStateWatcher.java @@ -0,0 +1,105 @@ +package org.gtlcore.gtlcore.common.util; + +import org.gtlcore.gtlcore.GTLCore; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.event.level.LevelEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; + +@Mod.EventBusSubscriber(modid = GTLCore.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE, value = Dist.DEDICATED_SERVER) +public class BlockStateWatcher { + + private static final Map>>> LEVEL_DATA = new ConcurrentHashMap<>(); + + public static WatcherHandle addWatcher(Level level, BlockPos pos, + Consumer callback) { + if (level == null || pos == null || callback == null) { + throw new IllegalArgumentException("Level, position and callback cannot be null"); + } + + long posLong = pos.asLong(); + + LEVEL_DATA.computeIfAbsent(level, k -> new Long2ObjectOpenHashMap<>()) + .computeIfAbsent(posLong, k -> new ObjectArrayList<>()) + .add(callback); + + return new WatcherHandle(level, posLong, callback); + } + + public static void removeWatcher(WatcherHandle handle) { + if (handle == null) return; + + Long2ObjectMap>> watchers = LEVEL_DATA.get(handle.level); + if (watchers != null) { + List> callbacks = watchers.get(handle.posLong); + if (callbacks != null) { + callbacks.remove(handle.callback); + if (callbacks.isEmpty()) { + watchers.remove(handle.posLong); + } + } + if (watchers.isEmpty()) { + LEVEL_DATA.remove(handle.level); + } + } + } + + public static void clearLevel(Level level) { + LEVEL_DATA.remove(level); + } + + public static void notifyWatchersInternal(ServerLevel level, BlockPos pos, BlockState newState) { + Long2ObjectMap>> watchers = LEVEL_DATA.get(level); + if (watchers == null) return; + + List> callbacks = watchers.get(pos.asLong()); + if (callbacks == null || callbacks.isEmpty()) return; + + for (Consumer callback : callbacks) { + try { + callback.accept(newState); + } catch (Exception e) { + GTLCore.LOGGER.error("Error in BlockStateWatcher callback at {}: {}", + pos, e.getMessage(), e); + } + } + } + + @SubscribeEvent + public static void onLevelUnload(LevelEvent.Unload event) { + if (event.getLevel() instanceof Level level) { + clearLevel(level); + } + } + + public static class WatcherHandle { + + private final Level level; + private final long posLong; + private final Consumer callback; + + WatcherHandle(Level level, long posLong, Consumer callback) { + this.level = level; + this.posLong = posLong; + this.callback = callback; + } + + public void remove() { + BlockStateWatcher.removeWatcher(this); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/config/AE2CalculationMode.java b/src/main/java/org/gtlcore/gtlcore/config/AE2CalculationMode.java new file mode 100644 index 000000000..e3c7dbe3c --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/config/AE2CalculationMode.java @@ -0,0 +1,24 @@ +package org.gtlcore.gtlcore.config; + +import lombok.Getter; + +@Getter +public enum AE2CalculationMode { + + LEGACY("legacy", "使用原版AE2合成算法"), + FAST("fast", "使用快速合成算法(可能导致极端情况下计算失败)"), + ULTRA_FAST("ultra_fast", "使用超快速合成算法(不会在不同合成路径间均分)"); + + private final String name; + private final String description; + + AE2CalculationMode(String name, String description) { + this.name = name; + this.description = description; + } + + @Override + public String toString() { + return name; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/config/ConfigHolder.java b/src/main/java/org/gtlcore/gtlcore/config/ConfigHolder.java index 3e9f27ca4..8910a2f51 100644 --- a/src/main/java/org/gtlcore/gtlcore/config/ConfigHolder.java +++ b/src/main/java/org/gtlcore/gtlcore/config/ConfigHolder.java @@ -48,6 +48,32 @@ public static void init() { @Configurable.Comment("连锁黑名单,支持通配符*") @Configurable.Synchronized public String[] blackBlockList = { "ae2:cable_bus", "minecraft:grass_block" }; + @Configurable + @Configurable.Comment("ME样板总成输出最小间隔") + @Configurable.Range(min = 1, max = 100) + public int MEPatternOutputMin = 5; + @Configurable + @Configurable.Comment("ME样板总成输出最大间隔") + @Configurable.Range(min = 1, max = 200) + public int MEPatternOutputMax = 80; + + @Configurable + @Configurable.Comment("是否启用ME库存极限拉取模式(保证机器不会停机, 但是会大幅降低TPS!)") + public boolean enableUltimateMEStocking = false; + + @Configurable + @Configurable.Comment("AE2合成更新间隔(tick), 值越大性能越好但响应越慢, 必须是2的幂次(1,2,4,8,16)") + @Configurable.Range(min = 1, max = 16) + public int ae2CraftingServiceUpdateInterval = 4; + + @Configurable + @Configurable.Comment("AE2库存更新间隔(tick), 值越大性能越好但响应越慢, 必须是2的幂次(1,2,4,8,16)") + @Configurable.Range(min = 1, max = 16) + public int ae2StorageServiceUpdateInterval = 8; + + @Configurable + @Configurable.Comment("AE2合成计算模式: LEGACY(原版), FAST(快速), ULTRA_FAST(最快)") + public AE2CalculationMode ae2CalculationMode = AE2CalculationMode.ULTRA_FAST; @Configurable public String[] mobList1 = new String[] { "chicken", "rabbit", "sheep", "cow", "horse", "pig", "donkey", "skeleton_horse", "iron_golem", "wolf", "goat", "parrot", "camel", "cat", "fox", "llama", "panda", "polar_bear" }; diff --git a/src/main/java/org/gtlcore/gtlcore/data/recipe/MachineRecipe.java b/src/main/java/org/gtlcore/gtlcore/data/recipe/MachineRecipe.java index e4676672d..29e98f0c5 100644 --- a/src/main/java/org/gtlcore/gtlcore/data/recipe/MachineRecipe.java +++ b/src/main/java/org/gtlcore/gtlcore/data/recipe/MachineRecipe.java @@ -18,7 +18,6 @@ import com.gregtechceu.gtceu.common.data.GTItems; import com.gregtechceu.gtceu.common.data.GTMachines; import com.gregtechceu.gtceu.common.data.GTMaterials; -import com.gregtechceu.gtceu.common.data.machines.GTAEMachines; import com.gregtechceu.gtceu.common.data.machines.GTResearchMachines; import com.gregtechceu.gtceu.data.recipe.CustomTags; import com.gregtechceu.gtceu.data.recipe.VanillaRecipeHelper; @@ -217,6 +216,14 @@ public static void init(Consumer provider) { "SGS", "GHG", "SGS", 'H', GTMachines.HULL[GTValues.UEV].asStack(), 'G', new UnificationEntry(TagPrefix.gear, GTLMaterials.AstralTitanium), 'S', new UnificationEntry(TagPrefix.gearSmall, GTLMaterials.Quantanium)); + VanillaRecipeHelper.addShapedRecipe(provider, true, GTLCore.id("ultimate_terminal"), + GTLItems.ULTIMATE_TERMINAL.asStack(), + "ABA", "CDC", "CEC", + 'A', new UnificationEntry(TagPrefix.screw, GTMaterials.BlackBronze), + 'B', net.minecraftforge.common.Tags.Items.GLASS_PANES, + 'C', new UnificationEntry(TagPrefix.plate, GTMaterials.Silicon), + 'D', CustomItems.ADVANCED_TERMINAL.asStack(), + 'E', CustomTags.MV_CIRCUITS); registerMachineRecipe(provider, ArrayUtils.subarray(GTMachines.TRANSFORMER, GTValues.UHV, GTValues.MAX), "WCC", "TH ", "WCC", 'W', POWER_COMPONENT, 'C', CABLE, 'T', CABLE_TIER_UP, 'H', HULL); @@ -619,8 +626,8 @@ public static void init(Consumer provider) { ASSEMBLER_RECIPES .recipeBuilder(GTLCore.id("heat_sensor")) - .inputItems(GTAEMachines.STOCKING_IMPORT_BUS_ME, 2) - .inputItems(GTAEMachines.STOCKING_IMPORT_HATCH_ME, 2) + .inputItems(GTLMachines.GTAEMachines.STOCKING_IMPORT_BUS_ME, 2) + .inputItems(GTLMachines.GTAEMachines.STOCKING_IMPORT_HATCH_ME, 2) .inputItems(CIRCUIT.getIngredient(IV)) .inputFluids(GTMaterials.SolderingAlloy.getFluid(1440)) .outputItems(GTLMachines.ME_DUAL_HATCH_STOCK_PART_MACHINE) diff --git a/src/main/java/org/gtlcore/gtlcore/data/recipe/Misc.java b/src/main/java/org/gtlcore/gtlcore/data/recipe/Misc.java index dc94d10af..d13994a6f 100644 --- a/src/main/java/org/gtlcore/gtlcore/data/recipe/Misc.java +++ b/src/main/java/org/gtlcore/gtlcore/data/recipe/Misc.java @@ -1,14 +1,19 @@ package org.gtlcore.gtlcore.data.recipe; +import org.gtlcore.gtlcore.GTLCore; import org.gtlcore.gtlcore.common.data.GTLItems; +import org.gtlcore.gtlcore.common.data.GTLMachines; +import org.gtlcore.gtlcore.common.data.machines.AdditionalMultiBlockMachine; import org.gtlcore.gtlcore.common.data.machines.AdvancedMultiBlockMachine; import org.gtlcore.gtlcore.common.recipe.condition.GravityCondition; import org.gtlcore.gtlcore.config.ConfigHolder; +import org.gtlcore.gtlcore.utils.Registries; import com.gregtechceu.gtceu.api.data.chemical.material.stack.UnificationEntry; import com.gregtechceu.gtceu.api.data.tag.TagPrefix; import com.gregtechceu.gtceu.api.machine.multiblock.CleanroomType; -import com.gregtechceu.gtceu.common.data.GTMaterials; +import com.gregtechceu.gtceu.common.data.*; +import com.gregtechceu.gtceu.data.recipe.CustomTags; import com.gregtechceu.gtceu.data.recipe.VanillaRecipeHelper; import net.minecraft.data.recipes.FinishedRecipe; @@ -16,14 +21,19 @@ import net.minecraft.world.item.Items; import net.minecraft.world.level.block.Blocks; +import appeng.api.util.AEColor; +import appeng.core.definitions.AEBlocks; + import java.util.function.Consumer; +import static appeng.core.definitions.AEItems.*; +import static com.glodblock.github.extendedae.common.EPPItemAndBlock.*; import static com.gregtechceu.gtceu.api.GTValues.*; import static com.gregtechceu.gtceu.api.data.tag.TagPrefix.*; import static com.gregtechceu.gtceu.common.data.GTItems.*; import static com.gregtechceu.gtceu.common.data.GTMaterials.*; import static com.gregtechceu.gtceu.common.data.GTRecipeTypes.*; -import static org.gtlcore.gtlcore.common.data.GTLMaterials.WaterAgarMix; +import static org.gtlcore.gtlcore.common.data.GTLMaterials.*; import static org.gtlcore.gtlcore.common.data.GTLRecipeTypes.*; import static org.gtlcore.gtlcore.common.data.machines.MultiBlockMachineB.PRIMITIVE_VOID_ORE; @@ -32,6 +42,32 @@ public class Misc { public static void init(Consumer provider) { VanillaRecipeHelper.addShapelessRecipe(provider, "structure_detect", GTLItems.STRUCTURE_DETECT.asStack(), "A", Items.PAPER); + VanillaRecipeHelper.addShapelessRecipe(provider, GTLCore.id("me_pattern_buffer_copy"), + GTLItems.ME_PATTERN_BUFFER_COPY.asStack(), + "A", MEMORY_CARD); + VanillaRecipeHelper.addShapedRecipe(provider, true, GTLCore.id("heat_sensor"), + GTLMachines.HEAT_SENSOR.asStack(), + "FDF", "BAB", "CEC", + 'A', GTMachines.HULL[12].asStack(), + 'B', new UnificationEntry(wireFine, EnrichedNaquadahTriniumEuropiumDuranide), + 'C', SENSOR_ZPM.asStack(), + 'D', Registries.getItemStack("gtceu:advanced_activity_detector_cover"), + 'E', Registries.getItemStack("gtceu:computer_monitor_cover"), + 'F', CustomTags.UV_CIRCUITS); + VanillaRecipeHelper.addShapedRecipe(provider, true, GTLCore.id("fast_infinity_cell_0"), + GTLItems.FAST_INFINITY_CELL.asStack(), + "AAA", "BCD", "AAA", + 'A', QUANTUM_ENTANGLED_SINGULARITY.stack(), + 'B', GTLItems.ITEM_INFINITY_CELL.asStack(), + 'C', ZERO_POINT_MODULE.asStack(), + 'D', GTLItems.FLUID_INFINITY_CELL.asStack()); + VanillaRecipeHelper.addShapedRecipe(provider, true, GTLCore.id("fast_infinity_cell_1"), + GTLItems.FAST_INFINITY_CELL.asStack(), + "AAA", "DCB", "AAA", + 'A', QUANTUM_ENTANGLED_SINGULARITY.stack(), + 'B', GTLItems.ITEM_INFINITY_CELL.asStack(), + 'C', ZERO_POINT_MODULE.asStack(), + 'D', GTLItems.FLUID_INFINITY_CELL.asStack()); if (ConfigHolder.INSTANCE.enablePrimitiveVoidOre) { VanillaRecipeHelper.addShapedRecipe(provider, true, "primitive_void_ore_recipes", PRIMITIVE_VOID_ORE.asStack(), "DCD", "CGC", "DCD", @@ -144,5 +180,112 @@ public static void init(Consumer provider) { .circuitMeta(1) .outputItems(dust, EnderPearl, 10) .save(provider); + + ASSEMBLY_LINE_RECIPES.recipeBuilder("molecular_assembler_matrix") + .inputItems(frameGt, NaquadahAlloy, 16) + .inputItems(FIELD_GENERATOR_UV, 8) + .inputItems(ULTIMATE_BATTERY) + .inputItems(CustomTags.UHV_CIRCUITS, 16) + .inputItems(wireFine, RutheniumTriniumAmericiumNeutronate, 64) + .inputItems(wireFine, RutheniumTriniumAmericiumNeutronate, 64) + .inputItems(plate, AbyssalAlloy, 16) + .inputItems(EX_INTERFACE.asItem(), 8) + .inputItems(INGREDIENT_BUFFER.asItem(), 8) + .inputItems(EX_IO_PORT.asItem(), 8) + .inputItems(EX_PATTERN_PROVIDER.asItem(), 8) + .inputItems(EX_ASSEMBLER.asItem(), 8) + .inputFluids(MutatedLivingSolder.getFluid(2304)) + .inputFluids(Highurabilityompoundteel.getFluid(1728)) + .inputFluids(Antimatter.getFluid(4000)) + .inputFluids(Polyetheretherketone.getFluid(2340)) + .outputItems(AdditionalMultiBlockMachine.MOLECULAR_ASSEMBLER_MATRIX) + .EUt(VA[9]).duration(200) + .stationResearch((b) -> b.researchStack(EX_ASSEMBLER.asItem().getDefaultInstance()) + .dataStack(GTItems.TOOL_DATA_MODULE.asStack()).EUt(VA[9]).CWUt(256)) + .save(provider); + + ASSEMBLY_LINE_RECIPES.recipeBuilder("me_molecular_assembler_io") + .inputItems(frameGt, Neutronium, 16) + .inputItems(SENSOR_UV, 8) + .inputItems(EMITTER_UV, 8) + .inputItems(CustomTags.UHV_CIRCUITS, 4) + .inputItems(EX_INTERFACE.asItem(), 32) + .inputItems(INGREDIENT_BUFFER.asItem(), 32) + .inputItems(EX_IO_PORT.asItem(), 32) + .inputItems(QUANTUM_ENTANGLED_SINGULARITY.asItem(), 2) + .inputItems(QUANTUM_ENTANGLED_SINGULARITY.asItem(), 2) + .inputItems(SPATIAL_128_CELL_COMPONENT.asItem(), 16) + .inputFluids(MutatedLivingSolder.getFluid(1152)) + .inputFluids(Naquadria.getFluid(1728)) + .inputFluids(Plutonium241.getFluid(2340)) + .inputFluids(Mithril.getFluid(2340)) + .outputItems(GTLMachines.GTAEMachines.ME_MOLECULAR_ASSEMBLER_IO) + .EUt(VA[9]).duration(200) + .stationResearch((b) -> b.researchStack(EX_IO_PORT.asItem().getDefaultInstance()) + .dataStack(GTItems.TOOL_DATA_MODULE.asStack()).EUt(VA[9]).CWUt(128)) + .save(provider); + + ASSEMBLY_LINE_RECIPES.recipeBuilder("me_craft_speed_core") + .inputItems(GTMachines.WORLD_ACCELERATOR[UV], 2) + .inputItems(FLUID_REGULATOR_UV, 8) + .inputItems(CONVEYOR_MODULE_UV, 8) + .inputItems(Registries.getItemStack("kubejs:bioware_processing_core", 4)) + .inputItems(SPEED_CARD.asItem(), 64) + .inputItems(SINGULARITY.asItem(), 64) + .inputItems(COLORED_LUMEN_PAINT_BALL.stack(AEColor.RED, 64)) + .inputItems(COLORED_LUMEN_PAINT_BALL.stack(AEColor.RED, 64)) + .inputItems(COLORED_LUMEN_PAINT_BALL.stack(AEColor.RED, 64)) + .inputItems(COLORED_LUMEN_PAINT_BALL.stack(AEColor.RED, 64)) + .inputFluids(MutatedLivingSolder.getFluid(1152)) + .inputFluids(Nobelium.getFluid(1728)) + .inputFluids(Orichalcum.getFluid(2340)) + .inputFluids(Mithril.getFluid(2340)) + .outputItems(GTLMachines.GTAEMachines.ME_CRAFT_SPEED_CORE) + .EUt(VA[9]).duration(200) + .stationResearch((b) -> b.researchStack(GTMachines.WORLD_ACCELERATOR[UV].asStack()) + .dataStack(GTItems.TOOL_DATA_MODULE.asStack()).EUt(VA[9]).CWUt(128)) + .save(provider); + + ASSEMBLY_LINE_RECIPES.recipeBuilder("me_craft_parallel_core") + .inputItems(ELECTRIC_MOTOR_UV, 16) + .inputItems(FIELD_GENERATOR_UV, 4) + .inputItems(Registries.getItemStack("kubejs:bioware_processing_core", 4)) + .inputItems(EX_ASSEMBLER.asItem(), 64) + .inputItems(CRAFTING_CARD.asItem(), 64) + .inputItems(SINGULARITY.asItem(), 64) + .inputItems(COLORED_LUMEN_PAINT_BALL.stack(AEColor.PURPLE, 64)) + .inputItems(COLORED_LUMEN_PAINT_BALL.stack(AEColor.PURPLE, 64)) + .inputItems(COLORED_LUMEN_PAINT_BALL.stack(AEColor.PURPLE, 64)) + .inputItems(COLORED_LUMEN_PAINT_BALL.stack(AEColor.PURPLE, 64)) + .inputFluids(MutatedLivingSolder.getFluid(1152)) + .inputFluids(Neptunium.getFluid(1728)) + .inputFluids(Orichalcum.getFluid(2340)) + .inputFluids(Mithril.getFluid(2340)) + .outputItems(GTLMachines.GTAEMachines.ME_CRAFT_PARALLEL_CORE) + .EUt(VA[9]).duration(200) + .stationResearch((b) -> b.researchStack(AEBlocks.MOLECULAR_ASSEMBLER.stack()) + .dataStack(GTItems.TOOL_DATA_MODULE.asStack()).EUt(VA[9]).CWUt(128)) + .save(provider); + + ASSEMBLY_LINE_RECIPES.recipeBuilder("me_craft_pattern_container") + .inputItems(GTMachines.HULL[UV]) + .inputItems(FIELD_GENERATOR_UV, 4) + .inputItems(CustomTags.UV_CIRCUITS) + .inputItems(EX_PATTERN_PROVIDER.asItem(), 64) + .inputItems(CAPACITY_CARD.asItem(), 64) + .inputItems(SINGULARITY.asItem(), 64) + .inputItems(COLORED_LUMEN_PAINT_BALL.stack(AEColor.BLUE, 64)) + .inputItems(COLORED_LUMEN_PAINT_BALL.stack(AEColor.BLUE, 64)) + .inputItems(COLORED_LUMEN_PAINT_BALL.stack(AEColor.BLUE, 64)) + .inputItems(COLORED_LUMEN_PAINT_BALL.stack(AEColor.BLUE, 64)) + .inputFluids(MutatedLivingSolder.getFluid(1152)) + .inputFluids(Mendelevium.getFluid(1728)) + .inputFluids(DamascusSteel.getFluid(2304)) + .inputFluids(Titanium50.getFluid(2304)) + .outputItems(GTLMachines.GTAEMachines.ME_CRAFT_PATTERN_CONTAINER) + .EUt(VA[9]).duration(200) + .stationResearch((b) -> b.researchStack(EX_PATTERN_PROVIDER.asItem().getDefaultInstance()) + .dataStack(GTItems.TOOL_DATA_MODULE.asStack()).EUt(VA[9]).CWUt(128)) + .save(provider); } } diff --git a/src/main/java/org/gtlcore/gtlcore/data/recipe/RemoveRecipe.java b/src/main/java/org/gtlcore/gtlcore/data/recipe/RemoveRecipe.java index 49b6e916e..b03cfaf8f 100644 --- a/src/main/java/org/gtlcore/gtlcore/data/recipe/RemoveRecipe.java +++ b/src/main/java/org/gtlcore/gtlcore/data/recipe/RemoveRecipe.java @@ -7,6 +7,8 @@ import net.minecraft.resources.ResourceLocation; +import com.glodblock.github.extendedae.ExtendedAE; + import java.util.function.Consumer; import static com.gregtechceu.gtceu.api.GTValues.VN; @@ -80,5 +82,9 @@ public static void init(Consumer consumer) { consumer.accept(GTCEu.id("assembler/" + "dual_export_bus_" + VN[tier].toLowerCase() + "_" + fluidMap[j].getName())); } } + + consumer.accept(ExtendedAE.id("assembler_matrix_frame")); + consumer.accept(ExtendedAE.id("assembler_matrix_wall")); + consumer.accept(ExtendedAE.id("assembler_matrix_glass")); } } diff --git a/src/main/java/org/gtlcore/gtlcore/forge/ForgeServerEventListener.java b/src/main/java/org/gtlcore/gtlcore/forge/ForgeServerEventListener.java new file mode 100644 index 000000000..29894f3da --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/forge/ForgeServerEventListener.java @@ -0,0 +1,18 @@ +package org.gtlcore.gtlcore.forge; + +import org.gtlcore.gtlcore.GTLCore; +import org.gtlcore.gtlcore.integration.ae2.async.AEWriteService; + +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.event.server.ServerStoppingEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +@Mod.EventBusSubscriber(modid = GTLCore.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE, value = Dist.DEDICATED_SERVER) +public class ForgeServerEventListener { + + @SubscribeEvent + public static void onServerStopping(ServerStoppingEvent e) { + AEWriteService.INSTANCE.shutDownGracefully(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/integration/ae2/AEUtils.java b/src/main/java/org/gtlcore/gtlcore/integration/ae2/AEUtils.java new file mode 100644 index 000000000..6e9977d82 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/integration/ae2/AEUtils.java @@ -0,0 +1,382 @@ +package org.gtlcore.gtlcore.integration.ae2; + +import org.gtlcore.gtlcore.api.recipe.ingredient.CacheHashStrategies; +import org.gtlcore.gtlcore.api.recipe.ingredient.LongIngredient; +import org.gtlcore.gtlcore.common.machine.multiblock.part.ae.MEPatternBufferPartMachine; +import org.gtlcore.gtlcore.config.AE2CalculationMode; +import org.gtlcore.gtlcore.config.ConfigHolder; + +import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; +import com.gregtechceu.gtceu.common.data.GTItems; +import com.gregtechceu.gtceu.common.item.IntCircuitBehaviour; + +import com.lowdragmc.lowdraglib.side.fluid.FluidStack; + +import net.minecraft.nbt.*; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.material.Fluid; + +import appeng.api.config.Actionable; +import appeng.api.crafting.IPatternDetails; +import appeng.api.crafting.PatternDetailsHelper; +import appeng.api.networking.IGrid; +import appeng.api.networking.security.IActionSource; +import appeng.api.stacks.*; +import appeng.api.storage.MEStorage; +import appeng.api.storage.StorageHelper; +import appeng.blockentity.crafting.IMolecularAssemblerSupportedPattern; +import appeng.crafting.execution.CraftingCpuHelper; +import appeng.crafting.inv.ICraftingInventory; +import appeng.crafting.pattern.*; +import it.unimi.dsi.fastutil.objects.*; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; + +import static appeng.crafting.execution.CraftingCpuHelper.*; + +public class AEUtils { + + private static final int BATCH_SIZE = 64; + private static final int MAX_FAILED_ATTEMPTS = 5; + + public static boolean reFunds(Object2LongMap buffer, @Nullable IGrid network, IActionSource actionSource) { + if (buffer.isEmpty()) return false; + + if (network == null) return false; + + final MEStorage networkInv = network.getStorageService().getInventory(); + final var energy = network.getEnergyService(); + int operationsBatched = 0, consecutiveFailures = 0; + boolean didWork = false; + + for (var it = Object2LongMaps.fastIterator(buffer); it.hasNext() && operationsBatched < BATCH_SIZE;) { + var entry = it.next(); + long amount = entry.getLongValue(); + + if (amount <= 0) { + it.remove(); + continue; + } + + long inserted = StorageHelper.poweredInsert(energy, networkInv, entry.getKey(), amount, actionSource); + operationsBatched++; + + if (inserted > 0) { + didWork = true; + consecutiveFailures = 0; + long left = amount - inserted; + if (left <= 0) { + it.remove(); + } else { + entry.setValue(left); + } + } else { + consecutiveFailures++; + if (consecutiveFailures >= MAX_FAILED_ATTEMPTS) { + break; + } + } + } + return didWork; + } + + // ======================================== + // Recipe + // ======================================== + + public static boolean testFluidIngredient(FluidIngredient fluidIngredient, AEFluidKey fluidKey) { + if (fluidIngredient.isEmpty()) { + return false; + } else if (fluidIngredient.getNbt() != null && !fluidIngredient.getNbt().equals(fluidKey.getTag())) { + return false; + } else { + for (FluidStack fluidStack : fluidIngredient.getStacks()) { + if (fluidStack.getFluid() == fluidKey.getFluid()) { + return true; + } + } + return false; + } + } + + // ======================================== + // Persist + // ======================================== + + public static ListTag createListTag(Function keySerializer, Object2LongMap map) { + ListTag tag = new ListTag(); + for (var it = Object2LongMaps.fastIterator(map); it.hasNext();) { + var entry = it.next(); + var ct = keySerializer.apply(entry.getKey()); + ct.putLong("real", entry.getLongValue()); + tag.add(ct); + } + return tag; + } + + public static void loadInventory(ListTag tag, Function keyExtractor, Object2LongMap targetMap) { + for (Tag t : tag) { + if (!(t instanceof CompoundTag ct)) continue; + K key = keyExtractor.apply(ct); + long value = ct.getLong("real"); + if (key != null && value > 0) { + targetMap.put(key, value); + } + } + } + + public static ListTag createListTag(Function keySerializer, ObjectSet map) { + ListTag tag = new ListTag(); + for (T t : map) { + var ct = keySerializer.apply(t); + tag.add(ct); + } + return tag; + } + + public static void loadInventory(ListTag tag, Function keyExtractor, ObjectSet targetMap) { + for (Tag t : tag) { + if (!(t instanceof CompoundTag ct)) continue; + K key = keyExtractor.apply(ct); + if (key != null) { + targetMap.add(key); + } + } + } + + public static CompoundTag writeTag(@Nullable GenericStack stack) { + if (stack == null) { + return new CompoundTag(); + } else { + CompoundTag tag = stack.what().toTagGeneric().copy(); + tag.putLong("#", stack.amount()); + return tag; + } + } + + // ======================================== + // ME IO Machine Utils + // ======================================== + + public static Pair, Object2LongOpenHashMap> mergeInternalSlot(MEPatternBufferPartMachine.InternalSlot[] internalSlots) { + Object2LongOpenHashMap items = new Object2LongOpenHashMap<>(); + Object2LongOpenHashMap fluids = new Object2LongOpenHashMap<>(); + for (var internalSlot : Arrays.stream(internalSlots).filter(MEPatternBufferPartMachine.InternalSlot::isActive).toList()) { + for (var it = Object2LongMaps.fastIterator(internalSlot.getItemInventory()); it.hasNext();) { + var entry = it.next(); + items.addTo(entry.getKey().getItem(), entry.getLongValue()); + } + for (var it = Object2LongMaps.fastIterator(internalSlot.getFluidInventory()); it.hasNext();) { + var entry = it.next(); + fluids.addTo(entry.getKey().getFluid(), entry.getLongValue()); + } + } + return new ImmutablePair<>(items, fluids); + } + + public static Object2LongMap ingredientsMapWithOutCircuit(List ingredients, Consumer consumer) { + var result = new Object2LongOpenCustomHashMap<>(CacheHashStrategies.IngredientHashStrategy.INSTANCE); + for (Ingredient ingredient : ingredients) { + var items = ingredient.getItems(); + if (items.length == 0 || items[0].isEmpty()) { + continue; + } + if (GTItems.INTEGRATED_CIRCUIT.is(items[0].getItem())) { + consumer.accept(IntCircuitBehaviour.getCircuitConfiguration(items[0])); + continue; + } + result.addTo(ingredient, ingredient instanceof LongIngredient longIngredient ? longIngredient.getActualAmount() : items[0].getCount()); + } + return result; + } + + public static Object2LongMap ingredientsMap(List ingredients) { + var result = new Object2LongOpenCustomHashMap<>(CacheHashStrategies.IngredientHashStrategy.INSTANCE); + for (Ingredient ingredient : ingredients) { + var items = ingredient.getItems(); + if (items.length == 0 || items[0].isEmpty()) { + continue; + } + result.addTo(ingredient, ingredient instanceof LongIngredient longIngredient ? longIngredient.getActualAmount() : items[0].getCount()); + } + return result; + } + + public static Object2LongMap fluidIngredientsMap(List ingredients) { + var result = new Object2LongOpenCustomHashMap<>(CacheHashStrategies.FluidIngredientHashStrategy.INSTANCE); + for (FluidIngredient ingredient : ingredients) { + if (ingredient.isEmpty()) continue; + result.addTo(ingredient, ingredient.getAmount()); + } + return result; + } + + public static Function PROCESS_FILTER = stack -> stack.getItem() instanceof ProcessingPatternItem; + + public static boolean molecularFilter(ItemStack stack, Level level) { + final var item = stack.getItem(); + if (item instanceof CraftingPatternItem craftingPatternItem) { + var pattern = craftingPatternItem.decode(stack, level, false); + if (pattern != null) { + return !hasContainerItems(pattern); + } + } else { + return item instanceof SmithingTablePatternItem || item instanceof StonecuttingPatternItem; + } + return false; + } + + private static boolean hasContainerItems(AECraftingPattern pattern) { + IPatternDetails.IInput[] inputs = pattern.getInputs(); + + for (IPatternDetails.IInput input : inputs) { + AEKey key = input.getPossibleInputs()[0].what(); + AEKey remainingKey = input.getRemainingKey(key); + if (remainingKey != null) { + if (!(remainingKey instanceof AEItemKey itemKey) || itemKey.toStack().isDamageableItem()) { + return true; + } + } + } + + return false; + } + + public static Pair> createProcessingFromCraftPattern(IMolecularAssemblerSupportedPattern molecularAssemblerSupportedPattern, Level level) { + IPatternDetails.IInput[] inputs = molecularAssemblerSupportedPattern.getInputs(); + + ObjectArrayList normalInputs = new ObjectArrayList<>(); + ObjectSet remainingInputs = new ObjectArraySet<>(); + for (IPatternDetails.IInput input : inputs) { + final var stack = input.getPossibleInputs()[0]; + final var remaining = input.getRemainingKey(stack.what()); + if (remaining != null) { + assert remaining instanceof AEItemKey; + if (((AEItemKey) remaining).toStack().isDamageableItem()) { + return ImmutablePair.of(null, null); + + } else remainingInputs.add(((AEItemKey) remaining).getItem()); + } else { + normalInputs.add(new GenericStack(stack.what(), stack.amount() * input.getMultiplier())); + } + } + + ItemStack pattern = PatternDetailsHelper.encodeProcessingPattern(normalInputs.toArray(new GenericStack[0]), molecularAssemblerSupportedPattern.getOutputs()); + + return ImmutablePair.of(PatternDetailsHelper.decodePattern(pattern, level), remainingInputs); + } + + // ======================================== + // ME Processing Pattern Multiply + // ======================================== + + public static final AE2CalculationMode CALCULATION_MODE = ConfigHolder.INSTANCE.ae2CalculationMode; + + public static void pushInputsToMEPatternBufferInventory(KeyCounter[] inputHolder, IPatternDetails.PatternInputSink inputSink) { + for (var inputList : inputHolder) { + for (var input : inputList) { + inputSink.pushInput(input.getKey(), input.getLongValue()); + } + } + } + + public static KeyCounter[] extractForProcessingPattern(AEProcessingPattern originDetail, + ICraftingInventory sourceInv, + KeyCounter expectedOutputs) { + return extractForProcessingPattern(originDetail, sourceInv, expectedOutputs, 1); + } + + public static KeyCounter[] extractForProcessingPattern(AEProcessingPattern originDetail, + ICraftingInventory sourceInv, + KeyCounter expectedOutputs, + long multiplier) { + IPatternDetails.IInput[] inputs = originDetail.getInputs(); + KeyCounter[] inputHolder = new KeyCounter[inputs.length]; + boolean found = true; + + for (int x = 0; x < inputs.length; x++) { + var list = inputHolder[x] = new KeyCounter(); + AEKey key = inputs[x].getPossibleInputs()[0].what(); + long amount = inputs[x].getMultiplier() * multiplier; + long extracted = AEUtils.extractTemplates(sourceInv, key, amount); + list.add(key, extracted); + if (extracted < amount) { + found = false; + break; + } + } + + if (!found) { + reinjectPatternInputs(sourceInv, inputHolder); + return null; + } else { + for (GenericStack output : originDetail.getOutputs()) { + expectedOutputs.add(output.what(), output.amount() * multiplier); + } + return inputHolder; + } + } + + private static long extractTemplates(ICraftingInventory inv, AEKey key, long amount) { + if (amount == 0) return 0; + long simEx = inv.extract(key, amount, Actionable.SIMULATE); + if (simEx == 0) return 0; + long extracted = inv.extract(key, simEx, Actionable.MODULATE); + if (extracted == 0 || extracted != simEx) { + throw new IllegalStateException("Failed to correctly extract whole number. Invalid simulation!"); + } + return extracted; + } + + public static KeyCounter[] extractForCraftPattern(IPatternDetails details, + ICraftingInventory sourceInv, + Level level, + KeyCounter expectedOutputs, + KeyCounter expectedContainerItems) { + var inputs = details.getInputs(); + KeyCounter[] inputHolder = new KeyCounter[inputs.length]; + boolean found = true; + + for (int x = 0; x < inputs.length; x++) { + var list = inputHolder[x] = new KeyCounter(); + long remainingMultiplier = inputs[x].getMultiplier(); + for (var template : getValidItemTemplates(sourceInv, inputs[x], level)) { + long extracted = CraftingCpuHelper.extractTemplates(sourceInv, template, remainingMultiplier); + list.add(template.key(), extracted * template.amount()); + + var containerItem = inputs[x].getRemainingKey(template.key()); + if (containerItem != null) { + expectedContainerItems.add(containerItem, extracted); + } + + remainingMultiplier -= extracted; + if (remainingMultiplier == 0) + break; + } + + if (remainingMultiplier > 0) { + found = false; + break; + } + } + + if (!found) { + reinjectPatternInputs(sourceInv, inputHolder); + return null; + } + + for (GenericStack output : details.getOutputs()) { + expectedOutputs.add(output.what(), output.amount()); + } + + return inputHolder; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/integration/ae2/FastInfinityCell.java b/src/main/java/org/gtlcore/gtlcore/integration/ae2/FastInfinityCell.java new file mode 100644 index 000000000..e70a5603d --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/integration/ae2/FastInfinityCell.java @@ -0,0 +1,62 @@ +package org.gtlcore.gtlcore.integration.ae2; + +import org.gtlcore.gtlcore.integration.ae2.storage.FastInfinityCellHandler; + +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; + +import appeng.api.config.FuzzyMode; +import appeng.api.storage.cells.ICellWorkbenchItem; +import appeng.api.upgrades.IUpgradeInventory; +import appeng.api.upgrades.UpgradeInventories; +import appeng.hooks.AEToolItem; +import com.google.common.base.Preconditions; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +import javax.annotation.Nullable; + +public class FastInfinityCell extends Item implements ICellWorkbenchItem, AEToolItem { + + public FastInfinityCell() { + super(new Properties().stacksTo(1).fireResistant()); + } + + @Override + public boolean isEditable(ItemStack is) { + return true; + } + + @Override + public FuzzyMode getFuzzyMode(final ItemStack is) { + final String fz = is.getOrCreateTag().getString("FuzzyMode"); + if (fz.isEmpty()) { + return FuzzyMode.IGNORE_ALL; + } + try { + return FuzzyMode.valueOf(fz); + } catch (final Throwable t) { + return FuzzyMode.IGNORE_ALL; + } + } + + @Override + public void setFuzzyMode(final ItemStack is, final FuzzyMode fzMode) { + is.getOrCreateTag().putString("FuzzyMode", fzMode.name()); + } + + @Override + public IUpgradeInventory getUpgrades(ItemStack is) { + return UpgradeInventories.forItem(is, 2); + } + + @Override + public void appendHoverText(ItemStack stack, @Nullable Level world, @NotNull List tooltip, @NotNull TooltipFlag context) { + Preconditions.checkArgument(stack.getItem() == this); + FastInfinityCellHandler.INSTANCE.addCellInformationToTooltip(stack, tooltip); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/integration/ae2/InfinityCellGuiHandler.java b/src/main/java/org/gtlcore/gtlcore/integration/ae2/InfinityCellGuiHandler.java index c4fbdf3f4..52e36ad07 100755 --- a/src/main/java/org/gtlcore/gtlcore/integration/ae2/InfinityCellGuiHandler.java +++ b/src/main/java/org/gtlcore/gtlcore/integration/ae2/InfinityCellGuiHandler.java @@ -15,7 +15,7 @@ public class InfinityCellGuiHandler implements ICellGuiHandler { @Override public boolean isSpecializedFor(ItemStack cell) { - return cell.getItem() instanceof InfinityCell; + return cell.getItem() instanceof InfinityCell || cell.getItem() instanceof FastInfinityCell; } @Override diff --git a/src/main/java/org/gtlcore/gtlcore/integration/ae2/async/AEAccumulator.java b/src/main/java/org/gtlcore/gtlcore/integration/ae2/async/AEAccumulator.java new file mode 100644 index 000000000..901a6926f --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/integration/ae2/async/AEAccumulator.java @@ -0,0 +1,36 @@ +package org.gtlcore.gtlcore.integration.ae2.async; + +import appeng.api.stacks.AEKey; +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; +import lombok.Getter; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.LongAdder; + +public final class AEAccumulator { + + @Getter + private final ConcurrentHashMap acc = new ConcurrentHashMap<>(); + + public void add(AEKey key, long delta) { + if (key == null || delta == 0) return; + acc.computeIfAbsent(key, k -> new LongAdder()).add(delta); + } + + public void drainTo(Object2LongOpenHashMap buffer) { + for (var it = acc.entrySet().iterator(); it.hasNext();) { + var e = it.next(); + long d = e.getValue().sumThenReset(); + if (d != 0) buffer.addTo(e.getKey(), d); + if (e.getValue().sum() == 0) it.remove(); + } + } + + public void clear() { + acc.clear(); + } + + public boolean isEmpty() { + return acc.isEmpty(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/integration/ae2/async/AEWriteService.java b/src/main/java/org/gtlcore/gtlcore/integration/ae2/async/AEWriteService.java new file mode 100644 index 000000000..23ddecb50 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/integration/ae2/async/AEWriteService.java @@ -0,0 +1,123 @@ +package org.gtlcore.gtlcore.integration.ae2.async; + +import org.gtlcore.gtlcore.api.recipe.ingredient.LongIngredient; + +import com.gregtechceu.gtceu.api.recipe.ingredient.*; + +import com.lowdragmc.lowdraglib.side.fluid.FluidStack; + +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + +import appeng.api.stacks.*; +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; + +import java.lang.ref.WeakReference; +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +public final class AEWriteService implements AutoCloseable { + + public static final AEWriteService INSTANCE = new AEWriteService(); + public static int THREAD_PRIORITY = Thread.NORM_PRIORITY - 1; + public static int TIME_OUT = 10; + + private final ThreadPoolExecutor executor = new ThreadPoolExecutor( + 1, 1, 30, TimeUnit.SECONDS, + new ArrayBlockingQueue<>(4096), + r -> { + Thread t = new Thread(r, "AE-Writer"); + t.setDaemon(true); + t.setPriority(Math.max(Thread.MIN_PRIORITY, Math.min(Thread.MAX_PRIORITY, THREAD_PRIORITY))); + return t; + }, + new ThreadPoolExecutor.CallerRunsPolicy()); + + public void submitIngredientLeft(WeakReference accRef, List left) { + if (left == null || left.isEmpty()) return; + executor.execute(() -> { + var acc = accRef.get(); + if (acc == null) return; + for (Ingredient ingredient : left) { + if (ingredient instanceof IntProviderIngredient intProvider) { + intProvider.setItemStacks(null); + intProvider.setSampledCount(null); + } + + ItemStack[] items = ingredient.getItems(); + if (items.length != 0) { + ItemStack output = items[0]; + if (!output.isEmpty()) { + acc.add(AEItemKey.of(output), ingredient instanceof LongIngredient longIngredient ? longIngredient.getActualAmount() : output.getCount()); + } + } + } + }); + } + + public void submitFluidIngredientLeft(WeakReference accRef, List left) { + if (left == null || left.isEmpty()) return; + executor.execute(() -> { + var acc = accRef.get(); + if (acc == null) return; + for (FluidIngredient fluidIngredient : left) { + if (!fluidIngredient.isEmpty()) { + FluidStack[] fluids = fluidIngredient.getStacks(); + if (fluids.length != 0) { + FluidStack output = fluids[0]; + acc.add(AEFluidKey.of(output.getFluid()), output.getAmount()); + } + } + } + }); + } + + public void prepareDrainedData(WeakReference accRef, + AtomicReference> targetRef, + AtomicBoolean drainRequested) { + executor.execute(() -> { + var acc = accRef.get(); + if (acc == null) { + drainRequested.set(false); + return; + } + + if (targetRef.get() != null) { + drainRequested.set(false); + return; + } + + Object2LongOpenHashMap drainedData = new Object2LongOpenHashMap<>(); + acc.drainTo(drainedData); + + if (!drainedData.isEmpty()) { + if (!targetRef.compareAndSet(null, drainedData)) { + drainedData.object2LongEntrySet().fastForEach(e -> acc.add(e.getKey(), e.getLongValue())); + } + } + + drainRequested.set(false); + }); + } + + public void shutDownGracefully() { + executor.shutdown(); + try { + if (!executor.awaitTermination(TIME_OUT, TimeUnit.SECONDS)) { + executor.shutdownNow(); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + executor.shutdownNow(); + } + } + + @Override + public void close() { + shutDownGracefully(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/integration/ae2/crafting/FastCraftingCalculation.java b/src/main/java/org/gtlcore/gtlcore/integration/ae2/crafting/FastCraftingCalculation.java new file mode 100644 index 000000000..cf424325b --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/integration/ae2/crafting/FastCraftingCalculation.java @@ -0,0 +1,6 @@ +package org.gtlcore.gtlcore.integration.ae2.crafting; + +public class FastCraftingCalculation { + + public static final Object object = new Object(); +} diff --git a/src/main/java/org/gtlcore/gtlcore/integration/ae2/crafting/ICraftingCalculation.java b/src/main/java/org/gtlcore/gtlcore/integration/ae2/crafting/ICraftingCalculation.java new file mode 100644 index 000000000..290d2ecb0 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/integration/ae2/crafting/ICraftingCalculation.java @@ -0,0 +1,6 @@ +package org.gtlcore.gtlcore.integration.ae2.crafting; + +public interface ICraftingCalculation { + + void handlePausing() throws InterruptedException; +} diff --git a/src/main/java/org/gtlcore/gtlcore/integration/ae2/crafting/ICraftingTreeNode.java b/src/main/java/org/gtlcore/gtlcore/integration/ae2/crafting/ICraftingTreeNode.java new file mode 100644 index 000000000..3424975fe --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/integration/ae2/crafting/ICraftingTreeNode.java @@ -0,0 +1,18 @@ +package org.gtlcore.gtlcore.integration.ae2.crafting; + +import appeng.api.stacks.KeyCounter; +import appeng.crafting.CraftBranchFailure; +import appeng.crafting.inv.CraftingSimulationState; +import org.jetbrains.annotations.Nullable; + +public interface ICraftingTreeNode { + + void ultraFastRequest(CraftingSimulationState inv, long requestedAmount, + @Nullable KeyCounter containerItems) throws CraftBranchFailure, InterruptedException; + + void fastRequest(CraftingSimulationState inv, long requestedAmount, + @Nullable KeyCounter containerItems) throws CraftBranchFailure, InterruptedException; + + void legacyRequest(CraftingSimulationState inv, long requestedAmount, + @Nullable KeyCounter containerItems) throws CraftBranchFailure, InterruptedException; +} diff --git a/src/main/java/org/gtlcore/gtlcore/integration/ae2/crafting/ICraftingTreeProcess.java b/src/main/java/org/gtlcore/gtlcore/integration/ae2/crafting/ICraftingTreeProcess.java new file mode 100644 index 000000000..147d9466b --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/integration/ae2/crafting/ICraftingTreeProcess.java @@ -0,0 +1,23 @@ +package org.gtlcore.gtlcore.integration.ae2.crafting; + +import appeng.api.crafting.IPatternDetails; +import appeng.api.stacks.AEKey; +import appeng.crafting.CraftBranchFailure; +import appeng.crafting.inv.CraftingSimulationState; + +public interface ICraftingTreeProcess { + + void fastRequest(CraftingSimulationState inv, long times) throws CraftBranchFailure, InterruptedException; + + void ultraFastRequest(CraftingSimulationState inv, long times) throws CraftBranchFailure, InterruptedException; + + IPatternDetails getDetails(); + + boolean getPossible(); + + void setPossible(boolean b); + + long getOutputCountTest(AEKey what); + + boolean limitsQuantityTest(); +} diff --git a/src/main/java/org/gtlcore/gtlcore/integration/ae2/handler/PatternCircuitHandler.java b/src/main/java/org/gtlcore/gtlcore/integration/ae2/handler/PatternCircuitHandler.java new file mode 100644 index 000000000..7c68dcee5 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/integration/ae2/handler/PatternCircuitHandler.java @@ -0,0 +1,125 @@ +package org.gtlcore.gtlcore.integration.ae2.handler; + +import org.gtlcore.gtlcore.api.machine.trait.NotifiableCircuitItemStackHandler; + +import com.gregtechceu.gtceu.common.data.GTItems; +import com.gregtechceu.gtceu.common.item.IntCircuitBehaviour; + +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; + +import appeng.api.crafting.IPatternDetails; +import appeng.api.crafting.PatternDetailsHelper; +import appeng.api.stacks.AEItemKey; +import appeng.api.stacks.GenericStack; +import appeng.crafting.pattern.AEProcessingPattern; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; + +import java.util.Arrays; +import java.util.Objects; +import java.util.function.Consumer; + +/** + * 样板电路处理模块 + * 负责处理样板中的电路逻辑,包括: + * - 电路提取和存储 + * - 样板重构(移除电路) + * - 电路来源判断 + */ +public class PatternCircuitHandler { + + private final NotifiableCircuitItemStackHandler mePatternCircuitInventory; + + public PatternCircuitHandler(NotifiableCircuitItemStackHandler mePatternCircuitInventory) { + this.mePatternCircuitInventory = mePatternCircuitInventory; + } + + /** + * @return ME样板总成是否配置有共享电路 + */ + public boolean haveSharedPatternCircuit() { + return !mePatternCircuitInventory.storage.getStackInSlot(0).isEmpty(); + } + + /** + * 处理包含电路的样板:提取电路并返回无电路的样板 + * + * @param originalPatternStack 原始样板 + * @param storedCircuit 存储电路的引用 + * @return 处理结果,包含无电路样板和提取的电路 + */ + public IPatternDetails processPatternWithCircuit(ItemStack originalPatternStack, Consumer storedCircuit, Level level) { + if (PatternDetailsHelper.decodePattern(originalPatternStack, level) instanceof AEProcessingPattern processingPattern) { + // 提取电路 + int extractedCircuit = extractCircuitFromPattern(processingPattern); + if (extractedCircuit < 0) { + return processingPattern; // 没有电路,直接返回 + } + + storedCircuit.accept(extractedCircuit); + + // 创建无电路的样板 + return createPatternWithoutCircuit(processingPattern, level); + } else { + return null; + } + } + + /** + * 获取用于配方的电路 + * + * @param storedCircuit 存储的电路 + * @return 电路ItemStack,可能为空 + */ + public ItemStack getCircuitForRecipe(ItemStack storedCircuit) { + if (storedCircuit == ItemStack.EMPTY || storedCircuit == null) { + if (haveSharedPatternCircuit()) { + return mePatternCircuitInventory.storage.getStackInSlot(0); // 返回配置的电路 + } else { + return ItemStack.EMPTY; + } + } + return storedCircuit; + } + + /** + * 从样板中提取电路 + * + * @param processingPattern 处理样板 + * @return 提取的电路,如果没有则返回-1 + */ + private int extractCircuitFromPattern(AEProcessingPattern processingPattern) { + for (var input : Arrays.stream(processingPattern.getSparseInputs()).filter(Objects::nonNull).toList()) { + if (input.what() instanceof AEItemKey itemKey) { + ItemStack itemStack = itemKey.toStack(); + if (IntCircuitBehaviour.isIntegratedCircuit(itemStack)) { + return IntCircuitBehaviour.getCircuitConfiguration(itemStack); + } + } + } + return -1; + } + + /** + * 创建一个移除了电路的新样板 + * + * @param pattern 原始处理样板 + * @return 无电路的样板 + */ + private IPatternDetails createPatternWithoutCircuit(AEProcessingPattern pattern, Level level) { + var originalInputs = pattern.getSparseInputs(); + var originalOutputs = pattern.getSparseOutputs(); + var filteredInputs = new ObjectArrayList(); + + for (var input : Arrays.stream(originalInputs).filter(Objects::nonNull).toList()) { + if (input.what() instanceof AEItemKey itemKey) { + if (itemKey.getItem() == GTItems.INTEGRATED_CIRCUIT.asItem()) { + continue; // 跳过电路 + } + } + filteredInputs.add(input); + } + + return PatternDetailsHelper.decodePattern(PatternDetailsHelper.encodeProcessingPattern(filteredInputs.toArray(new GenericStack[0]), originalOutputs), level); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/integration/ae2/handler/SlotCacheManager.java b/src/main/java/org/gtlcore/gtlcore/integration/ae2/handler/SlotCacheManager.java new file mode 100644 index 000000000..9e1be77d6 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/integration/ae2/handler/SlotCacheManager.java @@ -0,0 +1,269 @@ +package org.gtlcore.gtlcore.integration.ae2.handler; + +import org.gtlcore.gtlcore.api.recipe.ingredient.CacheHashStrategies; + +import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; +import com.gregtechceu.gtceu.common.item.IntCircuitBehaviour; + +import com.lowdragmc.lowdraglib.syncdata.ITagSerializable; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + +import appeng.api.stacks.AEFluidKey; +import appeng.api.stacks.AEItemKey; +import com.google.gson.JsonParser; +import it.unimi.dsi.fastutil.objects.*; +import lombok.Getter; + +/** + * 负责管理单个槽位的所有缓存数据,包括: + * - BestMatch缓存:ingredient到配方实际使用AEKey的映射 + * - 缓存失效和清理 + */ +public class SlotCacheManager implements ITagSerializable { + + private final Object2ObjectMap itemBestMatchCache = new Object2ObjectOpenCustomHashMap<>(CacheHashStrategies.IngredientHashStrategy.INSTANCE); + private final Object2ObjectMap fluidBestMatchCache = new Object2ObjectOpenCustomHashMap<>(CacheHashStrategies.FluidIngredientHashStrategy.INSTANCE); + + public void setCircuitCache(int circuitCache) { + this.circuitCache = circuitCache; + this.circuitStack = IntCircuitBehaviour.stack(circuitCache); + } + + @Getter + private int circuitCache = -1; + @Getter + private ItemStack circuitStack = ItemStack.EMPTY; + + /** + * 获取或计算item的最佳匹配 + * + * @param ingredient 原料 + * @param inventory 物品库存映射 + * @param needAmount 需要的数量 + * @return 最佳匹配的AEItemKey,如果没有足够库存则返回null + */ + public AEItemKey getBestItemMatch(Ingredient ingredient, Object2LongMap inventory, long needAmount) { + AEItemKey cached = itemBestMatchCache.get(ingredient); + + if (cached != null && inventory.getLong(cached) >= needAmount) { + return cached; + } + + AEItemKey bestMatch = findBestItemMatch(ingredient, inventory, needAmount); + if (bestMatch != null) { + itemBestMatchCache.put(ingredient, bestMatch); + } + return bestMatch; + } + + /** + * 获取或计算fluid的最佳匹配 + * + * @param ingredient 流体原料 + * @param inventory 流体库存映射 + * @param needAmount 需要的数量 + * @return 最佳匹配的AEFluidKey,如果没有足够库存则返回null + */ + public AEFluidKey getBestFluidMatch(FluidIngredient ingredient, Object2LongMap inventory, long needAmount) { + AEFluidKey cached = fluidBestMatchCache.get(ingredient); + + if (cached != null && inventory.getLong(cached) >= needAmount) { + return cached; + } + + AEFluidKey bestMatch = findBestFluidMatch(ingredient, inventory, needAmount); + if (bestMatch != null) { + fluidBestMatchCache.put(ingredient, bestMatch); + } + return bestMatch; + } + + private static AEItemKey findBestItemMatch(Ingredient ingredient, Object2LongMap inventory, long needAmount) { + var items = ingredient.getItems(); + for (var item : items) { + if (!item.isEmpty()) { + AEItemKey aeKey = AEItemKey.of(item); + if (inventory.getLong(aeKey) >= needAmount) { + return aeKey; + } + } + } + return null; + } + + private static AEFluidKey findBestFluidMatch(FluidIngredient ingredient, Object2LongMap inventory, long needAmount) { + var stacks = ingredient.getStacks(); + for (var stack : stacks) { + if (!stack.isEmpty()) { + AEFluidKey aeKey = AEFluidKey.of(stack.getFluid()); + if (inventory.getLong(aeKey) >= needAmount) { + return aeKey; + } + } + } + return null; + } + + // ======================================== + // Simulate with Catalyst + // ======================================== + public AEItemKey getBestItemMatchSimulate(Ingredient ingredient, Object2LongMap inventory, Object2LongMap catalystInventory, long needAmount) { + AEItemKey cached = itemBestMatchCache.get(ingredient); + + if (cached != null && (inventory.getLong(cached) >= needAmount || catalystInventory.getLong(cached) >= needAmount)) { + return cached; + } + + AEItemKey bestMatch = findBestItemMatchSimulate(ingredient, inventory, catalystInventory, needAmount); + if (bestMatch != null) { + itemBestMatchCache.put(ingredient, bestMatch); + } + return bestMatch; + } + + public AEFluidKey getBestFluidMatchSimulate(FluidIngredient ingredient, Object2LongMap inventory, Object2LongMap catalystInventory, long needAmount) { + AEFluidKey cached = fluidBestMatchCache.get(ingredient); + + if (cached != null && (inventory.getLong(cached) >= needAmount || catalystInventory.getLong(cached) >= needAmount)) { + return cached; + } + + AEFluidKey bestMatch = findBestFluidMatchSimulate(ingredient, inventory, catalystInventory, needAmount); + if (bestMatch != null) { + fluidBestMatchCache.put(ingredient, bestMatch); + } + return bestMatch; + } + + private static AEItemKey findBestItemMatchSimulate(Ingredient ingredient, Object2LongMap inventory, Object2LongMap catalystInventory, long needAmount) { + var items = ingredient.getItems(); + for (var item : items) { + if (!item.isEmpty()) { + AEItemKey aeKey = AEItemKey.of(item); + if (inventory.getLong(aeKey) >= needAmount || catalystInventory.getLong(aeKey) >= needAmount) { + return aeKey; + } + } + } + return null; + } + + private static AEFluidKey findBestFluidMatchSimulate(FluidIngredient ingredient, Object2LongMap inventory, Object2LongMap catalystInventory, long needAmount) { + var stacks = ingredient.getStacks(); + for (var stack : stacks) { + if (!stack.isEmpty()) { + AEFluidKey aeKey = AEFluidKey.of(stack.getFluid()); + if (inventory.getLong(aeKey) >= needAmount || catalystInventory.getLong(aeKey) >= needAmount) { + return aeKey; + } + } + } + return null; + } + + public void clearAllCaches() { + itemBestMatchCache.clear(); + fluidBestMatchCache.clear(); + circuitCache = -1; + circuitStack = ItemStack.EMPTY; + } + + @Override + public CompoundTag serializeNBT() { + CompoundTag tag = new CompoundTag(); + + if (!itemBestMatchCache.isEmpty()) { + ListTag itemCacheList = new ListTag(); + for (var entry : Object2ObjectMaps.fastIterable(itemBestMatchCache)) { + CompoundTag entryTag = new CompoundTag(); + entryTag.put("ingredient", serializeIngredient(entry.getKey())); + entryTag.put("aekey", entry.getValue().toTag()); + itemCacheList.add(entryTag); + } + tag.put("itemCache", itemCacheList); + } + + if (!fluidBestMatchCache.isEmpty()) { + ListTag fluidCacheList = new ListTag(); + for (var entry : Object2ObjectMaps.fastIterable(fluidBestMatchCache)) { + CompoundTag entryTag = new CompoundTag(); + entryTag.put("ingredient", serializeFluidIngredient(entry.getKey())); + entryTag.put("aekey", entry.getValue().toTag()); + fluidCacheList.add(entryTag); + } + tag.put("fluidCache", fluidCacheList); + } + + return tag; + } + + @Override + public void deserializeNBT(CompoundTag tag) { + clearAllCaches(); + + if (tag.contains("itemCache", Tag.TAG_LIST)) { + ListTag itemCacheList = tag.getList("itemCache", Tag.TAG_COMPOUND); + for (Tag t : itemCacheList) { + if (!(t instanceof CompoundTag entryTag)) continue; + Ingredient ingredient = deserializeIngredient(entryTag.getCompound("ingredient")); + AEItemKey aeKey = AEItemKey.fromTag(entryTag.getCompound("aekey")); + if (ingredient != null && aeKey != null) { + itemBestMatchCache.put(ingredient, aeKey); + } + } + } + + if (tag.contains("fluidCache", Tag.TAG_LIST)) { + ListTag fluidCacheList = tag.getList("fluidCache", Tag.TAG_COMPOUND); + for (Tag t : fluidCacheList) { + if (!(t instanceof CompoundTag entryTag)) continue; + FluidIngredient ingredient = deserializeFluidIngredient(entryTag.getCompound("ingredient")); + AEFluidKey aeKey = AEFluidKey.fromTag(entryTag.getCompound("aekey")); + if (ingredient != null && aeKey != null) { + fluidBestMatchCache.put(ingredient, aeKey); + } + } + } + } + + private static CompoundTag serializeIngredient(Ingredient ingredient) { + CompoundTag tag = new CompoundTag(); + var json = ingredient.toJson(); + tag.putString("json", json.toString()); + return tag; + } + + private static CompoundTag serializeFluidIngredient(FluidIngredient ingredient) { + CompoundTag tag = new CompoundTag(); + var json = ingredient.toJson(); + tag.putString("json", json.toString()); + return tag; + } + + private static Ingredient deserializeIngredient(CompoundTag tag) { + try { + String jsonStr = tag.getString("json"); + if (jsonStr.isEmpty()) return null; + var json = JsonParser.parseString(jsonStr); + return Ingredient.fromJson(json, false); + } catch (Exception e) { + return null; + } + } + + private static FluidIngredient deserializeFluidIngredient(CompoundTag tag) { + try { + String jsonStr = tag.getString("json"); + if (jsonStr.isEmpty()) return null; + var json = JsonParser.parseString(jsonStr); + return FluidIngredient.fromJson(json); + } catch (Exception e) { + return null; + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/integration/ae2/storage/FastInfinityCellHandler.java b/src/main/java/org/gtlcore/gtlcore/integration/ae2/storage/FastInfinityCellHandler.java new file mode 100644 index 000000000..c93654ade --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/integration/ae2/storage/FastInfinityCellHandler.java @@ -0,0 +1,38 @@ +package org.gtlcore.gtlcore.integration.ae2.storage; + +import org.gtlcore.gtlcore.integration.ae2.FastInfinityCell; +import org.gtlcore.gtlcore.utils.NumberUtils; + +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; + +import appeng.api.storage.cells.ICellHandler; +import appeng.api.storage.cells.ISaveProvider; + +import java.util.List; + +public class FastInfinityCellHandler implements ICellHandler { + + public static final FastInfinityCellHandler INSTANCE = new FastInfinityCellHandler(); + + @Override + public boolean isCell(ItemStack is) { + return is.getItem() instanceof FastInfinityCell; + } + + @Override + public FastInfinityCellInventory getCellInventory(ItemStack is, ISaveProvider container) { + return FastInfinityCellInventory.createInventory(is, container); + } + + public void addCellInformationToTooltip(ItemStack stack, List lines) { + FastInfinityCellInventory handler = getCellInventory(stack, null); + if (handler != null && handler.hasDiskUUID()) { + lines.add(Component.literal("UUID: ").withStyle(ChatFormatting.GRAY) + .append(Component.literal(handler.getDiskUUID().toString()).withStyle(ChatFormatting.AQUA))); + lines.add(Component.literal("Byte: ").withStyle(ChatFormatting.GRAY) + .append(NumberUtils.numberText(handler.getNbtItemCount()).withStyle(ChatFormatting.GREEN))); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/integration/ae2/storage/FastInfinityCellInventory.java b/src/main/java/org/gtlcore/gtlcore/integration/ae2/storage/FastInfinityCellInventory.java new file mode 100644 index 000000000..133a33f38 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/integration/ae2/storage/FastInfinityCellInventory.java @@ -0,0 +1,301 @@ +package org.gtlcore.gtlcore.integration.ae2.storage; + +import org.gtlcore.gtlcore.GTLCore; +import org.gtlcore.gtlcore.integration.ae2.FastInfinityCell; +import org.gtlcore.gtlcore.utils.StorageManager; +import org.gtlcore.gtlcore.utils.datastructure.Int128; + +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.LongArrayTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; + +import appeng.api.config.Actionable; +import appeng.api.networking.security.IActionSource; +import appeng.api.stacks.AEItemKey; +import appeng.api.stacks.AEKey; +import appeng.api.stacks.KeyCounter; +import appeng.api.storage.cells.CellState; +import appeng.api.storage.cells.ISaveProvider; +import appeng.api.storage.cells.StorageCell; +import appeng.core.AELog; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; + +import java.util.Objects; +import java.util.UUID; + +public class FastInfinityCellInventory implements StorageCell { + + private final ISaveProvider container; + private double storedItemCount; + private Object2ObjectOpenHashMap storedMap; + private final ItemStack stack; + private boolean isPersisted = true; + private final KeyCounter lists = new KeyCounter(); + + // Pool + private final Int128 tempInt128 = Int128.ZERO(); + private final Int128 sumInt128 = Int128.ZERO(); + + public FastInfinityCellInventory(ItemStack stack, ISaveProvider saveProvider) { + this.stack = stack; + this.container = saveProvider; + this.storedMap = null; + initData(); + } + + private InfinityCellDataStorage getDiskStorage() { + if (getDiskUUID() != null) + return getStorageInstance().getOrCreateDisk(getDiskUUID(), true); + else + return InfinityCellDataStorage.FAST_EMPTY; + } + + private void initData() { + if (hasDiskUUID()) { + this.storedItemCount = getDiskStorage().totalAmount; + } else { + this.storedItemCount = 0; + getCellItems(); + } + } + + @Override + public CellState getStatus() { + if (this.storedItemCount == 0) return CellState.EMPTY; + return CellState.NOT_EMPTY; + } + + @Override + public double getIdleDrain() { + return Integer.MAX_VALUE; + } + + @Override + public void persist() { + if (this.isPersisted) { + return; + } + + if (storedItemCount == 0) { + if (hasDiskUUID()) { + getStorageInstance().removeDisk(getDiskUUID()); + if (stack.getTag() != null) { + stack.getTag().remove("diskuuid"); + stack.getTag().remove("count"); + } + initData(); + } + return; + } + var keys = new ListTag(); + var amount = new ListTag(); + sumInt128.set(0, 0); // 重置为0 + + for (var it = storedMap.object2ObjectEntrySet().fastIterator(); it.hasNext();) { + var entry = it.next(); + var a = entry.getValue(); + if (a.isPositive()) { + sumInt128.add(a); + keys.add(entry.getKey().toTagGeneric()); + // 使用LongArrayTag存储高低位,格式为[high, low] + amount.add(new LongArrayTag(new long[] { a.getHigh(), a.getLow() })); + } + } + + if (keys.isEmpty()) { + getStorageInstance().updateDisk(getDiskUUID(), new InfinityCellDataStorage(true)); + } else { + getStorageInstance().modifyDisk(getDiskUUID(), keys, amount, sumInt128.doubleValue(), true); + } + + this.storedItemCount = sumInt128.doubleValue(); + stack.getOrCreateTag().putDouble("count", this.storedItemCount); + + this.isPersisted = true; + } + + @Override + public Component getDescription() { + return null; + } + + public static FastInfinityCellInventory createInventory(ItemStack stack, ISaveProvider saveProvider) { + Objects.requireNonNull(stack, "Cannot create cell inventory for null itemstack"); + + if (!(stack.getItem() instanceof FastInfinityCell)) { + return null; + } + + return new FastInfinityCellInventory(stack, saveProvider); + } + + public boolean hasDiskUUID() { + return stack.hasTag() && stack.getOrCreateTag().contains("diskuuid"); + } + + public static boolean hasDiskUUID(ItemStack disk) { + if (disk.getItem() instanceof FastInfinityCell) { + return disk.hasTag() && disk.getOrCreateTag().contains("diskuuid"); + } + return false; + } + + public UUID getDiskUUID() { + if (hasDiskUUID()) + return stack.getOrCreateTag().getUUID("diskuuid"); + else + return null; + } + + private boolean isStorageCell(AEItemKey key) { + var type = getStorageCell(key); + return type != null; + } + + private static FastInfinityCell getStorageCell(AEItemKey itemKey) { + if (itemKey.getItem() instanceof FastInfinityCell fastInfinityCell) { + return fastInfinityCell; + } + + return null; + } + + private static boolean isCellEmpty(FastInfinityCellInventory inv) { + if (inv != null) { + return inv.getAvailableStacks().isEmpty(); + } + return true; + } + + protected Object2ObjectOpenHashMap getCellItems() { + if (this.storedMap == null) { + this.storedMap = new Object2ObjectOpenHashMap<>(); + this.loadCellItems(); + } + return this.storedMap; + } + + @Override + public void getAvailableStacks(KeyCounter out) { + this.getCellItems(); + out.addAll(lists); + } + + private void loadCellItems() { + boolean corruptedTag = false; + + if (!stack.hasTag()) { + return; + } + + var amounts = getDiskStorage().amounts; + var stackKeys = getDiskStorage().stackKeys; + if (amounts.size() != stackKeys.size()) { + AELog.warn("Loading storage cell with mismatched amounts/tags: %d != %d", amounts.size(), stackKeys.size()); + } + + for (int i = 0; i < amounts.size(); i++) { + var amountTag = amounts.get(i); + var key = AEKey.fromTagGeneric(stackKeys.getCompound(i)); + if (key == null || !(amountTag instanceof LongArrayTag longArrayTag)) corruptedTag = true; + else { + var longArray = longArrayTag.getAsLongArray(); + var count = new Int128(longArray[0], longArray[1]); + storedMap.put(key, count); + lists.add(key, count.longValue()); + this.storedItemCount += count.doubleValue(); + } + } + + if (corruptedTag) { + this.saveChanges(0); + } + } + + protected void saveChanges(double incur) { + this.storedItemCount += incur; + + this.isPersisted = false; + if (this.container != null) this.container.saveChanges(); + else this.persist(); + } + + private StorageManager getStorageInstance() { + return GTLCore.STORAGE_INSTANCE; + } + + @Override + public long insert(AEKey what, long amount, Actionable mode, IActionSource source) { + if (amount == 0 || what == null) { + return 0; + } + + if (what instanceof AEItemKey itemKey && this.isStorageCell(itemKey)) { + var meInventory = createInventory(itemKey.toStack(), null); + if (!isCellEmpty(meInventory)) { + return 0; + } + } + + if (!hasDiskUUID()) { + stack.getOrCreateTag().putUUID("diskuuid", UUID.randomUUID()); + getStorageInstance().getOrCreateDisk(getDiskUUID(), true); + loadCellItems(); + } + + if (mode == Actionable.MODULATE) { + tempInt128.set(0, amount); // 复用临时对象 + Int128 newValue = getCellItems().compute(what, (k, v) -> { + if (v == null) { + return new Int128(0, amount); + } else { + return v.add(tempInt128); + } + }); + // 直接设置为Int128转换后的long值,而不是累加 + lists.set(what, newValue.longValue()); + this.saveChanges(amount); + } + + return amount; + } + + @Override + public long extract(AEKey what, long amount, Actionable mode, IActionSource source) { + var currentAmount = getCellItems().get(what); + if (currentAmount == null) { + return 0L; + } else if (currentAmount.isPositive()) { + tempInt128.set(0, amount); // 复用临时对象 + if (currentAmount.compareTo(tempInt128) <= 0) { + if (mode == Actionable.MODULATE) { + this.storedMap.remove(what); + lists.remove(what); + this.saveChanges(-currentAmount.longValue()); + } + return currentAmount.longValue(); + } else { + if (mode == Actionable.MODULATE) { + // 直接修改现有对象以减少对象创建 + currentAmount.subtract(tempInt128); + // 直接设置为Int128转换后的long值 + lists.set(what, currentAmount.longValue()); + this.saveChanges(-amount); + } + return amount; + } + } else { + return 0L; + } + } + + public double getNbtItemCount() { + if (hasDiskUUID()) { + if (stack.getTag() != null) { + return stack.getTag().getDouble("count"); + } + } + return 0; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/integration/ae2/storage/InfinityCellDataStorage.java b/src/main/java/org/gtlcore/gtlcore/integration/ae2/storage/InfinityCellDataStorage.java index 665f3c10c..66a2578e2 100755 --- a/src/main/java/org/gtlcore/gtlcore/integration/ae2/storage/InfinityCellDataStorage.java +++ b/src/main/java/org/gtlcore/gtlcore/integration/ae2/storage/InfinityCellDataStorage.java @@ -2,45 +2,50 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.Tag; public class InfinityCellDataStorage { - public static final InfinityCellDataStorage EMPTY = new InfinityCellDataStorage(); + public static final InfinityCellDataStorage EMPTY = new InfinityCellDataStorage(false); + public static final InfinityCellDataStorage FAST_EMPTY = new InfinityCellDataStorage(true); public ListTag stackKeys; - public long[] stackAmounts; - public long itemCount; + public ListTag amounts; + public double totalAmount; + public boolean isFastCell; - public InfinityCellDataStorage() { + public InfinityCellDataStorage(boolean isFastCell) { stackKeys = new ListTag(); - stackAmounts = new long[0]; - itemCount = 0; + amounts = new ListTag(); + totalAmount = 0; + this.isFastCell = isFastCell; } - public InfinityCellDataStorage(ListTag stackKeys, long[] stackAmounts, long itemCount) { + public InfinityCellDataStorage(ListTag stackKeys, ListTag amounts, double totalAmount, boolean isFastCell) { this.stackKeys = stackKeys; - this.stackAmounts = stackAmounts; - this.itemCount = itemCount; + this.amounts = amounts; + this.totalAmount = totalAmount; + this.isFastCell = isFastCell; } public CompoundTag toNbt() { CompoundTag nbt = new CompoundTag(); + nbt.putBoolean("isFastCell", isFastCell); nbt.put("keys", stackKeys); - nbt.putLongArray("amounts", stackAmounts); - if (itemCount != 0) { - nbt.putLong("count", itemCount); + nbt.put("amounts", amounts); + if (totalAmount != 0) { + nbt.putDouble("totalAmount", totalAmount); } return nbt; } public static InfinityCellDataStorage fromNbt(CompoundTag nbt) { - long itemCount = 0; - ListTag stackKeys = nbt.getList("keys", Tag.TAG_COMPOUND); - long[] stackAmounts = nbt.getLongArray("amounts"); - if (nbt.contains("count")) { - itemCount = nbt.getLong("count"); + double totalAmount = 0; + boolean isFastCell = nbt.getBoolean("isFastCell"); + ListTag stackKeys = nbt.getList("keys", 10); + ListTag amounts = nbt.getList("amounts", isFastCell ? 12 : 8); + if (nbt.contains("totalAmount")) { + totalAmount = nbt.getDouble("totalAmount"); } - return new InfinityCellDataStorage(stackKeys, stackAmounts, itemCount); + return new InfinityCellDataStorage(stackKeys, amounts, totalAmount, isFastCell); } } diff --git a/src/main/java/org/gtlcore/gtlcore/integration/ae2/storage/InfinityCellInventory.java b/src/main/java/org/gtlcore/gtlcore/integration/ae2/storage/InfinityCellInventory.java index 3799f395d..8dcc83a7a 100755 --- a/src/main/java/org/gtlcore/gtlcore/integration/ae2/storage/InfinityCellInventory.java +++ b/src/main/java/org/gtlcore/gtlcore/integration/ae2/storage/InfinityCellInventory.java @@ -2,27 +2,22 @@ import org.gtlcore.gtlcore.GTLCore; import org.gtlcore.gtlcore.integration.ae2.InfinityCell; +import org.gtlcore.gtlcore.utils.NumberUtils; import org.gtlcore.gtlcore.utils.StorageManager; import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.StringTag; import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; import appeng.api.config.Actionable; import appeng.api.networking.security.IActionSource; -import appeng.api.stacks.AEItemKey; -import appeng.api.stacks.AEKey; -import appeng.api.stacks.AEKeyType; -import appeng.api.stacks.KeyCounter; -import appeng.api.storage.cells.CellState; -import appeng.api.storage.cells.ISaveProvider; -import appeng.api.storage.cells.StorageCell; +import appeng.api.stacks.*; +import appeng.api.storage.cells.*; import appeng.core.AELog; -import it.unimi.dsi.fastutil.longs.LongArrayList; -import it.unimi.dsi.fastutil.objects.Object2LongMap; -import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; -import lombok.Getter; +import it.unimi.dsi.fastutil.objects.*; +import java.math.BigInteger; import java.util.Objects; import java.util.UUID; @@ -30,30 +25,30 @@ public class InfinityCellInventory implements StorageCell { private final ISaveProvider container; private final AEKeyType keyType; - @Getter - private long storedItemCount; - private Object2LongMap storedAmounts; + private double storedItemCount; + private Object2ObjectOpenHashMap storedMap; private final ItemStack stack; private boolean isPersisted = true; + private final KeyCounter lists = new KeyCounter(); public InfinityCellInventory(AEKeyType keyType, ItemStack stack, ISaveProvider saveProvider) { this.stack = stack; this.container = saveProvider; this.keyType = keyType; - this.storedAmounts = null; + this.storedMap = null; initData(); } private InfinityCellDataStorage getDiskStorage() { if (getDiskUUID() != null) - return getStorageInstance().getOrCreateDisk(getDiskUUID()); + return getStorageInstance().getOrCreateDisk(getDiskUUID(), false); else return InfinityCellDataStorage.EMPTY; } private void initData() { if (hasDiskUUID()) { - this.storedItemCount = getDiskStorage().itemCount; + this.storedItemCount = getDiskStorage().totalAmount; } else { this.storedItemCount = 0; getCellItems(); @@ -62,13 +57,8 @@ private void initData() { @Override public CellState getStatus() { - if (this.getStoredItemCount() == 0) { - return CellState.EMPTY; - } - if (this.getFreeBytes() > 0) { - return CellState.NOT_EMPTY; - } - return CellState.FULL; + if (this.storedItemCount == 0) return CellState.EMPTY; + return CellState.NOT_EMPTY; } @Override @@ -93,30 +83,28 @@ public void persist() { } return; } - - long itemCount = 0; - - LongArrayList amounts = new LongArrayList(storedAmounts.size()); - ListTag keys = new ListTag(); - - for (Object2LongMap.Entry entry : this.storedAmounts.object2LongEntrySet()) { - long amount = entry.getLongValue(); - - if (amount > 0) { - itemCount += amount; + var keys = new ListTag(); + var amount = new ListTag(); + var count = BigInteger.ZERO; + + for (var it = storedMap.object2ObjectEntrySet().fastIterator(); it.hasNext();) { + var entry = it.next(); + var a = entry.getValue(); + if (a.compareTo(BigInteger.ZERO) > 0) { + count = count.add(a); keys.add(entry.getKey().toTagGeneric()); - amounts.add(amount); + amount.add(StringTag.valueOf(a.toString())); } } if (keys.isEmpty()) { - getStorageInstance().updateDisk(getDiskUUID(), new InfinityCellDataStorage()); + getStorageInstance().updateDisk(getDiskUUID(), new InfinityCellDataStorage(false)); } else { - getStorageInstance().modifyDisk(getDiskUUID(), keys, amounts.toArray(new long[0]), itemCount); + getStorageInstance().modifyDisk(getDiskUUID(), keys, amount, count.doubleValue(), false); } - this.storedItemCount = itemCount; - stack.getOrCreateTag().putLong("count", itemCount); + this.storedItemCount = count.doubleValue(); + stack.getOrCreateTag().putDouble("count", this.storedItemCount); this.isPersisted = true; } @@ -155,7 +143,7 @@ public UUID getDiskUUID() { } private boolean isStorageCell(AEItemKey key) { - InfinityCell type = getStorageCell(key); + var type = getStorageCell(key); return type != null; } @@ -174,20 +162,18 @@ private static boolean isCellEmpty(InfinityCellInventory inv) { return true; } - protected Object2LongMap getCellItems() { - if (this.storedAmounts == null) { - this.storedAmounts = new Object2LongOpenHashMap<>(); + protected Object2ObjectOpenHashMap getCellItems() { + if (this.storedMap == null) { + this.storedMap = new Object2ObjectOpenHashMap<>(); this.loadCellItems(); } - - return this.storedAmounts; + return this.storedMap; } @Override public void getAvailableStacks(KeyCounter out) { - for (Object2LongMap.Entry entry : this.getCellItems().object2LongEntrySet()) { - out.add(entry.getKey(), entry.getLongValue()); - } + this.getCellItems(); + out.addAll(lists); } private void loadCellItems() { @@ -197,49 +183,39 @@ private void loadCellItems() { return; } - long[] amounts = getDiskStorage().stackAmounts; - ListTag tags = getDiskStorage().stackKeys; - if (amounts.length != tags.size()) { - AELog.warn("Loading storage cell with mismatched amounts/tags: %d != %d", - amounts.length, tags.size()); + var amounts = getDiskStorage().amounts; + var stackKeys = getDiskStorage().stackKeys; + if (amounts.size() != stackKeys.size()) { + AELog.warn("Loading storage cell with mismatched amounts/tags: %d != %d", amounts.size(), stackKeys.size()); } - for (int i = 0; i < amounts.length; i++) { - long amount = amounts[i]; - AEKey key = AEKey.fromTagGeneric(tags.getCompound(i)); - - if (amount <= 0 || key == null) { - corruptedTag = true; - } else { - storedAmounts.put(key, amount); + for (int i = 0; i < amounts.size(); i++) { + var amount = amounts.getString(i); + var key = AEKey.fromTagGeneric(stackKeys.getCompound(i)); + if (amount.isEmpty() || key == null) corruptedTag = true; + else { + var count = new BigInteger(amount); + storedMap.put(key, count); + lists.add(key, NumberUtils.getLongValue(count)); + this.storedItemCount += count.doubleValue(); } } if (corruptedTag) { - this.saveChanges(); + this.saveChanges(0); } } - private StorageManager getStorageInstance() { - return GTLCore.STORAGE_INSTANCE; - } - - protected void saveChanges() { - this.storedItemCount = 0; - for (long storedAmount : this.storedAmounts.values()) { - this.storedItemCount += storedAmount; - } + protected void saveChanges(double incur) { + this.storedItemCount += incur; this.isPersisted = false; - if (this.container != null) { - this.container.saveChanges(); - } else { - this.persist(); - } + if (this.container != null) this.container.saveChanges(); + else this.persist(); } - public long getRemainingItemCount() { - return this.getFreeBytes() > 0 ? this.getFreeBytes() : 0; + private StorageManager getStorageInstance() { + return GTLCore.STORAGE_INSTANCE; } @Override @@ -249,7 +225,7 @@ public long insert(AEKey what, long amount, Actionable mode, IActionSource sourc } if (what instanceof AEItemKey itemKey && this.isStorageCell(itemKey)) { - InfinityCellInventory meInventory = createInventory(itemKey.toStack(), null); + var meInventory = createInventory(itemKey.toStack(), null); if (!isCellEmpty(meInventory)) { return 0; } @@ -257,20 +233,15 @@ public long insert(AEKey what, long amount, Actionable mode, IActionSource sourc if (!hasDiskUUID()) { stack.getOrCreateTag().putUUID("diskuuid", UUID.randomUUID()); - getStorageInstance().getOrCreateDisk(getDiskUUID()); + getStorageInstance().getOrCreateDisk(getDiskUUID(), false); loadCellItems(); } - long currentAmount = this.getCellItems().getLong(what); - long remainingItemCount = getRemainingItemCount(); - - if (amount > remainingItemCount) { - amount = remainingItemCount; - } - if (mode == Actionable.MODULATE) { - getCellItems().put(what, currentAmount + amount); - this.saveChanges(); + BigInteger finalAmount = BigInteger.valueOf(amount); + getCellItems().compute(what, (k, v) -> v == null ? finalAmount : v.add(finalAmount)); + lists.add(what, amount); + this.saveChanges(amount); } return amount; @@ -278,36 +249,36 @@ public long insert(AEKey what, long amount, Actionable mode, IActionSource sourc @Override public long extract(AEKey what, long amount, Actionable mode, IActionSource source) { - long currentAmount = getCellItems().getLong(what); - if (currentAmount > 0) { - if (amount >= currentAmount) { + var currentAmount = getCellItems().get(what); + if (currentAmount == null) { + return 0L; + } else if (currentAmount.signum() > 0) { + var extractAmount = BigInteger.valueOf(amount); + if (currentAmount.compareTo(extractAmount) < 1) { if (mode == Actionable.MODULATE) { - getCellItems().remove(what, currentAmount); - this.saveChanges(); + this.storedMap.remove(what); + lists.remove(what); + this.saveChanges(-amount); } - - return currentAmount; + return currentAmount.longValue(); } else { if (mode == Actionable.MODULATE) { - getCellItems().put(what, currentAmount - amount); - this.saveChanges(); + var sub = currentAmount.subtract(extractAmount); + this.storedMap.put(what, sub); + lists.remove(what, amount); + this.saveChanges(-amount); } - return amount; } + } else { + return 0L; } - - return 0; - } - - public long getFreeBytes() { - return Long.MAX_VALUE - this.getStoredItemCount(); } - public long getNbtItemCount() { + public double getNbtItemCount() { if (hasDiskUUID()) { if (stack.getTag() != null) { - return stack.getTag().getLong("count"); + return stack.getTag().getDouble("count"); } } return 0; diff --git a/src/main/java/org/gtlcore/gtlcore/integration/ae2/widget/AEPatternViewExtendSlotWidget.java b/src/main/java/org/gtlcore/gtlcore/integration/ae2/widget/AEPatternViewExtendSlotWidget.java new file mode 100644 index 000000000..6ea634e4a --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/integration/ae2/widget/AEPatternViewExtendSlotWidget.java @@ -0,0 +1,101 @@ +package org.gtlcore.gtlcore.integration.ae2.widget; + +import com.gregtechceu.gtceu.integration.ae2.gui.widget.slot.AEPatternViewSlotWidget; + +import com.lowdragmc.lowdraglib.gui.modular.ModularUIGuiContainer; +import com.lowdragmc.lowdraglib.side.item.IItemTransfer; + +import net.minecraft.client.Minecraft; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; + +import com.mojang.blaze3d.platform.InputConstants; +import org.jetbrains.annotations.Nullable; + +import javax.annotation.Nonnull; + +public class AEPatternViewExtendSlotWidget extends AEPatternViewSlotWidget { + + public AEPatternViewExtendSlotWidget(IItemTransfer inventory, int slotIndex, int xPosition, int yPosition) { + super(inventory, slotIndex, xPosition, yPosition); + } + + @Nullable + private Runnable onMiddleClick; + @Nullable + private Runnable onPatternSlotChanged; + + public AEPatternViewExtendSlotWidget setOnMiddleClick(@Nullable Runnable runnable) { + this.onMiddleClick = runnable; + return this; + } + + public AEPatternViewExtendSlotWidget setOnPatternSlotChanged(@Nullable Runnable runnable) { + this.onPatternSlotChanged = runnable; + return this; + } + + @Override + protected Slot createSlot(IItemTransfer itemHandler, int index) { + return new ExtendWidgetSlotItemTransfer(itemHandler, index, 0, 0); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (slotReference != null && isMouseOverElement(mouseX, mouseY) && gui != null) { + if (button == 2 && onMiddleClick != null) { + writeClientAction(10, writer -> writer.writeBoolean(true)); + onMiddleClick.run(); + return true; + } + + var stack = slotReference.getItem(); + if (canPutItems && stack.isEmpty() || canTakeItems && !stack.isEmpty()) { + ModularUIGuiContainer modularUIGui = gui.getModularUIGui(); + boolean last = modularUIGui.getQuickCrafting(); + InputConstants.Key mouseKey = InputConstants.Type.MOUSE.getOrCreate(button); + HOVER_SLOT = slotReference; + gui.getModularUIGui().superMouseClicked(mouseX, mouseY, button); + HOVER_SLOT = null; + if (last != modularUIGui.getQuickCrafting()) { + modularUIGui.dragSplittingButton = button; + if (button == 0) { + modularUIGui.dragSplittingLimit = 0; + } else if (button == 1) { + modularUIGui.dragSplittingLimit = 1; + } else if (Minecraft.getInstance().options.keyPickItem.matchesMouse(mouseKey.getValue())) { + modularUIGui.dragSplittingLimit = 2; + } + } + return true; + } + } + return false; + } + + @Override + public void handleClientAction(int id, FriendlyByteBuf buffer) { + super.handleClientAction(id, buffer); + if (id == 10) { + if (onMiddleClick != null) { + onMiddleClick.run(); + } + } + } + + protected class ExtendWidgetSlotItemTransfer extends WidgetSlotItemTransfer { + + public ExtendWidgetSlotItemTransfer(IItemTransfer itemHandler, int index, int xPosition, int yPosition) { + super(itemHandler, index, xPosition, yPosition); + } + + @Override + public void set(@Nonnull ItemStack stack) { + super.set(stack); + if (onPatternSlotChanged != null) { + onPatternSlotChanged.run(); + } + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/integration/extendedae/ItemTagPriority.java b/src/main/java/org/gtlcore/gtlcore/integration/extendedae/ItemTagPriority.java new file mode 100644 index 000000000..e375a30df --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/integration/extendedae/ItemTagPriority.java @@ -0,0 +1,70 @@ +package org.gtlcore.gtlcore.integration.extendedae; + +import net.minecraft.core.Holder; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.material.Fluid; +import net.minecraftforge.registries.ForgeRegistries; + +import appeng.api.stacks.AEKey; +import appeng.util.prioritylist.IPartitionList; +import it.unimi.dsi.fastutil.objects.Reference2BooleanMap; +import it.unimi.dsi.fastutil.objects.Reference2BooleanOpenHashMap; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Set; + +public class ItemTagPriority implements IPartitionList { + + private final Set> whiteSet; + private final Set> blackSet; + private final String tagExp; + private final Reference2BooleanMap memory = new Reference2BooleanOpenHashMap<>(); + + public ItemTagPriority(Set> whiteSet, Set> blackSet, String tagExp) { + this.whiteSet = whiteSet; + this.blackSet = blackSet; + this.tagExp = tagExp; + } + + @Override + public boolean isListed(AEKey aeKey) { + Object key = aeKey.getPrimaryKey(); + return this.memory.computeIfAbsent(key, this::eval); + } + + @Override + public boolean isEmpty() { + return tagExp.isEmpty(); + } + + @Override + public Iterable getItems() { + return List.of(); + } + + private boolean eval(@NotNull Object obj) { + Holder refer = null; + if (obj instanceof Item item) { + refer = ForgeRegistries.ITEMS.getHolder(item).orElse(null); + } else if (obj instanceof Fluid) { + return false; + } + + if (refer != null) { + if (this.whiteSet.isEmpty()) { + return false; + } + + boolean pass = refer.tags().anyMatch(whiteSet::contains); + if (pass) { + if (!this.blackSet.isEmpty()) { + return refer.tags().noneMatch(blackSet::contains); + } + return true; + } + } + return false; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/integration/gtmt/InfinityEnergyContainer.java b/src/main/java/org/gtlcore/gtlcore/integration/gtmt/InfinityEnergyContainer.java new file mode 100644 index 000000000..36d4660ad --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/integration/gtmt/InfinityEnergyContainer.java @@ -0,0 +1,62 @@ +package org.gtlcore.gtlcore.integration.gtmt; + +import org.gtlcore.gtlcore.api.capability.IInt128EnergyContainer; + +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.trait.NotifiableEnergyContainer; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; + +import net.minecraft.core.Direction; + +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class InfinityEnergyContainer extends NotifiableEnergyContainer implements IInt128EnergyContainer { + + public InfinityEnergyContainer(MetaMachine machine, long maxCapacity, long maxInputVoltage, long maxInputAmperage, long maxOutputVoltage, long maxOutputAmperage) { + super(machine, maxCapacity, maxInputVoltage, maxInputAmperage, maxOutputVoltage, maxOutputAmperage); + } + + @Override + public List handleRecipeInner(IO io, GTRecipe recipe, List left, @Nullable String slotName, boolean simulate) { + return null; + } + + @Override + public long changeEnergy(long energyToAdd) { + long oldEnergyStored = getEnergyStored(); + long newEnergyStored = (getEnergyCapacity() - oldEnergyStored < energyToAdd) ? getEnergyCapacity() : (oldEnergyStored + energyToAdd); + if (newEnergyStored < 0) newEnergyStored = 0; + final long change = newEnergyStored - oldEnergyStored; + addEnergyPerSec(change); + return change; + } + + @Override + public void checkOutputSubscription() {} + + @Override + public void serverTick() {} + + @Override + public long acceptEnergyFromNetwork(Direction side, long voltage, long amperage) { + return 0; + } + + @Override + public boolean outputsEnergy(Direction side) { + return false; + } + + @Override + public boolean inputsEnergy(Direction side) { + return false; + } + + @Override + public long getEnergyStored() { + return this.getEnergyCapacity(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/integration/gtmt/InfinityLaserContainer.java b/src/main/java/org/gtlcore/gtlcore/integration/gtmt/InfinityLaserContainer.java new file mode 100644 index 000000000..bdc2cf322 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/integration/gtmt/InfinityLaserContainer.java @@ -0,0 +1,62 @@ +package org.gtlcore.gtlcore.integration.gtmt; + +import org.gtlcore.gtlcore.api.capability.IInt128EnergyContainer; + +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.trait.NotifiableLaserContainer; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; + +import net.minecraft.core.Direction; + +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class InfinityLaserContainer extends NotifiableLaserContainer implements IInt128EnergyContainer { + + public InfinityLaserContainer(MetaMachine machine, long maxCapacity, long maxInputVoltage, long maxInputAmperage, long maxOutputVoltage, long maxOutputAmperage) { + super(machine, maxCapacity, maxInputVoltage, maxInputAmperage, maxOutputVoltage, maxOutputAmperage); + } + + @Override + public List handleRecipeInner(IO io, GTRecipe recipe, List left, @Nullable String slotName, boolean simulate) { + return null; + } + + @Override + public long changeEnergy(long energyToAdd) { + long oldEnergyStored = getEnergyStored(); + long newEnergyStored = (getEnergyCapacity() - oldEnergyStored < energyToAdd) ? getEnergyCapacity() : (oldEnergyStored + energyToAdd); + if (newEnergyStored < 0) newEnergyStored = 0; + final long change = newEnergyStored - oldEnergyStored; + addEnergyPerSec(change); + return change; + } + + @Override + public void checkOutputSubscription() {} + + @Override + public void serverTick() {} + + @Override + public long acceptEnergyFromNetwork(Direction side, long voltage, long amperage) { + return 0; + } + + @Override + public boolean outputsEnergy(Direction side) { + return false; + } + + @Override + public boolean inputsEnergy(Direction side) { + return false; + } + + @Override + public long getEnergyStored() { + return this.getEnergyCapacity(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/integration/gtmt/NewGTValues.java b/src/main/java/org/gtlcore/gtlcore/integration/gtmt/NewGTValues.java new file mode 100644 index 000000000..4a70ab085 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/integration/gtmt/NewGTValues.java @@ -0,0 +1,101 @@ +package org.gtlcore.gtlcore.integration.gtmt; + +import static net.minecraft.ChatFormatting.*; + +public class NewGTValues { + + public static final String[] VNF = new String[] { + DARK_GRAY + "ULV", + GRAY + "LV", + AQUA + "MV", + GOLD + "HV", + DARK_PURPLE + "EV", + BLUE + "IV", + LIGHT_PURPLE + "LuV", + RED + "ZPM", + DARK_AQUA + "UV", + DARK_RED + "UHV", + GREEN + "UEV", + DARK_GREEN + "UIV", + YELLOW + "UXV", + BLUE.toString() + BOLD + "OpV", + RED.toString() + BOLD + "MAX", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "1", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "2", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "3", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "4", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "5", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "6", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "7", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "8", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "9", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "10", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "11", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "12", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "13", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "14", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "15", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "16", + }; + + public static final String[] LASER_VNF = new String[] { + BLUE + "IV", + LIGHT_PURPLE + "LuV", + RED + "ZPM", + DARK_AQUA + "UV", + DARK_RED + "UHV", + GREEN + "UEV", + DARK_GREEN + "UIV", + YELLOW + "UXV", + BLUE.toString() + BOLD + "OpV", + RED.toString() + BOLD + "MAX", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "1", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "2", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "3", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "4", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "5", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "6", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "7", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "8", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "9", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "10", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "11", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "12", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "13", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "14", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "15", + RED.toString() + BOLD + "M" + GREEN + BOLD + "A" + BLUE + BOLD + "X" + YELLOW + BOLD + "+" + RED + BOLD + + "16", + }; +} diff --git a/src/main/java/org/gtlcore/gtlcore/integration/jade/GTLJadePlugin.java b/src/main/java/org/gtlcore/gtlcore/integration/jade/GTLJadePlugin.java index ca3775c03..02900c069 100644 --- a/src/main/java/org/gtlcore/gtlcore/integration/jade/GTLJadePlugin.java +++ b/src/main/java/org/gtlcore/gtlcore/integration/jade/GTLJadePlugin.java @@ -1,7 +1,6 @@ package org.gtlcore.gtlcore.integration.jade; -import org.gtlcore.gtlcore.integration.jade.provider.TickTimeProvider; -import org.gtlcore.gtlcore.integration.jade.provider.WirelessOpticalDataHatchProvide; +import org.gtlcore.gtlcore.integration.jade.provider.*; import com.gregtechceu.gtceu.api.block.MetaMachineBlock; import com.gregtechceu.gtceu.api.blockentity.MetaMachineBlockEntity; @@ -22,15 +21,19 @@ public class GTLJadePlugin implements IWailaPlugin { @Override public void register(IWailaCommonRegistration registration) { - registration.registerBlockDataProvider(new WirelessOpticalDataHatchProvide(), - BlockEntity.class); + registration.registerBlockDataProvider(new WirelessOpticalDataHatchProvide(), BlockEntity.class); registration.registerBlockDataProvider(new TickTimeProvider(), MetaMachineBlockEntity.class); + registration.registerBlockDataProvider(new MEPatternBufferProvider(), BlockEntity.class); + registration.registerBlockDataProvider(new MEPatternBufferProxyProvider(), BlockEntity.class); + registration.registerBlockDataProvider(new MEMAIOProvider(), BlockEntity.class); } @Override public void registerClient(IWailaClientRegistration registration) { - registration.registerBlockComponent(new WirelessOpticalDataHatchProvide(), - Block.class); + registration.registerBlockComponent(new WirelessOpticalDataHatchProvide(), Block.class); registration.registerBlockComponent(new TickTimeProvider(), MetaMachineBlock.class); + registration.registerBlockComponent(new MEPatternBufferProvider(), Block.class); + registration.registerBlockComponent(new MEPatternBufferProxyProvider(), Block.class); + registration.registerBlockComponent(new MEMAIOProvider(), Block.class); } } diff --git a/src/main/java/org/gtlcore/gtlcore/integration/jade/provider/MEMAIOProvider.java b/src/main/java/org/gtlcore/gtlcore/integration/jade/provider/MEMAIOProvider.java new file mode 100644 index 000000000..6ad23d3bb --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/integration/jade/provider/MEMAIOProvider.java @@ -0,0 +1,105 @@ +package org.gtlcore.gtlcore.integration.jade.provider; + +import org.gtlcore.gtlcore.common.machine.multiblock.part.ae.MEMolecularAssemblerIOPartMachine; +import org.gtlcore.gtlcore.utils.NumberUtils; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.integration.jade.GTElementHelper; + +import net.minecraft.ChatFormatting; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; + +import appeng.api.stacks.AEFluidKey; +import appeng.api.stacks.AEItemKey; +import appeng.api.stacks.AEKey; +import com.google.common.primitives.Ints; +import snownee.jade.api.BlockAccessor; +import snownee.jade.api.IBlockComponentProvider; +import snownee.jade.api.IServerDataProvider; +import snownee.jade.api.ITooltip; +import snownee.jade.api.config.IPluginConfig; +import snownee.jade.api.fluid.JadeFluidObject; + +import static net.minecraft.network.chat.ComponentUtils.wrapInSquareBrackets; + +public class MEMAIOProvider implements IBlockComponentProvider, IServerDataProvider { + + @Override + public void appendTooltip(ITooltip iTooltip, BlockAccessor blockAccessor, IPluginConfig iPluginConfig) { + if (blockAccessor.getBlockEntity() instanceof IMachineBlockEntity blockEntity) { + if (blockEntity.getMetaMachine() instanceof MEMolecularAssemblerIOPartMachine) { + CompoundTag serverData = blockAccessor.getServerData(); + readBufferContents(iTooltip, serverData); + } + } + } + + @Override + public void appendServerData(CompoundTag compoundTag, BlockAccessor blockAccessor) { + if (blockAccessor.getBlockEntity() instanceof IMachineBlockEntity blockEntity) { + if (blockEntity.getMetaMachine() instanceof MEMolecularAssemblerIOPartMachine ioPartMachine) { + putTag(compoundTag, ioPartMachine); + } + } + } + + public static void readBufferContents(ITooltip iTooltip, CompoundTag serverData) { + // Display pending refunds + ListTag pending = serverData.getList("pending", Tag.TAG_COMPOUND); + if (!pending.isEmpty()) { + iTooltip.add(Component.translatable("gtceu.top.pending_refunds") + .withStyle(ChatFormatting.RED, ChatFormatting.BOLD)); + for (var tag : pending) { + if (!(tag instanceof CompoundTag keyTag) || keyTag.isEmpty()) continue; + var key = AEKey.fromTagGeneric((CompoundTag) keyTag.get("key")); + if (key instanceof AEItemKey itemKey) { + var stack = itemKey.toStack(); + long count = keyTag.getLong("count"); + iTooltip.add(iTooltip.getElementHelper().smallItem(stack)); + Component text = Component.literal(" ") + .append(String.valueOf(count)) + .append(" × ") + .append(wrapInSquareBrackets(stack.getItem().getDescription()).withStyle(ChatFormatting.WHITE)) + .withStyle(ChatFormatting.WHITE); + iTooltip.append(text); + } else if (key instanceof AEFluidKey fluidKey) { + long count = keyTag.getLong("count"); + var fluid = fluidKey.toStack(Ints.saturatedCast(count)); + iTooltip.add(GTElementHelper.smallFluid(JadeFluidObject.of(fluid.getFluid(), count))); + Component text = Component.literal(" ") + .append(count < 1000L ? count + "mB" : NumberUtils.formatLong(count / 1000) + "B") + .append(" ") + .append(wrapInSquareBrackets(fluid.getDisplayName()).withStyle(ChatFormatting.WHITE)) + .withStyle(ChatFormatting.WHITE); + iTooltip.append(text); + } + } + } + } + + public static void putTag(CompoundTag compoundTag, MEMolecularAssemblerIOPartMachine maMachine) { + // Add pending refund data + ListTag listTag = new ListTag(); + for (var it = maMachine.getBuffer().object2LongEntrySet().fastIterator(); it.hasNext();) { + var entry = it.next(); + var count = entry.getLongValue(); + if (count > 0) { + CompoundTag itemTag = new CompoundTag(); + itemTag.put("key", entry.getKey().toTagGeneric()); + itemTag.putLong("count", count); + listTag.add(itemTag); + } + } + compoundTag.put("pending", listTag); + } + + @Override + public ResourceLocation getUid() { + return GTCEu.id("me_molecular_assembler_io"); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/integration/jade/provider/MEPatternBufferProvider.java b/src/main/java/org/gtlcore/gtlcore/integration/jade/provider/MEPatternBufferProvider.java new file mode 100644 index 000000000..51b233507 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/integration/jade/provider/MEPatternBufferProvider.java @@ -0,0 +1,179 @@ +package org.gtlcore.gtlcore.integration.jade.provider; + +import org.gtlcore.gtlcore.common.machine.multiblock.part.ae.MEPatternBufferPartMachine; +import org.gtlcore.gtlcore.integration.ae2.AEUtils; +import org.gtlcore.gtlcore.utils.NumberUtils; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.integration.jade.GTElementHelper; + +import net.minecraft.ChatFormatting; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.material.Fluid; +import net.minecraftforge.registries.ForgeRegistries; + +import appeng.api.stacks.AEFluidKey; +import appeng.api.stacks.AEItemKey; +import appeng.api.stacks.AEKey; +import com.google.common.primitives.Ints; +import snownee.jade.api.BlockAccessor; +import snownee.jade.api.IBlockComponentProvider; +import snownee.jade.api.IServerDataProvider; +import snownee.jade.api.ITooltip; +import snownee.jade.api.config.IPluginConfig; +import snownee.jade.api.fluid.JadeFluidObject; + +import static net.minecraft.network.chat.ComponentUtils.wrapInSquareBrackets; + +public class MEPatternBufferProvider implements IBlockComponentProvider, IServerDataProvider { + + @Override + public void appendTooltip(ITooltip iTooltip, BlockAccessor blockAccessor, IPluginConfig iPluginConfig) { + if (blockAccessor.getBlockEntity() instanceof IMachineBlockEntity blockEntity) { + if (blockEntity.getMetaMachine() instanceof MEPatternBufferPartMachine) { + CompoundTag serverData = blockAccessor.getServerData(); + readBufferContents(iTooltip, serverData); + } + } + } + + @Override + public void appendServerData(CompoundTag compoundTag, BlockAccessor blockAccessor) { + if (blockAccessor.getBlockEntity() instanceof IMachineBlockEntity blockEntity) { + if (blockEntity.getMetaMachine() instanceof MEPatternBufferPartMachine buffer) { + putTag(compoundTag, buffer); + } + } + } + + public static void readBufferContents(ITooltip iTooltip, CompoundTag serverData) { + iTooltip.add(Component.translatable("gtceu.top.proxies_bound", serverData.getInt("proxies")) + .withStyle(ChatFormatting.LIGHT_PURPLE)); + + ListTag itemTags = serverData.getList("items", Tag.TAG_COMPOUND); + ListTag fluidTags = serverData.getList("fluids", Tag.TAG_COMPOUND); + + for (Tag t : itemTags) { + if (!(t instanceof CompoundTag itemTag)) continue; + Item item = ForgeRegistries.ITEMS.getValue(new ResourceLocation(itemTag.getString("item"))); + var count = itemTag.getLong("real"); + if (item != null && count > 0) { + var stack = new ItemStack(item); + iTooltip.add(iTooltip.getElementHelper().smallItem(new ItemStack(item))); + Component text = Component.literal(" ") + .append(Component.literal(String.valueOf(count)).withStyle(ChatFormatting.DARK_PURPLE)) + .append(Component.literal(" × ").withStyle(ChatFormatting.WHITE)) + .append(stack.getHoverName().copy().withStyle(ChatFormatting.GOLD)); + iTooltip.append(text); + } + } + for (Tag t : fluidTags) { + if (!(t instanceof CompoundTag fluidTag)) continue; + Fluid fluid = ForgeRegistries.FLUIDS.getValue(new ResourceLocation(fluidTag.getString("fluid"))); + var amount = fluidTag.getLong("real"); + if (fluid != null && amount > 0) { + iTooltip.add(GTElementHelper.smallFluid(JadeFluidObject.of(fluid))); + Component text = Component.literal(" ") + .append(amount < 1000L ? amount + "mB" : NumberUtils.formatLong(amount / 1000) + "B") + .withStyle(ChatFormatting.DARK_PURPLE) + .append(Component.literal(" ").withStyle(ChatFormatting.WHITE)) + .append(fluid.getFluidType().getDescription().copy().withStyle(ChatFormatting.DARK_AQUA)); + iTooltip.append(text); + } + } + + // Display pending refunds + ListTag pending = serverData.getList("pending", Tag.TAG_COMPOUND); + if (!pending.isEmpty()) { + iTooltip.add(Component.translatable("gtceu.top.pending_refunds") + .withStyle(ChatFormatting.RED, ChatFormatting.BOLD)); + for (var tag : pending) { + if (!(tag instanceof CompoundTag keyTag) || keyTag.isEmpty()) continue; + var key = AEKey.fromTagGeneric((CompoundTag) keyTag.get("key")); + if (key instanceof AEItemKey itemKey) { + var stack = itemKey.toStack(); + long count = keyTag.getLong("count"); + iTooltip.add(iTooltip.getElementHelper().smallItem(stack)); + Component text = Component.literal(" ") + .append(String.valueOf(count)) + .append(" × ") + .append(wrapInSquareBrackets(stack.getItem().getDescription()).withStyle(ChatFormatting.WHITE)) + .withStyle(ChatFormatting.WHITE); + iTooltip.append(text); + } else if (key instanceof AEFluidKey fluidKey) { + long count = keyTag.getLong("count"); + var fluid = fluidKey.toStack(Ints.saturatedCast(count)); + iTooltip.add(GTElementHelper.smallFluid(JadeFluidObject.of(fluid.getFluid(), count))); + Component text = Component.literal(" ") + .append(count < 1000L ? count + "mB" : NumberUtils.formatLong(count / 1000) + "B") + .append(" ") + .append(wrapInSquareBrackets(fluid.getDisplayName()).withStyle(ChatFormatting.WHITE)) + .withStyle(ChatFormatting.WHITE); + iTooltip.append(text); + } + } + } + } + + public static void putTag(CompoundTag compoundTag, MEPatternBufferPartMachine buffer) { + compoundTag.putInt("proxies", buffer.getProxies().size()); + + var merged = AEUtils.mergeInternalSlot(buffer.getInternalInventory()); + var items = merged.getLeft(); + var fluids = merged.getRight(); + + ListTag itemTags = new ListTag(); + for (var it = items.object2LongEntrySet().fastIterator(); it.hasNext();) { + var entry = it.next(); + Item item = entry.getKey(); + ResourceLocation key = ForgeRegistries.ITEMS.getKey(item); + if (key != null) { + CompoundTag itemTag = new CompoundTag(); + itemTag.putString("item", key.toString()); + itemTag.putLong("real", entry.getLongValue()); + itemTags.add(itemTag); + } + } + compoundTag.put("items", itemTags); + + ListTag fluidTags = new ListTag(); + for (var it = fluids.object2LongEntrySet().fastIterator(); it.hasNext();) { + var entry = it.next(); + Fluid fluid = entry.getKey(); + ResourceLocation key = ForgeRegistries.FLUIDS.getKey(fluid); + if (key != null) { + CompoundTag fluidTag = new CompoundTag(); + fluidTag.putString("fluid", key.toString()); + fluidTag.putLong("real", entry.getLongValue()); + fluidTags.add(fluidTag); + } + } + compoundTag.put("fluids", fluidTags); + + // Add pending refund data + ListTag listTag = new ListTag(); + for (var it = buffer.getBuffer().object2LongEntrySet().fastIterator(); it.hasNext();) { + var entry = it.next(); + var count = entry.getLongValue(); + if (count > 0) { + CompoundTag itemTag = new CompoundTag(); + itemTag.put("key", entry.getKey().toTagGeneric()); + itemTag.putLong("count", count); + listTag.add(itemTag); + } + } + compoundTag.put("pending", listTag); + } + + @Override + public ResourceLocation getUid() { + return GTCEu.id("me_pattern_buffer"); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/integration/jade/provider/MEPatternBufferProxyProvider.java b/src/main/java/org/gtlcore/gtlcore/integration/jade/provider/MEPatternBufferProxyProvider.java new file mode 100644 index 000000000..55388df4f --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/integration/jade/provider/MEPatternBufferProxyProvider.java @@ -0,0 +1,79 @@ +package org.gtlcore.gtlcore.integration.jade.provider; + +import org.gtlcore.gtlcore.common.machine.multiblock.part.ae.MEPatternBufferProxyPartMachine; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.utils.GradientUtil; + +import net.minecraft.ChatFormatting; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextColor; +import net.minecraft.resources.ResourceLocation; + +import snownee.jade.api.BlockAccessor; +import snownee.jade.api.IBlockComponentProvider; +import snownee.jade.api.IServerDataProvider; +import snownee.jade.api.ITooltip; +import snownee.jade.api.config.IPluginConfig; + +public class MEPatternBufferProxyProvider implements IBlockComponentProvider, IServerDataProvider { + + @Override + public void appendTooltip(ITooltip iTooltip, BlockAccessor blockAccessor, IPluginConfig iPluginConfig) { + if (blockAccessor.getBlockEntity() instanceof IMachineBlockEntity blockEntity) { + if (blockEntity.getMetaMachine() instanceof MEPatternBufferProxyPartMachine) { + CompoundTag serverData = blockAccessor.getServerData(); + + if (!serverData.getBoolean("formed")) return; + if (!serverData.getBoolean("bound")) { + iTooltip.add(Component.translatable("gtceu.top.buffer_not_bound").withStyle(ChatFormatting.RED)); + return; + } + + int[] pos = serverData.getIntArray("pos"); + iTooltip.add(Component.translatable("gtceu.top.buffer_bound_pos", pos[0], pos[1], pos[2]) + .withStyle(style -> style.withColor(rainbowColor(1.25f)))); + + // Show buffer contents + MEPatternBufferProvider.readBufferContents(iTooltip, serverData); + } + } + } + + @Override + public void appendServerData(CompoundTag compoundTag, BlockAccessor blockAccessor) { + if (blockAccessor.getBlockEntity() instanceof IMachineBlockEntity blockEntity) { + if (blockEntity.getMetaMachine() instanceof MEPatternBufferProxyPartMachine proxy) { + if (!proxy.isFormed()) { + compoundTag.putBoolean("formed", false); + return; + } + compoundTag.putBoolean("formed", true); + + var buffer = proxy.getBuffer(); + if (buffer == null) { + compoundTag.putBoolean("bound", false); + return; + } + compoundTag.putBoolean("bound", true); + + var pos = buffer.getPos(); + compoundTag.putIntArray("pos", new int[] { pos.getX(), pos.getY(), pos.getZ() }); + + MEPatternBufferProvider.putTag(compoundTag, buffer); + } + } + } + + @Override + public ResourceLocation getUid() { + return GTCEu.id("me_pattern_buffer_proxy"); + } + + static TextColor rainbowColor(float speed) { + return TextColor.fromRgb(GradientUtil.toRGB((GTValues.CLIENT_TIME & ((1 << 20) - 1)) * speed, 95f, 60f)); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/integration/jade/provider/TickTimeProvider.java b/src/main/java/org/gtlcore/gtlcore/integration/jade/provider/TickTimeProvider.java index 935e9bd12..6d9c6f79a 100644 --- a/src/main/java/org/gtlcore/gtlcore/integration/jade/provider/TickTimeProvider.java +++ b/src/main/java/org/gtlcore/gtlcore/integration/jade/provider/TickTimeProvider.java @@ -19,6 +19,11 @@ import snownee.jade.api.ITooltip; import snownee.jade.api.config.IPluginConfig; +/** + * 代码参考自gto + * @line ... + */ + public class TickTimeProvider extends CapabilityBlockProvider { public TickTimeProvider() { diff --git a/src/main/java/org/gtlcore/gtlcore/integration/lowdragmc/misc/MutableItemTransferList.java b/src/main/java/org/gtlcore/gtlcore/integration/lowdragmc/misc/MutableItemTransferList.java new file mode 100644 index 000000000..7c6e23b3d --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/integration/lowdragmc/misc/MutableItemTransferList.java @@ -0,0 +1,395 @@ +package org.gtlcore.gtlcore.integration.lowdragmc.misc; + +import com.lowdragmc.lowdraglib.LDLib; +import com.lowdragmc.lowdraglib.misc.ItemStackTransfer; +import com.lowdragmc.lowdraglib.side.item.IItemTransfer; +import com.lowdragmc.lowdraglib.syncdata.IContentChangeAware; +import com.lowdragmc.lowdraglib.syncdata.ITagSerializable; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.world.item.ItemStack; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Set; +import java.util.function.Predicate; + +/** + * 可变的 ItemTransferList,但支持动态添加/删除IItemTransfer,提供统一的物品操作接口 + */ +public class MutableItemTransferList implements IItemTransfer, ITagSerializable, IContentChangeAware { + + @Getter + @Setter + private Runnable onContentsChanged = () -> {}; + private final ObjectArrayList<@NotNull IItemTransfer> transfers = new ObjectArrayList<>(); + private volatile int totalSlots = 0; + private volatile boolean slotsCacheDirty = true; + @Getter + protected Predicate filter = item -> true; + + public MutableItemTransferList() {} + + public MutableItemTransferList(int initialCapacity) { + transfers.ensureCapacity(initialCapacity); + } + + public MutableItemTransferList(Set initialTransfers) { + if (initialTransfers != null && !initialTransfers.isEmpty()) { + transfers.addAll(initialTransfers); + invalidateSlotsCache(); + } + } + + // ==================== IItemTransfer Change ==================== + + /** + * 添加一个IItemTransfer + * + * @param transfer 要添加的传输对象 + * @return 是否成功添加 + */ + public boolean addTransfer(@Nullable IItemTransfer transfer) { + if (transfer == null) return false; + boolean added = transfers.add(transfer); + if (added) { + invalidateSlotsCache(); + } + return added; + } + + /** + * 添加多个IItemTransfer + * + * @param transfers 要添加的传输对象列表 + * @return 是否成功添加 + */ + public boolean addTransfers(@Nullable Set transfers) { + if (transfers == null) return false; + boolean added = this.transfers.addAll(transfers); + if (added) { + invalidateSlotsCache(); + } + return added; + } + + /** + * 在指定位置插入IItemTransfer + * + * @param index 插入位置 + * @param transfer 要插入的传输对象 + * @throws IndexOutOfBoundsException 如果索引越界 + */ + public void addTransfer(int index, @NotNull IItemTransfer transfer) { + transfers.add(index, transfer); + invalidateSlotsCache(); + } + + /** + * 移除指定的IItemTransfer + * + * @param transfer 要移除的传输对象 + * @return 是否成功移除 + */ + public boolean removeTransfer(@Nullable IItemTransfer transfer) { + boolean removed = transfers.remove(transfer); + if (removed) { + invalidateSlotsCache(); + } + return removed; + } + + /** + * 移除指定位置的IItemTransfer + * + * @param index 要移除的位置 + * @return 被移除的传输对象 + * @throws IndexOutOfBoundsException 如果索引越界 + */ + public IItemTransfer removeTransfer(int index) { + IItemTransfer removed = transfers.remove(index); + invalidateSlotsCache(); + return removed; + } + + /** + * 移除所有满足条件的IItemTransfer + * + * @param filter 过滤条件 + * @return 是否有移除操作 + */ + public boolean removeTransfersIf(@NotNull Predicate filter) { + boolean removed = transfers.removeIf(filter); + if (removed) { + invalidateSlotsCache(); + } + return removed; + } + + /** + * 清空所有IItemTransfer + */ + public void clear() { + if (!transfers.isEmpty()) { + transfers.clear(); + totalSlots = 0; + slotsCacheDirty = false; + } + } + + /** + * 获取传输对象的数量 + * + * @return 传输对象数量 + */ + public int getTransferCount() { + return transfers.size(); + } + + /** + * 检查是否为空 + * + * @return 是否为空 + */ + public boolean isEmpty() { + return transfers.isEmpty(); + } + + // ==================== IItemTransfer Interface ==================== + + @Override + public int getSlots() { + if (slotsCacheDirty) { + updateSlotsCache(); + } + return totalSlots; + } + + @Override + public @NotNull ItemStack getStackInSlot(int slot) { + if (transfers.isEmpty()) { + return ItemStack.EMPTY; + } + + TransferSlotPair pair = findTransferForSlot(slot); + return pair != null ? pair.transfer.getStackInSlot(pair.localSlot) : ItemStack.EMPTY; + } + + @Override + public void setStackInSlot(int slot, @NotNull ItemStack stack) { + if (transfers.isEmpty()) { + return; + } + + TransferSlotPair pair = findTransferForSlot(slot); + if (pair != null) { + pair.transfer.setStackInSlot(pair.localSlot, stack); + } + } + + @Override + public @NotNull ItemStack insertItem(int slot, @NotNull ItemStack stack, boolean simulate, boolean notifyChanges) { + if (transfers.isEmpty() || stack.isEmpty() || !filter.test(stack)) { + return stack; + } + + TransferSlotPair pair = findTransferForSlot(slot); + return pair != null ? + pair.transfer.insertItem(pair.localSlot, stack, simulate, notifyChanges) : + stack; + } + + @Override + public @NotNull ItemStack extractItem(int slot, int amount, boolean simulate, boolean notifyChanges) { + if (transfers.isEmpty() || amount <= 0) { + return ItemStack.EMPTY; + } + + TransferSlotPair pair = findTransferForSlot(slot); + return pair != null ? + pair.transfer.extractItem(pair.localSlot, amount, simulate, notifyChanges) : + ItemStack.EMPTY; + } + + @Override + public int getSlotLimit(int slot) { + if (transfers.isEmpty()) { + return 0; + } + + TransferSlotPair pair = findTransferForSlot(slot); + return pair != null ? pair.transfer.getSlotLimit(pair.localSlot) : 0; + } + + @Override + public boolean isItemValid(int slot, @NotNull ItemStack stack) { + if (transfers.isEmpty() || !filter.test(stack)) { + return false; + } + + TransferSlotPair pair = findTransferForSlot(slot); + return pair != null && pair.transfer.isItemValid(pair.localSlot, stack); + } + + @Override + public void onContentsChanged() { + for (IItemTransfer transfer : transfers) { + transfer.onContentsChanged(); + } + } + + @SuppressWarnings("all") + @NotNull + @Override + public Object createSnapshot() { + return transfers.stream().map(IItemTransfer::createSnapshot).toArray(Object[]::new); + } + + @SuppressWarnings("all") + @Override + public void restoreFromSnapshot(Object snapshot) { + if (snapshot instanceof Object[] array && array.length == transfers.size()) { + for (int i = 0; i < array.length; i++) { + IItemTransfer transfer = transfers.get(i); + if (array[i] != null) { + transfer.restoreFromSnapshot(array[i]); + } + } + } + } + + // ==================== Filter ==================== + + /** + * 设置物品过滤器 + * + * @param filter 过滤器函数 + */ + public void setFilter(@NotNull Predicate filter) { + this.filter = filter; + } + + // ==================== Utils ==================== + + private void updateSlotsCache() { + int slots = 0; + for (IItemTransfer transfer : transfers) { + slots += transfer.getSlots(); + } + totalSlots = slots; + slotsCacheDirty = false; + } + + private void invalidateSlotsCache() { + slotsCacheDirty = true; + } + + /** + * 查找指定槽位对应的传输对象和本地槽位 + * + * @param globalSlot 全局槽位索引 + * @return 传输对象和本地槽位的配对,如果未找到返回null + */ + @Nullable + private TransferSlotPair findTransferForSlot(int globalSlot) { + int index = 0; + for (IItemTransfer transfer : transfers) { + if (globalSlot - index < transfer.getSlots()) { + return new TransferSlotPair(transfer, globalSlot - index); + } + index += transfer.getSlots(); + } + return null; + } + + protected record TransferSlotPair(IItemTransfer transfer, int localSlot) {} + + // ==================== Factory ==================== + + /** + * 创建空的ItemTransferList + * + * @return 空的列表实例 + */ + public static MutableItemTransferList empty() { + return new MutableItemTransferList(); + } + + /** + * 从单个传输对象创建列表 + * + * @param transfer 传输对象 + * @return 包含单个传输对象的列表 + */ + public static MutableItemTransferList of(@NotNull IItemTransfer transfer) { + MutableItemTransferList list = new MutableItemTransferList(1); + list.addTransfer(transfer); + return list; + } + + /** + * 从多个传输对象创建列表 + * + * @param transfers 传输对象数组 + * @return 包含所有传输对象的列表 + */ + public static MutableItemTransferList of(@NotNull IItemTransfer... transfers) { + MutableItemTransferList list = new MutableItemTransferList(transfers.length); + for (IItemTransfer transfer : transfers) { + list.addTransfer(transfer); + } + return list; + } + + // ==================== ITagSerializable ==================== + + @Override + public CompoundTag serializeNBT() { + var tag = new CompoundTag(); + var list = new ListTag(); + for (var transfer : transfers) { + if (transfer instanceof ItemStackTransfer serializable) { + list.add(serializable.serializeNBT()); + } else { + LDLib.LOGGER.warn("[ItemTransferList] internal container doesn't support serialization"); + } + } + tag.put("slots", list); + return tag; + } + + @Override + public void deserializeNBT(CompoundTag nbt) { + var list = nbt.getList("slots", Tag.TAG_COMPOUND); + transfers.clear(); + for (Tag tag : list) { + if (tag instanceof CompoundTag compoundTag) { + var itemStackTransfer = new ItemStackTransfer(); + itemStackTransfer.deserializeNBT(compoundTag); + this.addTransfer(itemStackTransfer); + } else { + LDLib.LOGGER.warn("[ItemTransferList] internal container doesn't support serialization"); + } + } + } + + // ==================== Object ==================== + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof MutableItemTransferList other)) return false; + return transfers.equals(other.transfers); + } + + @Override + public int hashCode() { + return transfers.hashCode(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/CreativeCellInventoryMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/CreativeCellInventoryMixin.java new file mode 100644 index 000000000..21ea5182f --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/CreativeCellInventoryMixin.java @@ -0,0 +1,18 @@ +package org.gtlcore.gtlcore.mixin.ae2; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +@Mixin(targets = "appeng.me.cells.CreativeCellInventory") +public class CreativeCellInventoryMixin { + + @ModifyArg(method = "getAvailableStacks", + at = @At(value = "INVOKE", + target = "Lappeng/api/stacks/KeyCounter;add(Lappeng/api/stacks/AEKey;J)V"), + index = 1, + remap = false) + public long getAvailableStacks(long amount) { + return Long.MAX_VALUE; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/CursedInternalSlotMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/CursedInternalSlotMixin.java index 0a70dfe4a..40629ec04 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/CursedInternalSlotMixin.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/CursedInternalSlotMixin.java @@ -1,6 +1,7 @@ package org.gtlcore.gtlcore.mixin.ae2; import org.gtlcore.gtlcore.GTLCore; +import org.gtlcore.gtlcore.integration.ae2.storage.FastInfinityCellInventory; import org.gtlcore.gtlcore.integration.ae2.storage.InfinityCellDataStorage; import org.gtlcore.gtlcore.integration.ae2.storage.InfinityCellInventory; @@ -36,11 +37,22 @@ public void doClick(int slotIndex, int button, ClickType actionType, Player play Slot i = slots.get(slotIndex); if (InfinityCellInventory.hasDiskUUID(i.getItem())) { InfinityCellDataStorage storage = GTLCore.STORAGE_INSTANCE - .getOrCreateDisk(i.getItem().getOrCreateTag().getUUID("diskuuid")); + .getOrCreateDisk(i.getItem().getOrCreateTag().getUUID("diskuuid"), false); ItemStack newStack = new ItemStack(i.getItem().getItem()); UUID id = UUID.randomUUID(); newStack.getOrCreateTag().putUUID("diskuuid", id); - newStack.getOrCreateTag().putLong("count", storage.itemCount); + newStack.getOrCreateTag().putLong("count", Double.doubleToLongBits(storage.totalAmount)); + GTLCore.STORAGE_INSTANCE.updateDisk(id, storage); + newStack.setCount(newStack.getMaxStackSize()); + setCarried(newStack); + ci.cancel(); + } else if (FastInfinityCellInventory.hasDiskUUID(i.getItem())) { + InfinityCellDataStorage storage = GTLCore.STORAGE_INSTANCE + .getOrCreateDisk(i.getItem().getOrCreateTag().getUUID("diskuuid"), true); + ItemStack newStack = new ItemStack(i.getItem().getItem()); + UUID id = UUID.randomUUID(); + newStack.getOrCreateTag().putUUID("diskuuid", id); + newStack.getOrCreateTag().putLong("count", Double.doubleToLongBits(storage.totalAmount)); GTLCore.STORAGE_INSTANCE.updateDisk(id, storage); newStack.setCount(newStack.getMaxStackSize()); setCarried(newStack); diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/IOPortBlockEntityMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/IOPortBlockEntityMixin.java new file mode 100644 index 000000000..c4d07f42a --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/IOPortBlockEntityMixin.java @@ -0,0 +1,94 @@ +package org.gtlcore.gtlcore.mixin.ae2; + +import appeng.api.config.Actionable; +import appeng.api.config.OperationMode; +import appeng.api.config.Settings; +import appeng.api.networking.IGrid; +import appeng.api.networking.security.IActionSource; +import appeng.api.stacks.KeyCounter; +import appeng.api.storage.MEStorage; +import appeng.api.storage.StorageHelper; +import appeng.api.storage.cells.StorageCell; +import appeng.blockentity.storage.IOPortBlockEntity; +import appeng.util.ConfigManager; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(IOPortBlockEntity.class) +public abstract class IOPortBlockEntityMixin { + + @Shadow(remap = false) + @Final + private ConfigManager manager; + + @Shadow(remap = false) + @Final + private IActionSource mySrc; + + private static final int MAX_TRANSFER_LOOPS = 8192; + + /** + * @author Dragons + * @reason 防止单tick循环过多卡死 + */ + @Overwrite(remap = false) + private long transferContents(IGrid grid, StorageCell cellInv, long itemsToMove) { + var networkInv = grid.getStorageService().getInventory(); + + KeyCounter srcList; + MEStorage src, destination; + if (this.manager.getSetting(Settings.OPERATION_MODE) == OperationMode.EMPTY) { + src = cellInv; + srcList = cellInv.getAvailableStacks(); + destination = networkInv; + } else { + src = networkInv; + srcList = grid.getStorageService().getCachedInventory(); + destination = cellInv; + } + + var energy = grid.getEnergyService(); + boolean didStuff; + int loopBudget = MAX_TRANSFER_LOOPS; + + do { + if (loopBudget-- <= 0) { + return itemsToMove; + } + + didStuff = false; + + for (var srcEntry : srcList) { + var totalStackSize = srcEntry.getLongValue(); + if (totalStackSize > 0) { + var what = srcEntry.getKey(); + var possible = destination.insert(what, totalStackSize, Actionable.SIMULATE, this.mySrc); + + if (possible > 0) { + possible = Math.min(possible, itemsToMove * what.getAmountPerOperation()); + + possible = src.extract(what, possible, Actionable.MODULATE, this.mySrc); + if (possible > 0) { + var inserted = StorageHelper.poweredInsert(energy, destination, what, possible, this.mySrc); + + if (inserted < possible) { + src.insert(what, possible - inserted, Actionable.MODULATE, this.mySrc); + } + + if (inserted > 0) { + itemsToMove -= Math.max(1, inserted / what.getAmountPerOperation()); + didStuff = true; + } + + break; + } + } + } + } + } while (itemsToMove > 0 && didStuff); + + return itemsToMove; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/crafting/CraftingCalculationMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/crafting/CraftingCalculationMixin.java new file mode 100644 index 000000000..b111e7614 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/crafting/CraftingCalculationMixin.java @@ -0,0 +1,100 @@ +package org.gtlcore.gtlcore.mixin.ae2.crafting; + +import org.gtlcore.gtlcore.integration.ae2.AEUtils; +import org.gtlcore.gtlcore.integration.ae2.crafting.ICraftingCalculation; +import org.gtlcore.gtlcore.integration.ae2.crafting.ICraftingTreeNode; + +import appeng.api.networking.crafting.ICraftingPlan; +import appeng.api.stacks.KeyCounter; +import appeng.core.AELog; +import appeng.crafting.CraftBranchFailure; +import appeng.crafting.CraftingCalculation; +import appeng.crafting.CraftingTreeNode; +import appeng.crafting.inv.CraftingSimulationState; +import org.spongepowered.asm.mixin.*; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.concurrent.atomic.AtomicBoolean; + +@Mixin(CraftingCalculation.class) +public abstract class CraftingCalculationMixin implements ICraftingCalculation { + + @Unique + private final AtomicBoolean gTLCore$done = new AtomicBoolean(false); + + @Shadow(remap = false) + private void logCraftingJob(ICraftingPlan plan) { + throw new AssertionError(); + } + + @Shadow(remap = false) + private ICraftingPlan computePlan() throws InterruptedException { + throw new AssertionError(); + } + + /** + * @author Dragons + * @reason 优化性能 + */ + @Overwrite(remap = false) + public ICraftingPlan run() { + try { + var plan = computePlan(); + this.logCraftingJob(plan); + return plan; + } catch (Exception ex) { + AELog.info(ex, "Exception during async crafting calculation."); + throw new RuntimeException(ex); + } finally { + this.finish(); + } + } + + /** + * @author Dragons + * @reason 优化性能 + */ + @Overwrite(remap = false) + private void finish() { + gTLCore$done.set(true); + } + + /** + * @author Dragons + * @reason 优化性能 + */ + @Overwrite(remap = false) + public boolean simulateFor(int micros) { + return !this.gTLCore$done.get(); + } + + /** + * @author Dragons + * @reason 优化性能 + */ + @Overwrite(remap = false) + public void handlePausing() throws InterruptedException { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + } + + @Redirect( + method = "runCraftAttempt", + at = @At( + value = "INVOKE", + target = "Lappeng/crafting/CraftingTreeNode;request(Lappeng/crafting/inv/CraftingSimulationState;JLappeng/api/stacks/KeyCounter;)V"), + remap = false) + private void redirectTreeRequest( + CraftingTreeNode tree, + CraftingSimulationState craftingInventory, + long amount, + KeyCounter containerItems) throws CraftBranchFailure, InterruptedException { + switch (AEUtils.CALCULATION_MODE) { + case ULTRA_FAST -> ((ICraftingTreeNode) tree).ultraFastRequest(craftingInventory, amount, containerItems); + case FAST -> ((ICraftingTreeNode) tree).fastRequest(craftingInventory, amount, containerItems); + case LEGACY -> ((ICraftingTreeNode) tree).legacyRequest(craftingInventory, amount, containerItems); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/crafting/CraftingCpuHelperMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/crafting/CraftingCpuHelperMixin.java new file mode 100644 index 000000000..0d8a6f7b4 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/crafting/CraftingCpuHelperMixin.java @@ -0,0 +1,37 @@ +package org.gtlcore.gtlcore.mixin.ae2.crafting; + +import net.minecraft.world.level.Level; + +import appeng.api.crafting.IPatternDetails; +import appeng.api.stacks.*; +import appeng.crafting.execution.CraftingCpuHelper; +import appeng.crafting.execution.InputTemplate; +import appeng.crafting.inv.ICraftingInventory; +import com.google.common.collect.Iterables; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.spongepowered.asm.mixin.*; + +import java.util.List; + +@Mixin(CraftingCpuHelper.class) +public class CraftingCpuHelperMixin { + + /** + * @author . + * @reason 提升性能 + */ + @Overwrite(remap = false) + public static Iterable getValidItemTemplates(ICraftingInventory inv, + IPatternDetails.IInput input, Level level) { + var possibleInputs = input.getPossibleInputs(); + List substitutes = new ObjectArrayList<>(possibleInputs.length); + for (var stack : possibleInputs) { + for (var fuzz : inv.findFuzzyTemplates(stack.what())) { + if (fuzz instanceof AEItemKey aeItemKey && !aeItemKey.matches(stack)) continue; + else if (fuzz instanceof AEFluidKey aeFluidKey && !aeFluidKey.matches(stack)) continue; + substitutes.add(new InputTemplate(fuzz, stack.amount())); + } + } + return Iterables.filter(substitutes, stack -> input.isValid(stack.key(), level)); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/crafting/CraftingTreeNodeMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/crafting/CraftingTreeNodeMixin.java new file mode 100644 index 000000000..0f994cfb2 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/crafting/CraftingTreeNodeMixin.java @@ -0,0 +1,345 @@ +package org.gtlcore.gtlcore.mixin.ae2.crafting; + +import org.gtlcore.gtlcore.integration.ae2.crafting.ICraftingCalculation; +import org.gtlcore.gtlcore.integration.ae2.crafting.ICraftingTreeNode; +import org.gtlcore.gtlcore.integration.ae2.crafting.ICraftingTreeProcess; + +import net.minecraft.world.level.Level; + +import appeng.api.config.Actionable; +import appeng.api.crafting.IPatternDetails; +import appeng.api.networking.crafting.ICraftingService; +import appeng.api.stacks.*; +import appeng.crafting.*; +import appeng.crafting.execution.CraftingCpuHelper; +import appeng.crafting.execution.InputTemplate; +import appeng.crafting.inv.ChildCraftingSimulationState; +import appeng.crafting.inv.CraftingSimulationState; +import appeng.crafting.inv.ICraftingInventory; +import appeng.crafting.pattern.AEProcessingPattern; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.*; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@SuppressWarnings("AddedMixinMembersNamePattern") +@Mixin(CraftingTreeNode.class) +public class CraftingTreeNodeMixin implements ICraftingTreeNode { + + @Unique + private IPatternDetails patternDetails; + + @Shadow(remap = false) + @Final + @Mutable + final IPatternDetails.@Nullable IInput parentInput; + @Shadow(remap = false) + @Final + @Mutable + private final Level level; + @Shadow(remap = false) + @Final + @Mutable + private final AEKey what; + @Shadow(remap = false) + @Final + private appeng.crafting.CraftingCalculation job; + @Shadow(remap = false) + private ArrayList nodes; + @Shadow(remap = false) + @Final + private long amount; + @Shadow(remap = false) + @Final + private boolean canEmit; + + public CraftingTreeNodeMixin(IPatternDetails.@Nullable IInput parentInput, Level level, AEKey what) { + this.parentInput = parentInput; + this.level = level; + this.what = what; + } + + @Inject(method = "", at = @At("TAIL")) + private void CraftingTreeNode(ICraftingService cc, appeng.crafting.CraftingCalculation job, AEKey what, long amount, CraftingTreeProcess par, int slot, CallbackInfo ci) { + this.patternDetails = slot == -1 ? null : ((ICraftingTreeProcess) par).getDetails(); + } + + @Shadow(remap = false) + private void addContainerItems(AEKey template, long multiplier, @Nullable KeyCounter outputList) { + throw new AssertionError(); + } + + @Shadow(remap = false) + private void buildChildPatterns() { + throw new AssertionError(); + } + + @Shadow(remap = false) + void request(CraftingSimulationState inv, long requestedAmount, @Nullable KeyCounter containerItems) throws CraftBranchFailure, InterruptedException { + throw new AssertionError(); + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + private Iterable getValidItemTemplates(ICraftingInventory inv) { + if (this.parentInput == null) + return List.of(new InputTemplate(what, 1)); + else if (this.patternDetails instanceof AEProcessingPattern) { + GenericStack stack = this.parentInput.getPossibleInputs()[0]; + return List.of(new InputTemplate(stack.what(), stack.amount())); + } + return CraftingCpuHelper.getValidItemTemplates(inv, this.parentInput, level); + } + + @Override + @Unique + public void legacyRequest(CraftingSimulationState inv, long requestedAmount, + @Nullable KeyCounter containerItems) throws CraftBranchFailure, InterruptedException { + request(inv, requestedAmount, containerItems); + } + + @Override + @Unique + public void fastRequest(CraftingSimulationState inv, long requestedAmount, + @Nullable KeyCounter containerItems) throws CraftBranchFailure, InterruptedException { + ((ICraftingCalculation) this.job).handlePausing(); + + inv.addStackBytes(what, amount, requestedAmount); + + for (var template : getValidItemTemplates(inv)) { + long extracted = CraftingCpuHelper.extractTemplates(inv, template, requestedAmount); + + if (extracted > 0) { + requestedAmount -= extracted; + addContainerItems(template.key(), extracted, containerItems); + + if (requestedAmount == 0) { + return; + } + } + } + + addContainerItems(what, requestedAmount, containerItems); + + if (this.canEmit) { + inv.emitItems(this.what, this.amount * requestedAmount); + return; + } + + buildChildPatterns(); + long totalRequestedItems = requestedAmount * this.amount; + if (this.nodes.size() == 1) { + final ICraftingTreeProcess pro = (ICraftingTreeProcess) (this.nodes.get(0)); + var craftedPerPattern = pro.getOutputCountTest(this.what); + + while (pro.getPossible() && totalRequestedItems > 0) { + long times; + if (pro.limitsQuantityTest()) { + times = 1; + } else { + times = (totalRequestedItems + craftedPerPattern - 1) / craftedPerPattern; + } + pro.fastRequest(inv, times); + + var available = inv.extract(this.what, totalRequestedItems, Actionable.MODULATE); + if (available != 0) { + totalRequestedItems -= available; + + if (totalRequestedItems <= 0) { + return; + } + } else { + var pattern = pro.getDetails().getDefinition(); + String outputs = Stream.of(pro.getDetails().getOutputs()) + .map(GenericStack::toString) + .collect(Collectors.joining(", ")); + String errorMessage = """ + Unexpected error in the crafting calculation: can't find created items. + This is an AE2 bug, please report it, with the following important information: + + - Found none of %s. Remaining request: %d of %d*%d. + - Tried crafting %d times the pattern %s with tag %s. + - Pattern outputs: %s. + """.formatted(what, totalRequestedItems, requestedAmount, amount, times, pattern, + pattern.getTag(), outputs); + throw new UnsupportedOperationException(errorMessage); + } + } + } else if (this.nodes.size() > 1) { + // Multiple branches: distribute load evenly across all branches + // This optimization strategy divides the request equally among available patterns + // and continues trying all patterns in subsequent iterations + + while (totalRequestedItems > 0) { + int processCount = this.nodes.size(); + long baseAmount = totalRequestedItems / processCount; + long remainder = totalRequestedItems % processCount; + boolean anySucceeded = false; + + for (int i = 0; i < this.nodes.size(); i++) { + ICraftingTreeProcess pro = (ICraftingTreeProcess) (this.nodes.get(i)); + + if (!pro.getPossible()) + continue; + + long targetAmount = baseAmount + (i < remainder ? 1 : 0); + if (targetAmount <= 0) continue; + + try { + var craftedPerPattern = pro.getOutputCountTest(this.what); + long times = pro.limitsQuantityTest() ? 1 : (targetAmount + craftedPerPattern - 1) / craftedPerPattern; + + if (times > 0) { + final ChildCraftingSimulationState child = new ChildCraftingSimulationState(inv); + pro.fastRequest(child, times); + + var available = child.extract(this.what, targetAmount, Actionable.MODULATE); + + if (available != 0) { + child.applyDiff(inv); + anySucceeded = true; + + totalRequestedItems -= available; + + if (totalRequestedItems <= 0) { + return; + } + } else { + pro.setPossible(false); + } + } + } catch (CraftBranchFailure fail) { + // This process failed this iteration, but might succeed later + } + } + + // If no process succeeded in this iteration, we're done trying + if (!anySucceeded) { + break; + } + } + } + + if (this.job.isSimulation()) { + this.job.getMissingItems().add(this.what, totalRequestedItems); + } else { + throw new CraftBranchFailure(this.what, totalRequestedItems); + } + } + + @Override + @Unique + public void ultraFastRequest(CraftingSimulationState inv, long requestedAmount, + @Nullable KeyCounter containerItems) throws CraftBranchFailure, InterruptedException { + ((ICraftingCalculation) this.job).handlePausing(); + + inv.addStackBytes(what, amount, requestedAmount); + + for (var template : getValidItemTemplates(inv)) { + long extracted = CraftingCpuHelper.extractTemplates(inv, template, requestedAmount); + + if (extracted > 0) { + requestedAmount -= extracted; + addContainerItems(template.key(), extracted, containerItems); + + if (requestedAmount == 0) { + return; + } + } + } + + addContainerItems(what, requestedAmount, containerItems); + + if (this.canEmit) { + inv.emitItems(this.what, this.amount * requestedAmount); + return; + } + + buildChildPatterns(); + long totalRequestedItems = requestedAmount * this.amount; + if (this.nodes.size() == 1) { + final ICraftingTreeProcess pro = (ICraftingTreeProcess) (this.nodes.get(0)); + var craftedPerPattern = pro.getOutputCountTest(this.what); + + while (pro.getPossible() && totalRequestedItems > 0) { + long times; + if (pro.limitsQuantityTest()) { + times = 1; + } else { + times = (totalRequestedItems + craftedPerPattern - 1) / craftedPerPattern; + } + pro.ultraFastRequest(inv, times); + + var available = inv.extract(this.what, totalRequestedItems, Actionable.MODULATE); + if (available != 0) { + totalRequestedItems -= available; + + if (totalRequestedItems <= 0) { + return; + } + } else { + var pattern = pro.getDetails().getDefinition(); + String outputs = Stream.of(pro.getDetails().getOutputs()) + .map(GenericStack::toString) + .collect(Collectors.joining(", ")); + String errorMessage = """ + Unexpected error in the crafting calculation: can't find created items. + This is an AE2 bug, please report it, with the following important information: + + - Found none of %s. Remaining request: %d of %d*%d. + - Tried crafting %d times the pattern %s with tag %s. + - Pattern outputs: %s. + """.formatted(what, totalRequestedItems, requestedAmount, amount, times, pattern, + pattern.getTag(), outputs); + throw new UnsupportedOperationException(errorMessage); + } + } + } else if (this.nodes.size() > 1) { + // Multiple branches: try maximum value for each node only once + // This optimization strategy attempts the full remaining request on each pattern once + + for (CraftingTreeProcess node : this.nodes) { + ICraftingTreeProcess pro = (ICraftingTreeProcess) node; + + try { + var craftedPerPattern = pro.getOutputCountTest(this.what); + long times = pro.limitsQuantityTest() ? 1 : (totalRequestedItems + craftedPerPattern - 1) / craftedPerPattern; + + if (times > 0) { + final ChildCraftingSimulationState child = new ChildCraftingSimulationState(inv); + pro.ultraFastRequest(child, times); + + var available = child.extract(this.what, totalRequestedItems, Actionable.MODULATE); + + if (available != 0) { + child.applyDiff(inv); + + totalRequestedItems -= available; + + if (totalRequestedItems <= 0) { + return; + } + } + } + } catch (CraftBranchFailure fail) { + // This process failed, move to next node + } + } + } + + if (this.job.isSimulation()) { + this.job.getMissingItems().add(this.what, totalRequestedItems); + } else { + throw new CraftBranchFailure(this.what, totalRequestedItems); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/crafting/CraftingTreeProcessMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/crafting/CraftingTreeProcessMixin.java new file mode 100644 index 000000000..1a516e50f --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/crafting/CraftingTreeProcessMixin.java @@ -0,0 +1,121 @@ +package org.gtlcore.gtlcore.mixin.ae2.crafting; + +import org.gtlcore.gtlcore.integration.ae2.crafting.ICraftingCalculation; +import org.gtlcore.gtlcore.integration.ae2.crafting.ICraftingTreeNode; +import org.gtlcore.gtlcore.integration.ae2.crafting.ICraftingTreeProcess; + +import appeng.api.config.Actionable; +import appeng.api.crafting.IPatternDetails; +import appeng.api.stacks.AEKey; +import appeng.api.stacks.GenericStack; +import appeng.api.stacks.KeyCounter; +import appeng.crafting.CraftBranchFailure; +import appeng.crafting.CraftingTreeNode; +import appeng.crafting.CraftingTreeProcess; +import appeng.crafting.inv.CraftingSimulationState; +import lombok.Getter; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; + +import java.util.Map; + +@SuppressWarnings("AddedMixinMembersNamePattern") +@Mixin(CraftingTreeProcess.class) +public abstract class CraftingTreeProcessMixin implements ICraftingTreeProcess { + + @Shadow(remap = false) + @Final + private appeng.crafting.CraftingCalculation job; + @Shadow(remap = false) + private boolean containerItems; + @Shadow(remap = false) + @Final + private Map nodes; + @Getter + @Shadow(remap = false) + @Final + IPatternDetails details; + @Shadow(remap = false) + boolean possible; + @Shadow(remap = false) + private boolean limitQty; + + @Override + @Unique + public void fastRequest(CraftingSimulationState inv, long times) throws CraftBranchFailure, InterruptedException { + ((ICraftingCalculation) this.job).handlePausing(); + + var containerItems = this.containerItems ? new KeyCounter() : null; + + for (var entry : this.nodes.entrySet()) { + ((ICraftingTreeNode) entry.getKey()).fastRequest(inv, entry.getValue() * times, containerItems); + } + + onSucceed(inv, times, containerItems); + } + + @Override + @Unique + public void ultraFastRequest(CraftingSimulationState inv, long times) throws CraftBranchFailure, InterruptedException { + ((ICraftingCalculation) this.job).handlePausing(); + + var containerItems = this.containerItems ? new KeyCounter() : null; + + for (var entry : this.nodes.entrySet()) { + ((ICraftingTreeNode) entry.getKey()).ultraFastRequest(inv, entry.getValue() * times, containerItems); + } + + onSucceed(inv, times, containerItems); + } + + @Unique + private void onSucceed(CraftingSimulationState inv, long times, KeyCounter containerItems) { + if (containerItems != null) { + for (var stack : containerItems) { + inv.insert(stack.getKey(), stack.getLongValue(), Actionable.MODULATE); + inv.addStackBytes(stack.getKey(), stack.getLongValue(), 1); + } + } + + for (var out : this.details.getOutputs()) { + inv.insert(out.what(), out.amount() * times, Actionable.MODULATE); + } + + inv.addCrafting(details, times); + inv.addBytes(times); + } + + @Override + @Unique + public boolean getPossible() { + return this.possible; + } + + @Override + @Unique + public void setPossible(boolean b) { + this.possible = b; + } + + @Override + @Unique + public long getOutputCountTest(AEKey what) { + long tot = 0L; + + for (GenericStack is : this.details.getOutputs()) { + if (what.matches(is)) { + tot += is.amount(); + } + } + + return tot; + } + + @Override + @Unique + public boolean limitsQuantityTest() { + return this.limitQty; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/gui/PatternEncodingTermMenuMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/gui/PatternEncodingTermMenuMixin.java index af37845e7..5d670e0a3 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/gui/PatternEncodingTermMenuMixin.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/gui/PatternEncodingTermMenuMixin.java @@ -2,37 +2,62 @@ import org.gtlcore.gtlcore.client.gui.PatterEncodingTermMenuModify; +import com.gregtechceu.gtceu.common.data.GTItems; + import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import appeng.api.config.Actionable; +import appeng.api.crafting.PatternDetailsHelper; +import appeng.api.stacks.AEItemKey; import appeng.api.stacks.GenericStack; import appeng.api.storage.ITerminalHost; +import appeng.core.definitions.AEItems; import appeng.helpers.IMenuCraftingPacket; import appeng.helpers.IPatternTerminalMenuHost; import appeng.menu.me.common.MEStorageMenu; import appeng.menu.me.items.PatternEncodingTermMenu; +import appeng.menu.slot.RestrictedInputSlot; import appeng.util.ConfigInventory; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; +import com.google.common.math.LongMath; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; /** * @author EasterFG on 2024/9/12 */ -@Mixin(PatternEncodingTermMenu.class) +@Mixin(value = PatternEncodingTermMenu.class, priority = 900) public abstract class PatternEncodingTermMenuMixin extends MEStorageMenu implements IMenuCraftingPacket, PatterEncodingTermMenuModify { + @Unique + private static final ItemStack Pattern = AEItems.BLANK_PATTERN.stack(); + @Shadow(remap = false) @Final private ConfigInventory encodedInputsInv; - @Shadow(remap = false) @Final private ConfigInventory encodedOutputsInv; + @Shadow(remap = false) + @Final + private RestrictedInputSlot blankPatternSlot; + @Shadow(remap = false) + @Final + private RestrictedInputSlot encodedPatternSlot; + + @Shadow(remap = false) + protected abstract @Nullable ItemStack encodePattern(); + + @Shadow(remap = false) + protected abstract boolean isPattern(ItemStack output); + + @Shadow(remap = false) + protected abstract void clearPattern(); public PatternEncodingTermMenuMixin(MenuType menuType, int id, Inventory ip, ITerminalHost host) { super(menuType, id, ip, host); @@ -46,6 +71,67 @@ public void initHooks(MenuType menuType, int id, Inventory ip, IPatternTermin this::gTLCore$modifyPatter); } + /** + * @author . + * @reason 样板不足时自动填充(如果库存有) + */ + @Overwrite(remap = false) + public void encode() { + if (isClientSide()) { + sendClientAction("encode"); + return; + } + + ItemStack encodedPattern = encodePattern(); + if (encodedPattern != null) { + var encodeOutput = this.encodedPatternSlot.getItem(); + + // first check the output slots, should either be null, or a pattern (encoded or otherwise) + if (!encodeOutput.isEmpty() && !PatternDetailsHelper.isEncodedPattern(encodeOutput) && !AEItems.BLANK_PATTERN.isSameAs(encodeOutput)) { + return; + } // if nothing is there we should snag a new pattern. + else if (encodeOutput.isEmpty()) { + var blankPattern = this.blankPatternSlot.getItem(); + if (!isPattern(blankPattern)) { + return; // no blanks. + } + + // remove one, and clear the input slot. + blankPattern.shrink(1); + if (blankPattern.getCount() <= 0) { + if (this.storage != null) { + long extract = this.storage.extract(AEItemKey.of(Pattern), 64, Actionable.SIMULATE, this.getActionSource()); + if (extract > 0) { + extract = this.storage.extract(AEItemKey.of(Pattern), extract, Actionable.MODULATE, this.getActionSource()); + this.blankPatternSlot.set(Pattern.copyWithCount((int) extract)); + } + } else this.blankPatternSlot.set(ItemStack.EMPTY); + } + } + + this.encodedPatternSlot.set(encodedPattern); + } else { + clearPattern(); + } + } + + @Redirect(method = "encodeProcessingPattern", + at = @At(value = "INVOKE", + target = "Lappeng/util/ConfigInventory;getStack(I)Lappeng/api/stacks/GenericStack;", + ordinal = 0), + remap = false) + private GenericStack filterData(ConfigInventory instance, int slot) { + var stack = instance.getStack(slot); + if (stack != null && stack.what() instanceof AEItemKey aeItemKey) { + if (aeItemKey.hasTag() && + (aeItemKey.getItem() == GTItems.TOOL_DATA_STICK.asItem() || + aeItemKey.getItem() == GTItems.TOOL_DATA_ORB.asItem() || + aeItemKey.getItem() == GTItems.TOOL_DATA_MODULE.asItem())) + return new GenericStack(AEItemKey.of(aeItemKey.getItem()), stack.amount()); + } + return stack; + } + @Override public void gTLCore$modifyPatter(Integer data) { if (this.isClientSide()) { @@ -85,10 +171,11 @@ public void initHooks(MenuType menuType, int id, Inventory ip, IPatternTermin GenericStack stack = inv.getStack(slot); if (stack != null) { if (flag) { - if (data * stack.amount() > Integer.MAX_VALUE) { + long modify = LongMath.saturatedMultiply(data, stack.amount()); + if (modify == Long.MAX_VALUE || modify == Long.MIN_VALUE) { return null; } else { - result[slot] = new GenericStack(stack.what(), data * stack.amount()); + result[slot] = new GenericStack(stack.what(), modify); } } else { if (stack.amount() % data != 0) { diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/gui/ProcessingEncodingPanelMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/gui/ProcessingEncodingPanelMixin.java index 5e19babf6..4871124b2 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/gui/ProcessingEncodingPanelMixin.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/gui/ProcessingEncodingPanelMixin.java @@ -43,28 +43,28 @@ public ProcessingEncodingPanelMixin(PatternEncodingTermScreen screen, WidgetC @Inject(method = "", at = @At("TAIL")) public void init(PatternEncodingTermScreen screen, WidgetContainer widgets, CallbackInfo ci) { gTLCore$multipleTow = new ModifyIconButton(b -> ((PatterEncodingTermMenuModify) this.menu).gTLCore$modifyPatter(2), ModifyIcon.MULTIPLY_2, - Component.literal("样板配方 x 2"), - Component.literal("将样板材料和配方数量 x 2")); + Component.translatable("gui.gtlcore.pattern_recipe_multiply_2"), + Component.translatable("tooltip.gtlcore.pattern_materials_multiply_2")); gTLCore$multipleThree = new ModifyIconButton(b -> ((PatterEncodingTermMenuModify) this.menu).gTLCore$modifyPatter(3), ModifyIcon.MULTIPLY_3, - Component.literal("样板配方 x 3"), - Component.literal("将样板材料和配方数量 x 3")); + Component.translatable("gui.gtlcore.pattern_recipe_multiply_3"), + Component.translatable("tooltip.gtlcore.pattern_materials_multiply_3")); gTLCore$multipleFive = new ModifyIconButton(b -> ((PatterEncodingTermMenuModify) this.menu).gTLCore$modifyPatter(5), ModifyIcon.MULTIPLY_5, - Component.literal("样板配方 x 5"), - Component.literal("将样板材料和配方数量 x 5")); + Component.translatable("gui.gtlcore.pattern_recipe_multiply_5"), + Component.translatable("tooltip.gtlcore.pattern_materials_multiply_5")); gTLCore$dividingTow = new ModifyIconButton(b -> ((PatterEncodingTermMenuModify) this.menu).gTLCore$modifyPatter(-2), ModifyIcon.DIVISION_2, - Component.literal("样板配方 ÷ 2"), - Component.literal("将样板材料和配方数量 ÷ 2")); + Component.translatable("gui.gtlcore.pattern_recipe_divide_2"), + Component.translatable("tooltip.gtlcore.pattern_materials_divide_2")); gTLCore$dividingThree = new ModifyIconButton(b -> ((PatterEncodingTermMenuModify) this.menu).gTLCore$modifyPatter(-3), ModifyIcon.DIVISION_3, - Component.literal("样板配方 ÷ 3"), - Component.literal("将样板材料和配方数量 ÷ 3")); + Component.translatable("gui.gtlcore.pattern_recipe_divide_3"), + Component.translatable("tooltip.gtlcore.pattern_materials_divide_3")); gTLCore$dividingFive = new ModifyIconButton(b -> ((PatterEncodingTermMenuModify) this.menu).gTLCore$modifyPatter(-5), ModifyIcon.DIVISION_5, - Component.literal("样板配方 ÷ 5"), - Component.literal("将样板材料和配方数量 ÷ 5")); + Component.translatable("gui.gtlcore.pattern_recipe_divide_5"), + Component.translatable("tooltip.gtlcore.pattern_materials_divide_5")); widgets.add("modify1", gTLCore$multipleTow); widgets.add("modify2", gTLCore$multipleThree); diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/gui/SetProcessingPatternAmountScreenMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/gui/SetProcessingPatternAmountScreenMixin.java new file mode 100644 index 000000000..a69c5dd1e --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/gui/SetProcessingPatternAmountScreenMixin.java @@ -0,0 +1,22 @@ +package org.gtlcore.gtlcore.mixin.ae2.gui; + +import appeng.api.stacks.GenericStack; +import appeng.client.gui.me.items.SetProcessingPatternAmountScreen; +import org.spongepowered.asm.mixin.*; + +@Mixin(SetProcessingPatternAmountScreen.class) +public class SetProcessingPatternAmountScreenMixin { + + @Shadow(remap = false) + @Final + private GenericStack currentStack; + + /** + * @author . + * @reason 样板终端中键设置数量上限 + */ + @Overwrite(remap = false) + private long getMaxAmount() { + return (long) Integer.MAX_VALUE * this.currentStack.what().getAmountPerUnit(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/integration/EncodingHelperMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/integration/EncodingHelperMixin.java new file mode 100644 index 000000000..176ef8f52 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/integration/EncodingHelperMixin.java @@ -0,0 +1,133 @@ +package org.gtlcore.gtlcore.mixin.ae2.integration; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.item.crafting.RecipeType; + +import appeng.api.stacks.*; +import appeng.core.sync.network.NetworkHandler; +import appeng.core.sync.packets.InventoryActionPacket; +import appeng.helpers.InventoryAction; +import appeng.integration.modules.jeirei.EncodingHelper; +import appeng.menu.me.common.GridInventoryEntry; +import appeng.menu.me.common.MEStorageMenu; +import appeng.menu.me.items.PatternEncodingTermMenu; +import appeng.parts.encoding.EncodingMode; +import appeng.util.CraftingRecipeUtil; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.*; + +import java.util.*; +import java.util.function.Predicate; + +@Mixin(EncodingHelper.class) +public abstract class EncodingHelperMixin { + + @Shadow(remap = false) + @Final + static Comparator ENTRY_COMPARATOR; + + @Shadow(remap = false) + public static Map getIngredientPriorities(MEStorageMenu menu, Comparator comparator) { + return null; + } + + /** + * @author . + * @reason 优先选择通用电路 + */ + @Overwrite(remap = false) + private static GenericStack findBestIngredient(Map ingredientPriorities, List possibleIngredients) { + var list = possibleIngredients.stream() + .map(gi -> Pair.of(gi, ingredientPriorities.getOrDefault(gi.what(), Integer.MIN_VALUE))) + .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).map(Pair::getLeft).toList(); + return list.stream().filter(gi -> gi.what() instanceof AEItemKey aeItemKey && + aeItemKey.getItem().kjs$getId().contains("universal_circuit")) + .findFirst().orElseGet(() -> list.stream().findFirst().orElseThrow()); + } + + /** + * @author . + * @reason 优先选择通用电路 + */ + @Overwrite(remap = false) + public static void encodeCraftingRecipe(PatternEncodingTermMenu menu, + @Nullable Recipe recipe, + List> genericIngredients, + Predicate visiblePredicate) { + if (recipe != null && recipe.getType().equals(RecipeType.STONECUTTING)) { + menu.setMode(EncodingMode.STONECUTTING); + menu.setStonecuttingRecipeId(recipe.getId()); + } else if (recipe != null && recipe.getType().equals(RecipeType.SMITHING)) { + menu.setMode(EncodingMode.SMITHING_TABLE); + } else menu.setMode(EncodingMode.CRAFTING); + + // Note that this runs on the client and getClientRepo() is guaranteed to be available there. + var prioritizedNetworkInv = getIngredientPriorities(menu, ENTRY_COMPARATOR); + + var encodedInputs = NonNullList.withSize(menu.getCraftingGridSlots().length, ItemStack.EMPTY); + + if (recipe != null) { + // When we have access to a crafting recipe, we'll switch modes and try to find suitable + // ingredients based on the recipe ingredients, which allows for fuzzy-matching. + var ingredients3x3 = CraftingRecipeUtil.ensure3by3CraftingMatrix(recipe); + + // Find a good match for every ingredient + for (int slot = 0; slot < ingredients3x3.size(); slot++) { + var ingredient = ingredients3x3.get(slot); + if (ingredient.isEmpty()) continue; // Skip empty slots + + // Due to how some crafting recipes work, the ingredient can match more than just one item in the + // network inventory. We'll find all network inventory entries that it matches and sort them + // according to their suitability for encoding a pattern + var bestNetworkIngredient = prioritizedNetworkInv.entrySet().stream() + .filter(ni -> ni.getKey() instanceof AEItemKey itemKey && itemKey.matches(ingredient)) + .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) + .map(entry -> entry.getKey() instanceof AEItemKey itemKey ? itemKey.toStack() : null).toList(); + + var bestIngredient = bestNetworkIngredient.stream() + .filter(itemStack -> itemStack.getItem().kjs$getId().contains("universal_circuit")) + .findFirst().orElseGet(() -> bestNetworkIngredient.stream().findFirst().orElseGet(() -> { + + // To avoid encoding hidden entries, we'll cycle through the ingredient and try to find a + // visible + // stack, otherwise we'll use the first entry. + for (var stack : ingredient.getItems()) { + if (visiblePredicate.test(stack)) return stack; + } + return ingredient.getItems()[0]; + })); + + encodedInputs.set(slot, bestIngredient); + } + } else { + for (int slot = 0; slot < genericIngredients.size(); slot++) { + var genericIngredient = genericIngredients.get(slot); + if (genericIngredient.isEmpty()) continue; // Skip empty slots + + var bestIngredient = findBestIngredient(prioritizedNetworkInv, genericIngredient).what(); + + // Clamp amounts to 1 in crafting table mode + if (bestIngredient instanceof AEItemKey itemKey) { + encodedInputs.set(slot, itemKey.toStack()); + } else { + encodedInputs.set(slot, GenericStack.wrapInItemStack(bestIngredient, 1)); + } + } + } + + for (int i = 0; i < encodedInputs.size(); i++) { + ItemStack encodedInput = encodedInputs.get(i); + NetworkHandler.instance().sendToServer(new InventoryActionPacket( + InventoryAction.SET_FILTER, menu.getCraftingGridSlots()[i].index, encodedInput)); + } + + // Clear out the processing outputs + for (var outputSlot : menu.getProcessingOutputSlots()) { + NetworkHandler.instance().sendToServer(new InventoryActionPacket( + InventoryAction.SET_FILTER, outputSlot.index, ItemStack.EMPTY)); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/integration/ItemIngredientConverterMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/integration/ItemIngredientConverterMixin.java new file mode 100644 index 000000000..fe5b9178a --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/integration/ItemIngredientConverterMixin.java @@ -0,0 +1,34 @@ +package org.gtlcore.gtlcore.mixin.ae2.integration; + +import net.minecraft.world.item.ItemStack; + +import appeng.api.stacks.GenericStack; +import appeng.integration.modules.jei.ItemIngredientConverter; +import com.tterrag.registrate.util.entry.RegistryEntry; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +import java.util.*; + +import static com.gregtechceu.gtceu.common.data.GTItems.*; + +@Mixin(ItemIngredientConverter.class) +public class ItemIngredientConverterMixin { + + /** + * @author . + * @reason 填充样板跳过模头和模具 + */ + @Overwrite(remap = false) + public @Nullable GenericStack getStackFromIngredient(ItemStack itemStack) { + if (!itemStack.isEmpty()) { + var item = itemStack.getItem(); + boolean b1 = Arrays.stream(SHAPE_MOLDS).map(RegistryEntry::get).anyMatch((i) -> i.equals(item)); + boolean b2 = Arrays.stream(SHAPE_EXTRUDERS).filter(Objects::nonNull).map(RegistryEntry::get).anyMatch((i) -> i.equals(item)); + boolean b3 = itemStack.getTag() != null && itemStack.getTag().contains("assembly_line_research"); + if (b1 || b2 || b3) return null; + } + return GenericStack.fromItemStack(itemStack); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/logic/CraftingCpuLogicMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/logic/CraftingCpuLogicMixin.java new file mode 100644 index 000000000..bccd80f26 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/logic/CraftingCpuLogicMixin.java @@ -0,0 +1,132 @@ +package org.gtlcore.gtlcore.mixin.ae2.logic; + +import org.gtlcore.gtlcore.api.machine.trait.AECraft.IMECraftIOPart; +import org.gtlcore.gtlcore.api.machine.trait.MEPart.IMEPatternPartMachine; +import org.gtlcore.gtlcore.integration.ae2.AEUtils; + +import net.minecraft.world.level.Level; + +import appeng.api.config.Actionable; +import appeng.api.config.PowerMultiplier; +import appeng.api.networking.energy.IEnergyService; +import appeng.api.stacks.KeyCounter; +import appeng.crafting.execution.CraftingCpuHelper; +import appeng.crafting.execution.CraftingCpuLogic; +import appeng.crafting.execution.ExecutingCraftingJob; +import appeng.crafting.inv.ListCraftingInventory; +import appeng.crafting.pattern.AEProcessingPattern; +import appeng.me.cluster.implementations.CraftingCPUCluster; +import appeng.me.service.CraftingService; +import org.spongepowered.asm.mixin.*; + +@Mixin(CraftingCpuLogic.class) +public abstract class CraftingCpuLogicMixin { + + @Shadow(remap = false) + private ExecutingCraftingJob job; + + @Shadow(remap = false) + @Final + CraftingCPUCluster cluster; + + @Shadow(remap = false) + @Final + private ListCraftingInventory inventory; + + /** + * @author Dragons + * @reason ME样板总成自动翻倍 + */ + @Overwrite(remap = false) + public int executeCrafting(int maxPatterns, CraftingService craftingService, IEnergyService energyService, + Level level) { + var job = (ExecutingCraftingJobAccessor) (this.job); + if (job == null) return 0; + + var pushedPatterns = 0; + + var it = job.getTasks().entrySet().iterator(); + taskLoop: + while (it.hasNext()) { + var task = it.next(); + var taskProgress = (ExecutingCraftingJobTaskProgressAccessor) (task.getValue()); + if (taskProgress.getValue() <= 0) { + it.remove(); + continue; + } + + var details = task.getKey(); + final boolean isProcessing = details instanceof AEProcessingPattern; + + KeyCounter expectedOutputs = new KeyCounter(), expectedContainerItems = new KeyCounter(); + KeyCounter[] craftingContainer = null; + boolean needExtract = true; + + for (var provider : craftingService.getProviders(details)) { + final boolean autoExpand = isProcessing && (provider instanceof IMEPatternPartMachine || provider instanceof IMECraftIOPart); + + if (needExtract) { + craftingContainer = isProcessing ? (autoExpand ? AEUtils.extractForProcessingPattern((AEProcessingPattern) details, inventory, expectedOutputs, taskProgress.getValue()) : AEUtils.extractForProcessingPattern((AEProcessingPattern) details, inventory, expectedOutputs)) : AEUtils.extractForCraftPattern(details, inventory, level, expectedOutputs, expectedContainerItems); + needExtract = false; + if (craftingContainer == null) { + break; + } + } + + if (provider.isBusy()) continue; + + var patternPower = CraftingCpuHelper.calculatePatternPower(craftingContainer); + if (energyService.extractAEPower(patternPower, Actionable.SIMULATE, PowerMultiplier.CONFIG) < patternPower - 0.01) { + break; + } + + if (provider.pushPattern(details, craftingContainer)) { + energyService.extractAEPower(patternPower, Actionable.MODULATE, PowerMultiplier.CONFIG); + pushedPatterns++; + + for (var expectedOutput : expectedOutputs) { + job.getWaitingFor().insert(expectedOutput.getKey(), expectedOutput.getLongValue(), + Actionable.MODULATE); + } + for (var expectedContainerItem : expectedContainerItems) { + job.getWaitingFor().insert(expectedContainerItem.getKey(), expectedContainerItem.getLongValue(), + Actionable.MODULATE); + ((ElapsedTimeTrackerAccessor) job.getTimeTracker()).invokeAddMaxItems(expectedContainerItem.getLongValue(), + expectedContainerItem.getKey().getType()); + } + + cluster.markDirty(); + + // 1) AutoExpand + if (autoExpand) { + taskProgress.setValue(0); + it.remove(); + continue taskLoop; + } + + // 2) Others + taskProgress.setValue(taskProgress.getValue() - 1); + if (taskProgress.getValue() <= 0) { + it.remove(); + continue taskLoop; + } + + if (pushedPatterns == maxPatterns) { + break taskLoop; + } + + expectedOutputs.reset(); + expectedContainerItems.reset(); + craftingContainer = null; + needExtract = true; + } + } + + if (craftingContainer != null) { + CraftingCpuHelper.reinjectPatternInputs(inventory, craftingContainer); + } + } + + return pushedPatterns; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/logic/ElapsedTimeTrackerAccessor.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/logic/ElapsedTimeTrackerAccessor.java new file mode 100644 index 000000000..3adecf162 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/logic/ElapsedTimeTrackerAccessor.java @@ -0,0 +1,13 @@ +package org.gtlcore.gtlcore.mixin.ae2.logic; + +import appeng.api.stacks.AEKeyType; +import appeng.crafting.execution.ElapsedTimeTracker; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(ElapsedTimeTracker.class) +public interface ElapsedTimeTrackerAccessor { + + @Invoker(remap = false) + void invokeAddMaxItems(long itemDiff, AEKeyType keyType); +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/logic/ExecutingCraftingJobAccessor.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/logic/ExecutingCraftingJobAccessor.java new file mode 100644 index 000000000..a8f4f08e8 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/logic/ExecutingCraftingJobAccessor.java @@ -0,0 +1,23 @@ +package org.gtlcore.gtlcore.mixin.ae2.logic; + +import appeng.api.crafting.IPatternDetails; +import appeng.crafting.execution.ElapsedTimeTracker; +import appeng.crafting.execution.ExecutingCraftingJob; +import appeng.crafting.inv.ListCraftingInventory; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(ExecutingCraftingJob.class) +public interface ExecutingCraftingJobAccessor { + + @Accessor(value = "tasks", remap = false) + Map getTasks(); + + @Accessor(value = "waitingFor", remap = false) + ListCraftingInventory getWaitingFor(); + + @Accessor(value = "timeTracker", remap = false) + ElapsedTimeTracker getTimeTracker(); +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/logic/ExecutingCraftingJobTaskProgressAccessor.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/logic/ExecutingCraftingJobTaskProgressAccessor.java new file mode 100644 index 000000000..0be5009d6 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/logic/ExecutingCraftingJobTaskProgressAccessor.java @@ -0,0 +1,14 @@ +package org.gtlcore.gtlcore.mixin.ae2.logic; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(targets = "appeng.crafting.execution.ExecutingCraftingJob$TaskProgress") +public interface ExecutingCraftingJobTaskProgressAccessor { + + @Accessor(value = "value", remap = false) + long getValue(); + + @Accessor(value = "value", remap = false) + void setValue(long v); +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/pattern/AEProcessingPatternMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/pattern/AEProcessingPatternMixin.java new file mode 100644 index 000000000..0472182cf --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/pattern/AEProcessingPatternMixin.java @@ -0,0 +1,20 @@ +package org.gtlcore.gtlcore.mixin.ae2.pattern; + +import appeng.api.crafting.IPatternDetails; +import appeng.api.stacks.KeyCounter; +import appeng.crafting.pattern.AEProcessingPattern; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +@Mixin(AEProcessingPattern.class) +public abstract class AEProcessingPatternMixin implements IPatternDetails { + + /** + * @author Dragons + * @reason Performance + */ + @Overwrite(remap = false) + public void pushInputsToExternalInventory(KeyCounter[] inputHolder, IPatternDetails.PatternInputSink inputSink) { + IPatternDetails.super.pushInputsToExternalInventory(inputHolder, inputSink); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/service/CraftingServiceMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/service/CraftingServiceMixin.java new file mode 100644 index 000000000..33e2bc3b8 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/service/CraftingServiceMixin.java @@ -0,0 +1,26 @@ +package org.gtlcore.gtlcore.mixin.ae2.service; + +import org.gtlcore.gtlcore.config.ConfigHolder; +import org.gtlcore.gtlcore.utils.NumberUtils; + +import appeng.hooks.ticking.TickHandler; +import appeng.me.service.CraftingService; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(CraftingService.class) +public class CraftingServiceMixin { + + @Unique + private static final int CRAFT_MASK = NumberUtils.nearestPow2Lookup(ConfigHolder.INSTANCE.ae2CraftingServiceUpdateInterval) - 1; + + @Inject(method = "onServerEndTick", at = @At("HEAD"), cancellable = true, remap = false) + public void onServerEndTick(CallbackInfo ci) { + if ((TickHandler.instance().getCurrentTick() & CRAFT_MASK) != 0) { + ci.cancel(); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/service/StorageServiceMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/service/StorageServiceMixin.java new file mode 100644 index 000000000..cfa04fcd4 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/service/StorageServiceMixin.java @@ -0,0 +1,50 @@ +package org.gtlcore.gtlcore.mixin.ae2.service; + +import org.gtlcore.gtlcore.config.ConfigHolder; +import org.gtlcore.gtlcore.utils.NumberUtils; + +import appeng.api.networking.storage.IStorageWatcherNode; +import appeng.hooks.ticking.TickHandler; +import appeng.me.helpers.InterestManager; +import appeng.me.helpers.StackWatcher; +import appeng.me.service.StorageService; +import org.spongepowered.asm.mixin.*; + +/** + * 代码参考自gto + * @line ... + */ + +@Mixin(StorageService.class) +public abstract class StorageServiceMixin { + + @Shadow(remap = false) + @Final + @Mutable + private final InterestManager> interestManager; + @Shadow(remap = false) + private boolean cachedStacksNeedUpdate; + + @Shadow(remap = false) + protected abstract void updateCachedStacks(); + + @Unique + private static final int STORAGE_MASK = NumberUtils.nearestPow2Lookup(ConfigHolder.INSTANCE.ae2StorageServiceUpdateInterval) - 1; + + public StorageServiceMixin(InterestManager> interestManager) { + this.interestManager = interestManager; + } + + /** + * @author . + * @reason 减少更新频率 + */ + @Overwrite(remap = false) + public void onServerEndTick() { + if (this.interestManager.isEmpty()) { + this.cachedStacksNeedUpdate = true; + } else { + if ((TickHandler.instance().getCurrentTick() & STORAGE_MASK) == 0) this.updateCachedStacks(); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/stacks/AEItemKeyMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/stacks/AEItemKeyMixin.java new file mode 100644 index 000000000..45d30e947 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/stacks/AEItemKeyMixin.java @@ -0,0 +1,36 @@ +package org.gtlcore.gtlcore.mixin.ae2.stacks; + +import net.minecraft.world.item.Item; + +import appeng.api.stacks.AEItemKey; +import org.spongepowered.asm.mixin.*; + +/** + * 代码参考自gto + * @line ... + */ + +@Mixin(AEItemKey.class) +public abstract class AEItemKeyMixin { + + private int fuzzySearchMaxValue = -1; + + @Shadow(remap = false) + @Final + @Mutable + private final Item item; + + public AEItemKeyMixin(Item item) { + this.item = item; + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public int getFuzzySearchMaxValue() { + if (this.fuzzySearchMaxValue < 0) this.fuzzySearchMaxValue = this.item.getMaxDamage(); + return fuzzySearchMaxValue; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/stacks/AEKey2LongMapMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/stacks/AEKey2LongMapMixin.java new file mode 100644 index 000000000..efd411f65 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/stacks/AEKey2LongMapMixin.java @@ -0,0 +1,30 @@ +package org.gtlcore.gtlcore.mixin.ae2.stacks; + +import appeng.api.stacks.AEKey; +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(targets = "appeng.api.stacks.AEKey2LongMap$OpenHashMap", remap = false) +public abstract class AEKey2LongMapMixin extends Object2LongOpenHashMap { + + @Override + public long addTo(AEKey key, long incr) { + long oldValue = getLong(key); + + // Fast path: zero increment + if (incr == 0) { + return oldValue; + } + + long newValue = oldValue + incr; + + // Check for overflow using bitwise operations (faster than bounds checking) + // Overflow occurs when operands have same sign but result has different sign + if (((oldValue ^ newValue) & (incr ^ newValue)) < 0) { + newValue = incr > 0 ? Long.MAX_VALUE : Long.MIN_VALUE; + } + + put(key, newValue); + return oldValue; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/stacks/AEKeyMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/stacks/AEKeyMixin.java new file mode 100644 index 000000000..4427f9f7f --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/stacks/AEKeyMixin.java @@ -0,0 +1,40 @@ +package org.gtlcore.gtlcore.mixin.ae2.stacks; + +import net.minecraft.nbt.CompoundTag; + +import appeng.api.stacks.AEKey; +import appeng.api.stacks.AEKeyType; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; + +@Mixin(AEKey.class) +public abstract class AEKeyMixin { + + @Unique + private CompoundTag gTLCore$tagGenericCache; + + @Shadow(remap = false) + public abstract CompoundTag toTag(); + + @Shadow(remap = false) + public abstract AEKeyType getType(); + + /** + * @author Dragons + * @reason Performance + */ + @Overwrite(remap = false) + public final CompoundTag toTagGeneric() { + return gTLCore$tagGenericCache != null ? gTLCore$tagGenericCache : gTLCore$saveAndReturnTagGeneric(); + } + + @Unique + private CompoundTag gTLCore$saveAndReturnTagGeneric() { + CompoundTag tag = this.toTag(); + tag.putString("#c", this.getType().getId().toString()); + this.gTLCore$tagGenericCache = tag; + return tag; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/stacks/GenericStackMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/stacks/GenericStackMixin.java new file mode 100644 index 000000000..9c0469f05 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/stacks/GenericStackMixin.java @@ -0,0 +1,27 @@ +package org.gtlcore.gtlcore.mixin.ae2.stacks; + +import net.minecraft.nbt.CompoundTag; + +import appeng.api.stacks.GenericStack; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +@Mixin(GenericStack.class) +public abstract class GenericStackMixin { + + /** + * @author Dragons + * @reason 修复序列化问题 + */ + @Overwrite(remap = false) + public static CompoundTag writeTag(@Nullable GenericStack stack) { + if (stack == null) { + return new CompoundTag(); + } else { + CompoundTag tag = stack.what().toTagGeneric().copy(); + tag.putLong("#", stack.amount()); + return tag; + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ae2/ticking/TickHandlerMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/ticking/TickHandlerMixin.java new file mode 100644 index 000000000..397b042bd --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ae2/ticking/TickHandlerMixin.java @@ -0,0 +1,67 @@ +package org.gtlcore.gtlcore.mixin.ae2.ticking; + +import net.minecraft.CrashReport; +import net.minecraft.ReportedException; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; + +import appeng.crafting.CraftingCalculation; +import appeng.hooks.ticking.TickHandler; +import appeng.me.Grid; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(TickHandler.class) +public abstract class TickHandlerMixin { + + @Shadow(remap = false) + public Iterable getGridList() { + throw new AssertionError(); + } + + @Shadow(remap = false) + private void readyBlockEntities(ServerLevel level) { + throw new AssertionError(); + } + + /** + * @author Dragons + * @reason 删去simulateCraftingJobs调用 + */ + @Overwrite(remap = false) + private void onServerLevelTickEnd(ServerLevel level) { + this.readyBlockEntities(level); + + // tick networks + for (var g : this.getGridList()) { + try { + g.onLevelEndTick(level); + } catch (Throwable t) { + CrashReport crashReport = CrashReport.forThrowable(t, "Ticking grid on end of level tick"); + g.fillCrashReportCategory(crashReport.addCategory("Grid being ticked")); + level.fillReportDetails(crashReport); + throw new ReportedException(crashReport); + } + } + } + + /** + * @author Dragons + * @reason 禁用 + */ + @Overwrite(remap = false) + public void registerCraftingSimulation(Level level, CraftingCalculation craftingCalculation) { + throw new AssertionError(); + } + + /** + * @author Dragons + * @reason 禁用 + */ + @Overwrite(remap = false) + private void simulateCraftingJobs(LevelAccessor level) { + throw new AssertionError(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/extendedae/InfinityCellMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/extendedae/InfinityCellMixin.java new file mode 100644 index 000000000..a0ecd7a23 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/extendedae/InfinityCellMixin.java @@ -0,0 +1,19 @@ +package org.gtlcore.gtlcore.mixin.extendedae; + +import appeng.api.stacks.AEKey; +import com.glodblock.github.extendedae.common.items.InfinityCell; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +@Mixin(InfinityCell.class) +public abstract class InfinityCellMixin { + + /** + * @author Dragons + * @reason 无限元件提供9.2P下单能力 + */ + @Overwrite(remap = false) + public static long getAsIntMax(AEKey key) { + return Long.MAX_VALUE; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/extendedae/TileExIOPortMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/extendedae/TileExIOPortMixin.java new file mode 100644 index 000000000..e57522a9f --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/extendedae/TileExIOPortMixin.java @@ -0,0 +1,15 @@ +package org.gtlcore.gtlcore.mixin.extendedae; + +import com.glodblock.github.extendedae.common.tileentities.TileExIOPort; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.ModifyConstant; + +@Mixin(TileExIOPort.class) +public abstract class TileExIOPortMixin { + + @ModifyConstant(method = "tickingRequest", constant = @Constant(longValue = 2048L), remap = false) + private long replaceItemsToMove(long original) { + return Integer.MAX_VALUE * 64L; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/CommonProxyMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/CommonProxyMixin.java new file mode 100644 index 000000000..4d2769a33 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/CommonProxyMixin.java @@ -0,0 +1,32 @@ +package org.gtlcore.gtlcore.mixin.gtm; + +import org.gtlcore.gtlcore.api.recipe.ingredient.LongIngredient; + +import com.gregtechceu.gtceu.api.recipe.ingredient.*; +import com.gregtechceu.gtceu.common.CommonProxy; + +import net.minecraftforge.common.crafting.CraftingHelper; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +@Mixin(CommonProxy.class) +public class CommonProxyMixin { + + /** + * @author . + * @reason . + */ + @SubscribeEvent + @Overwrite(remap = false) + public void commonSetup(FMLCommonSetupEvent event) { + event.enqueueWork(() -> { + CraftingHelper.register(SizedIngredient.TYPE, SizedIngredient.SERIALIZER); + CraftingHelper.register(LongIngredient.TYPE, LongIngredient.SERIALIZER); + CraftingHelper.register(IntCircuitIngredient.TYPE, IntCircuitIngredient.SERIALIZER); + CraftingHelper.register(IntProviderIngredient.TYPE, IntProviderIngredient.SERIALIZER); + }); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/MEStockingBusPartMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/MEStockingBusPartMachineMixin.java deleted file mode 100644 index 7cd8c3e5e..000000000 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/MEStockingBusPartMachineMixin.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.gtlcore.gtlcore.mixin.gtm; - -import com.gregtechceu.gtceu.integration.ae2.machine.MEStockingBusPartMachine; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyArg; - -/** - * @author EasterFG on 2025/2/10 - */ -@Mixin(MEStockingBusPartMachine.class) -public class MEStockingBusPartMachineMixin { - - @ModifyArg(method = "syncME", - at = @At(value = "INVOKE", - target = "Lappeng/api/storage/MEStorage;extract(Lappeng/api/stacks/AEKey;JLappeng/api/config/Actionable;Lappeng/api/networking/security/IActionSource;)J"), - index = 1, - remap = false) - private long modifySlotLimit(long limit) { - return Integer.MAX_VALUE; - } -} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/MultiblockInWorldPreviewRendererMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/MultiblockInWorldPreviewRendererMixin.java new file mode 100644 index 000000000..8ace188e8 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/MultiblockInWorldPreviewRendererMixin.java @@ -0,0 +1,153 @@ +package org.gtlcore.gtlcore.mixin.gtm; + +import com.gregtechceu.gtceu.api.block.IMachineBlock; +import com.gregtechceu.gtceu.api.block.MetaMachineBlock; +import com.gregtechceu.gtceu.api.data.RotationState; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.MultiblockMachineDefinition; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; +import com.gregtechceu.gtceu.api.machine.multiblock.MultiblockControllerMachine; +import com.gregtechceu.gtceu.api.pattern.MultiblockShapeInfo; +import com.gregtechceu.gtceu.client.renderer.MultiblockInWorldPreviewRenderer; + +import com.lowdragmc.lowdraglib.utils.BlockInfo; +import com.lowdragmc.lowdraglib.utils.TrackedDummyWorld; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.block.Rotation; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.*; + +@Mixin(MultiblockInWorldPreviewRenderer.class) +public class MultiblockInWorldPreviewRendererMixin { + + @Shadow(remap = false) + private static BlockPos LAST_POS; + @Shadow(remap = false) + private static int LAST_LAYER; + @Shadow(remap = false) + private static TrackedDummyWorld LEVEL; + + @Shadow(remap = false) + private static BlockPos rotateByFrontAxis(BlockPos pos, Direction front, Rotation rotation) { + return pos; + } + + @Shadow(remap = false) + private static void prepareBuffers(TrackedDummyWorld level, Collection renderedBlocks, int duration) {} + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public static void showPreview(BlockPos pos, MultiblockControllerMachine controller, int duration) { + if (!controller.getDefinition().isRenderWorldPreview()) return; + Direction front = controller.getFrontFacing(); + Direction up = controller.getUpwardsFacing(); + MultiblockShapeInfo shapeInfo = controller.getDefinition().getMatchingShapes().get(0); + + Map blockMap = new Object2ObjectOpenHashMap<>(); + IMultiController controllerBase = null; + LEVEL = new TrackedDummyWorld(); + + var blocks = shapeInfo.getBlocks(); + BlockPos controllerPatternPos = null; + var maxY = 0; + // find the pos of controller + l: + for (int x = 0; x < blocks.length; x++) { + BlockInfo[][] aisle = blocks[x]; + maxY = Math.max(maxY, aisle.length); + for (int y = 0; y < aisle.length; y++) { + BlockInfo[] column = aisle[y]; + for (int z = 0; z < column.length; z++) { + var info = column[z]; + if (info == null) continue; + if (info.getBlockState().getBlock() instanceof IMachineBlock machineBlock && + machineBlock.getDefinition() instanceof MultiblockMachineDefinition) { + controllerPatternPos = new BlockPos(x, y, z); + break l; + } + } + } + } + + if (controllerPatternPos == null) return; + + if (LAST_POS != null && LAST_POS.equals(pos)) { + LAST_LAYER++; + if (LAST_LAYER >= maxY) LAST_LAYER = -1; + } else LAST_LAYER = -1; + LAST_POS = pos; + + for (int x = 0; x < blocks.length; x++) { + BlockInfo[][] aisle = blocks[x]; + for (int y = 0; y < aisle.length; y++) { + BlockInfo[] column = aisle[y]; + if (LAST_LAYER != -1 && LAST_LAYER != y) continue; + for (int z = 0; z < column.length; z++) { + var info = column[z]; + if (info == null) continue; + var blockState = info.getBlockState(); + var offset = new BlockPos(x, y, z).subtract(controllerPatternPos); + + // rotation + offset = switch (front) { + case NORTH, UP, DOWN -> offset.rotate(Rotation.NONE); + case SOUTH -> offset.rotate(Rotation.CLOCKWISE_180); + case EAST -> offset.rotate(Rotation.COUNTERCLOCKWISE_90); + case WEST -> offset.rotate(Rotation.CLOCKWISE_90); + }; + + Rotation r = up == Direction.NORTH ? Rotation.NONE : up == Direction.EAST ? Rotation.CLOCKWISE_90 : + up == Direction.SOUTH ? Rotation.CLOCKWISE_180 : + up == Direction.WEST ? Rotation.COUNTERCLOCKWISE_90 : Rotation.NONE; + + offset = rotateByFrontAxis(offset, front, r); + + if (blockState.getBlock() instanceof MetaMachineBlock machineBlock) { + var rotationState = machineBlock.getRotationState(); + if (rotationState != RotationState.NONE) { + var face = blockState.getValue(rotationState.property); + if (face.getAxis() != Direction.Axis.Y) { + face = switch (front) { + case NORTH, UP, DOWN -> front; + case SOUTH -> face.getOpposite(); + case WEST -> face.getCounterClockWise(); + case EAST -> face.getClockWise(); + }; + } + if (rotationState.test(face)) { + blockState = blockState.setValue(rotationState.property, face); + } + } + } + + BlockPos realPos = pos.offset(offset); + + if (info.getBlockEntity(realPos) instanceof IMachineBlockEntity holder && + holder.getMetaMachine() instanceof IMultiController cont) { + holder.getSelf().setLevel(LEVEL); + controllerBase = cont; + } else { + blockMap.put(realPos, BlockInfo.fromBlockState(blockState)); + } + } + } + } + + LEVEL.addBlocks(blockMap); + if (controllerBase != null) { + LEVEL.setInnerBlockEntity(controllerBase.self().holder.getSelf()); + } + + prepareBuffers(LEVEL, blockMap.keySet(), duration); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/MultiblockInfoCategoryMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/MultiblockInfoCategoryMixin.java index 7a0ed284a..1b5c7de96 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/MultiblockInfoCategoryMixin.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/MultiblockInfoCategoryMixin.java @@ -1,15 +1,24 @@ package org.gtlcore.gtlcore.mixin.gtm; +import com.gregtechceu.gtceu.api.machine.MultiblockMachineDefinition; +import com.gregtechceu.gtceu.api.registry.GTRegistries; import com.gregtechceu.gtceu.integration.jei.multipage.MultiblockInfoCategory; +import com.gregtechceu.gtceu.integration.jei.multipage.MultiblockInfoWrapper; -import com.lowdragmc.lowdraglib.Platform; +import net.minecraft.client.Minecraft; +import net.minecraft.util.thread.BlockableEventLoop; import mezz.jei.api.registration.IRecipeRegistration; +import org.jetbrains.annotations.NotNull; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import java.util.concurrent.CompletableFuture; + +import static com.gregtechceu.gtceu.integration.jei.multipage.MultiblockInfoCategory.RECIPE_TYPE; + /** * @author EasterFG on 2024/10/28 */ @@ -17,9 +26,16 @@ public class MultiblockInfoCategoryMixin { @Inject(method = "registerRecipes", at = @At("HEAD"), cancellable = true, remap = false) - private static void registerRecipes(IRecipeRegistration registry, CallbackInfo ci) { - if (Platform.isDevEnv()) { - ci.cancel(); - } + private static void registerRecipes(IRecipeRegistration registry, @NotNull CallbackInfo ci) { + BlockableEventLoop executor = Minecraft.getInstance(); + CompletableFuture.supplyAsync(() -> GTRegistries.MACHINES.values().stream() + .filter(MultiblockMachineDefinition.class::isInstance) + .map(MultiblockMachineDefinition.class::cast) + .filter(MultiblockMachineDefinition::isRenderXEIPreview) + .map(MultiblockInfoWrapper::new) + .toList(), executor).thenAcceptAsync(recipes -> { + registry.addRecipes(RECIPE_TYPE, recipes); + }, executor); + ci.cancel(); } } diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/MultiblockInfoWrapperMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/MultiblockInfoWrapperMixin.java new file mode 100644 index 000000000..b8ef11e5d --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/MultiblockInfoWrapperMixin.java @@ -0,0 +1,24 @@ +package org.gtlcore.gtlcore.mixin.gtm; + +import org.gtlcore.gtlcore.api.gui.PatternPreviewWidget; + +import com.gregtechceu.gtceu.api.machine.MultiblockMachineDefinition; +import com.gregtechceu.gtceu.integration.jei.multipage.MultiblockInfoWrapper; + +import com.lowdragmc.lowdraglib.gui.widget.Widget; + +import com.llamalad7.mixinextras.sugar.Local; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +@Mixin(MultiblockInfoWrapper.class) +public class MultiblockInfoWrapperMixin { + + @ModifyArg(method = "", + at = @At(value = "INVOKE", + target = "Lcom/lowdragmc/lowdraglib/jei/ModularWrapper;(Lcom/lowdragmc/lowdraglib/gui/widget/Widget;)V")) + private static Widget MultiblockInfoWrapper(Widget par1, @Local(ordinal = 0, argsOnly = true) MultiblockMachineDefinition definition) { + return PatternPreviewWidget.getPatternWidget(definition); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/OverclockingLogicMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/OverclockingLogicMixin.java index 8420e39d4..92aec5936 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/OverclockingLogicMixin.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/OverclockingLogicMixin.java @@ -1,5 +1,6 @@ package org.gtlcore.gtlcore.mixin.gtm; +import org.gtlcore.gtlcore.api.recipe.IAdvancedOCResult; import org.gtlcore.gtlcore.utils.NumberUtils; import com.gregtechceu.gtceu.api.recipe.OverclockingLogic; @@ -22,43 +23,89 @@ public class OverclockingLogicMixin { * @author mod_author * @reason 原版的高炉太慢 */ + @SuppressWarnings("DataFlowIssue") @Overwrite(remap = false) public static void heatingCoilOC(@NotNull OCParams params, @NotNull OCResult result, long maxVoltage, int providedTemp, int requiredTemp) { double duration = params.getDuration() * Math.max(0.5, (double) requiredTemp / providedTemp); double eut = params.getEut(); int ocAmount = params.getOcAmount(); - double parallel = 1; - int parallelIterAmount = 0; + double parallel = 1.0; boolean shouldParallel = false; - int ocLevel = 0; - while (ocAmount-- > 0) { - double potentialEUt = eut * STD_VOLTAGE_FACTOR; - if (potentialEUt > maxVoltage) break; - eut = potentialEUt; + int ocLevel; + int baseOCLevel = 0; + double vfPowParallel = 1.0; + + for (ocLevel = 0; ocAmount-- > 0; ++ocLevel) { + double potentialVoltage = eut * STD_VOLTAGE_FACTOR; + if (potentialVoltage > (double) maxVoltage) break; + + eut = potentialVoltage; if (shouldParallel) { parallel *= PERFECT_DURATION_FACTOR_INV; - parallelIterAmount++; + vfPowParallel *= STD_VOLTAGE_FACTOR; } else { double potentialDuration = duration * PERFECT_DURATION_FACTOR; - if (potentialDuration < 1) { + if (potentialDuration < 1.0) { parallel *= PERFECT_DURATION_FACTOR_INV; - parallelIterAmount++; + vfPowParallel *= STD_VOLTAGE_FACTOR; shouldParallel = true; } else { duration = potentialDuration; + ++baseOCLevel; } } - ocLevel++; } - result.init((long) (eut / Math.pow(STD_VOLTAGE_FACTOR, parallelIterAmount)), (int) duration, (int) parallel, - (long) eut, ocLevel); + eut *= Math.min(1, NumberUtils.pow95(Math.max(0, (providedTemp - requiredTemp) / 900))); + + ((IAdvancedOCResult) (Object) result).init((long) (eut / vfPowParallel), (int) duration, (int) parallel, (long) eut, baseOCLevel, ocLevel, PERFECT_DURATION_FACTOR, STD_VOLTAGE_FACTOR); } @Inject(method = "getOverclockForTier", at = @At("HEAD"), remap = false, cancellable = true) protected void getOverclockForTier(long voltage, CallbackInfoReturnable cir) { cir.setReturnValue(NumberUtils.getFakeVoltageTier(voltage)); } + + /** + * @author Dragons + * @reason 为OCResult提供更多信息 + */ + @SuppressWarnings("DataFlowIssue") + @Overwrite(remap = false) + public static void subTickParallelOC(@NotNull OCParams params, @NotNull OCResult result, long maxVoltage, double durationFactor, double voltageFactor) { + double duration = params.getDuration(); + double eut = (double) params.getEut(); + int ocAmount = params.getOcAmount(); + double parallel = 1.0; + boolean shouldParallel = false; + + int ocLevel; + int baseOCLevel = 0; + double vfPowParallel = 1.0; + + for (ocLevel = 0; ocAmount-- > 0; ++ocLevel) { + double potentialVoltage = eut * voltageFactor; + if (potentialVoltage > (double) maxVoltage) break; + + eut = potentialVoltage; + if (shouldParallel) { + parallel /= durationFactor; + vfPowParallel *= voltageFactor; + } else { + double potentialDuration = duration * durationFactor; + if (potentialDuration < 1.0) { + parallel /= durationFactor; + vfPowParallel *= voltageFactor; + shouldParallel = true; + } else { + duration = potentialDuration; + ++baseOCLevel; + } + } + } + + ((IAdvancedOCResult) (Object) result).init((long) (eut / vfPowParallel), (int) duration, (int) parallel, (long) eut, baseOCLevel, ocLevel, durationFactor, voltageFactor); + } } diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/RecipeLogicProviderMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/RecipeLogicProviderMixin.java index 76bad9101..c52eda79d 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/RecipeLogicProviderMixin.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/RecipeLogicProviderMixin.java @@ -1,5 +1,8 @@ package org.gtlcore.gtlcore.mixin.gtm; +import org.gtlcore.gtlcore.api.machine.trait.IRecipeStatus; +import org.gtlcore.gtlcore.utils.NumberUtils; + import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; import com.gregtechceu.gtceu.integration.jade.provider.CapabilityBlockProvider; @@ -7,7 +10,6 @@ import com.gregtechceu.gtceu.utils.FormattingUtil; import com.gregtechceu.gtceu.utils.GTUtil; -import net.minecraft.ChatFormatting; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; @@ -15,40 +17,36 @@ import net.minecraft.world.level.block.entity.BlockEntity; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import snownee.jade.api.BlockAccessor; import snownee.jade.api.ITooltip; import snownee.jade.api.config.IPluginConfig; +import static net.minecraft.ChatFormatting.*; +import static org.gtlcore.gtlcore.utils.TextUtil.GTL_CORE$VC; + /** * @author EasterFG on 2024/12/10 */ @Mixin(RecipeLogicProvider.class) public abstract class RecipeLogicProviderMixin extends CapabilityBlockProvider { - @Unique - private static final ChatFormatting[] GTL_CORE$VC = { - ChatFormatting.DARK_GRAY, - ChatFormatting.GRAY, - ChatFormatting.AQUA, - ChatFormatting.GOLD, - ChatFormatting.DARK_PURPLE, - ChatFormatting.BLUE, - ChatFormatting.LIGHT_PURPLE, - ChatFormatting.RED, - ChatFormatting.DARK_AQUA, - ChatFormatting.DARK_RED, - ChatFormatting.GREEN, - ChatFormatting.DARK_GREEN, - ChatFormatting.YELLOW, - ChatFormatting.BLUE, - ChatFormatting.RED - }; - protected RecipeLogicProviderMixin(ResourceLocation uid) { super(uid); } + @Inject(method = "write(Lnet/minecraft/nbt/CompoundTag;Lcom/gregtechceu/gtceu/api/machine/trait/RecipeLogic;)V", at = @At("HEAD"), remap = false) + protected void write(CompoundTag data, RecipeLogic capability, CallbackInfo ci) { + if (capability instanceof IRecipeStatus status) { + if (status.getRecipeStatus() != null && status.getRecipeStatus().reason() != null) + data.putString("reason", status.getRecipeStatus().reason().getString()); + if (status.getWorkingStatus() != null && status.getWorkingStatus().reason() != null) + data.putString("work_reason", status.getWorkingStatus().reason().getString()); + } + } + @Override protected void addTooltip(CompoundTag capData, ITooltip tooltip, Player player, BlockAccessor block, BlockEntity blockEntity, IPluginConfig config) { @@ -63,15 +61,15 @@ protected void addTooltip(CompoundTag capData, ITooltip tooltip, Player player, // Default behavior, if this TE is not a steam machine (or somehow not instanceof // IGregTechTileEntity...) var tier = GTUtil.getTierByVoltage(absEUt); - Component text = Component.literal(FormattingUtil.formatNumbers(absEUt)).withStyle(ChatFormatting.RED) - .append(Component.literal(" EU/t").withStyle(ChatFormatting.RESET) - .append(Component.literal(" (").withStyle(ChatFormatting.GREEN) + Component text = Component.literal(NumberUtils.formatLong(absEUt)).withStyle(RED) + .append(Component.literal(" EU/t").withStyle(RESET) + .append(Component.literal(" (").withStyle(GREEN) .append(Component .translatable("gtceu.top.electricity", FormattingUtil.formatNumber2Places(absEUt / ((float) GTValues.V[tier])), GTValues.VNF[tier]) .withStyle(style -> style.withColor(GTL_CORE$VC[tier]))) - .append(Component.literal(")").withStyle(ChatFormatting.GREEN)))); + .append(Component.literal(")").withStyle(GREEN)))); if (EUt > 0) { if (isInput) { @@ -81,6 +79,13 @@ protected void addTooltip(CompoundTag capData, ITooltip tooltip, Player player, } } } + String reason = capData.getString("work_reason"); + if (reason.isEmpty()) return; + tooltip.add(Component.translatable("gtceu.recipe.fail.reason", reason).withStyle(RED)); + } else { + String reason = capData.getString("reason"); + if (reason.isEmpty()) return; + tooltip.add(Component.translatable("gtceu.recipe.fail.reason", reason).withStyle(RED)); } } } diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/RecipeOutputProviderMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/RecipeOutputProviderMixin.java index cea10a25c..5f82a27b3 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/RecipeOutputProviderMixin.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/RecipeOutputProviderMixin.java @@ -1,7 +1,10 @@ package org.gtlcore.gtlcore.mixin.gtm; +import org.gtlcore.gtlcore.api.recipe.ingredient.LongIngredient; import org.gtlcore.gtlcore.utils.GTLUtil; +import org.gtlcore.gtlcore.utils.NumberUtils; +import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; import com.gregtechceu.gtceu.api.recipe.RecipeHelper; import com.gregtechceu.gtceu.integration.jade.provider.CapabilityBlockProvider; @@ -17,6 +20,7 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.level.block.entity.BlockEntity; import org.spongepowered.asm.mixin.Mixin; @@ -57,9 +61,30 @@ protected void write(CompoundTag data, RecipeLogic recipeLogic) { Map cache = new HashMap<>(); // int count = 0; ListTag itemTags = new ListTag(); - for (var stack : RecipeHelper.getOutputItems(recipe)) { - if (stack != null && !stack.isEmpty()) { - String sid = GTLUtil.getItemId(stack.getItem()); + for (Ingredient outputContent : RecipeHelper.getOutputContents(recipe, ItemRecipeCapability.CAP)) { + final var stack = outputContent.getItems()[0]; + if (stack == null || stack.isEmpty()) continue; + String sid = GTLUtil.getItemId(stack.getItem()); + if (outputContent instanceof LongIngredient longIngredient) { + if (cache.containsKey(sid)) { + CompoundTag tag = cache.get(sid); + if (tag != null) { + long amount = tag.getLong("Count"); + if (amount > 0) { + tag.putLong("Count", amount + longIngredient.getActualAmount()); + } + } + } else { + var itemTag = new CompoundTag(); + itemTag.putString("id", sid); + itemTag.putLong("Count", longIngredient.getActualAmount()); + if (stack.getTag() != null) { + itemTag.put("tag", stack.getTag().copy()); + } + cache.put(sid, itemTag); + itemTags.add(itemTag); + } + } else { if (cache.containsKey(sid)) { CompoundTag tag = cache.get(sid); if (tag != null) { @@ -158,7 +183,7 @@ protected void addTooltip(CompoundTag capData, ITooltip tooltip, Player player, long count = tag.getLong("Count"); iTooltip.add(helper.smallItem(stack)); Component text = Component.literal(" ") - .append(String.valueOf(count)) + .append(NumberUtils.formatLong(count)) .append("× ") .append(getItemName(stack)) .withStyle(ChatFormatting.WHITE); diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/RockBreakerConditionMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/RockBreakerConditionMixin.java index 7cad9c485..5b065b4b1 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/RockBreakerConditionMixin.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/RockBreakerConditionMixin.java @@ -1,5 +1,7 @@ package org.gtlcore.gtlcore.mixin.gtm; +import org.gtlcore.gtlcore.api.recipe.RecipeResult; + import com.gregtechceu.gtceu.api.capability.recipe.FluidRecipeCapability; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.machine.multiblock.WorkableElectricMultiblockMachine; @@ -38,6 +40,7 @@ public boolean test(@NotNull GTRecipe recipe, @NotNull RecipeLogic recipeLogic) if (tank.getFluidInTank(0).getFluid() == fluidA) hasFluidA = true; if (tank.getFluidInTank(0).getFluid() == fluidB) hasFluidB = true; if (hasFluidA && hasFluidB) return true; + else RecipeResult.of(recipeLogic.machine, RecipeResult.FAIL_LACK_FLUID); } } } else { @@ -49,6 +52,7 @@ public boolean test(@NotNull GTRecipe recipe, @NotNull RecipeLogic recipeLogic) if (fluid.getType() == fluidA) hasFluidA = true; if (fluid.getType() == fluidB) hasFluidB = true; if (hasFluidA && hasFluidB) return true; + else RecipeResult.of(recipeLogic.machine, RecipeResult.FAIL_LACK_FLUID); } } } diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEBusPartMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEBusPartMachineMixin.java new file mode 100644 index 000000000..4fcf239af --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEBusPartMachineMixin.java @@ -0,0 +1,31 @@ +package org.gtlcore.gtlcore.mixin.gtm.ae.machine; + +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.common.machine.multiblock.part.ItemBusPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.MEBusPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; + +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; + +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Mixin; + +import java.util.EnumSet; + +@Mixin(MEBusPartMachine.class) +public abstract class MEBusPartMachineMixin extends ItemBusPartMachine implements IGridConnectedMachine { + + public MEBusPartMachineMixin(IMachineBlockEntity holder, int tier, IO io, Object... args) { + super(holder, tier, io, args); + } + + @Override + public void loadCustomPersistedData(@NotNull CompoundTag tag) { + super.loadCustomPersistedData(tag); + if (tag.getCompound("ForgeData").getBoolean("isAllFacing")) { + getMainNode().setExposedOnSides(EnumSet.allOf(Direction.class)); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEHatchPartMachineAccessor.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEHatchPartMachineAccessor.java new file mode 100644 index 000000000..b47e05e17 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEHatchPartMachineAccessor.java @@ -0,0 +1,14 @@ +package org.gtlcore.gtlcore.mixin.gtm.ae.machine; + +import com.gregtechceu.gtceu.integration.ae2.machine.MEHatchPartMachine; + +import appeng.api.networking.security.IActionSource; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(MEHatchPartMachine.class) +public interface MEHatchPartMachineAccessor { + + @Accessor(value = "actionSource", remap = false) + IActionSource getActionSource(); +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEHatchPartMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEHatchPartMachineMixin.java new file mode 100644 index 000000000..ca4dbd1c2 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEHatchPartMachineMixin.java @@ -0,0 +1,31 @@ +package org.gtlcore.gtlcore.mixin.gtm.ae.machine; + +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.common.machine.multiblock.part.FluidHatchPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.MEHatchPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; + +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; + +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Mixin; + +import java.util.EnumSet; + +@Mixin(MEHatchPartMachine.class) +public abstract class MEHatchPartMachineMixin extends FluidHatchPartMachine implements IGridConnectedMachine { + + public MEHatchPartMachineMixin(IMachineBlockEntity holder, int tier, IO io, long initialCapacity, int slots, Object... args) { + super(holder, tier, io, initialCapacity, slots, args); + } + + @Override + public void loadCustomPersistedData(@NotNull CompoundTag tag) { + super.loadCustomPersistedData(tag); + if (tag.getCompound("ForgeData").getBoolean("isAllFacing")) { + getMainNode().setExposedOnSides(EnumSet.allOf(Direction.class)); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEInputBusPartMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEInputBusPartMachineMixin.java new file mode 100644 index 000000000..3ac5ed5bc --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEInputBusPartMachineMixin.java @@ -0,0 +1,45 @@ +package org.gtlcore.gtlcore.mixin.gtm.ae.machine; + +import org.gtlcore.gtlcore.api.machine.trait.MEStock.IMEPartMachine; + +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.integration.ae2.machine.MEBusPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.MEInputBusPartMachine; +import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEItemList; + +import appeng.api.networking.IGridNodeListener; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(MEInputBusPartMachine.class) +public abstract class MEInputBusPartMachineMixin extends MEBusPartMachine { + + @Shadow(remap = false) + protected ExportOnlyAEItemList aeItemHandler; + + public MEInputBusPartMachineMixin(IMachineBlockEntity holder, IO io, Object... args) { + super(holder, io, args); + } + + @Inject(method = "autoIO", + at = @At(value = "INVOKE", + target = "Lcom/gregtechceu/gtceu/integration/ae2/machine/MEInputBusPartMachine;syncME()V", + shift = At.Shift.AFTER), + remap = false) + public void autoIO(CallbackInfo ci) { + if (aeItemHandler instanceof IMEPartMachine machine) { + machine.setChanged(true); + } + } + + @Override + public void onMainNodeStateChanged(IGridNodeListener.@NotNull State reason) { + super.onMainNodeStateChanged(reason); + if (getMainNode().isOnline()) aeItemHandler.notifyListeners(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEInputHatchPartMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEInputHatchPartMachineMixin.java new file mode 100644 index 000000000..daf7dac9f --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEInputHatchPartMachineMixin.java @@ -0,0 +1,45 @@ +package org.gtlcore.gtlcore.mixin.gtm.ae.machine; + +import org.gtlcore.gtlcore.api.machine.trait.MEStock.IMEPartMachine; + +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.integration.ae2.machine.MEHatchPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.MEInputHatchPartMachine; +import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEFluidList; + +import appeng.api.networking.IGridNodeListener; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(MEInputHatchPartMachine.class) +public abstract class MEInputHatchPartMachineMixin extends MEHatchPartMachine { + + @Shadow(remap = false) + protected ExportOnlyAEFluidList aeFluidHandler; + + public MEInputHatchPartMachineMixin(IMachineBlockEntity holder, IO io, Object... args) { + super(holder, io, args); + } + + @Inject(method = "autoIO", + at = @At(value = "INVOKE", + target = "Lcom/gregtechceu/gtceu/integration/ae2/machine/MEInputHatchPartMachine;syncME()V", + shift = At.Shift.AFTER), + remap = false) + public void autoIO(CallbackInfo ci) { + if (aeFluidHandler instanceof IMEPartMachine machine) { + machine.setChanged(true); + } + } + + @Override + public void onMainNodeStateChanged(IGridNodeListener.@NotNull State reason) { + super.onMainNodeStateChanged(reason); + if (getMainNode().isOnline()) aeFluidHandler.notifyListeners(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEOutputBusPartMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEOutputBusPartMachineMixin.java new file mode 100644 index 000000000..ca1d8846b --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEOutputBusPartMachineMixin.java @@ -0,0 +1,72 @@ +package org.gtlcore.gtlcore.mixin.gtm.ae.machine; + +import org.gtlcore.gtlcore.api.machine.trait.MEPart.IMEOutputPart; + +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.gui.fancy.ConfiguratorPanel; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.integration.ae2.machine.MEBusPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.MEOutputBusPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; +import com.gregtechceu.gtceu.integration.ae2.utils.KeyStorage; + +import appeng.api.networking.IGrid; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; + +@SuppressWarnings("all") +@Mixin(MEOutputBusPartMachine.class) +public abstract class MEOutputBusPartMachineMixin extends MEBusPartMachine implements IMEOutputPart, IGridConnectedMachine { + + @Unique + private byte gTLCore$time; + + @Shadow(remap = false) + private KeyStorage internalBuffer; + + public MEOutputBusPartMachineMixin(IMachineBlockEntity holder, IO io, Object... args) { + super(holder, io, args); + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public void autoIO() { + if (this.isReturn()) { + this.gTLCore$time++; + if (this.updateMEStatus()) { + IGrid grid = this.getMainNode().getGrid(); + if (grid != null && !this.internalBuffer.isEmpty()) { + this.internalBuffer.insertInventory(grid.getStorageService().getInventory(), this.actionSource); + } + this.updateInventorySubscription(); + } + } else this.gTLCore$time++; + } + + @Override + public void attachConfigurators(@NotNull ConfiguratorPanel configuratorPanel) { + super.attachConfigurators(configuratorPanel); + IMEOutputPart.attachRecipeLockable(configuratorPanel, this); + } + + @Override + public void returnStorage() { + this.gTLCore$time = 0; + } + + @Override + public byte getTime() { + return gTLCore$time; + } + + @Override + public void setTime(byte time) { + gTLCore$time = time; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEOutputHatchPartMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEOutputHatchPartMachineMixin.java new file mode 100644 index 000000000..c103fc069 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEOutputHatchPartMachineMixin.java @@ -0,0 +1,72 @@ +package org.gtlcore.gtlcore.mixin.gtm.ae.machine; + +import org.gtlcore.gtlcore.api.machine.trait.MEPart.IMEOutputPart; + +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.gui.fancy.ConfiguratorPanel; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.integration.ae2.machine.MEHatchPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.MEOutputHatchPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; +import com.gregtechceu.gtceu.integration.ae2.utils.KeyStorage; + +import appeng.api.networking.IGrid; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; + +@SuppressWarnings("all") +@Mixin(MEOutputHatchPartMachine.class) +public abstract class MEOutputHatchPartMachineMixin extends MEHatchPartMachine implements IMEOutputPart, IGridConnectedMachine { + + @Unique + private byte gTLCore$time; + + @Shadow(remap = false) + private KeyStorage internalBuffer; + + public MEOutputHatchPartMachineMixin(IMachineBlockEntity holder, IO io, Object... args) { + super(holder, io, args); + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + protected void autoIO() { + if (this.isReturn()) { + this.gTLCore$time++; + if (this.updateMEStatus()) { + IGrid grid = this.getMainNode().getGrid(); + if (grid != null && !this.internalBuffer.isEmpty()) { + this.internalBuffer.insertInventory(grid.getStorageService().getInventory(), this.actionSource); + } + this.updateTankSubscription(); + } + } else this.gTLCore$time++; + } + + @Override + public void attachConfigurators(@NotNull ConfiguratorPanel configuratorPanel) { + super.attachConfigurators(configuratorPanel); + IMEOutputPart.attachRecipeLockable(configuratorPanel, this); + } + + @Override + public void returnStorage() { + this.gTLCore$time = 0; + } + + @Override + public byte getTime() { + return gTLCore$time; + } + + @Override + public void setTime(byte time) { + gTLCore$time = time; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEStockingBusPartMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEStockingBusPartMachineMixin.java new file mode 100644 index 000000000..d227b6ec8 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEStockingBusPartMachineMixin.java @@ -0,0 +1,116 @@ +package org.gtlcore.gtlcore.mixin.gtm.ae.machine; + +import org.gtlcore.gtlcore.api.machine.trait.MEStock.IMEPartMachine; +import org.gtlcore.gtlcore.api.machine.trait.MEStock.IMESlot; + +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.common.item.IntCircuitBehaviour; +import com.gregtechceu.gtceu.integration.ae2.machine.MEInputBusPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.MEStockingBusPartMachine; +import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEItemSlot; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.TickTask; +import net.minecraft.server.level.ServerLevel; + +import appeng.api.config.Actionable; +import appeng.api.networking.IGrid; +import appeng.api.stacks.*; +import appeng.api.storage.MEStorage; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import org.spongepowered.asm.mixin.*; + +import java.util.function.Predicate; + +@Mixin(MEStockingBusPartMachine.class) +public abstract class MEStockingBusPartMachineMixin extends MEInputBusPartMachine { + + @Shadow(remap = false) + private Predicate autoPullTest; + + @Shadow(remap = false) + public void setAutoPull(boolean autoPull) { + throw new AssertionError(); + } + + public MEStockingBusPartMachineMixin(IMachineBlockEntity holder, Object... args) { + super(holder, args); + } + + /** + * @author Dragons + * @reason 设置完所有slot才进行onConfigChanged + */ + @Overwrite(remap = false) + private void refreshList() { + IGrid grid = this.getMainNode().getGrid(); + if (grid == null) { + this.aeItemHandler.clearInventory(0); + } else { + MEStorage networkStorage = grid.getStorageService().getInventory(); + var counter = networkStorage.getAvailableStacks(); + + int index = 0; + for (Object2LongMap.Entry entry : counter) { + if (index >= 16) { + break; + } + + AEKey what = entry.getKey(); + long amount = entry.getLongValue(); + if (amount > 0L && what instanceof AEItemKey itemKey) { + long request = networkStorage.extract(what, amount, Actionable.SIMULATE, this.actionSource); + if (request != 0L && (this.autoPullTest == null || this.autoPullTest.test(new GenericStack(itemKey, amount)))) { + ExportOnlyAEItemSlot slot = this.aeItemHandler.getInventory()[index]; + ((IMESlot) slot).setConfigWithoutNotify(new GenericStack(what, 1L)); + slot.setStock(new GenericStack(what, request)); + ++index; + } + } + } + + this.aeItemHandler.clearInventory(index); + + ((IMEPartMachine) this.aeItemHandler).onConfigChanged(); + } + } + + @Override + protected void readConfigFromTag(CompoundTag tag) { + if (tag.getBoolean("AutoPull")) { + this.setAutoPull(true); + this.circuitInventory.setStackInSlot(0, IntCircuitBehaviour.stack(tag.getByte("GhostCircuit"))); + } else { + this.setAutoPull(false); + if (tag.contains("ConfigStacks")) { + CompoundTag configStacks = tag.getCompound("ConfigStacks"); + + final var inventory = this.aeItemHandler.getInventory(); + + for (int i = 0; i < 16; ++i) { + String key = Integer.toString(i); + if (configStacks.contains(key)) { + CompoundTag configTag = configStacks.getCompound(key); + ((IMESlot) inventory[i]).setConfigWithoutNotify(GenericStack.readTag(configTag)); + } else { + ((IMESlot) inventory[i]).setConfigWithoutNotify(null); + } + } + + ((IMEPartMachine) this.aeItemHandler).onConfigChanged(); + } + + if (tag.contains("GhostCircuit")) { + this.circuitInventory.setStackInSlot(0, IntCircuitBehaviour.stack(tag.getByte("GhostCircuit"))); + } + } + } + + @Override + public void onLoad() { + super.onLoad(); + if (getLevel() instanceof ServerLevel serverLevel) { + serverLevel.getServer().tell(new TickTask(1, () -> ((IMEPartMachine) this.aeItemHandler).onConfigChanged())); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEStockingHatchPartMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEStockingHatchPartMachineMixin.java new file mode 100644 index 000000000..123192d0e --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/machine/MEStockingHatchPartMachineMixin.java @@ -0,0 +1,117 @@ +package org.gtlcore.gtlcore.mixin.gtm.ae.machine; + +import org.gtlcore.gtlcore.api.machine.trait.MEStock.IMEPartMachine; +import org.gtlcore.gtlcore.api.machine.trait.MEStock.IMESlot; + +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.common.item.IntCircuitBehaviour; +import com.gregtechceu.gtceu.integration.ae2.machine.MEInputHatchPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.MEStockingHatchPartMachine; +import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEFluidSlot; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.TickTask; +import net.minecraft.server.level.ServerLevel; + +import appeng.api.config.Actionable; +import appeng.api.networking.IGrid; +import appeng.api.stacks.*; +import appeng.api.storage.MEStorage; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import org.spongepowered.asm.mixin.*; + +import java.util.function.Predicate; + +@Mixin(MEStockingHatchPartMachine.class) +public abstract class MEStockingHatchPartMachineMixin extends MEInputHatchPartMachine { + + @Shadow(remap = false) + private Predicate autoPullTest; + + @Shadow(remap = false) + public void setAutoPull(boolean autoPull) { + throw new AssertionError(); + } + + public MEStockingHatchPartMachineMixin(IMachineBlockEntity holder, Object... args) { + super(holder, args); + } + + /** + * @author Dragons + * @reason 设置完所有slot才进行onConfigChanged + */ + @Overwrite(remap = false) + private void refreshList() { + IGrid grid = this.getMainNode().getGrid(); + if (grid == null) { + this.aeFluidHandler.clearInventory(0); + } else { + MEStorage networkStorage = grid.getStorageService().getInventory(); + var counter = networkStorage.getAvailableStacks(); + + int index = 0; + + for (Object2LongMap.Entry entry : counter) { + if (index >= 16) { + break; + } + + AEKey what = entry.getKey(); + long amount = entry.getLongValue(); + if (amount > 0L && what instanceof AEFluidKey fluidKey) { + long request = networkStorage.extract(what, amount, Actionable.SIMULATE, this.actionSource); + if (request != 0L && (this.autoPullTest == null || this.autoPullTest.test(new GenericStack(fluidKey, amount)))) { + ExportOnlyAEFluidSlot slot = this.aeFluidHandler.getInventory()[index]; + ((IMESlot) slot).setConfigWithoutNotify(new GenericStack(what, 1L)); + slot.setStock(new GenericStack(what, request)); + ++index; + } + } + } + + this.aeFluidHandler.clearInventory(index); + + ((IMEPartMachine) this.aeFluidHandler).onConfigChanged(); + } + } + + @Override + protected void readConfigFromTag(CompoundTag tag) { + if (tag.getBoolean("AutoPull")) { + this.setAutoPull(true); + this.circuitInventory.setStackInSlot(0, IntCircuitBehaviour.stack(tag.getByte("GhostCircuit"))); + } else { + this.setAutoPull(false); + if (tag.contains("ConfigStacks")) { + CompoundTag configStacks = tag.getCompound("ConfigStacks"); + + final var inventory = this.aeFluidHandler.getInventory(); + + for (int i = 0; i < 16; ++i) { + String key = Integer.toString(i); + if (configStacks.contains(key)) { + CompoundTag configTag = configStacks.getCompound(key); + ((IMESlot) inventory[i]).setConfigWithoutNotify(GenericStack.readTag(configTag)); + } else { + ((IMESlot) inventory[i]).setConfigWithoutNotify(null); + } + } + + ((IMEPartMachine) this.aeFluidHandler).onConfigChanged(); + } + + if (tag.contains("GhostCircuit")) { + this.circuitInventory.setStackInSlot(0, IntCircuitBehaviour.stack(tag.getByte("GhostCircuit"))); + } + } + } + + @Override + public void onLoad() { + super.onLoad(); + if (getLevel() instanceof ServerLevel serverLevel) { + serverLevel.getServer().tell(new TickTask(1, () -> ((IMEPartMachine) this.aeFluidHandler).onConfigChanged())); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAEFluidListMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAEFluidListMixin.java new file mode 100644 index 000000000..10b0f51f8 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAEFluidListMixin.java @@ -0,0 +1,118 @@ +package org.gtlcore.gtlcore.mixin.gtm.ae.slot; + +import org.gtlcore.gtlcore.api.machine.trait.MEStock.IMEPartMachine; + +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.trait.NotifiableFluidTank; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; +import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEFluidList; +import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEFluidSlot; + +import com.lowdragmc.lowdraglib.side.fluid.FluidStack; + +import appeng.api.stacks.GenericStack; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.*; + +@Mixin(ExportOnlyAEFluidList.class) +public abstract class ExportOnlyAEFluidListMixin extends NotifiableFluidTank implements IMEPartMachine { + + @Getter + protected final ObjectArrayList fluidList = new ObjectArrayList<>(); + + @Setter + private boolean changed = true; + + @Shadow(remap = false) + protected ExportOnlyAEFluidSlot[] inventory; + + public ExportOnlyAEFluidListMixin(MetaMachine machine, int slots, long capacity, IO io, IO capabilityIO) { + super(machine, slots, capacity, io, capabilityIO); + } + + @Override + public List handleRecipeInner(IO io, GTRecipe recipe, List left, @Nullable String slotName, boolean simulate) { + if (io == IO.IN) { + boolean changed = false; + var listIterator = left.listIterator(); + while (listIterator.hasNext()) { + FluidIngredient ingredient = listIterator.next(); + if (ingredient.isEmpty()) { + listIterator.remove(); + } else { + long count = ingredient.getAmount(); + if (count < 1) listIterator.remove(); + else { + for (ExportOnlyAEFluidSlot i : this.inventory) { + GenericStack stored = i.getStock(); + if (stored != null) { + long amount = stored.amount(); + if (amount != 0L) { + if (ingredient.test(i.getFluid())) { + FluidStack drained = i.drain(count, simulate, !simulate); + if (drained.getAmount() > 0L) { + changed = true; + count -= drained.getAmount(); + ingredient.setAmount(count); + } + } + if (count <= 0L) { + listIterator.remove(); + break; + } + } + } + } + } + } + } + if (!simulate && changed) { + this.changed = true; + this.onContentsChanged(); + } + } + return left.isEmpty() ? null : left; + } + + @Override + public void onContentsChanged() { + super.onContentsChanged(); + this.changed = true; + } + + @Override + public boolean getChanged() { + return changed; + } + + @Override + public @NotNull List getMEFluidList() { + if (changed) { + changed = false; + fluidList.clear(); + for (var slot : inventory) { + GenericStack stock = slot.getStock(); + if (stock != null && stock.amount() != 0L) { + FluidStack stack = slot.getFluid(); + if (!stack.isEmpty()) fluidList.add(stack); + } + } + } + return fluidList; + } + + @SuppressWarnings("unchecked") + @Override + public List getContents() { + return (List) (List) getMEFluidList(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAEItemListMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAEItemListMixin.java new file mode 100644 index 000000000..9d3db50f7 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAEItemListMixin.java @@ -0,0 +1,123 @@ +package org.gtlcore.gtlcore.mixin.gtm.ae.slot; + +import org.gtlcore.gtlcore.api.machine.trait.MEStock.IMEPartMachine; +import org.gtlcore.gtlcore.api.recipe.ingredient.LongIngredient; + +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.trait.NotifiableItemStackHandler; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.ingredient.SizedIngredient; +import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEItemList; +import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEItemSlot; + +import com.lowdragmc.lowdraglib.misc.ItemStackTransfer; + +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + +import appeng.api.stacks.GenericStack; +import com.google.common.primitives.Ints; +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.*; +import java.util.function.Function; + +@Mixin(ExportOnlyAEItemList.class) +public abstract class ExportOnlyAEItemListMixin extends NotifiableItemStackHandler implements IMEPartMachine { + + @Getter + protected final Object2LongOpenHashMap itemMap = new Object2LongOpenHashMap<>(); + @Setter + protected boolean changed = true; + + @Shadow(remap = false) + protected ExportOnlyAEItemSlot[] inventory; + + public ExportOnlyAEItemListMixin(MetaMachine machine, int slots, @NotNull IO handlerIO, @NotNull IO capabilityIO, Function transferFactory) { + super(machine, slots, handlerIO, capabilityIO, transferFactory); + } + + @Override + public List handleRecipeInner(IO io, GTRecipe recipe, List left, @Nullable String slotName, boolean simulate) { + if (io == IO.IN) { + boolean changed = false; + var listIterator = left.listIterator(); + while (listIterator.hasNext()) { + Ingredient ingredient = listIterator.next(); + if (ingredient.isEmpty()) { + listIterator.remove(); + } else { + long amount; + if (ingredient instanceof LongIngredient li) amount = li.getActualAmount(); + else if (ingredient instanceof SizedIngredient si) amount = si.getAmount(); + else amount = 1; + if (amount < 1) listIterator.remove(); + else { + for (ExportOnlyAEItemSlot i : this.inventory) { + GenericStack stored = i.getStock(); + if (stored != null && stored.amount() != 0) { + if (ingredient.test(i.getStackInSlot(0))) { + ItemStack extracted = i.extractItem(0, Ints.saturatedCast(amount), simulate, !simulate); + if (extracted.getCount() > 0) { + changed = true; + amount -= extracted.getCount(); + } + } + if (amount <= 0L) { + listIterator.remove(); + break; + } + } + } + } + } + } + if (!simulate && changed) { + this.changed = true; + this.onContentsChanged(); + } + } + return left.isEmpty() ? null : left; + } + + @Override + public void onContentsChanged() { + super.onContentsChanged(); + this.changed = true; + } + + @Override + public Object2LongOpenHashMap getMEItemMap() { + if (changed) { + changed = false; + itemMap.clear(); + for (var slot : inventory) { + GenericStack stock = slot.getStock(); + if (stock != null && stock.amount() != 0L) { + ItemStack stack = slot.getStackInSlot(0); + if (!stack.isEmpty()) this.itemMap.addTo(stack, stock.amount()); + } + } + } + return itemMap.isEmpty() ? null : itemMap; + } + + @Override + public boolean getChanged() { + return changed; + } + + @Override + public List getContents() { + var itemMap = this.getMEItemMap(); + if (itemMap == null) return Collections.emptyList(); + return Arrays.asList(itemMap.keySet().toArray()); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAEItemSlotMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAEItemSlotMixin.java new file mode 100644 index 000000000..3cd0ae626 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAEItemSlotMixin.java @@ -0,0 +1,29 @@ +package org.gtlcore.gtlcore.mixin.gtm.ae.slot; + +import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEItemSlot; +import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAESlot; + +import net.minecraft.world.item.ItemStack; + +import appeng.api.stacks.AEItemKey; +import com.google.common.primitives.Ints; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +@Mixin(ExportOnlyAEItemSlot.class) +public abstract class ExportOnlyAEItemSlotMixin extends ExportOnlyAESlot { + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public ItemStack getStackInSlot(int slot) { + if (slot == 0 && this.stock != null) { + return this.stock.what() instanceof AEItemKey itemKey ? + itemKey.toStack(Ints.saturatedCast(this.stock.amount())) : + ItemStack.EMPTY; + } + return ItemStack.EMPTY; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAESlotAccessor.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAESlotAccessor.java new file mode 100644 index 000000000..ca3a937eb --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAESlotAccessor.java @@ -0,0 +1,19 @@ +package org.gtlcore.gtlcore.mixin.gtm.ae.slot; + +import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAESlot; + +import appeng.api.stacks.GenericStack; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(ExportOnlyAESlot.class) +public interface ExportOnlyAESlotAccessor { + + @Accessor(value = "config", remap = false) + @Nullable + GenericStack getConfig(); + + @Accessor(value = "onContentsChanged", remap = false) + Runnable getOnContentsChanged(); +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAEStockingFluidListMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAEStockingFluidListMixin.java new file mode 100644 index 000000000..8ffc0d6b2 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAEStockingFluidListMixin.java @@ -0,0 +1,163 @@ +package org.gtlcore.gtlcore.mixin.gtm.ae.slot; + +import org.gtlcore.gtlcore.api.machine.trait.MEStock.IMESlot; +import org.gtlcore.gtlcore.config.ConfigHolder; +import org.gtlcore.gtlcore.integration.ae2.AEUtils; +import org.gtlcore.gtlcore.mixin.gtm.ae.machine.MEHatchPartMachineAccessor; + +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; +import com.gregtechceu.gtceu.integration.ae2.machine.MEStockingHatchPartMachine; +import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEFluidSlot; +import com.gregtechceu.gtceu.integration.ae2.slot.IConfigurableSlot; +import com.gregtechceu.gtceu.integration.ae2.slot.IConfigurableSlotList; + +import com.lowdragmc.lowdraglib.side.fluid.FluidStack; + +import appeng.api.config.Actionable; +import appeng.api.networking.IGrid; +import appeng.api.networking.security.IActionSource; +import appeng.api.stacks.AEFluidKey; +import appeng.api.stacks.GenericStack; +import appeng.api.storage.MEStorage; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; +import java.util.Objects; + +@Mixin(targets = "com.gregtechceu.gtceu.integration.ae2.machine.MEStockingHatchPartMachine$ExportOnlyAEStockingFluidList", remap = false) +public abstract class ExportOnlyAEStockingFluidListMixin extends ExportOnlyAEFluidListMixin implements IConfigurableSlotList { + + protected ObjectArrayList configList; + + protected IntArrayList configIndexList; + + private static final boolean ENABLE_ULTIMATE_ME_STOCKING = ConfigHolder.INSTANCE.enableUltimateMEStocking; + + @SuppressWarnings("target") + @Shadow(remap = false) + @Final + MEStockingHatchPartMachine this$0; + + public ExportOnlyAEStockingFluidListMixin(MetaMachine machine, int slots, long capacity, IO io, IO capabilityIO) { + super(machine, slots, capacity, io, capabilityIO); + } + + @SuppressWarnings("target") + @Inject(method = "(Lcom/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine;Lcom/gregtechceu/gtceu/api/machine/MetaMachine;I)V", + at = @At("TAIL")) + private void gtl$onInit(MEStockingHatchPartMachine holder, MetaMachine slots, int par3, CallbackInfo ci) { + configList = new ObjectArrayList<>(); + configIndexList = new IntArrayList(); + for (ExportOnlyAEFluidSlot exportOnlyAEFluidSlot : inventory) { + ((IMESlot) exportOnlyAEFluidSlot).setOnConfigChanged(this::onConfigChanged); + } + } + + @Override + public void clearInventory(int startIndex) { + for (int i = startIndex; i < this.getConfigurableSlots(); ++i) { + IConfigurableSlot slot = this.getConfigurableSlot(i); + ((IMESlot) slot).setConfigWithoutNotify(null); + slot.setStock(null); + } + } + + @Unique + @Override + public void onConfigChanged() { + configList.clear(); + configIndexList.clear(); + for (int i = 0, inventoryLength = inventory.length; i < inventoryLength; i++) { + final var config = inventory[i].getConfig(); + if (config != null && config.what() instanceof AEFluidKey key) { + configList.add(key); + configIndexList.add(i); + } + } + } + + @Override + public List handleRecipeInner(IO io, GTRecipe recipe, List left, @Nullable String slotName, boolean simulate) { + if (io != IO.IN || left.isEmpty()) { + return left; + } + IGrid grid = this$0.getMainNode().getGrid(); + if (grid == null) { + return left; + } + + MEStorage aeNetwork = grid.getStorageService().getInventory(); + boolean changed = false; + var listIterator = left.listIterator(); + + while (listIterator.hasNext()) { + FluidIngredient ingredient = listIterator.next(); + if (ingredient.isEmpty()) { + listIterator.remove(); + } else { + long amount = ingredient.getAmount(); + if (amount < 1) listIterator.remove(); + else { + for (int i = 0, configListSize = configList.size(); i < configListSize; i++) { + AEFluidKey aeFluidKey = configList.get(i); + if (AEUtils.testFluidIngredient(ingredient, aeFluidKey)) { + long extracted = aeNetwork.extract(aeFluidKey, amount, simulate ? Actionable.SIMULATE : Actionable.MODULATE, ((MEHatchPartMachineAccessor) this$0).getActionSource()); + if (extracted > 0) { + changed = true; + amount -= extracted; + if (!simulate) { + var slot = this.inventory[configIndexList.getInt(i)]; + if (slot.getStock() != null) { + long amt = slot.getStock().amount() - extracted; + if (amt == 0) slot.setStock(null); + else slot.setStock(new GenericStack(aeFluidKey, amt)); + } + } + } + } + if (amount <= 0L) { + listIterator.remove(); + break; + } + } + } + } + } + if (!simulate && changed) { + setChanged(true); + this.onContentsChanged(); + } + return left.isEmpty() ? null : left; + } + + @Override + public @NotNull List getMEFluidList() { + if (ENABLE_ULTIMATE_ME_STOCKING || getChanged()) { + setChanged(false); + final var fluidList = getFluidList(); + fluidList.clear(); + final MEStorage aeNetwork = Objects.requireNonNull(this$0.getMainNode().getGrid()).getStorageService().getInventory(); + final IActionSource actionSource = ((MEHatchPartMachineAccessor) this$0).getActionSource(); + for (var key : configList) { + long extracted = aeNetwork.extract(key, Long.MAX_VALUE, Actionable.SIMULATE, actionSource); + if (extracted > 0) { + fluidList.add(FluidStack.create(key.getFluid(), extracted)); + } + } + } + return getFluidList(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAEStockingFluidSlotMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAEStockingFluidSlotMixin.java new file mode 100644 index 000000000..099134ce8 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAEStockingFluidSlotMixin.java @@ -0,0 +1,32 @@ +package org.gtlcore.gtlcore.mixin.gtm.ae.slot; + +import org.gtlcore.gtlcore.api.machine.trait.MEStock.IMESlot; + +import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEFluidSlot; + +import appeng.api.stacks.GenericStack; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; + +import static com.lowdragmc.lowdraglib.LDLib.isRemote; + +@Mixin(targets = "com.gregtechceu.gtceu.integration.ae2.machine.MEStockingHatchPartMachine$ExportOnlyAEStockingFluidSlot", remap = false) +public abstract class ExportOnlyAEStockingFluidSlotMixin extends ExportOnlyAEFluidSlot implements IMESlot { + + @Setter + @Getter + private Runnable onConfigChanged; + + @Override + public void setConfig(@Nullable GenericStack config) { + super.setConfig(config); + if (!isRemote()) onConfigChanged.run(); + } + + @Override + public void setConfigWithoutNotify(@Nullable GenericStack config) { + this.config = config; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAEStockingItemListMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAEStockingItemListMixin.java new file mode 100644 index 000000000..d4abd804b --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAEStockingItemListMixin.java @@ -0,0 +1,166 @@ +package org.gtlcore.gtlcore.mixin.gtm.ae.slot; + +import org.gtlcore.gtlcore.api.machine.trait.MEStock.IMESlot; +import org.gtlcore.gtlcore.api.recipe.ingredient.LongIngredient; +import org.gtlcore.gtlcore.config.ConfigHolder; + +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.ingredient.SizedIngredient; +import com.gregtechceu.gtceu.integration.ae2.machine.MEStockingBusPartMachine; +import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEItemSlot; +import com.gregtechceu.gtceu.integration.ae2.slot.IConfigurableSlot; +import com.gregtechceu.gtceu.integration.ae2.slot.IConfigurableSlotList; + +import com.lowdragmc.lowdraglib.misc.ItemStackTransfer; + +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + +import appeng.api.config.Actionable; +import appeng.api.networking.IGrid; +import appeng.api.stacks.AEItemKey; +import appeng.api.stacks.GenericStack; +import appeng.api.storage.MEStorage; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; +import java.util.Objects; +import java.util.function.Function; + +@Mixin(targets = "com.gregtechceu.gtceu.integration.ae2.machine.MEStockingBusPartMachine$ExportOnlyAEStockingItemList", remap = false) +public abstract class ExportOnlyAEStockingItemListMixin extends ExportOnlyAEItemListMixin implements IConfigurableSlotList { + + protected ObjectArrayList configList; + protected IntArrayList configIndexList; + + private static final boolean ENABLE_ULTIMATE_ME_STOCKING = ConfigHolder.INSTANCE.enableUltimateMEStocking; + + @SuppressWarnings("target") + @Shadow(remap = false) + @Final + MEStockingBusPartMachine this$0; + + public ExportOnlyAEStockingItemListMixin(MetaMachine machine, int slots, @NotNull IO handlerIO, @NotNull IO capabilityIO, Function transferFactory) { + super(machine, slots, handlerIO, capabilityIO, transferFactory); + } + + @SuppressWarnings("target") + @Inject(method = "(Lcom/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine;Lcom/gregtechceu/gtceu/api/machine/MetaMachine;I)V", + at = @At("TAIL")) + private void gtl$onInit(MEStockingBusPartMachine holder, MetaMachine slots, int par3, CallbackInfo ci) { + configList = new ObjectArrayList<>(); + configIndexList = new IntArrayList(); + for (ExportOnlyAEItemSlot exportOnlyAEItemSlot : inventory) { + ((IMESlot) exportOnlyAEItemSlot).setOnConfigChanged(this::onConfigChanged); + } + } + + @Override + public void clearInventory(int startIndex) { + for (int i = startIndex; i < this.getConfigurableSlots(); ++i) { + IConfigurableSlot slot = this.getConfigurableSlot(i); + ((IMESlot) slot).setConfigWithoutNotify(null); + slot.setStock(null); + } + } + + @Unique + @Override + public void onConfigChanged() { + configList.clear(); + configIndexList.clear(); + for (int i = 0, inventoryLength = inventory.length; i < inventoryLength; i++) { + final var config = inventory[i].getConfig(); + if (config != null && config.what() instanceof AEItemKey key) { + configList.add(key); + configIndexList.add(i); + } + } + } + + @Override + public List handleRecipeInner(IO io, GTRecipe recipe, List left, @Nullable String slotName, boolean simulate) { + if (io != IO.IN || left.isEmpty()) { + return left; + } + IGrid grid = this$0.getMainNode().getGrid(); + if (grid == null) { + return left; + } + + MEStorage aeNetwork = grid.getStorageService().getInventory(); + boolean changed = false; + var listIterator = left.listIterator(); + + while (listIterator.hasNext()) { + Ingredient ingredient = listIterator.next(); + if (ingredient.isEmpty()) { + listIterator.remove(); + } else { + long amount; + if (ingredient instanceof LongIngredient li) amount = li.getActualAmount(); + else if (ingredient instanceof SizedIngredient si) amount = si.getAmount(); + else amount = 1; + if (amount < 1) listIterator.remove(); + else { + for (int i = 0, configListSize = configList.size(); i < configListSize; i++) { + AEItemKey aeItemKey = configList.get(i); + if (aeItemKey.matches(ingredient)) { + long extracted = aeNetwork.extract(aeItemKey, amount, simulate ? Actionable.SIMULATE : Actionable.MODULATE, this$0.getActionSource()); + if (extracted > 0) { + changed = true; + amount -= extracted; + if (!simulate) { + var slot = this.inventory[configIndexList.getInt(i)]; + if (slot.getStock() != null) { + long amt = slot.getStock().amount() - extracted; + if (amt == 0) slot.setStock(null); + else slot.setStock(new GenericStack(aeItemKey, amt)); + } + } + } + } + if (amount <= 0L) { + listIterator.remove(); + break; + } + } + } + } + } + if (!simulate && changed) { + this.onContentsChanged(); + } + + return left.isEmpty() ? null : left; + } + + @Override + public Object2LongOpenHashMap getMEItemMap() { + if (ENABLE_ULTIMATE_ME_STOCKING || getChanged()) { + setChanged(false); + itemMap.clear(); + final MEStorage aeNetwork = Objects.requireNonNull(this$0.getMainNode().getGrid()).getStorageService().getInventory(); + for (var key : configList) { + long extracted = aeNetwork.extract(key, Long.MAX_VALUE, Actionable.SIMULATE, this$0.getActionSource()); + if (extracted > 0) { + itemMap.addTo(key.toStack(), extracted); + } + } + } + return itemMap.isEmpty() ? null : itemMap; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAEStockingItemSlotMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAEStockingItemSlotMixin.java new file mode 100644 index 000000000..92e503f2d --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/ae/slot/ExportOnlyAEStockingItemSlotMixin.java @@ -0,0 +1,32 @@ +package org.gtlcore.gtlcore.mixin.gtm.ae.slot; + +import org.gtlcore.gtlcore.api.machine.trait.MEStock.IMESlot; + +import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEItemSlot; + +import appeng.api.stacks.GenericStack; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; + +import static com.lowdragmc.lowdraglib.LDLib.isRemote; + +@Mixin(targets = "com.gregtechceu.gtceu.integration.ae2.machine.MEStockingBusPartMachine$ExportOnlyAEStockingItemSlot", remap = false) +public class ExportOnlyAEStockingItemSlotMixin extends ExportOnlyAEItemSlot implements IMESlot { + + @Setter + @Getter + private Runnable onConfigChanged; + + @Override + public void setConfig(@Nullable GenericStack config) { + super.setConfig(config); + if (!isRemote()) onConfigChanged.run(); + } + + @Override + public void setConfigWithoutNotify(@Nullable GenericStack config) { + this.config = config; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/CWURecipeCapabilityMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/capability/CWURecipeCapabilityMixin.java similarity index 96% rename from src/main/java/org/gtlcore/gtlcore/mixin/gtm/CWURecipeCapabilityMixin.java rename to src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/capability/CWURecipeCapabilityMixin.java index 326f909d4..c4fe15e05 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/CWURecipeCapabilityMixin.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/capability/CWURecipeCapabilityMixin.java @@ -1,4 +1,4 @@ -package org.gtlcore.gtlcore.mixin.gtm; +package org.gtlcore.gtlcore.mixin.gtm.api.capability; import com.gregtechceu.gtceu.api.capability.recipe.*; import com.gregtechceu.gtceu.api.machine.trait.NotifiableComputationContainer; @@ -21,6 +21,10 @@ protected CWURecipeCapabilityMixin(String name, int color, boolean doRenderSlot, @Override public int getMaxParallelRatio(IRecipeCapabilityHolder holder, GTRecipe recipe, int maxParallel) { + int recipeCWU = CWURecipeCapability.CAP.of(recipe.tickInputs.get(CWURecipeCapability.CAP).get(0).getContent()); + if (recipeCWU == 0) { + return Integer.MAX_VALUE; + } long maxCWU = 0; List> recipeHandlerList = Objects .requireNonNullElseGet(holder.getCapabilitiesProxy().get(IO.IN, CWURecipeCapability.CAP), Collections::>emptyList) @@ -31,10 +35,6 @@ public int getMaxParallelRatio(IRecipeCapabilityHolder holder, GTRecipe recipe, maxCWU += nc.requestCWUt(Integer.MAX_VALUE, true); } } - int recipeCWU = CWURecipeCapability.CAP.of(recipe.tickInputs.get(CWURecipeCapability.CAP).get(0).getContent()); - if (recipeCWU == 0) { - return Integer.MAX_VALUE; - } return Math.min(maxParallel, Math.abs(Ints.saturatedCast(maxCWU / recipeCWU))); } } diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/capability/FluidRecipeCapabilityMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/capability/FluidRecipeCapabilityMixin.java new file mode 100644 index 000000000..93a9521a8 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/capability/FluidRecipeCapabilityMixin.java @@ -0,0 +1,95 @@ +package org.gtlcore.gtlcore.mixin.gtm.api.capability; + +import org.gtlcore.gtlcore.api.recipe.IParallelLogic; + +import com.gregtechceu.gtceu.api.capability.recipe.*; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.chance.logic.ChanceLogic; +import com.gregtechceu.gtceu.api.recipe.content.*; +import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; +import com.gregtechceu.gtceu.client.TooltipsHandler; +import com.gregtechceu.gtceu.integration.GTRecipeWidget; + +import com.lowdragmc.lowdraglib.gui.widget.TankWidget; + +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.TooltipFlag; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import com.google.common.primitives.Ints; +import com.llamalad7.mixinextras.sugar.Local; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +import java.util.List; +import java.util.function.BiConsumer; + +import static com.gregtechceu.gtceu.api.capability.recipe.FluidRecipeCapability.CAP; + +@Mixin(FluidRecipeCapability.class) +public class FluidRecipeCapabilityMixin extends RecipeCapability { + + protected FluidRecipeCapabilityMixin(String name, int color, boolean doRenderSlot, int sortIndex, IContentSerializer serializer) { + super(name, color, doRenderSlot, sortIndex, serializer); + } + + /** + * @author Adonis + * @reason 流体输入输出上限改为long + */ + @Overwrite(remap = false) + public FluidIngredient copyWithModifier(FluidIngredient content, ContentModifier modifier) { + if (content.isEmpty()) { + return content.copy(); + } else { + FluidIngredient copy = content.copy(); + copy.setAmount(modifier.apply(copy.getAmount()).longValue()); + return copy; + } + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public int limitParallel(GTRecipe recipe, IRecipeCapabilityHolder holder, int multiplier) { + return Ints.saturatedCast(IParallelLogic.getOutputFluidParallel(holder, recipe, recipe.getOutputContents(CAP), multiplier)); + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public int getMaxParallelRatio(IRecipeCapabilityHolder holder, GTRecipe recipe, int parallelAmount) { + return Ints.saturatedCast(IParallelLogic.getInputFluidParallel(holder, recipe, parallelAmount)); + } + + @OnlyIn(Dist.CLIENT) + @ModifyArg(method = "applyWidgetInfo", + at = @At(value = "INVOKE", + target = "Lcom/lowdragmc/lowdraglib/gui/widget/TankWidget;setOnAddedTooltips(Ljava/util/function/BiConsumer;)Lcom/lowdragmc/lowdraglib/gui/widget/TankWidget;"), + remap = false) + public BiConsumer> applyWidgetInfo(BiConsumer onAddedTooltips, + @Local(name = "content") Content content, + @Local(name = "recipe") GTRecipe recipe, + @Local(name = "index") int index, + @Local(name = "io") IO io) { + return (w, tooltips) -> { + var ingredient = FluidRecipeCapability.CAP.of(content.content); + if (ingredient.getStacks().length > 0) { + var stack = ingredient.getStacks()[0]; + TooltipsHandler.appendFluidTooltips(stack.getFluid(), + stack.getAmount(), tooltips::add, TooltipFlag.NORMAL); + } + GTRecipeWidget.setConsumedChance(content, ChanceLogic.OR, tooltips); + if (isTickSlot(index, io, recipe)) { + tooltips.add(Component.translatable("gtceu.gui.content.per_tick")); + } + }; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/capability/ItemRecipeCapabilityMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/capability/ItemRecipeCapabilityMixin.java new file mode 100644 index 000000000..06647e0c1 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/capability/ItemRecipeCapabilityMixin.java @@ -0,0 +1,180 @@ +package org.gtlcore.gtlcore.mixin.gtm.api.capability; + +import org.gtlcore.gtlcore.api.recipe.IParallelLogic; +import org.gtlcore.gtlcore.api.recipe.ingredient.LongIngredient; + +import com.gregtechceu.gtceu.api.capability.recipe.*; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.GTRecipeType; +import com.gregtechceu.gtceu.api.recipe.ResearchData; +import com.gregtechceu.gtceu.api.recipe.chance.logic.ChanceLogic; +import com.gregtechceu.gtceu.api.recipe.content.*; +import com.gregtechceu.gtceu.api.recipe.ingredient.IntProviderIngredient; +import com.gregtechceu.gtceu.api.recipe.ingredient.SizedIngredient; +import com.gregtechceu.gtceu.api.recipe.ui.GTRecipeTypeUI; +import com.gregtechceu.gtceu.common.recipe.condition.ResearchCondition; +import com.gregtechceu.gtceu.common.valueprovider.*; +import com.gregtechceu.gtceu.config.ConfigHolder; +import com.gregtechceu.gtceu.utils.FormattingUtil; +import com.gregtechceu.gtceu.utils.ResearchManager; + +import com.lowdragmc.lowdraglib.gui.widget.SlotWidget; +import com.lowdragmc.lowdraglib.gui.widget.Widget; +import com.lowdragmc.lowdraglib.jei.IngredientIO; +import com.lowdragmc.lowdraglib.side.item.IItemTransfer; +import com.lowdragmc.lowdraglib.utils.CycleItemStackHandler; + +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.util.valueproviders.ConstantFloat; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + +import com.google.common.primitives.Ints; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnknownNullability; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Unique; + +import java.util.ArrayList; +import java.util.List; + +import static com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability.CAP; + +@Mixin(ItemRecipeCapability.class) +public class ItemRecipeCapabilityMixin extends RecipeCapability { + + protected ItemRecipeCapabilityMixin(String name, int color, boolean doRenderSlot, int sortIndex, IContentSerializer serializer) { + super(name, color, doRenderSlot, sortIndex, serializer); + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public int limitParallel(GTRecipe recipe, IRecipeCapabilityHolder holder, int multiplier) { + return Ints.saturatedCast(IParallelLogic.getOutputItemParallel(holder, recipe, recipe.getOutputContents(CAP), multiplier)); + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public int getMaxParallelRatio(IRecipeCapabilityHolder holder, GTRecipe recipe, int parallelAmount) { + return Ints.saturatedCast(IParallelLogic.getInputItemParallel(holder, recipe, parallelAmount)); + } + + /** + * @author Dragons + * @reason 提供Long级别物品处理 + */ + @Overwrite(remap = false) + public Ingredient copyWithModifier(Ingredient content, ContentModifier modifier) { + if (content instanceof LongIngredient longIngredient) { + return LongIngredient.create(longIngredient.getInner(), modifier.apply(longIngredient.getActualAmount()).longValue()); + } else if (content instanceof SizedIngredient sizedIngredient) { + return LongIngredient.create(sizedIngredient.getInner(), modifier.apply((long) sizedIngredient.getAmount()).longValue()); + } else if (content instanceof IntProviderIngredient intProviderIngredient) { + return new IntProviderIngredient(intProviderIngredient.getInner(), new FlooredInt(new AddedFloat(new MultipliedFloat(new CastedFloat(intProviderIngredient.getCountProvider()), ConstantFloat.of((float) modifier.getMultiplier())), ConstantFloat.of((float) modifier.getAddition())))); + } else { + return LongIngredient.create(content, modifier.apply(1).longValue()); + } + } + + /** + * @author Dragons + * @reason 提供Long级别物品处理 + */ + @Overwrite(remap = false) + public Ingredient copyInner(Ingredient content) { + return LongIngredient.copy(content); + } + + @Override + public void applyWidgetInfo(@NotNull Widget widget, + int index, + boolean isXEI, + IO io, + GTRecipeTypeUI.@UnknownNullability("null when storage == null") RecipeHolder recipeHolder, + @NotNull GTRecipeType recipeType, + @UnknownNullability("null when content == null") GTRecipe recipe, + @Nullable Content content, + @Nullable Object storage) { + if (widget instanceof SlotWidget slot) { + if (storage instanceof IItemTransfer items) { + if (index >= 0 && index < items.getSlots()) { + slot.setHandlerSlot(items, index); + slot.setIngredientIO(io == IO.IN ? IngredientIO.INPUT : IngredientIO.OUTPUT); + slot.setCanTakeItems(!isXEI); + slot.setCanPutItems(!isXEI && io.support(IO.IN)); + } + // 1 over container size. + // If in a recipe viewer and a research slot can be added, add it. + if (isXEI && recipeType.isHasResearchSlot() && index == items.getSlots()) { + if (ConfigHolder.INSTANCE.machines.enableResearch) { + ResearchCondition condition = recipeHolder.conditions().stream() + .filter(ResearchCondition.class::isInstance).findAny() + .map(ResearchCondition.class::cast).orElse(null); + if (condition != null) { + List dataItems = new ArrayList<>(); + for (ResearchData.ResearchEntry entry : condition.data) { + ItemStack dataStick = entry.getDataItem().copy(); + ResearchManager.writeResearchToNBT(dataStick.getOrCreateTag(), entry.getResearchId(), + recipeType); + dataItems.add(dataStick); + } + CycleItemStackHandler handler = new CycleItemStackHandler(List.of(dataItems)); + slot.setHandlerSlot(handler, 0); + slot.setIngredientIO(IngredientIO.INPUT); + slot.setCanTakeItems(false); + slot.setCanPutItems(false); + } + } + } + } + if (content != null) { + slot.setXEIChance((float) content.chance / content.maxChance); + slot.setOnAddedTooltips((w, tooltips) -> { + var ingredient = CAP.of(content.content); + long amount = 1; + if (ingredient instanceof SizedIngredient si) amount = si.getAmount(); + else if (ingredient instanceof LongIngredient li) amount = li.getActualAmount(); + tooltips.add(Component.translatable("gtceu.machine.quantum_chest.items_stored") + .withStyle(ChatFormatting.DARK_AQUA) + .append(Component.literal(String.valueOf(amount)))); + gTLCore$setConsumedChance(content, ChanceLogic.OR, tooltips, io); + if (this.isTickSlot(index, io, recipe)) { + tooltips.add(Component.translatable("gtceu.gui.content.per_tick")); + } + }); + } + } + } + + @Unique + private static void gTLCore$setConsumedChance(Content content, ChanceLogic logic, List tooltips, IO io) { + var chance = content.chance; + if (chance < ChanceLogic.getMaxChancedValue()) { + if (chance == 0) { + tooltips.add(Component.translatable("gtceu.gui.content.chance_0")); + } else { + float chanceFloat = 100 * (float) content.chance / content.maxChance; + if (logic != ChanceLogic.NONE && logic != ChanceLogic.OR) { + tooltips.add(Component.translatable(io == IO.IN ? "gtceu.gui.content.chance_1_logic_in" : "gtceu.gui.content.chance_1_logic", + FormattingUtil.formatNumber2Places(chanceFloat), logic.getTranslation()) + .withStyle(ChatFormatting.YELLOW)); + } else { + tooltips.add(FormattingUtil.formatPercentage2Places(io == IO.IN ? "gtceu.gui.content.chance_1_in" : "gtceu.gui.content.chance_1", chanceFloat)); + } + if (content.tierChanceBoost != 0) { + var formatNumber = content.tierChanceBoost > 0 ? "+" + FormattingUtil.formatNumber2Places(content.tierChanceBoost / 100.0f) : FormattingUtil.formatNumber2Places(content.tierChanceBoost / 100.0f); + tooltips.add(Component.translatable("gtceu.gui.content.tier_boost_fix", formatNumber).withStyle(ChatFormatting.YELLOW)); + } + } + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/FluidDrillLogicMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/FluidDrillLogicMixin.java new file mode 100644 index 000000000..ac7223565 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/FluidDrillLogicMixin.java @@ -0,0 +1,116 @@ +package org.gtlcore.gtlcore.mixin.gtm.api.machine; + +import org.gtlcore.gtlcore.api.recipe.RecipeRunnerHelper; + +import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.api.data.worldgen.bedrockfluid.*; +import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; +import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.FluidDrillMachine; +import com.gregtechceu.gtceu.common.machine.trait.FluidDrillLogic; +import com.gregtechceu.gtceu.data.recipe.builder.GTRecipeBuilder; + +import com.lowdragmc.lowdraglib.side.fluid.FluidStack; + +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.material.Fluid; + +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(FluidDrillLogic.class) +public abstract class FluidDrillLogicMixin extends RecipeLogic { + + @Shadow(remap = false) + private @Nullable Fluid veinFluid; + + @Shadow(remap = false) + protected abstract int getChunkX(); + + @Shadow(remap = false) + protected abstract int getChunkZ(); + + @Shadow(remap = false) + public abstract FluidDrillMachine getMachine(); + + @Shadow(remap = false) + protected abstract long getFluidToProduce(FluidVeinWorldEntry entry); + + @Shadow(remap = false) + protected abstract void depleteVein(); + + public FluidDrillLogicMixin(IRecipeLogicMachine machine) { + super(machine); + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public void findAndHandleRecipe() { + if (this.getMachine().getLevel() instanceof ServerLevel serverLevel) { + this.lastRecipe = null; + var data = BedrockFluidVeinSavedData.getOrCreate(serverLevel); + if (this.veinFluid == null) { + this.veinFluid = data.getFluidInChunk(this.getChunkX(), this.getChunkZ()); + if (this.veinFluid == null) { + if (this.subscription != null) { + this.subscription.unsubscribe(); + this.subscription = null; + } + return; + } + } + GTRecipe match = this.getFluidDrillRecipe(); + if (match != null) { + this.setupRecipe(match); + } + } + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + private @Nullable GTRecipe getFluidDrillRecipe() { + if (getMachine().getLevel() instanceof ServerLevel serverLevel && veinFluid != null) { + var data = BedrockFluidVeinSavedData.getOrCreate(serverLevel); + var recipe = GTRecipeBuilder.ofRaw() + .duration(20) + .EUt(GTValues.VA[getMachine().getEnergyTier()]) + .outputFluids(FluidStack.create(this.veinFluid, + this.getFluidToProduce(data.getFluidVeinWorldEntry(this.getChunkX(), this.getChunkZ())))) + .buildRawRecipe(); + if (RecipeRunnerHelper.matchRecipe(getMachine(), recipe) && recipe.matchTickRecipe(this.getMachine()).isSuccess()) { + return recipe; + } + } + return null; + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public void onRecipeFinish() { + this.machine.afterWorking(); + if (this.lastRecipe != null) { + RecipeRunnerHelper.handleRecipeOutput(this.machine, this.lastRecipe); + } + this.depleteVein(); + GTRecipe match = this.getFluidDrillRecipe(); + if (match != null) { + this.setupRecipe(match); + return; + } + this.setStatus(Status.IDLE); + this.progress = 0; + this.duration = 0; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/MetaMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/MetaMachineMixin.java index a3923f87f..e0b86bdf6 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/MetaMachineMixin.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/MetaMachineMixin.java @@ -13,6 +13,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; @@ -34,6 +35,11 @@ import java.util.Objects; import java.util.Set; +/** + * 代码参考自gto + * @line ... + */ + @Mixin(MetaMachine.class) public abstract class MetaMachineMixin implements IPerformanceDisplayMachine { @@ -132,9 +138,8 @@ public final void serverTick() { private void onToolClick(Set<@NotNull GTToolType> toolType, ItemStack itemStack, UseOnContext context, CallbackInfoReturnable> cir) { if (cir.getReturnValue().getSecond() == InteractionResult.PASS && toolType.contains(GTToolType.WIRE_CUTTER)) { Player player = context.getPlayer(); - if (player == null) return; - if (holder.getMetaMachine() instanceof IGridConnectedMachine gridConnectedMachine) { - cir.setReturnValue(Pair.of(GTToolType.WIRE_CUTTER, gtlcore$onWireCutterClick(player, context.getHand(), gridConnectedMachine))); + if (player instanceof ServerPlayer serverPlayer && holder.getMetaMachine() instanceof IGridConnectedMachine gridConnectedMachine) { + cir.setReturnValue(Pair.of(GTToolType.WIRE_CUTTER, gTLCore$onWireCutterClick(serverPlayer, context.getHand(), gridConnectedMachine))); } } } @@ -148,19 +153,15 @@ private void shouldRenderGrid(Player player, BlockPos pos, BlockState state, Ite } @Unique - private InteractionResult gtlcore$onWireCutterClick(Player playerIn, InteractionHand hand, IGridConnectedMachine machine) { - playerIn.swing(hand); + private InteractionResult gTLCore$onWireCutterClick(ServerPlayer serverPlayer, InteractionHand hand, IGridConnectedMachine machine) { + serverPlayer.swing(hand); if (holder.self().getPersistentData().getBoolean("isAllFacing")) { machine.getMainNode().setExposedOnSides(EnumSet.of(((MetaMachine) machine).getFrontFacing())); - if (isRemote()) { - playerIn.displayClientMessage(Component.translatable("gtlcore.me_front"), true); - } + serverPlayer.sendSystemMessage(Component.translatable("gtlcore.me_front"), true); holder.self().getPersistentData().putBoolean("isAllFacing", false); } else { machine.getMainNode().setExposedOnSides(EnumSet.allOf(Direction.class)); - if (isRemote()) { - playerIn.displayClientMessage(Component.translatable("gtlcore.me_any"), true); - } + serverPlayer.sendSystemMessage(Component.translatable("gtlcore.me_any"), true); holder.self().getPersistentData().putBoolean("isAllFacing", true); } return InteractionResult.CONSUME; diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/MultiblockControllerMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/MultiblockControllerMachineMixin.java new file mode 100644 index 000000000..10144702a --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/MultiblockControllerMachineMixin.java @@ -0,0 +1,91 @@ +package org.gtlcore.gtlcore.mixin.gtm.api.machine; + +import org.gtlcore.gtlcore.api.machine.trait.ICheckPatternMachine; + +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; +import com.gregtechceu.gtceu.api.machine.multiblock.MultiblockControllerMachine; +import com.gregtechceu.gtceu.api.pattern.BlockPattern; +import com.gregtechceu.gtceu.api.pattern.MultiblockWorldSavedData; + +import net.minecraft.server.level.ServerLevel; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; + +import java.util.concurrent.locks.Lock; + +/** + * 代码参考自gto + * @line ... + */ + +@Mixin(MultiblockControllerMachine.class) +public abstract class MultiblockControllerMachineMixin extends MetaMachine implements IMultiController, ICheckPatternMachine { + + @Unique + private int gtlcore$time = 1; + + @Shadow(remap = false) + protected boolean isFormed; + + @Shadow(remap = false) + public abstract Lock getPatternLock(); + + @Shadow(remap = false) + public abstract void setFlipped(boolean isFlipped); + + public MultiblockControllerMachineMixin(IMachineBlockEntity holder) { + super(holder); + } + + @Override + public boolean checkPattern() { + if (gtlcore$time < 1) { + BlockPattern pattern = getPattern(); + if (pattern != null && pattern.checkPatternAt(getMultiblockState(), false)) { + gtlcore$time = 0; + return true; + } else if (hasButton()) { + gtlcore$time = 10; + } + } else { + --gtlcore$time; + } + return false; + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public void asyncCheckPattern(long periodID) { + if ((getMultiblockState().hasError() || !isFormed) && (getHolder().getOffset() + periodID) % 4 == 0 && checkPatternWithTryLock()) { + if (getLevel() instanceof ServerLevel serverLevel) { + serverLevel.getServer().execute(() -> { + getPatternLock().lock(); + setFlipped(getMultiblockState().isNeededFlip()); + onStructureFormed(); + var mwsd = MultiblockWorldSavedData.getOrCreate(serverLevel); + mwsd.addMapping(getMultiblockState()); + mwsd.removeAsyncLogic(this); + getPatternLock().unlock(); + }); + } + } + } + + @Override + public void setTime(int time) { + this.gtlcore$time = time; + } + + @Override + public int getTime() { + return this.gtlcore$time; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/MultiblockStateMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/MultiblockStateMixin.java new file mode 100644 index 000000000..dcbc7b9ec --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/MultiblockStateMixin.java @@ -0,0 +1,106 @@ +package org.gtlcore.gtlcore.mixin.gtm.api.machine; + +import org.gtlcore.gtlcore.api.pattern.util.IMultiblockStateGet; + +import com.gregtechceu.gtceu.api.block.ActiveBlock; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; +import com.gregtechceu.gtceu.api.pattern.*; +import com.gregtechceu.gtceu.common.machine.multiblock.part.*; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; + +import com.hepdd.gtmthings.common.block.machine.multiblock.part.HugeBusPartMachine; +import it.unimi.dsi.fastutil.longs.*; +import org.spongepowered.asm.mixin.*; + +@Mixin(MultiblockState.class) +public abstract class MultiblockStateMixin implements IMultiblockStateGet { + + @Final + @Shadow(remap = false) + public Level world; + + @Final + @Shadow(remap = false) + public BlockPos controllerPos; + + @Shadow(remap = false) + public IMultiController lastController; + + @Unique + private boolean gtlCore$isProcessing = false; + + /** + * @author Dragons + * @reason Fix the issue that duplicate check form when the output work + */ + @Overwrite(remap = false) + public void onBlockStateChanged(BlockPos pos, BlockState state) { + if (gtlCore$isProcessing) return; + gtlCore$isProcessing = true; + try { + if (this.world instanceof ServerLevel serverLevel) { + if (pos.equals(this.controllerPos)) { + if (this.lastController != null && !state.is(this.lastController.self().getBlockState().getBlock())) { + this.lastController.onStructureInvalid(); + MultiblockWorldSavedData mwsd = MultiblockWorldSavedData.getOrCreate(serverLevel); + mwsd.removeMapping((MultiblockState) (Object) this); + } + } else { + final var tempThis = (MultiblockState) (Object) this; + final IMultiController controller = tempThis.getController(); + if (controller != null) { + final boolean formed = controller.isFormed(); + if (formed) { + if (state.getBlock() instanceof ActiveBlock) { + LongSet activeBlocks = tempThis.getMatchContext().getOrDefault("vaBlocks", LongSets.emptySet()); + if (activeBlocks.contains(pos.asLong())) { + return; + } + } else if (serverLevel.getBlockEntity(pos) instanceof IMachineBlockEntity IMBE) { + var metaMachine = IMBE.getMetaMachine(); + if (metaMachine instanceof ItemBusPartMachine || + metaMachine instanceof FluidHatchPartMachine || + metaMachine instanceof HugeBusPartMachine) + return; + } + } + + if (formed && controller.checkPatternWithLock()) { + controller.self().setFlipped(tempThis.isNeededFlip()); + controller.onStructureFormed(); + } else { + controller.self().setFlipped(false); + controller.onStructureInvalid(); + MultiblockWorldSavedData mwsd = MultiblockWorldSavedData.getOrCreate(serverLevel); + mwsd.removeMapping(tempThis); + mwsd.addAsyncLogic(controller); + } + } + } + } + } finally { + gtlCore$isProcessing = false; + } + } + + @Override + public void cleanState() { + this.clean(); + } + + @Override + public boolean updateState(BlockPos posIn, TraceabilityPredicate predicate) { + return this.update(posIn, predicate); + } + + @Shadow(remap = false) + protected abstract void clean(); + + @Shadow(remap = false) + protected abstract boolean update(BlockPos posIn, TraceabilityPredicate predicate); +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/RecipeLogicMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/RecipeLogicMixin.java index b48b13b67..5ee12314c 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/RecipeLogicMixin.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/RecipeLogicMixin.java @@ -1,32 +1,60 @@ package org.gtlcore.gtlcore.mixin.gtm.api.machine; -import org.gtlcore.gtlcore.api.recipe.RecipeRunnerHelper; +import org.gtlcore.gtlcore.api.machine.trait.ILockRecipe; +import org.gtlcore.gtlcore.api.machine.trait.IRecipeStatus; +import org.gtlcore.gtlcore.api.recipe.IGTRecipe; +import org.gtlcore.gtlcore.api.recipe.RecipeResult; import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.capability.recipe.IRecipeCapabilityHolder; import com.gregtechceu.gtceu.api.capability.recipe.RecipeCapability; import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; +import com.gregtechceu.gtceu.api.machine.feature.ITieredMachine; import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; import com.gregtechceu.gtceu.api.recipe.GTRecipe; import com.gregtechceu.gtceu.api.recipe.logic.OCParams; import com.gregtechceu.gtceu.api.recipe.logic.OCResult; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.research.ResearchStationMachine; + +import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; + +import net.minecraft.network.chat.Component; import it.unimi.dsi.fastutil.objects.Object2IntMap; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.*; import java.util.*; +import java.util.function.Predicate; + +import static org.gtlcore.gtlcore.api.recipe.RecipeRunnerHelper.*; @Mixin(RecipeLogic.class) -public abstract class RecipeLogicMixin { +public abstract class RecipeLogicMixin implements ILockRecipe, IRecipeStatus { + + @Persisted + @Getter + private boolean isLock = false; + @Persisted + @Getter + @Setter + private GTRecipe lockRecipe; + @Getter + @Setter + private RecipeResult recipeStatus; + @Getter + @Setter + private RecipeResult workingStatus; - @Mutable - @Final @Shadow(remap = false) - public final IRecipeLogicMachine machine; - @Mutable @Final + public IRecipeLogicMachine machine; @Shadow(remap = false) - protected final Map, Object2IntMap> chanceCaches; + @Final + protected Map, Object2IntMap> chanceCaches; @Shadow(remap = false) public List lastFailedMatches; @Shadow(remap = false) @@ -45,12 +73,10 @@ public abstract class RecipeLogicMixin { protected int duration; @Shadow(remap = false) private boolean isActive; - @Shadow(remap = false) - protected abstract void handleSearchingRecipes(Iterator matches); - + private RecipeLogic.Status status; @Shadow(remap = false) - protected abstract Iterator searchRecipe(); + protected long totalContinuousRunningTime; @Shadow(remap = false) public abstract void markLastRecipeDirty(); @@ -62,11 +88,24 @@ public abstract class RecipeLogicMixin { public abstract RecipeLogic.Status getStatus(); @Shadow(remap = false) - public abstract void setupRecipe(GTRecipe recipe); + public abstract void updateTickSubscription(); + + @Shadow(remap = false) + public abstract GTRecipe.ActionResult handleTickRecipe(GTRecipe recipe); + + @Shadow(remap = false) + public abstract void interruptRecipe(); + + @Shadow(remap = false) + public abstract void setWaiting(@Nullable Component reason); + + @Shadow(remap = false) + protected abstract void doDamping(); - public RecipeLogicMixin(IRecipeLogicMachine machine, Map, Object2IntMap> chanceCaches) { - this.machine = machine; - this.chanceCaches = chanceCaches; + public void setLock(boolean look) { + isLock = look; + lockRecipe = null; + updateTickSubscription(); } /** @@ -76,7 +115,73 @@ public RecipeLogicMixin(IRecipeLogicMachine machine, Map, Ob @Overwrite(remap = false) protected boolean handleRecipeIO(GTRecipe recipe, IO io) { if (!(this.machine.hasProxies() && io != IO.BOTH)) return false; - return RecipeRunnerHelper.handleRecipeInput(this.machine, recipe); + return handleRecipeInput(this.machine, recipe); + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public void handleRecipeWorking() { + assert this.lastRecipe != null; + var result = this.handleTickRecipe(this.lastRecipe); + if (result.isSuccess()) { + this.setStatus(RecipeLogic.Status.WORKING); + if (!this.machine.onWorking()) { + this.interruptRecipe(); + return; + } + ++this.progress; + ++this.totalContinuousRunningTime; + } else { + this.setWaiting(result.reason().get()); + } + if (this.status == RecipeLogic.Status.WAITING) { + this.doDamping(); + } + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public void setupRecipe(GTRecipe recipe) { + if (!this.machine.beforeWorking(recipe)) { + this.setStatus(RecipeLogic.Status.IDLE); + this.progress = 0; + this.duration = 0; + this.isActive = false; + return; + } + if (this.handleRecipeIO(recipe, IO.IN)) { + if (this.lastRecipe != null && !recipe.equals(this.lastRecipe)) { + this.chanceCaches.clear(); + } + this.recipeDirty = false; + this.lastRecipe = recipe; + this.setStatus(RecipeLogic.Status.WORKING); + this.progress = 0; + this.duration = recipe.duration; + this.isActive = true; + } + } + + /** + * @author Dragons + * @reason 删除lastFailedMatches操作 + */ + @Overwrite(remap = false) + private void handleSearchingRecipes(Iterator matches) { + while (matches != null && matches.hasNext()) { + var match = matches.next(); + if (match != null) { + if (this.checkMatchedRecipeAvailable(match)) { + return; + } + } + } } /** @@ -85,16 +190,38 @@ protected boolean handleRecipeIO(GTRecipe recipe, IO io) { */ @Overwrite(remap = false) public void findAndHandleRecipe() { - this.lastFailedMatches = null; - if (!this.recipeDirty && this.lastRecipe != null && gtlcore$checkLastRecipe(this.lastRecipe)) { - GTRecipe recipe = this.lastRecipe; - this.lastRecipe = null; - this.lastOriginRecipe = null; - this.setupRecipe(recipe); + this.lastRecipe = null; + this.recipeStatus = null; + this.workingStatus = null; + if (this.isLock && lockRecipe != null) { + this.lastOriginRecipe = lockRecipe; + var modified = machine.fullModifyRecipe(lastOriginRecipe.copy(), this.ocParams, this.ocResult); + if (modified != null && this.gtlcore$checkLastRecipe(modified)) { + setupRecipe(modified); + } } else { - this.lastRecipe = null; this.lastOriginRecipe = null; - this.handleSearchingRecipes(this.searchRecipe()); + this.handleSearchingRecipes(gtlcore$searchRecipe(this.machine, this.machine instanceof ResearchStationMachine ? + (r) -> { + if (!this.machine.hasProxies()) return false; + if (((IGTRecipe) r).getEuTier() > ((ITieredMachine) machine).getTier()) { + RecipeResult.of(machine, RecipeResult.FAIL_VOLTAGE_TIER); + return false; + } + var result = r.matchRecipeContents(IO.IN, this.machine, r.inputs, false); + if (!result.isSuccess()) { + RecipeResult.of(this.machine, RecipeResult.FAIL_FIND); + return false; + } else if (r.hasTick()) { + result = r.matchRecipeContents(IO.IN, this.machine, r.tickInputs, true); + if (!result.isSuccess() && result.reason() != null) { + var s = result.reason().get().toString(); + if (s.contains("cwu")) RecipeResult.of(this.machine, RecipeResult.FAIL_NO_ENOUGH_CWU_IN); + else if (s.contains("eu.name")) RecipeResult.of(this.machine, RecipeResult.FAIL_NO_ENOUGH_EU_IN); + } + return result.isSuccess(); + } else return true; + } : this::gtlcore$checkLastRecipe)); } this.recipeDirty = false; } @@ -105,15 +232,14 @@ public void findAndHandleRecipe() { */ @Overwrite(remap = false) public boolean checkMatchedRecipeAvailable(GTRecipe match) { - GTRecipe matchCopy = match.copy(); - GTRecipe modified = this.machine.fullModifyRecipe(matchCopy, this.ocParams, this.ocResult); + var modified = this.machine.fullModifyRecipe(match.copy(), this.ocParams, this.ocResult); if (modified != null) { if (gtlcore$checkLastRecipe(modified)) { this.setupRecipe(modified); } if (this.lastRecipe != null && this.getStatus() == RecipeLogic.Status.WORKING) { this.lastOriginRecipe = match; - this.lastFailedMatches = null; + if (this.isLock) this.lockRecipe = match; return true; } } @@ -128,11 +254,10 @@ public boolean checkMatchedRecipeAvailable(GTRecipe match) { public void onRecipeFinish() { this.machine.afterWorking(); if (this.lastRecipe != null) { - this.lastRecipe.postWorking(this.machine); - RecipeRunnerHelper.handleRecipeOutput(this.machine, this.lastRecipe); + handleRecipeOutput(this.machine, this.lastRecipe); if (this.machine.alwaysTryModifyRecipe()) { if (this.lastOriginRecipe != null) { - GTRecipe modified = this.machine.fullModifyRecipe(this.lastOriginRecipe.copy(), this.ocParams, this.ocResult); + var modified = this.machine.fullModifyRecipe(this.lastOriginRecipe.copy(), this.ocParams, this.ocResult); if (modified == null) { this.markLastRecipeDirty(); } else { @@ -153,9 +278,41 @@ public void onRecipeFinish() { } } + @Unique + public Iterator gtlcore$searchRecipe(IRecipeCapabilityHolder holder, + @NotNull Predicate canHandle) { + if (!holder.hasProxies()) { + return null; + } else { + var iterator = this.machine.getRecipeType().getLookup().getRecipeIterator(holder, canHandle); + + boolean any = false; + GTRecipe recipe = null; + while (iterator.hasNext()) { + recipe = iterator.next(); + if (recipe != null) { + any = true; + break; + } + } + + if (any) { + iterator.reset(); + return Collections.singleton(recipe).iterator(); + } else { + for (var logic : this.machine.getRecipeType().getCustomRecipeLogicRunners()) { + recipe = logic.createCustomRecipe(holder); + if (recipe != null) return Collections.singleton(recipe).iterator(); + } + if (recipeStatus == null || recipeStatus.isSuccess()) RecipeResult.of(this.machine, RecipeResult.FAIL_FIND); + return Collections.emptyIterator(); + } + } + } + @Unique private boolean gtlcore$checkLastRecipe(GTRecipe lastRecipe) { - return RecipeRunnerHelper.matchRecipe(this.machine, lastRecipe) && + return matchRecipe(this.machine, lastRecipe) && lastRecipe.matchTickRecipe(this.machine).isSuccess() && lastRecipe.checkConditions(this.machine.getRecipeLogic()).isSuccess(); } diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/ResearchStationRecipeLogicMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/ResearchStationRecipeLogicMixin.java deleted file mode 100644 index 287d08208..000000000 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/ResearchStationRecipeLogicMixin.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.gtlcore.gtlcore.mixin.gtm.api.machine; - -import com.gregtechceu.gtceu.api.capability.IObjectHolder; -import com.gregtechceu.gtceu.api.capability.recipe.IO; -import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; -import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; -import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; -import com.gregtechceu.gtceu.api.recipe.GTRecipe; -import com.gregtechceu.gtceu.common.machine.multiblock.electric.research.ResearchStationMachine; - -import net.minecraft.world.item.ItemStack; - -import org.jetbrains.annotations.NotNull; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; -import org.spongepowered.asm.mixin.Shadow; - -@Mixin(targets = "com.gregtechceu.gtceu.common.machine.multiblock.electric.research.ResearchStationMachine.ResearchStationRecipeLogic") -public abstract class ResearchStationRecipeLogicMixin extends RecipeLogic { - - public ResearchStationRecipeLogicMixin(IRecipeLogicMachine machine) { - super(machine); - } - - @Shadow(remap = false) - public abstract @NotNull ResearchStationMachine getMachine(); - - /** - * @author . - * @reason . - */ - @Overwrite(remap = false) - public void onRecipeFinish() { - this.machine.afterWorking(); - if (this.lastRecipe != null) { - this.lastRecipe.postWorking(this.machine); - this.handleRecipeIO(this.lastRecipe, IO.OUT); - if (this.machine.alwaysTryModifyRecipe()) { - if (this.lastOriginRecipe != null) { - GTRecipe modified = this.machine.fullModifyRecipe(this.lastOriginRecipe.copy(), this.ocParams, this.ocResult); - if (modified == null) { - this.markLastRecipeDirty(); - } else { - this.lastRecipe = modified; - } - } else { - this.markLastRecipeDirty(); - } - } - if (!this.recipeDirty && this.lastRecipe.matchRecipe(this.machine).isSuccess() && this.lastRecipe.matchTickRecipe(this.machine).isSuccess() && this.lastRecipe.checkConditions(this).isSuccess()) { - this.setupRecipe(this.lastRecipe); - } else { - this.setStatus(RecipeLogic.Status.IDLE); - this.progress = 0; - this.duration = 0; - } - } - IObjectHolder holder = this.getMachine().getObjectHolder(); - holder.setHeldItem(ItemStack.EMPTY); - ItemStack outputItem = ItemStack.EMPTY; - if (this.lastRecipe.getOutputContents(ItemRecipeCapability.CAP).size() >= 1) { - outputItem = ItemRecipeCapability.CAP.of(this.getLastRecipe().getOutputContents(ItemRecipeCapability.CAP).get(0).content).getItems()[0]; - } - holder.setDataItem(outputItem); - holder.setLocked(false); - } - - /** - * @author . - * @reason . - */ - @Overwrite(remap = false) - protected boolean handleRecipeIO(GTRecipe recipe, IO io) { - return io != IO.OUT ? recipe.handleRecipeIO(io, this.machine, this.chanceCaches) : true; - } -} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/WorkableMultiblockMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/WorkableMultiblockMachineMixin.java index 607212d01..1a5693c0a 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/WorkableMultiblockMachineMixin.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/WorkableMultiblockMachineMixin.java @@ -1,43 +1,56 @@ package org.gtlcore.gtlcore.mixin.gtm.api.machine; -import org.gtlcore.gtlcore.api.machine.trait.IDistinctMachine; -import org.gtlcore.gtlcore.api.recipe.RecipeRunner; +import org.gtlcore.gtlcore.api.machine.trait.*; +import org.gtlcore.gtlcore.api.machine.trait.MEPart.IMEFilterIOPartMachine; +import org.gtlcore.gtlcore.api.machine.trait.MEPart.IMEPatternPartMachine; +import org.gtlcore.gtlcore.api.recipe.RecipeResult; +import org.gtlcore.gtlcore.utils.datastructure.FirstFlagRecipePartSet; +import com.gregtechceu.gtceu.api.capability.IDataAccessHatch; +import com.gregtechceu.gtceu.api.capability.IParallelHatch; import com.gregtechceu.gtceu.api.capability.recipe.*; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; -import com.gregtechceu.gtceu.api.machine.feature.multiblock.IDistinctPart; -import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiPart; +import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.*; import com.gregtechceu.gtceu.api.machine.multiblock.MultiblockControllerMachine; import com.gregtechceu.gtceu.api.machine.multiblock.WorkableMultiblockMachine; +import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.logic.OCParams; +import com.gregtechceu.gtceu.api.recipe.logic.OCResult; import com.gregtechceu.gtceu.common.machine.multiblock.part.FluidHatchPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.MEOutputBusPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.MEOutputHatchPartMachine; -import net.minecraft.resources.ResourceLocation; +import com.lowdragmc.lowdraglib.syncdata.ISubscription; +import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; +import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.minecraft.server.level.ServerLevel; + +import com.hepdd.gtmthings.common.block.machine.multiblock.part.appeng.MEOutputPartMachine; +import it.unimi.dsi.fastutil.objects.*; import lombok.Getter; import lombok.Setter; -import org.spongepowered.asm.mixin.Mixin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; +import java.util.*; +@SuppressWarnings({ "AddedMixinMembersNamePattern", "MissingUnique" }) @Mixin(WorkableMultiblockMachine.class) -public abstract class WorkableMultiblockMachineMixin extends MultiblockControllerMachine implements IDistinctMachine { +public abstract class WorkableMultiblockMachineMixin extends MultiblockControllerMachine implements IRecipeCapabilityMachine, IRecipeLogicMachine { - @Setter - @Getter - private List recipeHandleParts = new ObjectArrayList<>(); - @Getter - @Setter - private RecipeRunner.RecipeHandlePart distinctHatch; - @Getter - @Setter - private ResourceLocation recipeId; + @Shadow(remap = false) + @Final + protected List traitSubscriptions; + @Shadow(remap = false) + @Final + public RecipeLogic recipeLogic; public WorkableMultiblockMachineMixin(IMachineBlockEntity holder) { super(holder); @@ -45,61 +58,376 @@ public WorkableMultiblockMachineMixin(IMachineBlockEntity holder) { @Inject(method = "onStructureFormed", at = @At("TAIL"), remap = false) public void onStructureFormed(CallbackInfo ci) { - this.upDate(); + if (this.getLevel() instanceof ServerLevel sl) { + sl.getServer().execute(this::upDate); + } } @Inject(method = "onStructureInvalid", at = @At("TAIL"), remap = false) public void onStructureInvalid(CallbackInfo ci) { - this.recipeHandleParts.clear(); + clear(); + RecipeResult.of(this, null); + RecipeResult.ofWorking(this, null); } @Inject(method = "onPartUnload", at = @At("TAIL"), remap = false) public void onPartUnload(CallbackInfo ci) { - this.recipeHandleParts.clear(); + clear(); } - public void upDate() { - recipeHandleParts.clear(); - distinctHatch = null; - recipeId = null; - Iterator parts = this.getParts().iterator(); - Object2ObjectOpenHashMap, List>> NodistinctParts = new Object2ObjectOpenHashMap<>(); - Object2ObjectOpenHashMap, List>> outputParts = new Object2ObjectOpenHashMap<>(); - while (parts.hasNext()) { - IMultiPart part = parts.next(); - if (part instanceof FluidHatchPartMachine || part instanceof IDistinctPart) { - List> itemPart = new ObjectArrayList<>(); - List> fluidPart = new ObjectArrayList<>(); - boolean isOutput = false; - for (var v : part.getRecipeHandlers()) { - if (!v.isProxy()) { - if (v.getHandlerIO() == IO.IN) { - if (v.getCapability() == ItemRecipeCapability.CAP) itemPart.add(v); - else if (v.getCapability() == FluidRecipeCapability.CAP) fluidPart.add(v); - } else if (v.getHandlerIO() == IO.OUT) { - isOutput = true; - if (v.getCapability() == ItemRecipeCapability.CAP) itemPart.add(v); - else if (v.getCapability() == FluidRecipeCapability.CAP) fluidPart.add(v); - } - } + @Inject(method = "setActiveRecipeType", at = @At("TAIL"), remap = false) + public void afterSetActiveRecipeType(int activeRecipeType, CallbackInfo ci) { + if (!isRemote()) recipeLogic.updateTickSubscription(); + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public final @Nullable GTRecipe doModifyRecipe(GTRecipe recipe, @NotNull OCParams params, @NotNull OCResult result) { + if (maintenanceMachine != null) recipe = maintenanceMachine.modifyRecipe(recipe); + if (recipe != null && mufflerMachine != null) recipe = mufflerMachine.modifyRecipe(recipe); + if (recipe != null && dataAccessHatch != null) recipe = dataAccessHatch.modifyRecipe(recipe); + return recipe != null ? this.self().getDefinition().getRecipeModifier().apply(this.self(), recipe, params, result) : null; + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public boolean onWorking() { + return this.self().getDefinition().getOnWorking().test(this); + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public void onWaiting() {} + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public boolean beforeWorking(@Nullable GTRecipe recipe) { + return this.self().getDefinition().getBeforeWorking().test(this, recipe); + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public void afterWorking() { + if (maintenanceMachine != null) maintenanceMachine.afterWorking((IWorkableMultiController) this); + if (mufflerMachine != null) mufflerMachine.afterWorking((IWorkableMultiController) this); + this.self().getDefinition().getAfterWorking().accept(this); + } + + // ======================================== + // IRecipeCapabilityMachine + // ======================================== + + @Persisted + @DescSynced + @Getter + @Setter + private boolean isDistinct = false; + private boolean meOutPutBus = false; + private boolean meOutPutHatch = false; + private boolean meOutPutDual = false; + private boolean meOutPutWithFilter = false; + private boolean meItemOutPutWithFilter = false; + private boolean meFluidOutPutWithFilter = false; + private boolean itemOutPutAlwaysMatch = false; + private boolean fluidOutPutAlwaysMatch = false; + + // ==================== Special Hatch ==================== + private @Nullable IParallelHatch parallelHatch = null; + private @Nullable IMaintenanceMachine maintenanceMachine = null; + private @Nullable IMufflerMachine mufflerMachine = null; + private @Nullable IDataAccessHatch dataAccessHatch = null; + + // ==================== Normal Part ==================== + private @Nullable RecipeHandlePart sharedInputRecipeHandlePart = null; + private final Map> normalCapabilities = new EnumMap<>(IO.class); // Only Distinct Part + + // ==================== ME Part ==================== + private final ObjectList mePatternRecipeHandleParts = new ObjectArrayList<>(); + private final ObjectList> meOutputRecipeHandleParts = new ObjectArrayList<>(); + + // ==================== Recipe -> Parts ==================== + private final Object2ObjectMap recipeHandleMap = new Object2ObjectOpenHashMap<>(); + + @Override + public boolean isRecipeOutputAlwaysMatch(GTRecipe recipe) { + if (meOutPutWithFilter) return false; + else if (meOutPutDual) return true; + else if (meOutPutBus || recipe.getOutputContents(ItemRecipeCapability.CAP).isEmpty()) + return meOutPutHatch || recipe.getOutputContents(FluidRecipeCapability.CAP).isEmpty(); + else return false; + } + + @Override + public boolean itemOutPutAlwaysMatch() { + return itemOutPutAlwaysMatch; + } + + @Override + public boolean fluidOutPutAlwaysMatch() { + return fluidOutPutAlwaysMatch; + } + + // ======================================== + // Recipe -> HandlePart Cache + // ======================================== + + @Override + public @Nullable IRecipeHandlePart getActiveRecipeHandle(GTRecipe recipe) { + final var handlers = recipeHandleMap.get(recipe); + return handlers != null ? handlers.getActive() : null; + } + + @Override + public @NotNull Iterator<@NotNull IRecipeHandlePart> getAllCachedRecipeHandlesIter(GTRecipe recipe) { + final var handlers = recipeHandleMap.get(recipe); + return handlers != null ? handlers.getReverseIterator() : ObjectIterators.emptyIterator(); + } + + @Override + public @NotNull ReferenceSet<@NotNull IRecipeHandlePart> getAllCachedRecipeHandles(GTRecipe recipe) { + final var handlers = recipeHandleMap.get(recipe); + return handlers != null ? handlers.getAll() : ReferenceSets.emptySet(); + } + + @Override + public void tryAddAndActiveMERhp(MEPatternRecipeHandlePart part, GTRecipe recipe, int slot) { + part.setLastRecipe2Slot(recipe, slot); + tryAddAndActiveRhp(recipe, part); + } + + @Override + public void tryAddAndActiveRhp(GTRecipe recipe, IRecipeHandlePart part) { + recipeHandleMap.computeIfAbsent(recipe, k -> new FirstFlagRecipePartSet()).addOrSetActive(part); + } + + @Override + public void sortMEOutput() { + if (!meOutputRecipeHandleParts.isEmpty()) { + meOutputRecipeHandleParts.sort(MEPatternRecipeHandlePart.COMPARATOR.reversed()); + + boolean allHaveItemFilter = true; + boolean allHaveFluidFilter = true; + for (MEIORecipeHandlePart meOutputRecipeHandlePart : meOutputRecipeHandleParts) { + if (!meOutputRecipeHandlePart.hasItemFilter()) { + allHaveItemFilter = false; } - if (isOutput) { - outputParts.computeIfAbsent(ItemRecipeCapability.CAP, k -> new ArrayList<>()).addAll(itemPart); - outputParts.computeIfAbsent(FluidRecipeCapability.CAP, k -> new ArrayList<>()).addAll(fluidPart); - } else { - if (part instanceof IDistinctPart iDistinctPart && iDistinctPart.isDistinct()) { - Object2ObjectOpenHashMap, List>> distinctParts = new Object2ObjectOpenHashMap<>(); - distinctParts.put(ItemRecipeCapability.CAP, itemPart); - distinctParts.put(FluidRecipeCapability.CAP, fluidPart); - recipeHandleParts.add(new RecipeRunner.RecipeHandlePart(IO.IN, distinctParts)); - } else { - NodistinctParts.computeIfAbsent(ItemRecipeCapability.CAP, k -> new ArrayList<>()).addAll(itemPart); - NodistinctParts.computeIfAbsent(FluidRecipeCapability.CAP, k -> new ArrayList<>()).addAll(fluidPart); - } + if (!meOutputRecipeHandlePart.hasFluidFilter()) { + allHaveFluidFilter = false; + } + + if (!allHaveItemFilter && !allHaveFluidFilter) { + break; } } + + meItemOutPutWithFilter = allHaveItemFilter; + meFluidOutPutWithFilter = allHaveFluidFilter; + meOutPutWithFilter = meItemOutPutWithFilter || meFluidOutPutWithFilter; + itemOutPutAlwaysMatch = !meItemOutPutWithFilter && (meOutPutBus || meOutPutDual); + fluidOutPutAlwaysMatch = !meFluidOutPutWithFilter && (meOutPutHatch || meOutPutDual); } - if (!NodistinctParts.isEmpty()) recipeHandleParts.add(new RecipeRunner.RecipeHandlePart(IO.IN, NodistinctParts)); - if (!outputParts.isEmpty()) recipeHandleParts.add(new RecipeRunner.RecipeHandlePart(IO.OUT, outputParts)); + } + + @Override + public boolean emptyRecipeHandlePart() { + return normalCapabilities.isEmpty() && mePatternRecipeHandleParts.isEmpty() && sharedInputRecipeHandlePart == null; + } + + @Override + public boolean emptyHandlePart() { + return normalCapabilities.isEmpty() && mePatternRecipeHandleParts.isEmpty() && meOutputRecipeHandleParts.isEmpty() && sharedInputRecipeHandlePart == null; + } + + @Override + @Nullable + public RecipeHandlePart getSharedRecipeHandlePart() { + return sharedInputRecipeHandlePart; + } + + @Override + public List getMEPatternRecipeHandleParts() { + return mePatternRecipeHandleParts; + } + + @Override + public List> getMEOutputRecipeHandleParts() { + return meOutputRecipeHandleParts; + } + + @Override + public @NotNull List getNormalRecipeHandlePart(IO io) { + return normalCapabilities.getOrDefault(io, Collections.emptyList()); + } + + @Override + public void upDate() { + if (!this.isFormed) return; + rebuildRecipeHandleParts(traitSubscriptions, getParts(), recipeLogic, isDistinct); + } + + @Override + public @Nullable IParallelHatch getParallelHatch() { + return parallelHatch; + } + + @Override + public @Nullable IMaintenanceMachine getMaintenanceMachine() { + return maintenanceMachine; + } + + @Override + public @Nullable IDataAccessHatch getDataAccessHatch() { + return dataAccessHatch; + } + + // ======================================== + // Rebuild + // ======================================== + + private void rebuildRecipeHandleParts(List traitSubscriptions, List parts, RecipeLogic recipeLogic, boolean isDistinct) { + clear(); + + final var sharedInputHandlers = new ObjectArrayList>(); + for (IMultiPart part : parts) { + if (part instanceof IMEFilterIOPartMachine mePart) { + handleMETraitPart(traitSubscriptions, recipeLogic, mePart); + } else if (part instanceof FluidHatchPartMachine || part instanceof IDistinctPart) { + handleNormalPart(part, isDistinct, sharedInputHandlers); + } else { + handleSpecialPart(part); + } + } + + if (!isDistinct && !sharedInputHandlers.isEmpty()) { + sharedInputRecipeHandlePart = RecipeHandlePart.of(IO.IN, sharedInputHandlers); + mergeSharedHandlePart(sharedInputRecipeHandlePart); + } + sortMEOutput(); + } + + private void handleMETraitPart(List traitSubscriptions, RecipeLogic recipeLogic, IMEFilterIOPartMachine mePart) { + var pair = mePart.getMERecipeHandlerTraits(); + traitSubscriptions.add(pair.left().addBufferChangedListener(recipeLogic::updateTickSubscription)); + traitSubscriptions.add(pair.right().addBufferChangedListener(recipeLogic::updateTickSubscription)); + + IO io = mePart.getMETrait().getIO(); + if (mePart instanceof IMEPatternPartMachine mePatternPart) { + var me = MEPatternRecipeHandlePart.of(mePatternPart); + me.restoreMachineCache(this::tryAddAndActiveRhp); + mePatternRecipeHandleParts.add(me); + if (io == IO.BOTH) { + meOutputRecipeHandleParts.add(me); + setAllMEOutputFlags(); + } + } else if (io == IO.OUT) { + meOutputRecipeHandleParts.add(MEIORecipeHandlePart.of(mePart)); + setAllMEOutputFlags(); + } + } + + private void handleNormalPart(IMultiPart part, boolean isDistinct, List> sharedInputHandlers) { + if (part instanceof MEOutputPartMachine) { + setAllMEOutputFlags(); + } else if (part instanceof MEOutputBusPartMachine) { + meOutPutBus = true; + } else if (part instanceof MEOutputHatchPartMachine) { + meOutPutHatch = true; + } + + List> fluidHandlers = new ObjectArrayList<>(); + List> isolableHandlers = new ObjectArrayList<>(); + List> outputHandlers = new ObjectArrayList<>(); + boolean isOutput = false; + + for (var handler : part.getRecipeHandlers()) { + if (handler.isProxy()) continue; + + IO io = handler.getHandlerIO(); + if (io == IO.IN) { + if (handler.getCapability() == FluidRecipeCapability.CAP) fluidHandlers.add(handler); + else isolableHandlers.add(handler); + } else if (io == IO.OUT) { + isOutput = true; + outputHandlers.add(handler); + } + } + + if (isOutput) { + normalCapabilities.computeIfAbsent(IO.OUT, ignore -> new ReferenceArrayList<>()).add(RecipeHandlePart.of(IO.OUT, outputHandlers)); + } else if (isDistinct) { + isolableHandlers.addAll(fluidHandlers); + normalCapabilities.computeIfAbsent(IO.IN, ignore -> new ReferenceArrayList<>()).add(RecipeHandlePart.of(IO.IN, isolableHandlers)); + } else if (part instanceof IDistinctPart distinctPart && distinctPart.isDistinct()) { + normalCapabilities.computeIfAbsent(IO.IN, ignore -> new ReferenceArrayList<>()).add(RecipeHandlePart.of(IO.IN, isolableHandlers)); + sharedInputHandlers.addAll(fluidHandlers); + } else { + sharedInputHandlers.addAll(isolableHandlers); + sharedInputHandlers.addAll(fluidHandlers); + } + } + + private void handleSpecialPart(IMultiPart part) { + if (part instanceof IParallelHatch parallel) { + this.parallelHatch = parallel; + } else if (part instanceof IMufflerMachine muffler) { + this.mufflerMachine = muffler; + } else if (part instanceof IMaintenanceMachine maintenance) { + this.maintenanceMachine = maintenance; + } else if (part instanceof IDataAccessHatch data) { + this.dataAccessHatch = data; + } + } + + private void mergeSharedHandlePart(@NotNull RecipeHandlePart shared) { + for (List value : normalCapabilities.values()) { + for (RecipeHandlePart part : value) { + part.addSharedFluidHandlers(shared.getCapability(FluidRecipeCapability.CAP)); + } + } + } + + // ======================================== + // Utils Methods + // ======================================== + + private void setAllMEOutputFlags() { + meOutPutBus = true; + meOutPutHatch = true; + meOutPutDual = true; + } + + private void clear() { + meOutPutBus = false; + meOutPutHatch = false; + meOutPutDual = false; + meOutPutWithFilter = false; + meItemOutPutWithFilter = false; + meFluidOutPutWithFilter = false; + itemOutPutAlwaysMatch = false; + fluidOutPutAlwaysMatch = false; + parallelHatch = null; + mufflerMachine = null; + maintenanceMachine = null; + dataAccessHatch = null; + sharedInputRecipeHandlePart = null; + normalCapabilities.clear(); + mePatternRecipeHandleParts.clear(); + meOutputRecipeHandleParts.clear(); + recipeHandleMap.clear(); } } diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/trait/NotifiableEnergyContainerMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/trait/NotifiableEnergyContainerMixin.java new file mode 100644 index 000000000..bb24a67e9 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/trait/NotifiableEnergyContainerMixin.java @@ -0,0 +1,115 @@ +package org.gtlcore.gtlcore.mixin.gtm.api.machine.trait; + +import org.gtlcore.gtlcore.api.capability.IInt128EnergyContainer; +import org.gtlcore.gtlcore.utils.datastructure.Int128; + +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.trait.NotifiableEnergyContainer; +import com.gregtechceu.gtceu.api.machine.trait.NotifiableRecipeHandlerTrait; + +import org.spongepowered.asm.mixin.*; + +@Implements(@Interface( + iface = IInt128EnergyContainer.class, + prefix = "gTLCore$")) +@Mixin(NotifiableEnergyContainer.class) +public abstract class NotifiableEnergyContainerMixin extends NotifiableRecipeHandlerTrait { + + @Shadow(remap = false) + protected long energyStored; + + @Shadow(remap = false) + private long energyCapacity; + + @Shadow(remap = false) + public void checkOutputSubscription() { + throw new AssertionError(); + } + + @Unique + protected Int128 gTLCore$lastEnergyInputPerSec = Int128.ZERO(); + @Unique + protected Int128 gTLCore$lastEnergyOutputPerSec = Int128.ZERO(); + @Unique + protected Int128 gTLCore$energyInputPerSec = Int128.ZERO(); + @Unique + protected Int128 gTLCore$energyOutputPerSec = Int128.ZERO(); + + public NotifiableEnergyContainerMixin(MetaMachine machine) { + super(machine); + } + + /** + * @author Dragons + * @reason PerSec溢出修复 + */ + @Overwrite(remap = false) + public void setEnergyStored(long energyStored) { + if (this.energyStored == energyStored) return; + gTLCore$addEnergyPerSec(energyStored - this.energyStored); + this.energyStored = energyStored; + checkOutputSubscription(); + notifyListeners(); + } + + /** + * @author Dragons + * @reason PerSec溢出修复 + */ + @Overwrite(remap = false) + public void updateTick() { + if (getMachine().getOffsetTimer() % 20 == 0) { + gTLCore$lastEnergyOutputPerSec = gTLCore$energyOutputPerSec.copy(); + gTLCore$lastEnergyInputPerSec = gTLCore$energyOutputPerSec.copy(); + gTLCore$energyOutputPerSec.set(0, 0); + gTLCore$energyOutputPerSec.set(0, 0); + } + } + + /** + * @author Dragons + * @reason 兼容旧接口 + */ + @Overwrite(remap = false) + public long getInputPerSec() { + return gTLCore$lastEnergyInputPerSec.longValue(); + } + + /** + * @author Dragons + * @reason 兼容旧接口 + */ + @Overwrite(remap = false) + public long getOutputPerSec() { + return gTLCore$lastEnergyOutputPerSec.longValue(); + } + + @Unique + public Int128 gTLCore$getInt128InputPerSec() { + return gTLCore$lastEnergyInputPerSec.copy(); + } + + @Unique + public Int128 gTLCore$getInt128OutputPerSec() { + return gTLCore$lastEnergyOutputPerSec.copy(); + } + + @Unique + public void gTLCore$addEnergyPerSec(long energy) { + if (energy > 0) { + gTLCore$energyInputPerSec.add(energy); + } else if (energy < 0) { + gTLCore$energyOutputPerSec.add(-energy); + } + } + + @Unique + public Int128 gTLCore$getInt128EnergyStored() { + return new Int128(energyStored); + } + + @Unique + public Int128 gTLCore$getInt128EnergyCapacity() { + return new Int128(energyCapacity); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/trait/NotifiableItemStackHandlerMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/trait/NotifiableItemStackHandlerMixin.java new file mode 100644 index 000000000..aa7e6abf6 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/machine/trait/NotifiableItemStackHandlerMixin.java @@ -0,0 +1,146 @@ +package org.gtlcore.gtlcore.mixin.gtm.api.machine.trait; + +import org.gtlcore.gtlcore.api.recipe.ingredient.LongIngredient; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.machine.trait.NotifiableItemStackHandler; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.ingredient.IntProviderIngredient; + +import com.lowdragmc.lowdraglib.misc.ItemStackTransfer; + +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + +import com.google.common.primitives.Ints; +import dev.latvian.mods.kubejs.recipe.ingredientaction.IngredientAction; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.*; + +import java.util.List; + +@Mixin(NotifiableItemStackHandler.class) +public abstract class NotifiableItemStackHandlerMixin { + + @Shadow(remap = false) + @Final + public IO handlerIO; + + @Shadow(remap = false) + @Final + public ItemStackTransfer storage; + + /** + * @author Dragons + * @reason 适配LongIngredient + */ + @Overwrite(remap = false) + public List handleRecipeInner(IO io, GTRecipe recipe, List left, @Nullable String slotName, + boolean simulate) { + return handleLongIngredient(io, recipe, left, simulate, this.handlerIO, storage); + } + + @Unique + @Nullable + private static List handleLongIngredient(IO io, GTRecipe recipe, List left, boolean simulate, + IO handlerIO, ItemStackTransfer storage) { + if (io != handlerIO) return left; + if (io != IO.IN && io != IO.OUT) return left.isEmpty() ? null : left; + + // Temporarily remove listener so that we can broadcast the entire set of transactions once + Runnable listener = storage.getOnContentsChanged(); + storage.setOnContentsChanged(() -> {}); + boolean changed = false; + + // Store the ItemStack in each slot after an operation + // Necessary for simulation since we don't actually modify the slot's contents + // Doesn't hurt for execution, and definitely cheaper than copying the entire storage + ItemStack[] visited = new ItemStack[storage.getSlots()]; + for (var it = left.listIterator(); it.hasNext();) { + var ingredient = it.next(); + if (ingredient.isEmpty()) { + it.remove(); + continue; + } + + ItemStack[] items; + long amount; + if (io == IO.OUT && ingredient instanceof IntProviderIngredient provider) { + provider.setItemStacks(null); + provider.setSampledCount(-1); + } + + items = ingredient.getItems(); + if (items.length == 0 || items[0].isEmpty()) { + it.remove(); + continue; + } + if (ingredient instanceof LongIngredient li) amount = li.getActualAmount(); + else amount = items[0].getCount(); + + for (int slot = 0; slot < storage.getSlots(); ++slot) { + ItemStack current = visited[slot] == null ? storage.getStackInSlot(slot) : visited[slot]; + int count = current.getCount(); + + if (io == IO.IN) { + if (current.isEmpty()) continue; + if (ingredient.test(current)) { + var extracted = getActioned(storage, slot, recipe.ingredientActions); + if (extracted == null) extracted = storage.extractItem(slot, Ints.saturatedCast(Math.min(count, amount)), simulate); + if (!extracted.isEmpty()) { + changed = true; + visited[slot] = extracted.copyWithCount(count - extracted.getCount()); + } + amount -= extracted.getCount(); + } + } else { + ItemStack template = items[0]; + // Only try this slot if not visited or if visited with the same type of item + if (visited[slot] == null || ItemStack.isSameItemSameTags(visited[slot], template)) { + int slotLimit = storage.getSlotLimit(slot); + int canHold = slotLimit - count; + if (canHold > 0) { + int tryPut = Ints.saturatedCast(Math.min(amount, canHold)); + ItemStack remainder = getActioned(storage, slot, recipe.ingredientActions); + if (remainder == null) remainder = storage.insertItem(slot, template.copyWithCount(tryPut), simulate); + int actuallyPut = tryPut - remainder.getCount(); + if (actuallyPut > 0) { + changed = true; + visited[slot] = template.copyWithCount(count + actuallyPut); + amount -= actuallyPut; + } + } + } + } + + if (amount <= 0) { + it.remove(); + break; + } + } + // Modify ingredient if we didn't finish it off + if (amount > 0) { + if (ingredient instanceof LongIngredient li) { + li.setActualAmount(amount); + } else { + items[0].setCount(Ints.saturatedCast(amount)); + } + } + } + + storage.setOnContentsChanged(listener); + if (changed && !simulate) listener.run(); + + return left.isEmpty() ? null : left; + } + + @Unique + private static @Nullable ItemStack getActioned(ItemStackTransfer storage, int index, List actions) { + if (!GTCEu.isKubeJSLoaded()) return null; + // noinspection unchecked + var actioned = NotifiableItemStackHandler.KJSCallWrapper.applyIngredientAction(storage, index, (List) actions); + if (!actioned.isEmpty()) return actioned; + return null; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/misc/EnergyContainerListMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/misc/EnergyContainerListMixin.java new file mode 100644 index 000000000..47dd50026 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/misc/EnergyContainerListMixin.java @@ -0,0 +1,176 @@ +package org.gtlcore.gtlcore.mixin.gtm.api.misc; + +import org.gtlcore.gtlcore.api.capability.IInt128EnergyContainer; +import org.gtlcore.gtlcore.utils.NumberUtils; +import org.gtlcore.gtlcore.utils.datastructure.Int128; + +import com.gregtechceu.gtceu.api.capability.IEnergyContainer; +import com.gregtechceu.gtceu.api.misc.EnergyContainerList; + +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.*; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; + +@Implements(@Interface( + iface = IInt128EnergyContainer.class, + prefix = "gTLCore$")) +@Mixin(EnergyContainerList.class) +public abstract class EnergyContainerListMixin { + + @Shadow(remap = false) + @Final + private List energyContainerList; + + @Mutable + @Shadow(remap = false) + @Final + private long inputVoltage; + + @Mutable + @Shadow(remap = false) + @Final + private long outputVoltage; + + @Mutable + @Shadow(remap = false) + @Final + private long inputAmperage; + + @Mutable + @Shadow(remap = false) + @Final + private long outputAmperage; + + @Inject(method = "", at = @At("TAIL")) + public void EnergyContainerList(List energyContainerList, CallbackInfo ci) { + long totalInputEut = 0; + long totalOutputEut = 0; + + for (IEnergyContainer container : energyContainerList) { + totalInputEut = NumberUtils.saturatedAdd(totalInputEut, NumberUtils.saturatedMultiply(container.getInputVoltage(), container.getInputAmperage())); + totalOutputEut = NumberUtils.saturatedAdd(totalOutputEut, NumberUtils.saturatedMultiply(container.getOutputVoltage(), container.getOutputAmperage())); + } + + this.inputVoltage = totalInputEut; + this.inputAmperage = totalInputEut > 0 ? 1 : 0; + this.outputVoltage = totalOutputEut; + this.outputAmperage = totalOutputEut > 0 ? 1 : 0; + } + + /** + * @author Dragons + * @reason Long Saturated + */ + @Overwrite(remap = false) + public long getEnergyStored() { + long energyStored = 0L; + for (IEnergyContainer iEnergyContainer : energyContainerList) { + energyStored = NumberUtils.saturatedAdd(energyStored, iEnergyContainer.getEnergyStored()); + } + return energyStored; + } + + /** + * @author Dragons + * @reason Long Saturated + */ + @Overwrite(remap = false) + public long getEnergyCapacity() { + long energyCapacity = 0L; + for (IEnergyContainer iEnergyContainer : energyContainerList) { + energyCapacity = NumberUtils.saturatedAdd(energyCapacity, iEnergyContainer.getEnergyCapacity()); + } + return energyCapacity; + } + + /** + * @author Dragons + * @reason Long Saturated + */ + @Overwrite(remap = false) + public long getInputPerSec() { + long sum = 0; + List energyContainerList = this.energyContainerList; + for (IEnergyContainer iEnergyContainer : energyContainerList) { + sum = NumberUtils.saturatedAdd(sum, iEnergyContainer.getInputPerSec()); + } + return sum; + } + + /** + * @author Dragons + * @reason Long Saturated + */ + @Overwrite(remap = false) + public long getOutputPerSec() { + long sum = 0; + List energyContainerList = this.energyContainerList; + for (IEnergyContainer iEnergyContainer : energyContainerList) { + sum = NumberUtils.saturatedAdd(sum, iEnergyContainer.getOutputPerSec()); + } + return sum; + } + + /** + * @author Dragons + * @reason Dont Use + */ + @SuppressWarnings("NullableProblems") + @Overwrite(remap = false) + private static @NotNull long[] calculateVoltageAmperage(long voltage, long amperage) { + return new long[] { voltage, amperage }; + } + + /** + * @author Dragons + * @reason Dont Use + */ + @Overwrite(remap = false) + private static boolean hasPrimeFactorGreaterThanTwo(long l) { + return l > 0 && (l & (l - 1)) != 0; + } + + @Unique + public Int128 gTLCore$getInt128InputPerSec() { + Int128 sum = Int128.ZERO(); + List energyContainerList = this.energyContainerList; + for (IEnergyContainer iEnergyContainer : energyContainerList) { + sum.add(((IInt128EnergyContainer) iEnergyContainer).getInt128InputPerSec()); + } + return sum; + } + + @Unique + public Int128 gTLCore$getInt128OutputPerSec() { + Int128 sum = Int128.ZERO(); + List energyContainerList = this.energyContainerList; + for (IEnergyContainer iEnergyContainer : energyContainerList) { + sum.add(((IInt128EnergyContainer) iEnergyContainer).getInt128OutputPerSec()); + } + return sum; + } + + @Unique + public Int128 gTLCore$getInt128EnergyStored() { + Int128 sum = Int128.ZERO(); + List energyContainerList = this.energyContainerList; + for (IEnergyContainer iEnergyContainer : energyContainerList) { + sum.add(iEnergyContainer.getEnergyStored()); + } + return sum; + } + + @Unique + public Int128 gTLCore$getInt128EnergyCapacity() { + Int128 sum = Int128.ZERO(); + List energyContainerList = this.energyContainerList; + for (IEnergyContainer iEnergyContainer : energyContainerList) { + sum.add(iEnergyContainer.getEnergyCapacity()); + } + return sum; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/misc/LaserContainerListMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/misc/LaserContainerListMixin.java new file mode 100644 index 000000000..9f35b86f5 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/misc/LaserContainerListMixin.java @@ -0,0 +1,47 @@ +package org.gtlcore.gtlcore.mixin.gtm.api.misc; + +import org.gtlcore.gtlcore.utils.NumberUtils; + +import com.gregtechceu.gtceu.api.capability.ILaserContainer; +import com.gregtechceu.gtceu.api.misc.LaserContainerList; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.List; + +@Mixin(LaserContainerList.class) +public abstract class LaserContainerListMixin { + + @Shadow(remap = false) + @Final + private List energyContainerList; + + /** + * @author Dragons + * @reason Long Saturated + */ + @Overwrite(remap = false) + public long getInputVoltage() { + long inputVoltage = 0L; + for (ILaserContainer container : energyContainerList) { + inputVoltage = NumberUtils.saturatedAdd(inputVoltage, NumberUtils.saturatedMultiply(container.getInputVoltage(), container.getInputAmperage())); + } + return inputVoltage; + } + + /** + * @author Dragons + * @reason Long Saturated + */ + @Overwrite(remap = false) + public long getOutputVoltage() { + long outputVoltage = 0L; + for (ILaserContainer container : energyContainerList) { + outputVoltage = NumberUtils.saturatedAdd(outputVoltage, NumberUtils.saturatedMultiply(container.getOutputVoltage(), container.getOutputAmperage())); + } + return outputVoltage; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/pattern/BlockPatternMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/pattern/BlockPatternMixin.java new file mode 100644 index 000000000..5f02615cf --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/pattern/BlockPatternMixin.java @@ -0,0 +1,313 @@ +package org.gtlcore.gtlcore.mixin.gtm.api.pattern; + +import org.gtlcore.gtlcore.api.pattern.util.IMultiblockStateGet; + +import com.gregtechceu.gtceu.api.block.ActiveBlock; +import com.gregtechceu.gtceu.api.block.MetaMachineBlock; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiPart; +import com.gregtechceu.gtceu.api.pattern.*; +import com.gregtechceu.gtceu.api.pattern.error.*; +import com.gregtechceu.gtceu.api.pattern.predicates.SimplePredicate; +import com.gregtechceu.gtceu.api.pattern.util.PatternMatchContext; +import com.gregtechceu.gtceu.api.pattern.util.RelativeDirection; + +import com.lowdragmc.lowdraglib.utils.BlockInfo; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; + +import it.unimi.dsi.fastutil.longs.*; +import it.unimi.dsi.fastutil.objects.*; +import org.spongepowered.asm.mixin.*; + +import java.lang.reflect.Array; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Consumer; + +/** + * 思路参考自gto + * @line ... + */ + +@Mixin(BlockPattern.class) +public abstract class BlockPatternMixin { + + @Mutable + @Shadow(remap = false) + @Final + protected final TraceabilityPredicate[][][] blockMatches; + @Mutable + @Shadow(remap = false) + @Final + protected final int fingerLength; + @Mutable + @Shadow(remap = false) + @Final + protected final int thumbLength; + @Mutable + @Shadow(remap = false) + @Final + protected final int palmLength; + @Mutable + @Shadow(remap = false) + @Final + public final int[][] aisleRepetitions; + @Mutable + @Shadow(remap = false) + @Final + public final RelativeDirection[] structureDir; + @Mutable + @Shadow(remap = false) + @Final + protected final int[] centerOffset; + + protected BlockPatternMixin(TraceabilityPredicate[][][] blockMatches, int fingerLength, int thumbLength, int palmLength, int[][] aisleRepetitions, RelativeDirection[] structureDir, int[] centerOffset) { + this.blockMatches = blockMatches; + this.fingerLength = fingerLength; + this.thumbLength = thumbLength; + this.palmLength = palmLength; + this.aisleRepetitions = aisleRepetitions; + this.structureDir = structureDir; + this.centerOffset = centerOffset; + } + + @Shadow(remap = false) + protected abstract BlockPos setActualRelativeOffset(int x, int y, int z, Direction facing, Direction upwardsFacing, boolean isFlipped); + + @Shadow(remap = false) + protected abstract void resetFacing(BlockPos pos, BlockState blockState, Direction facing, BiFunction checker, Consumer consumer); + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public boolean checkPatternAt(MultiblockState worldState, BlockPos centerPos, Direction frontFacing, Direction upwardsFacing, boolean isFlipped, boolean savePredicate) { + boolean findFirstAisle = false; + int minZ = -centerOffset[4]; + if (worldState instanceof IMultiblockStateGet stateGet) stateGet.cleanState(); + PatternMatchContext matchContext = worldState.getMatchContext(); + Map globalCount = worldState.getGlobalCount(); + Map layerCount = worldState.getLayerCount(); + // Checking aisles + for (int c = 0, z = minZ++, r; c < this.fingerLength; c++) { + // Checking repeatable slices + loop: + for (r = 0; (findFirstAisle ? r < aisleRepetitions[c][1] : z <= -centerOffset[3]); r++) { + // Checking single slice + layerCount.clear(); + + for (int b = 0, y = -centerOffset[1]; b < this.thumbLength; b++, y++) { + for (int a = 0, x = -centerOffset[0]; a < this.palmLength; a++, x++) { + worldState.setError(null); + TraceabilityPredicate predicate = this.blockMatches[c][b][a]; + if (predicate.isAny()) continue; + BlockPos pos = setActualRelativeOffset(x, y, z, frontFacing, upwardsFacing, isFlipped).offset(centerPos.getX(), + centerPos.getY(), centerPos.getZ()); + if (worldState instanceof IMultiblockStateGet stateGet && !stateGet.updateState(pos, predicate)) { + return false; + } + if (predicate.addCache()) { + worldState.addPosCache(pos); + if (savePredicate) { + matchContext.getOrCreate("predicates", Object2ObjectOpenHashMap::new).put(pos, predicate); + } + } + boolean canPartShared = true; + if (worldState.getTileEntity() instanceof IMachineBlockEntity machineBlockEntity && + machineBlockEntity.getMetaMachine() instanceof IMultiPart part) { // add detected parts + if (!predicate.isAny()) { + if (part.isFormed() && !part.canShared() && + !part.hasController(worldState.controllerPos)) { // check part can be shared + canPartShared = false; + worldState.setError(new PatternStringError("multiblocked.pattern.error.share")); + } else { + matchContext.getOrCreate("parts", ObjectOpenHashSet::new).add(part); + } + } + } + if (worldState.getBlockState().getBlock() instanceof ActiveBlock) { + matchContext.getOrCreate("vaBlocks", LongOpenHashSet::new) + .add(worldState.getPos().asLong()); + } + if (!predicate.test(worldState) || !canPartShared) { // matching failed + if (findFirstAisle) { + if (r < aisleRepetitions[c][0]) {// retreat to see if the first aisle can start later + r = c = 0; + z = minZ++; + matchContext.reset(); + findFirstAisle = false; + } + } else { + z++;// continue searching for the first aisle + } + continue loop; + } + matchContext.getOrCreate("ioMap", Long2ObjectOpenHashMap::new).put(worldState.getPos().asLong(), + worldState.io); + } + } + findFirstAisle = true; + z++; + + // Check layer-local matcher predicate + for (var entry : layerCount.entrySet()) { + if (entry.getValue() < entry.getKey().minLayerCount) { + worldState.setError(new SinglePredicateError(entry.getKey(), 3)); + return false; + } + } + } + // Repetitions out of range + if (r < aisleRepetitions[c][0] || worldState.hasError() || !findFirstAisle) { + if (!worldState.hasError()) { + worldState.setError(new PatternError()); + } + return false; + } + } + + // Check count matches amount + for (var entry : globalCount.entrySet()) { + if (entry.getValue() < entry.getKey().minCount) { + worldState.setError(new SinglePredicateError(entry.getKey(), 1)); + return false; + } + } + + worldState.setError(null); + worldState.setNeededFlip(isFlipped); + return true; + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public BlockInfo[][][] getPreview(int[] repetition) { + Object2IntOpenHashMap cacheGlobal = new Object2IntOpenHashMap<>(); + Long2ObjectOpenHashMap blocks = new Long2ObjectOpenHashMap<>(); + int minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE, minZ = Integer.MAX_VALUE; + int maxX = Integer.MIN_VALUE, maxY = Integer.MIN_VALUE, maxZ = Integer.MIN_VALUE; + for (int l = 0, x = 0; l < this.fingerLength; l++) { + for (int r = 0; r < repetition[l]; r++) { + Object2IntOpenHashMap cacheLayer = new Object2IntOpenHashMap<>(); + for (int y = 0; y < this.thumbLength; y++) { + for (int z = 0; z < this.palmLength; z++) { + TraceabilityPredicate predicate = this.blockMatches[l][y][z]; + if (predicate.isAny()) continue; + boolean find = false; + BlockInfo[] infos = null; + for (SimplePredicate limit : predicate.limited) { + if (limit.minLayerCount > 0) { + if (cacheLayer.getInt(limit) < limit.minLayerCount) { + cacheLayer.addTo(limit, 1); + } else continue; + if (cacheGlobal.getInt(limit) < limit.previewCount) { + cacheGlobal.addTo(limit, 1); + } else continue; + } else continue; + infos = limit.candidates == null ? null : limit.candidates.get(); + find = true; + break; + } + if (!find) { + for (SimplePredicate limit : predicate.limited) { + if (limit.minCount == -1 && limit.previewCount == -1) continue; + if (cacheGlobal.getInt(limit) < limit.previewCount) { + cacheGlobal.addTo(limit, 1); + } else if (limit.minCount > 0) { + if (cacheGlobal.getInt(limit) < limit.minCount) { + cacheGlobal.addTo(limit, 1); + } else continue; + } else continue; + infos = limit.candidates == null ? null : limit.candidates.get(); + find = true; + break; + } + } + if (!find) { + for (SimplePredicate common : predicate.common) { + if (common.previewCount > 0) { + if (cacheGlobal.getInt(common) < common.previewCount) { + cacheGlobal.addTo(common, 1); + } else continue; + } else continue; + infos = common.candidates == null ? null : common.candidates.get(); + find = true; + break; + } + } + if (!find) { + for (SimplePredicate common : predicate.common) { + if (common.previewCount == -1) { + infos = common.candidates == null ? null : common.candidates.get(); + find = true; + break; + } + } + } + if (!find) { + for (SimplePredicate limit : predicate.limited) { + if (limit.previewCount != -1) continue; + if (limit.maxCount != -1 || limit.maxLayerCount != -1) { + if (cacheGlobal.getOrDefault(limit, 0) < limit.maxCount) { + cacheGlobal.addTo(limit, 1); + } else if (cacheLayer.getOrDefault(limit, 0) < limit.maxLayerCount) { + cacheLayer.addTo(limit, 1); + } else continue; + } + infos = limit.candidates == null ? null : limit.candidates.get(); + break; + } + } + BlockInfo info = infos == null || infos.length == 0 ? BlockInfo.EMPTY : infos[0]; + BlockPos pos = setActualRelativeOffset(z, y, x, Direction.NORTH, Direction.UP, false); + + blocks.put(pos.asLong(), info); + minX = Math.min(pos.getX(), minX); + minY = Math.min(pos.getY(), minY); + minZ = Math.min(pos.getZ(), minZ); + maxX = Math.max(pos.getX(), maxX); + maxY = Math.max(pos.getY(), maxY); + maxZ = Math.max(pos.getZ(), maxZ); + } + } + x++; + } + } + + BlockInfo[][][] result = (BlockInfo[][][]) Array.newInstance(BlockInfo.class, new int[] { maxX - minX + 1, maxY - minY + 1, maxZ - minZ + 1 }); + int finalMinX = minX; + int finalMinY = minY; + int finalMinZ = minZ; + blocks.long2ObjectEntrySet().fastForEach(entry -> { + var blockPos = entry.getLongKey(); + var pos = BlockPos.of(blockPos); + var info = entry.getValue(); + resetFacing(pos, info.getBlockState(), null, (p, f) -> { + BlockInfo blockInfo = blocks.get(p.relative(f).asLong()); + if (blockInfo == null || blockInfo.getBlockState().getBlock() == Blocks.AIR) { + if (blocks.get(blockPos).getBlockState().getBlock() instanceof MetaMachineBlock machineBlock) { + if (machineBlock.newBlockEntity(BlockPos.ZERO, + machineBlock.defaultBlockState()) instanceof IMachineBlockEntity machineBlockEntity) { + var machine = machineBlockEntity.getMetaMachine(); + if (machine instanceof IMultiController) return false; + else return machine.isFacingValid(f); + } + } + return true; + } + return false; + }, info::setBlockState); + result[pos.getX() - finalMinX][pos.getY() - finalMinY][pos.getZ() - finalMinZ] = info; + }); + return result; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/ChanceLogicOrMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/ChanceLogicOrMixin.java new file mode 100644 index 000000000..3844424ef --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/ChanceLogicOrMixin.java @@ -0,0 +1,43 @@ +package org.gtlcore.gtlcore.mixin.gtm.api.recipe; + +import org.gtlcore.gtlcore.api.recipe.chance.LongChanceLogic; + +import com.gregtechceu.gtceu.api.capability.recipe.RecipeCapability; +import com.gregtechceu.gtceu.api.recipe.chance.boost.ChanceBoostFunction; +import com.gregtechceu.gtceu.api.recipe.content.Content; + +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.*; + +import java.util.List; + +@Mixin(targets = "com.gregtechceu.gtceu.api.recipe.chance.logic.ChanceLogic$1") +public abstract class ChanceLogicOrMixin { + + /** + * @author Dragons + * @reason 修复1/Parallel导致的精度问题 + */ + @Overwrite(remap = false) + public @Nullable List roll( + List chancedEntries, + @NotNull ChanceBoostFunction boostFunction, + int baseTier, + int machineTier, + @Nullable Object2IntMap cache, + int times, + RecipeCapability cap) { + List out = new ObjectArrayList<>(chancedEntries.size()); + + for (Content entry : chancedEntries) { + int maxChance = entry.maxChance; + int newChance = LongChanceLogic.getChance(entry, boostFunction, baseTier, machineTier); + LongChanceLogic.modifyByChanceSafe(cache, times, cap, out, entry, maxChance, newChance); + } + + return out.isEmpty() ? null : out; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/ContentModifierMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/ContentModifierMixin.java new file mode 100644 index 000000000..070ddac25 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/ContentModifierMixin.java @@ -0,0 +1,57 @@ +package org.gtlcore.gtlcore.mixin.gtm.api.recipe; + +import org.gtlcore.gtlcore.api.recipe.IAdvancedContentModifier; + +import com.gregtechceu.gtceu.api.recipe.content.ContentModifier; + +import org.spongepowered.asm.mixin.*; + +import java.math.BigDecimal; +import java.math.BigInteger; + +@Mixin(ContentModifier.class) +public class ContentModifierMixin implements IAdvancedContentModifier { + + @Unique + private long numerator = -1; + + @Unique + private long denominator = -1; + + @Unique + private boolean useFraction; + + @Shadow(remap = false) + @Final + private double multiplier; + + @Shadow(remap = false) + @Final + private double addition; + + /** + * @author Dragons + * @reason QFT空转等问题 + */ + @Overwrite(remap = false) + public Number apply(Number number) { + if (number instanceof Long l) { + if (useFraction) return l / denominator * numerator; + return number.doubleValue() * this.multiplier + this.addition; + } else if (number instanceof BigDecimal decimal) { + return decimal.multiply(BigDecimal.valueOf(this.multiplier)).add(BigDecimal.valueOf(this.addition)); + } else if (number instanceof BigInteger bigInteger) { + return bigInteger.multiply(BigInteger.valueOf((long) this.multiplier)).add(BigInteger.valueOf((long) this.addition)); + } else { + return number.doubleValue() * this.multiplier + this.addition; + } + } + + @Override + @Unique + public void setDivision(long numerator, long denominator) { + this.numerator = numerator; + this.denominator = denominator; + this.useFraction = true; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/FluidRecipeCapabilityMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/FluidRecipeCapabilityMixin.java deleted file mode 100644 index c9bc36e36..000000000 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/FluidRecipeCapabilityMixin.java +++ /dev/null @@ -1,143 +0,0 @@ -package org.gtlcore.gtlcore.mixin.gtm.api.recipe; - -import org.gtlcore.gtlcore.api.machine.trait.IDistinctMachine; - -import com.gregtechceu.gtceu.api.capability.recipe.*; -import com.gregtechceu.gtceu.api.recipe.GTRecipe; -import com.gregtechceu.gtceu.api.recipe.content.Content; -import com.gregtechceu.gtceu.api.recipe.content.ContentModifier; -import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; - -import com.lowdragmc.lowdraglib.side.fluid.FluidStack; - -import it.unimi.dsi.fastutil.objects.Object2LongMap; -import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; -import org.spongepowered.asm.mixin.*; - -import java.util.*; - -@Mixin(FluidRecipeCapability.class) -public class FluidRecipeCapabilityMixin { - - /** - * @author Adonis - * @reason 流体输入输出上限改为long - */ - @Overwrite(remap = false) - public FluidIngredient copyWithModifier(FluidIngredient content, ContentModifier modifier) { - if (content.isEmpty()) { - return content.copy(); - } else { - FluidIngredient copy = content.copy(); - copy.setAmount(modifier.apply(copy.getAmount()).longValue()); - return copy; - } - } - - /** - * @author Adonis - * @reason 支持流体隔离 - */ - @Overwrite(remap = false) - public int getMaxParallelRatio(IRecipeCapabilityHolder holder, GTRecipe recipe, int parallelAmount) { - if (holder instanceof IDistinctMachine iDistinctMachine) { - if (iDistinctMachine.getRecipeHandleParts().isEmpty()) return 0; - Object2LongOpenHashMap ingredientStacks = new Object2LongOpenHashMap<>(); - if (iDistinctMachine.getDistinctHatch() != null) { - List> distinctIMultiPart = iDistinctMachine.getDistinctHatch().allHandles().get(FluidRecipeCapability.CAP); - for (IRecipeHandler handler : distinctIMultiPart) { - for (Object o : handler.getContents()) { - if (o instanceof FluidStack fluidStack) { - ingredientStacks.computeLong(fluidStack, (k, v) -> v == null ? fluidStack.getAmount() : v + fluidStack.getAmount()); - } - } - } - } else { - Object2LongOpenHashMap map = new Object2LongOpenHashMap<>(); - for (var container : Objects.requireNonNullElseGet(holder.getCapabilitiesProxy().get(IO.IN, FluidRecipeCapability.CAP), - Collections::>emptyList).stream().toList()) { - Object2LongOpenHashMap fluidMap = new Object2LongOpenHashMap<>(); - for (Object object : container.getContents()) { - if (object instanceof FluidStack fluidStack) { - fluidMap.computeLong(fluidStack, (k, v) -> v == null ? fluidStack.getAmount() : v + fluidStack.getAmount()); - } - } - if (container.isDistinct()) { - for (var entry : fluidMap.object2LongEntrySet()) { - ingredientStacks.computeLong(entry.getKey(), (k, v) -> v == null ? entry.getLongValue() : Math.max(v, entry.getLongValue())); - } - } else { - for (Object2LongMap.Entry obj : fluidMap.object2LongEntrySet()) { - map.computeLong(obj.getKey(), (k, v) -> v == null ? obj.getLongValue() : v + obj.getLongValue()); - } - } - } - for (var entry : map.object2LongEntrySet()) { - ingredientStacks.computeLong(entry.getKey(), (k, v) -> v == null ? entry.getLongValue() : Math.max(v, entry.getLongValue())); - } - } - - int minMultiplier = Integer.MAX_VALUE; - Object2LongOpenHashMap fluidCountMap = new Object2LongOpenHashMap<>(); - Object2LongOpenHashMap notConsumableMap = new Object2LongOpenHashMap<>(); - for (Content content : recipe.getInputContents(FluidRecipeCapability.CAP)) { - FluidIngredient fluidInput = FluidRecipeCapability.CAP.of(content.content); - long fluidAmount = fluidInput.getAmount(); - if (content.chance == 0) { - notConsumableMap.computeIfPresent(fluidInput, - (k, v) -> v + fluidAmount); - notConsumableMap.putIfAbsent(fluidInput, fluidAmount); - } else { - fluidCountMap.computeIfPresent(fluidInput, - (k, v) -> v + fluidAmount); - fluidCountMap.putIfAbsent(fluidInput, fluidAmount); - } - } - for (Map.Entry notConsumableFluid : notConsumableMap.entrySet()) { - long needed = notConsumableFluid.getValue(); - long available = 0; - for (Map.Entry inputFluid : ingredientStacks.entrySet()) { - if (notConsumableFluid.getKey().test( - FluidStack.create(inputFluid.getKey().getFluid(), inputFluid.getValue(), inputFluid.getKey().getTag()))) { - available = inputFluid.getValue(); - if (available > needed) { - inputFluid.setValue(available - needed); - needed -= available; - break; - } else { - inputFluid.setValue(0L); - notConsumableFluid.setValue(needed - available); - needed -= available; - } - } - } - if (needed >= available) { - return 0; - } - } - if (fluidCountMap.isEmpty() && !notConsumableMap.isEmpty()) { - return parallelAmount; - } - for (Map.Entry fs : fluidCountMap.entrySet()) { - long needed = fs.getValue(); - long available = 0; - for (Map.Entry inputFluid : ingredientStacks.entrySet()) { - if (fs.getKey().test( - FluidStack.create(inputFluid.getKey().getFluid(), inputFluid.getValue(), inputFluid.getKey().getTag()))) { - available += inputFluid.getValue(); - } - } - if (available >= needed) { - int ratio = (int) Math.min(parallelAmount, available / needed); - if (ratio < minMultiplier) { - minMultiplier = ratio; - } - } else { - return 0; - } - } - return minMultiplier; - } - return 0; - } -} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/GTRecipeLookupMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/GTRecipeLookupMixin.java index 80f3f987b..9cd6bcf72 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/GTRecipeLookupMixin.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/GTRecipeLookupMixin.java @@ -1,40 +1,48 @@ package org.gtlcore.gtlcore.mixin.gtm.api.recipe; -import org.gtlcore.gtlcore.api.machine.trait.IDistinctMachine; -import org.gtlcore.gtlcore.api.recipe.RecipeRunner; -import org.gtlcore.gtlcore.api.recipe.RecipeRunnerHelper; +import org.gtlcore.gtlcore.api.machine.multiblock.CoilWorkableElectricMultipleRecipesMachine; +import org.gtlcore.gtlcore.api.machine.trait.IRecipeCapabilityMachine; +import org.gtlcore.gtlcore.api.machine.trait.MEPatternRecipeHandlePart; +import org.gtlcore.gtlcore.api.machine.trait.RecipeHandlePart; +import org.gtlcore.gtlcore.api.recipe.IAdditionalRecipeIterator; +import org.gtlcore.gtlcore.api.recipe.IRecipeIterator; +import org.gtlcore.gtlcore.common.machine.multiblock.electric.WorkableElectricMultipleRecipesMachine; import com.gregtechceu.gtceu.api.capability.recipe.*; import com.gregtechceu.gtceu.api.machine.WorkableTieredMachine; import com.gregtechceu.gtceu.api.machine.steam.SteamWorkableMachine; import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.GTRecipeType; import com.gregtechceu.gtceu.api.recipe.lookup.*; import com.gregtechceu.gtceu.common.machine.multiblock.electric.research.ResearchStationMachine; import com.gregtechceu.gtceu.common.machine.multiblock.primitive.PrimitiveWorkableMachine; import com.mojang.datafixers.util.Either; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.ints.*; +import it.unimi.dsi.fastutil.objects.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.*; +import java.lang.ref.WeakReference; import java.util.*; import java.util.function.Predicate; @Mixin(GTRecipeLookup.class) -public abstract class GTRecipeLookupMixin implements IDistinctMachine { +public abstract class GTRecipeLookupMixin { + + @Shadow(remap = false) + @Final + private GTRecipeType recipeType; @Unique - private IRecipeCapabilityHolder gtlcore$machine; + private IRecipeCapabilityHolder gtlCore$machine; - /** - * @author . - * @reason . - */ - @Overwrite(remap = false) - public @Nullable GTRecipe findRecipe(IRecipeCapabilityHolder holder) { - return this.find(holder, (recipe) -> RecipeRunnerHelper.matchRecipe(holder, recipe)); - } + @Shadow(remap = false) + private static final WeakHashMap> ingredientRoot = new WeakHashMap(); + + @Unique + private static final Comparator SPECIAL_INGREDIENT_COMPARATOR = Comparator.comparing(i -> !i.isSpecialIngredient()); /** * @author . @@ -42,43 +50,123 @@ public abstract class GTRecipeLookupMixin implements IDistinctMachine { */ @Overwrite(remap = false) protected @Nullable List> prepareRecipeFind(@NotNull IRecipeCapabilityHolder holder) { - this.gtlcore$machine = holder; - if (holder instanceof ResearchStationMachine || holder instanceof WorkableTieredMachine || - holder instanceof SteamWorkableMachine || holder instanceof PrimitiveWorkableMachine) { - List> list = new ObjectArrayList<>(2); + this.gtlCore$machine = holder; + if (holder instanceof PrimitiveWorkableMachine || holder instanceof SteamWorkableMachine || + holder instanceof WorkableTieredMachine || holder instanceof ResearchStationMachine) { + int totalSize = 0; + for (var entries : holder.getCapabilitiesProxy().row(IO.IN).entrySet()) { + int size = 0; + if ((entries.getKey()).isRecipeSearchFilter()) { + for (var entry : entries.getValue()) + if (entry.getSize() != -1) size += entry.getSize(); + if (size == Integer.MAX_VALUE) return null; + totalSize += size; + } + } + if (totalSize == 0) return null; + List> list = new ObjectArrayList<>(totalSize); list.addAll(fromHolder(holder)); if (list.isEmpty()) return null; return list; - } else if (holder instanceof IDistinctMachine iDistinctMachine) { - if (iDistinctMachine.getRecipeHandleParts().isEmpty()) return null; - List> list = new ObjectArrayList<>(iDistinctMachine.getRecipeHandleParts().size()); - list.addAll(this.gtlcore$fromHolder(iDistinctMachine)); + } else if (holder instanceof IRecipeCapabilityMachine machine) { + if (machine.emptyRecipeHandlePart()) return null; + List> list = new ObjectArrayList<>(); + list.addAll(this.gtlCore$fromHolder(machine)); if (list.isEmpty()) { return null; } return list; } - return null; + return List.of(); } @Unique - protected @NotNull List> gtlcore$fromHolder(@NotNull IDistinctMachine r) { - List> list; - List recipeHandleParts = r.getRecipeHandleParts().stream().filter(h -> h.io() == IO.IN).toList(); - list = new ObjectArrayList<>(recipeHandleParts.size()); - for (var part : recipeHandleParts) { - ObjectArrayList ingredients = new ObjectArrayList<>(); - for (var it = part.allHandles().object2ObjectEntrySet().fastIterator(); it.hasNext();) { - var next = it.next(); - var cap = next.getKey(); - for (var handler : next.getValue()) { - List compressed = cap.compressIngredients(handler.getContents()); - for (Object content : compressed) { - ingredients.addAll(cap.convertToMapIngredient(content).stream().sorted(Comparator.comparing(i -> !i.isSpecialIngredient())).toList()); + protected @NotNull List> gtlCore$fromHolder(@NotNull IRecipeCapabilityMachine r) { + if (r.emptyRecipeHandlePart()) return Collections.emptyList(); + List recipeHandleParts = r.getNormalRecipeHandlePart(IO.IN); + List mePatternRecipeHandleParts = r.getMEPatternRecipeHandleParts(); + List> list = new ObjectArrayList<>(recipeHandleParts.size() + mePatternRecipeHandleParts.size()); + + // ==================== ME Pattern for uncached slots ==================== + if (!mePatternRecipeHandleParts.isEmpty()) { + for (var part : mePatternRecipeHandleParts) { + var finalMap = new Int2ReferenceArrayMap, List>>(); + + // slot -> (RecipeCapability -> Contents) + for (var meHandler : part.getMERecipeHandlers()) { + for (var e : Int2ObjectMaps.fastIterable(meHandler.getActiveAndUnCachedSlotsLimitContentsMap())) { + int slot = e.getIntKey(); + var contents = e.getValue(); + + finalMap.computeIfAbsent(slot, k -> new Reference2ReferenceArrayMap<>(2)) + .computeIfAbsent(meHandler.getCapability(), ignore -> new ObjectArrayList<>()).addAll(contents); + } + } + + for (var slotEntry : Int2ReferenceMaps.fastIterable(finalMap)) { + List ingredient = new ObjectArrayList<>(); + + for (var capEntry : Reference2ReferenceMaps.fastIterable(slotEntry.getValue())) { + var cap = capEntry.getKey(); + for (var content : cap.compressIngredients(capEntry.getValue())) { + ingredient.addAll(cap.convertToMapIngredient(content)); + } + } + + if (!ingredient.isEmpty()) { + ingredient.sort(SPECIAL_INGREDIENT_COMPARATOR); + list.add(ingredient); } } } - list.add(ingredients); + } + // ==================== End ==================== + + if (r.isDistinct()) { + for (var part : recipeHandleParts) { + List ingredients = new ObjectArrayList<>(); + for (var entry : part.getHandlerFastIterable()) { + var cap = entry.getKey(); + for (var handler : entry.getValue()) { + for (var content : cap.compressIngredients(handler.getContents())) { + var converted = cap.convertToMapIngredient(content); + converted.sort(SPECIAL_INGREDIENT_COMPARATOR); + ingredients.addAll(converted); + } + } + } + list.add(ingredients); + } + } else { + RecipeHandlePart shared = r.getSharedRecipeHandlePart(); + List fluidIngredients = new ObjectArrayList<>(); + if (shared != null) { + for (var fluidHandler : shared.getCapability(FluidRecipeCapability.CAP)) { + for (var content : FluidRecipeCapability.CAP.compressIngredients(fluidHandler.getContents())) { + fluidIngredients.addAll(FluidRecipeCapability.CAP.convertToMapIngredient(content)); + } + } + + List itemIngredients = new ObjectArrayList<>(); + for (var itemHandler : shared.getCapability(ItemRecipeCapability.CAP)) { + for (var content : ItemRecipeCapability.CAP.compressIngredients(itemHandler.getContents())) { + itemIngredients.addAll(ItemRecipeCapability.CAP.convertToMapIngredient(content)); + } + } + itemIngredients.addAll(fluidIngredients); + list.add(itemIngredients); + } + + for (var otherPart : recipeHandleParts) { + List itemIngredients = new ObjectArrayList<>(); + for (var itemHandler : otherPart.getCapability(ItemRecipeCapability.CAP)) { + for (var content : ItemRecipeCapability.CAP.compressIngredients(itemHandler.getContents())) { + itemIngredients.addAll(ItemRecipeCapability.CAP.convertToMapIngredient(content)); + } + } + itemIngredients.addAll(fluidIngredients); + list.add(itemIngredients); + } } return list; } @@ -92,11 +180,11 @@ public abstract class GTRecipeLookupMixin implements IDistinctMachine { @NotNull Branch branchMap, @NotNull Predicate canHandle, int index, int count, long skip) { if (count == ingredients.size()) { return null; - } else if (this.gtlcore$machine instanceof ResearchStationMachine || this.gtlcore$machine instanceof WorkableTieredMachine || - this.gtlcore$machine instanceof SteamWorkableMachine || this.gtlcore$machine instanceof PrimitiveWorkableMachine) { + } else if (this.gtlCore$machine instanceof PrimitiveWorkableMachine || this.gtlCore$machine instanceof SteamWorkableMachine || + this.gtlCore$machine instanceof WorkableTieredMachine || this.gtlCore$machine instanceof ResearchStationMachine) { for (AbstractMapIngredient obj : ingredients.get(index)) { - Map> targetMap = determineRootNodes(obj, branchMap); - Either result = targetMap.get(obj); + var targetMap = determineRootNodes(obj, branchMap); + var result = targetMap.get(obj); if (result != null) { GTRecipe r = result.map(potentialRecipe -> canHandle.test(potentialRecipe) ? potentialRecipe : null, potentialBranch -> diveIngredientTreeFindRecipe(ingredients, potentialBranch, canHandle, index, count, skip)); @@ -106,44 +194,123 @@ public abstract class GTRecipeLookupMixin implements IDistinctMachine { } } } else - if (this.gtlcore$machine instanceof IDistinctMachine) { - List ingredient = new ObjectArrayList<>(ingredients.get(index)); - return this.gtlcore$diveIngredientTreeFindRecipe(ingredient, branchMap, canHandle); + if (this.gtlCore$machine instanceof IRecipeCapabilityMachine) { + var ingredient = new ObjectArrayList<>(ingredients.get(index)); + return IRecipeIterator.diveIngredientTreeFindRecipe(ingredient, branchMap, canHandle); } return null; } - @Unique - private @Nullable GTRecipe gtlcore$diveIngredientTreeFindRecipe(@NotNull List ingredients, @NotNull Branch branchMap, - @NotNull Predicate canHandle) { - if (ingredients.isEmpty()) return null; - for (var o : ingredients) { - Map> targetMap = determineRootNodes(o, branchMap); - Either result = targetMap.get(o); - if (result != null) { - GTRecipe r = result.map((potentialRecipe) -> canHandle.test(potentialRecipe) ? potentialRecipe : null, - (potentialBranch) -> this.gtlcore$diveIngredientTreeFindRecipe(ingredients, potentialBranch, canHandle)); - if (r != null) { - return r; + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + private @Nullable GTRecipe diveIngredientTreeFindRecipe(@NotNull List> ingredients, @NotNull Branch map, @NotNull Predicate canHandle, int currentIndex, int count, long skip) { + int i = (currentIndex + 1) % ingredients.size(); + while (i != currentIndex) { + if (((skip & (1L << i)) == 0)) { + GTRecipe found = recurseIngredientTreeFindRecipe(ingredients, map, canHandle, i, count + 1, skip | (1L << i)); + if (found != null) { + return found; } } + i = (i + 1) % ingredients.size(); } return null; } + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + protected @NotNull List> fromRecipe(@NotNull GTRecipe r) { + List> list = new ObjectArrayList<>(r.inputs.size()); + r.inputs.forEach((cap, contents) -> { + if (cap.isRecipeSearchFilter() && !contents.isEmpty()) { + var ingredients = new ArrayList<>(); + for (var content : contents) ingredients.add(content.getContent()); + for (var ingredient : cap.compressIngredients(ingredients)) + retrieveCachedIngredient(list, cap.convertToMapIngredient(ingredient), ingredientRoot); + } + }); + return list; + } + @Shadow(remap = false) protected abstract @NotNull List> fromHolder(@NotNull IRecipeCapabilityHolder r); @Shadow(remap = false) - public abstract @Nullable GTRecipe find(@NotNull IRecipeCapabilityHolder holder, @NotNull Predicate canHandle); + protected static void retrieveCachedIngredient(@NotNull List> list, @NotNull List ingredients, @NotNull WeakHashMap> cache) {} @Shadow(remap = false) - private @Nullable GTRecipe diveIngredientTreeFindRecipe(@NotNull List> ingredients, @NotNull Branch map, @NotNull Predicate canHandle, int currentIndex, int count, long skip) { - return null; + protected static @NotNull Map> determineRootNodes(@NotNull AbstractMapIngredient ingredient, @NotNull Branch branchMap) { + throw new RuntimeException(); } @Shadow(remap = false) - protected static @NotNull Map> determineRootNodes(@NotNull AbstractMapIngredient ingredient, @NotNull Branch branchMap) { + private GTRecipe recurseIngredientTreeFindRecipe(@NotNull List> ingredients, + @NotNull Branch branchRoot, + @NotNull Predicate canHandle) { throw new RuntimeException(); } + + @Shadow(remap = false) + @Final + private Branch lookup; + + /** + * @author Dragons + * @reason 跨配方并行机器的lock功能修复 + */ + @Overwrite(remap = false) + @Nullable + public GTRecipe find(@NotNull IRecipeCapabilityHolder holder, @NotNull Predicate canHandle) { + // find Cached Recipe in MEHandlers First + if (holder instanceof IRecipeCapabilityMachine rlm) { + var parts = rlm.getMEPatternRecipeHandleParts(); + for (var part : parts) { + var cachedMERecipes = part.getCachedGTRecipe(); + if (!cachedMERecipes.isEmpty()) { + return cachedMERecipes.iterator().next(); + } + } + } + List> list = prepareRecipeFind(holder); + // couldn't build any inputs to use for search, so no recipe could be found + if (list == null) return null; + return recurseIngredientTreeFindRecipe(list, this.lookup, canHandle); + } + + /** + * @author Dragons + * @reason 为RecipeIterator添加ME配方缓存支持 + */ + @Overwrite(remap = false) + public @NotNull RecipeIterator getRecipeIterator(@NotNull IRecipeCapabilityHolder holder, @NotNull Predicate canHandle) { + List> list = this.prepareRecipeFind(holder); + RecipeIterator iterator = RecipeIteratorAccessor.newRecipeIterator(this.recipeType, list, canHandle); + + // 检查是否有ME配方缓存需要合并 + if (holder instanceof IRecipeCapabilityMachine rlm) { + var parts = rlm.getMEPatternRecipeHandleParts(); + if (!parts.isEmpty()) { + List meRecipes = new ObjectArrayList<>(); + for (var part : parts) { + meRecipes.addAll(part.getCachedGTRecipe()); + } + meRecipes = meRecipes.stream().filter(r -> r.recipeType == recipeType).toList(); + if (!meRecipes.isEmpty()) { + ((IAdditionalRecipeIterator) iterator).setAdditionalRecipes(meRecipes); + } + } + + if (holder instanceof WorkableElectricMultipleRecipesMachine || holder instanceof CoilWorkableElectricMultipleRecipesMachine) { + ((IAdditionalRecipeIterator) iterator).setUseDiveIngredientTreeFind(true); + } + } + + return iterator; + } } diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/GTRecipeMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/GTRecipeMixin.java new file mode 100644 index 000000000..07e5aab26 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/GTRecipeMixin.java @@ -0,0 +1,112 @@ +package org.gtlcore.gtlcore.mixin.gtm.api.recipe; + +import org.gtlcore.gtlcore.api.recipe.IGTRecipe; +import org.gtlcore.gtlcore.api.recipe.RecipeResult; + +import com.gregtechceu.gtceu.api.capability.recipe.*; +import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; +import com.gregtechceu.gtceu.api.machine.multiblock.WorkableElectricMultiblockMachine; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.GTRecipeType; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; + +import com.google.common.primitives.Ints; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.*; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; +import java.util.Map; + +@Mixin(GTRecipe.class) +public abstract class GTRecipeMixin implements IGTRecipe { + + @Unique + private int tier = -1; + @Unique + @Getter + private long realParallels = 1; + @Unique + @Setter + private boolean hasTick; + @Unique + private IO io; + + @Shadow(remap = false) + public int parallels; + @Shadow(remap = false) + public ResourceLocation id; + @Shadow(remap = false) + public @NotNull CompoundTag data; + @Shadow(remap = false) + @Final + public GTRecipeType recipeType; + + @Inject(method = "(Lcom/gregtechceu/gtceu/api/recipe/GTRecipeType;Lnet/minecraft/resources/ResourceLocation;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/List;Ljava/util/List;Lnet/minecraft/nbt/CompoundTag;IZ)V", + at = @At("RETURN"), + remap = false) + public void GTRecipe(GTRecipeType recipeType, ResourceLocation id, Map inputs, Map outputs, Map tickInputs, Map tickOutputs, Map inputChanceLogics, Map outputChanceLogics, Map tickInputChanceLogics, Map tickOutputChanceLogics, List conditions, List ingredientActions, CompoundTag data, int duration, boolean isFuel, CallbackInfo ci) { + this.tier = this.getEuTier(); + this.hasTick = !tickInputs.isEmpty() || !tickOutputs.isEmpty(); + this.io = tickInputs.isEmpty() ? (tickOutputs.isEmpty() ? IO.NONE : IO.OUT) : IO.IN; + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public boolean hasTick() { + return this.hasTick; + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public GTRecipe.ActionResult matchTickRecipe(IRecipeCapabilityHolder holder) { + if (!this.hasTick()) return GTRecipe.ActionResult.SUCCESS; + if (holder instanceof WorkableElectricMultiblockMachine machine && this.io == IO.IN) { + var lastRecipe = machine.getRecipeLogic().getLastOriginRecipe(); + if (lastRecipe == null || !this.id.equals(lastRecipe.id)) { + if (this.getEuTier() > GTUtil.getFloorTierByVoltage(machine.getMaxVoltage())) { + RecipeResult.of((IRecipeLogicMachine) holder, RecipeResult.FAIL_VOLTAGE_TIER); + return GTRecipe.ActionResult.fail(() -> null); + } + } + } + var result = this.matchRecipe(holder, true); + if (!result.isSuccess() && result.reason() != null) { + String s = result.reason().get().toString(); + if (s.contains("_in")) { + if (s.contains("cwu")) RecipeResult.of((IRecipeLogicMachine) holder, RecipeResult.FAIL_NO_ENOUGH_CWU_IN); + else if (s.contains("eu.name")) RecipeResult.of((IRecipeLogicMachine) holder, RecipeResult.FAIL_NO_ENOUGH_EU_IN); + } else if (s.contains("_out")) { + if (s.contains("eu.name")) RecipeResult.of((IRecipeLogicMachine) holder, RecipeResult.FAIL_NO_ENOUGH_EU_OUT); + } + } + return result; + } + + @Shadow(remap = false) + protected abstract GTRecipe.ActionResult matchRecipe(IRecipeCapabilityHolder holder, boolean tick); + + @Override + public int getEuTier() { + return this.tier = tier == -1 ? this.data.getInt("euTier") : tier; + } + + @Override + public void setRealParallels(long realParallel) { + this.realParallels = realParallel; + this.parallels = Ints.saturatedCast(realParallel); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/ItemRecipeCapabilityMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/ItemRecipeCapabilityMixin.java deleted file mode 100644 index de0fc6fef..000000000 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/ItemRecipeCapabilityMixin.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.gtlcore.gtlcore.mixin.gtm.api.recipe; - -import org.gtlcore.gtlcore.api.machine.trait.IDistinctMachine; - -import com.gregtechceu.gtceu.api.capability.recipe.*; -import com.gregtechceu.gtceu.utils.GTHashMaps; -import com.gregtechceu.gtceu.utils.ItemStackHashStrategy; - -import net.minecraft.world.item.ItemStack; - -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; - -import java.util.*; -import java.util.stream.Collectors; - -@Mixin(ItemRecipeCapability.class) -public class ItemRecipeCapabilityMixin { - - /** - * @author . - * @reason . - */ - @Overwrite(remap = false) - private Object2IntMap getIngredientStacks(IRecipeCapabilityHolder holder) { - Object2IntMap map = new Object2IntOpenCustomHashMap<>(ItemStackHashStrategy.comparingAllButCount()); - Object2IntMap result = new Object2IntOpenHashMap<>(); - if (holder instanceof IDistinctMachine iDistinctMachine && iDistinctMachine.getDistinctHatch() != null) { - List> distinctIMultiPart = iDistinctMachine.getDistinctHatch().allHandles().get(ItemRecipeCapability.CAP); - for (IRecipeHandler handler : distinctIMultiPart) { - for (Object o : handler.getContents()) { - if (o instanceof ItemStack itemStack) { - result.computeInt(itemStack, (k, v) -> v == null ? itemStack.getCount() : v + itemStack.getCount()); - } - } - } - return result; - } - List> recipeHandlerList = Objects.requireNonNullElseGet(holder.getCapabilitiesProxy().get(IO.IN, ItemRecipeCapability.CAP), - Collections::>emptyList).stream().filter(handler -> !handler.isProxy()).toList(); - for (IRecipeHandler container : recipeHandlerList) { - Object2IntOpenCustomHashMap itemMap = container.getContents().stream().filter(ItemStack.class::isInstance) - .map(ItemStack.class::cast).flatMap(con -> GTHashMaps.fromItemStackCollection(Collections.singleton(con)).object2IntEntrySet() - .stream()) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, Integer::sum, - () -> new Object2IntOpenCustomHashMap<>(ItemStackHashStrategy.comparingAllButCount()))); - if (container.isDistinct()) { - result.putAll(itemMap); - } else { - for (Object2IntMap.Entry obj : itemMap.object2IntEntrySet()) { - map.computeInt(obj.getKey(), (k, v) -> v == null ? obj.getIntValue() : v + obj.getIntValue()); - } - } - } - result.putAll(map); - return result; - } -} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/OCResultMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/OCResultMixin.java new file mode 100644 index 000000000..e9bc15984 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/OCResultMixin.java @@ -0,0 +1,77 @@ +package org.gtlcore.gtlcore.mixin.gtm.api.recipe; + +import org.gtlcore.gtlcore.api.recipe.IAdvancedOCResult; + +import com.gregtechceu.gtceu.api.recipe.logic.OCResult; + +import org.spongepowered.asm.mixin.*; + +@Implements({ + @Interface( + iface = IAdvancedOCResult.class, + prefix = "gTLCore$") +}) +@Mixin(OCResult.class) +public abstract class OCResultMixin { + + @Shadow(remap = false) + private long eut; + @Shadow(remap = false) + private long parallelEUt; + @Shadow(remap = false) + private int duration; + @Shadow(remap = false) + private int parallel; + @Shadow(remap = false) + private int ocLevel; + + @Unique + private int gTLCore$baseOCLevel; + @Unique + private double gTLCore$durationFactor = 0; + @Unique + private double gTLCore$voltageFactor = 0; + + /** + * @author Dragons + * @reason 兼容 + */ + @Overwrite(remap = false) + public void init(long eut, int duration, int parallel, long parallelEUt, int ocLevel) { + this.eut = eut; + this.duration = duration; + this.parallel = parallel; + this.parallelEUt = parallelEUt; + this.ocLevel = ocLevel; + gTLCore$baseOCLevel = ocLevel; + gTLCore$durationFactor = 0; + gTLCore$voltageFactor = 0; + } + + @Unique + public void gTLCore$init(long eut, int duration, int parallel, long parallelEUt, int baseOCLevel, int totalOCLevel, double durationFactor, double voltageFactor) { + this.eut = eut; + this.duration = duration; + this.parallel = parallel; + this.parallelEUt = parallelEUt; + this.ocLevel = totalOCLevel; + gTLCore$baseOCLevel = baseOCLevel; + gTLCore$durationFactor = durationFactor; + gTLCore$voltageFactor = voltageFactor; + } + + @Unique + public int gTLCore$getBaseOCLevel() { + return gTLCore$baseOCLevel; + } + + @Unique + public double gTLCore$getDurationFactor() { + return gTLCore$durationFactor; + } + + @Unique + public double gTLCore$getVoltageFactor() { + return gTLCore$voltageFactor; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/ParallelLogicMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/ParallelLogicMixin.java new file mode 100644 index 000000000..14eee3e7f --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/ParallelLogicMixin.java @@ -0,0 +1,86 @@ +package org.gtlcore.gtlcore.mixin.gtm.api.recipe; + +import org.gtlcore.gtlcore.api.recipe.IGTRecipe; +import org.gtlcore.gtlcore.api.recipe.IParallelLogic; + +import com.gregtechceu.gtceu.api.capability.recipe.IRecipeCapabilityHolder; +import com.gregtechceu.gtceu.api.capability.recipe.RecipeCapability; +import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.content.ContentModifier; +import com.gregtechceu.gtceu.api.recipe.modifier.ParallelLogic; + +import com.google.common.primitives.Ints; +import com.mojang.datafixers.util.Pair; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +import java.util.Objects; +import java.util.function.Predicate; + +@Mixin(ParallelLogic.class) +public class ParallelLogicMixin { + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public static int getMaxRecipeMultiplier(@NotNull GTRecipe recipe, @NotNull IRecipeCapabilityHolder holder, int parallelAmount) { + int minimum = Integer.MAX_VALUE; + minimum = Ints.saturatedCast(Math.min(minimum, IParallelLogic.getMaxParallel(holder, recipe, parallelAmount))); + for (RecipeCapability cap : recipe.tickInputs.keySet()) { + if (cap.doMatchInRecipe()) { + minimum = Math.min(minimum, cap.getMaxParallelRatio(holder, recipe, parallelAmount)); + } + } + return minimum; + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public static int limitByOutputMerging(@NotNull GTRecipe recipe, @NotNull IRecipeCapabilityHolder holder, int parallelAmount, Predicate> canVoid) { + int max = parallelAmount; + max = Ints.saturatedCast(Math.min(max, IParallelLogic.getMinParallel(holder, recipe, parallelAmount))); + for (RecipeCapability cap : recipe.tickOutputs.keySet()) { + if (canVoid.test(cap) || !cap.doMatchInRecipe()) { + continue; + } + if (!recipe.getTickOutputContents(cap).isEmpty()) { + int limit = cap.limitParallel(recipe, holder, parallelAmount); + if (limit == 0) { + return 0; + } + max = Math.min(max, limit); + } + } + return max; + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public static @NotNull Pair doParallelRecipes(@NotNull GTRecipe currentRecipe, @NotNull IRecipeLogicMachine machine, int parallelAmount, boolean modifyDuration) { + int multiplierByInputs = getMaxRecipeMultiplier(currentRecipe, machine, parallelAmount); + if (multiplierByInputs == 0) { + return Pair.of(currentRecipe, 1); + } else { + Objects.requireNonNull(machine); + int limitByOutput = limitByOutputMerging(currentRecipe, machine, multiplierByInputs, machine::canVoidRecipeOutputs); + if (limitByOutput > 1) { + GTRecipe multiRecipe = currentRecipe.copy(ContentModifier.multiplier(limitByOutput), modifyDuration); + ((IGTRecipe) multiRecipe).setRealParallels((long) limitByOutput * ((IGTRecipe) currentRecipe).getRealParallels()); + multiRecipe = IParallelLogic.getRecipeOutputChance(machine, multiRecipe); + return Pair.of(multiRecipe, limitByOutput); + } else { + return Pair.of(currentRecipe, limitByOutput); + } + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/RecipeIteratorAccessor.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/RecipeIteratorAccessor.java new file mode 100644 index 000000000..7ea0bd63a --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/RecipeIteratorAccessor.java @@ -0,0 +1,29 @@ +package org.gtlcore.gtlcore.mixin.gtm.api.recipe; + +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.GTRecipeType; +import com.gregtechceu.gtceu.api.recipe.lookup.AbstractMapIngredient; +import com.gregtechceu.gtceu.api.recipe.lookup.RecipeIterator; + +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; + +import java.util.List; +import java.util.function.Predicate; + +@Mixin(RecipeIterator.class) +public interface RecipeIteratorAccessor { + + @Accessor(value = "index", remap = false) + int getIndex(); + + @Accessor(value = "index", remap = false) + void setIndex(int index); + + @Invoker("") + static RecipeIterator newRecipeIterator(@NotNull GTRecipeType recipeMap, List> ingredients, @NotNull Predicate canHandle) { + throw new AssertionError(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/RecipeIteratorMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/RecipeIteratorMixin.java new file mode 100644 index 000000000..e3ed91711 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/RecipeIteratorMixin.java @@ -0,0 +1,229 @@ +package org.gtlcore.gtlcore.mixin.gtm.api.recipe; + +import org.gtlcore.gtlcore.api.recipe.IAdditionalRecipeIterator; + +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.GTRecipeType; +import com.gregtechceu.gtceu.api.recipe.lookup.AbstractMapIngredient; +import com.gregtechceu.gtceu.api.recipe.lookup.RecipeIterator; + +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.*; + +import java.util.*; +import java.util.function.Predicate; + +import static org.gtlcore.gtlcore.api.recipe.IRecipeIterator.diveIngredientTreeFindRecipeCollection; + +@Implements(@Interface( + iface = IAdditionalRecipeIterator.class, + prefix = "gTLCore$")) +@Mixin(RecipeIterator.class) +public class RecipeIteratorMixin { + + @Shadow(remap = false) + int index; + + @Shadow(remap = false) + List> ingredients; + @Shadow(remap = false) + @NotNull + GTRecipeType recipeMap; + + @Shadow(remap = false) + @NotNull + Predicate canHandle; + + @Unique + private List gtlCore$additionalRecipes = null; + + @Unique + private Iterator gtlCore$presentIndexGTRecipesIterator = null; + + @Unique + private int gtlCore$additionalIndex = 0; + + @Unique + private boolean gtlCore$useOriginal = true; + + @Unique + private boolean gtlCore$useDiveIngredientTreeFind = false; + + @Unique + private GTRecipe gtlCore$singleRecipe = null; + + @Unique + private GTRecipe gtlCore$cachedNextAdditional = null; + + // ==================== IAdditionalRecipeIterator ==================== + + @Unique + public void gTLCore$setUseDiveIngredientTreeFind(boolean useDiveIngredientTreeFind) { + this.gtlCore$useDiveIngredientTreeFind = useDiveIngredientTreeFind; + } + + @Unique + public void gTLCore$setAdditionalRecipes(@NotNull List<@NotNull GTRecipe> additionalRecipes) { + if (!additionalRecipes.isEmpty()) { + this.gtlCore$additionalRecipes = additionalRecipes; + this.gtlCore$additionalIndex = 0; + this.gtlCore$useOriginal = true; + this.gtlCore$cachedNextAdditional = null; + } + } + + @Unique + @Nullable + public List gTLCore$getAdditionalRecipes() { + return this.gtlCore$additionalRecipes; + } + + // ==================== Iterator ==================== + + /** + * @author Dragons + * @reason 支持双重迭代器逻辑,优化单配方模式性能 + */ + @Overwrite(remap = false) + public boolean hasNext() { + if (gtlCore$useOriginal) { + // 非dive模式:检查是否有单个配方缓存 + if (!gtlCore$useDiveIngredientTreeFind && gtlCore$singleRecipe != null) { + return true; + } + + // dive模式:如果当前迭代器还有元素,直接返回true + if (gtlCore$useDiveIngredientTreeFind && gtlCore$presentIndexGTRecipesIterator != null && gtlCore$presentIndexGTRecipesIterator.hasNext()) { + return true; + } + + // 尝试查找下一个有效的ingredient集合或单个配方 + if (gtlCore$findNextRecipeSet()) { + return true; + } + + // 原始配方已遍历完,切换到额外配方 + gtlCore$useOriginal = false; + } + + // 检查额外配方,使用index遍历并应用canHandle测试 + return gtlCore$hasValidAdditionalRecipe(); + } + + /** + * @author Dragons + * @reason 使其可以找全每个HandlePart对应List中的所有配方,优化单配方模式性能 + * 不会返回null + */ + @Overwrite(remap = false) + @NotNull + public GTRecipe next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + + if (gtlCore$useOriginal) { + // 非dive模式:优先使用单个配方缓存 + if (!gtlCore$useDiveIngredientTreeFind) { + GTRecipe recipe = gtlCore$singleRecipe; + gtlCore$singleRecipe = null; + return recipe; + } + + // dive模式:使用迭代器 + return gtlCore$presentIndexGTRecipesIterator.next(); + } else { + return gtlCore$getNextValidAdditionalRecipe(); + } + } + + /** + * @author Dragons + * @reason 重置所有状态 + */ + @Overwrite(remap = false) + public void reset() { + this.index = 0; + this.gtlCore$singleRecipe = null; + this.gtlCore$useOriginal = true; + this.gtlCore$presentIndexGTRecipesIterator = null; + this.gtlCore$additionalIndex = 0; + this.gtlCore$cachedNextAdditional = null; // 清空缓存 + } + + @Unique + private boolean gtlCore$findNextRecipeSet() { + if (ingredients == null) return false; + + if (gtlCore$useDiveIngredientTreeFind) { + // 使用diveIngredientTreeFind模式,收集所有配方 + ObjectOpenHashSet recipeSet = new ObjectOpenHashSet<>(); + while (this.index < this.ingredients.size()) { + diveIngredientTreeFindRecipeCollection(ingredients.get(this.index), + recipeMap.getLookup().getLookup(), + canHandle, recipeSet); + this.index++; + + recipeSet.remove(null); + if (!recipeSet.isEmpty()) { + gtlCore$presentIndexGTRecipesIterator = recipeSet.iterator(); + return true; + } + } + } else { + // 非dive模式:直接缓存单个配方,不使用迭代器 + while (this.index < this.ingredients.size()) { + GTRecipe recipe = this.recipeMap.getLookup().recurseIngredientTreeFindRecipe( + this.ingredients, + this.recipeMap.getLookup().getLookup(), + this.canHandle, + this.index, + 0, + 1L << this.index); + + this.index++; + + if (recipe != null) { + gtlCore$singleRecipe = recipe; + return true; + } + } + } + + gtlCore$presentIndexGTRecipesIterator = null; + return false; + } + + @Unique + private boolean gtlCore$hasValidAdditionalRecipe() { + if (gtlCore$cachedNextAdditional != null) { + return true; + } + + if (gtlCore$additionalRecipes == null) return false; + + while (gtlCore$additionalIndex < gtlCore$additionalRecipes.size()) { + GTRecipe recipe = gtlCore$additionalRecipes.get(gtlCore$additionalIndex); + gtlCore$additionalIndex++; + + if (canHandle.test(recipe)) { + gtlCore$cachedNextAdditional = recipe; // 缓存找到的配方 + return true; + } + } + return false; + } + + @Unique + private GTRecipe gtlCore$getNextValidAdditionalRecipe() { + GTRecipe recipe = gtlCore$cachedNextAdditional; + gtlCore$cachedNextAdditional = null; + + if (recipe == null) { + throw new NoSuchElementException(); + } + return recipe; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/RecipeLogicAccessor.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/RecipeLogicAccessor.java new file mode 100644 index 000000000..2bebb6308 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/RecipeLogicAccessor.java @@ -0,0 +1,13 @@ +package org.gtlcore.gtlcore.mixin.gtm.api.recipe; + +import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(RecipeLogic.class) +public interface RecipeLogicAccessor { + + @Accessor(value = "isActive", remap = false) + void setIsActive(boolean isActive); +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/RecipeModifierListMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/RecipeModifierListMixin.java new file mode 100644 index 000000000..8f48d8893 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/RecipeModifierListMixin.java @@ -0,0 +1,101 @@ +package org.gtlcore.gtlcore.mixin.gtm.api.recipe; + +import org.gtlcore.gtlcore.api.recipe.IAdvancedOCResult; +import org.gtlcore.gtlcore.utils.NumberUtils; + +import com.gregtechceu.gtceu.api.capability.recipe.EURecipeCapability; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.chance.logic.ChanceLogic; +import com.gregtechceu.gtceu.api.recipe.content.Content; +import com.gregtechceu.gtceu.api.recipe.logic.OCParams; +import com.gregtechceu.gtceu.api.recipe.logic.OCResult; +import com.gregtechceu.gtceu.api.recipe.modifier.ParallelLogic; +import com.gregtechceu.gtceu.api.recipe.modifier.RecipeModifier; +import com.gregtechceu.gtceu.api.recipe.modifier.RecipeModifierList; + +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.List; + +import javax.annotation.Nullable; + +@Mixin(RecipeModifierList.class) +public abstract class RecipeModifierListMixin { + + @Shadow(remap = false) + @Final + private RecipeModifier[] modifiers; + + /** + * @author Dragonator + * @reason fix OCTier And EU + */ + @SuppressWarnings("DataFlowIssue") + @Overwrite(remap = false) + @Nullable + public GTRecipe apply(MetaMachine machine, @NotNull GTRecipe recipe, @NotNull OCParams params, @NotNull OCResult result) { + GTRecipe modifiedRecipe = recipe; + + for (RecipeModifier modifier : this.modifiers) { + if (modifiedRecipe != null) { + modifiedRecipe = modifier.apply(machine, modifiedRecipe, params, result); + } + } + + if (modifiedRecipe != null && result.getDuration() != 0) { + modifiedRecipe.duration = result.getDuration(); + + if (result.getEut() > 0L) { + modifiedRecipe.tickInputs.put(EURecipeCapability.CAP, List.of(new Content(result.getEut(), ChanceLogic.getMaxChancedValue(), ChanceLogic.getMaxChancedValue(), 0, null, null))); + } else if (result.getEut() < 0L) { + modifiedRecipe.tickOutputs.put(EURecipeCapability.CAP, List.of(new Content(-result.getEut(), ChanceLogic.getMaxChancedValue(), ChanceLogic.getMaxChancedValue(), 0, null, null))); + } + + final int tryParallelMultiply = result.getParallel(); + final IAdvancedOCResult advancedOCResult = (IAdvancedOCResult) (Object) result; + + if (tryParallelMultiply > 1) { + var modifyResult = ParallelLogic.applyParallel(machine, modifiedRecipe, tryParallelMultiply, false); + + modifiedRecipe = modifyResult.getFirst(); + modifiedRecipe.ocTier = advancedOCResult.getBaseOCLevel(); // if none of subTick Modifier, baseOCLevel + // == ocLevel, else return real base Level + final int actualAppliedMultiply = modifyResult.getSecond(); + + if ((actualAppliedMultiply > 1)) { + final double durationFactor = advancedOCResult.getDurationFactor(); + final double voltageFactor = advancedOCResult.getVoltageFactor(); + + // last was subTick Modifier + if (durationFactor != 0 && voltageFactor != 0) { + long actualEUt; + if (actualAppliedMultiply == tryParallelMultiply) { + actualEUt = result.getParallelEUt(); + modifiedRecipe.ocTier = result.getOcLevel(); + } else { + int additionalTier = NumberUtils.getAdditionalTier(durationFactor, actualAppliedMultiply); + actualEUt = Math.round((Math.pow(voltageFactor, additionalTier) * result.getEut() * actualAppliedMultiply / tryParallelMultiply)); + modifiedRecipe.ocTier += additionalTier; + } + + if (result.getEut() > 0L) { + modifiedRecipe.tickInputs.put(EURecipeCapability.CAP, List.of(new Content(actualEUt, ChanceLogic.getMaxChancedValue(), ChanceLogic.getMaxChancedValue(), 0, null, null))); + } else if (result.getEut() < 0L) { + modifiedRecipe.tickOutputs.put(EURecipeCapability.CAP, List.of(new Content(-actualEUt, ChanceLogic.getMaxChancedValue(), ChanceLogic.getMaxChancedValue(), 0, null, null))); + } + } + } + } else { + modifiedRecipe.ocTier = advancedOCResult.getBaseOCLevel(); + } + } + + result.reset(); + return modifiedRecipe; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/condition/CleanroomConditionMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/condition/CleanroomConditionMixin.java new file mode 100644 index 000000000..ab941cdc8 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/api/recipe/condition/CleanroomConditionMixin.java @@ -0,0 +1,54 @@ +package org.gtlcore.gtlcore.mixin.gtm.api.recipe.condition; + +import org.gtlcore.gtlcore.api.recipe.RecipeResult; + +import com.gregtechceu.gtceu.api.capability.ICleanroomReceiver; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.feature.ICleanroomProvider; +import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; +import com.gregtechceu.gtceu.api.machine.multiblock.CleanroomType; +import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.common.recipe.condition.CleanroomCondition; +import com.gregtechceu.gtceu.config.ConfigHolder; + +import net.minecraft.network.chat.Component; + +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(CleanroomCondition.class) +public class CleanroomConditionMixin { + + @Shadow(remap = false) + private CleanroomType cleanroom; + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public boolean test(@NotNull GTRecipe recipe, @NotNull RecipeLogic recipeLogic) { + if (ConfigHolder.INSTANCE.machines.enableCleanroom) { + MetaMachine machine = recipeLogic.getMachine(); + if (machine instanceof ICleanroomReceiver receiver) { + if (this.cleanroom != null) { + if (ConfigHolder.INSTANCE.machines.cleanMultiblocks && machine instanceof IMultiController) return true; + ICleanroomProvider provider = receiver.getCleanroom(); + if (provider != null && provider.isClean() && provider.getTypes().contains(this.cleanroom)) + return true; + else { + RecipeResult.of((IRecipeLogicMachine) machine, + RecipeResult.fail(Component.translatable("gtceu.recipe.fail.cleanroom", + Component.translatable(this.cleanroom.getTranslationKey()).getString()))); + return false; + } + } + } + } + return true; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/fix/WorkableElectricMultiblockMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/fix/WorkableElectricMultiblockMachineMixin.java index f204a02b9..0fcf5df7c 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/fix/WorkableElectricMultiblockMachineMixin.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/fix/WorkableElectricMultiblockMachineMixin.java @@ -1,18 +1,40 @@ package org.gtlcore.gtlcore.mixin.gtm.fix; -import org.gtlcore.gtlcore.utils.NumberUtils; +import org.gtlcore.gtlcore.api.machine.trait.ICheckPatternMachine; +import org.gtlcore.gtlcore.api.machine.trait.ILockRecipe; +import org.gtlcore.gtlcore.api.machine.trait.IRecipeCapabilityMachine; +import org.gtlcore.gtlcore.api.machine.trait.IRecipeStatus; +import org.gtlcore.gtlcore.api.recipe.RecipeText; import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.api.gui.fancy.ConfiguratorPanel; +import com.gregtechceu.gtceu.api.gui.fancy.IFancyConfiguratorButton; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.feature.IFancyUIMachine; import com.gregtechceu.gtceu.api.machine.multiblock.WorkableElectricMultiblockMachine; +import com.gregtechceu.gtceu.api.machine.multiblock.WorkableMultiblockMachine; import com.gregtechceu.gtceu.api.misc.EnergyContainerList; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.research.ResearchStationMachine; import com.gregtechceu.gtceu.utils.GTUtil; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; -import org.spongepowered.asm.mixin.Shadow; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.HoverEvent; + +import org.spongepowered.asm.mixin.*; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; @Mixin(WorkableElectricMultiblockMachine.class) -public abstract class WorkableElectricMultiblockMachineMixin { +public abstract class WorkableElectricMultiblockMachineMixin extends WorkableMultiblockMachine implements IFancyUIMachine, ICheckPatternMachine { + + public WorkableElectricMultiblockMachineMixin(IMachineBlockEntity holder, Object... args) { + super(holder, args); + } @Shadow(remap = false) protected EnergyContainerList energyContainer; @@ -20,35 +42,78 @@ public abstract class WorkableElectricMultiblockMachineMixin { @Shadow(remap = false) public abstract EnergyContainerList getEnergyContainer(); + @Shadow(remap = false) + protected int tier; + + @Shadow(remap = false) + public boolean isGenerator() { + throw new AssertionError(); + } + /** - * @author mod_author - * @reason fix + * @author mod_author, Dragons + * @reason always 1A amperage, Fix 2 64A EnergyHatch */ @Overwrite(remap = false) public long getOverclockVoltage() { if (this.energyContainer == null) { this.energyContainer = this.getEnergyContainer(); } - long voltage; - long amperage; - if (energyContainer.getInputVoltage() > energyContainer.getOutputVoltage()) { - voltage = energyContainer.getInputVoltage(); - amperage = energyContainer.getInputAmperage(); - } else { - voltage = energyContainer.getOutputVoltage(); - amperage = energyContainer.getOutputAmperage(); + + return Math.max(energyContainer.getInputVoltage(), energyContainer.getOutputVoltage()); + } + + /** + * @author Dragons + * @reason always 1A amperage + */ + @Overwrite(remap = false) + public long getMaxVoltage() { + if (this.energyContainer == null) { + this.energyContainer = getEnergyContainer(); } + return this.isGenerator() ? energyContainer.getOutputVoltage() : energyContainer.getNumHighestInputContainers() > 1 ? GTValues.V[Math.min(GTUtil.getTierByVoltage(energyContainer.getHighestInputVoltage()) + 1, GTValues.MAX)] : energyContainer.getHighestInputVoltage(); + } - if (amperage == 1) { - // amperage is 1 when the energy is not exactly on a tier - // the voltage for recipe search is always on tier, so take the closest lower tier - if (voltage > Integer.MAX_VALUE) return NumberUtils.getVoltageFromFakeTier(NumberUtils.getFakeVoltageTier(voltage)); - return GTValues.V[GTUtil.getFloorTierByVoltage(voltage)]; - } else { - // amperage != 1 means the voltage is exactly on a tier - // ignore amperage, since only the voltage is relevant for recipe search - // amps are never > 3 in an EnergyContainerList - return voltage; + @Override + public void attachConfigurators(ConfiguratorPanel configuratorPanel) { + configuratorPanel.attachConfigurators(new IFancyConfiguratorButton.Toggle( + GuiTextures.BUTTON_POWER.getSubTexture(0, 0, 1, 0.5), + GuiTextures.BUTTON_POWER.getSubTexture(0, 0.5, 1, 0.5), + this::isWorkingEnabled, (clickData, pressed) -> this.setWorkingEnabled(pressed)) + .setTooltipsSupplier(pressed -> List.of( + Component.translatable(pressed ? "behaviour.soft_hammer.enabled" : "behaviour.soft_hammer.disabled")))); + ICheckPatternMachine.attachConfigurators(configuratorPanel, self()); + if (this.self() instanceof ResearchStationMachine) return; + IRecipeCapabilityMachine.attachConfigurators(configuratorPanel, (WorkableElectricMultiblockMachine) self()); + ILockRecipe.attachRecipeLockable(configuratorPanel, this.getRecipeLogic()); + } + + @Inject(method = "addDisplayText", at = @At(value = "INVOKE", target = "Lcom/gregtechceu/gtceu/api/machine/multiblock/WorkableElectricMultiblockMachine;getDefinition()Lcom/gregtechceu/gtceu/api/machine/MultiblockMachineDefinition;"), remap = false) + public void addDisplayText(List textList, CallbackInfo ci) { + if (this.isFormed()) { + if (this.getRecipeLogic() instanceof IRecipeStatus status && + status.getRecipeStatus() != null && + status.getRecipeStatus().reason() != null) { + textList.add(status.getRecipeStatus().reason().copy().withStyle(ChatFormatting.RED)); + if (status.getWorkingStatus() != null && status.getWorkingStatus().reason() != null) + textList.add(status.getWorkingStatus().reason().copy().withStyle(ChatFormatting.RED)); + } + } + if (this.getRecipeLogic() instanceof ILockRecipe iLockRecipe) { + if (iLockRecipe.isLock() && iLockRecipe.getLockRecipe() != null) { + textList.add(Component.translatable("gui.gtlcore.recipe_lock.recipe") + .withStyle((style -> style.withHoverEvent((new HoverEvent(HoverEvent.Action.SHOW_TEXT, + RecipeText.getRecipeInputText(iLockRecipe.getLockRecipe()) + .append(RecipeText.getRecipeOutputText(iLockRecipe.getLockRecipe())))))))); + } else { + textList.add(Component.translatable("gui.gtlcore.recipe_lock.no_recipe")); + } } } + + @Override + public boolean hasButton() { + return true; + } } diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/gui/GTRecipeWidgetMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/gui/GTRecipeWidgetMixin.java new file mode 100644 index 000000000..e4fd63774 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/gui/GTRecipeWidgetMixin.java @@ -0,0 +1,78 @@ +package org.gtlcore.gtlcore.mixin.gtm.gui; + +import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.api.capability.recipe.CWURecipeCapability; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.content.Content; +import com.gregtechceu.gtceu.integration.GTRecipeWidget; +import com.gregtechceu.gtceu.utils.FormattingUtil; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; + +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.*; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.*; + +import static org.gtlcore.gtlcore.utils.NumberUtils.*; +import static org.gtlcore.gtlcore.utils.TextUtil.GTL_CORE$VC; + +@Mixin(GTRecipeWidget.class) +public class GTRecipeWidgetMixin { + + @Shadow(remap = false) + @Final + private GTRecipe recipe; + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + private static @NotNull List getRecipeParaText(GTRecipe recipe, int duration, long inputEUt, long outputEUt) { + List texts = new ArrayList<>(); + if (!recipe.data.getBoolean("hide_duration")) { + texts.add(Component.translatable("gtceu.recipe.duration", FormattingUtil.formatNumbers(duration / 20f))); + } + var EUt = inputEUt; + boolean isOutput = false; + if (EUt == 0) { + EUt = outputEUt; + isOutput = true; + } + if (EUt > 0) { + long euTotal = EUt * duration; + // sadly we still need a custom override here, since computation uses duration and EU/t very differently + if (recipe.data.getBoolean("duration_is_total_cwu") && + recipe.tickInputs.containsKey(CWURecipeCapability.CAP)) { + int minimumCWUt = Math.max(recipe.tickInputs.get(CWURecipeCapability.CAP).stream() + .map(Content::getContent).mapToInt(CWURecipeCapability.CAP::of).sum(), 1); + texts.add(Component.translatable("gtceu.recipe.max_eu", + FormattingUtil.formatNumbers(euTotal / minimumCWUt))); + } else texts.add(Component.translatable("gtceu.recipe.total", formatLong(euTotal))); + long absEUt = Math.abs(EUt); + var tier = GTUtil.getTierByVoltage(absEUt); + var component = Component.translatable(!isOutput ? "gtceu.recipe.eu" : "gtceu.recipe.eu_inverted", + formatLong(EUt)); + if (!isOutput) component.append( + Component.literal(" (").withStyle(ChatFormatting.GREEN) + .append(Component + .literal(formatDouble((double) absEUt / GTValues.V[tier]) + "A") + .withStyle(style -> style.withColor(GTL_CORE$VC[tier]))) + .append(Component.literal(")").withStyle(ChatFormatting.GREEN))); + texts.add(component); + } + + return texts; + } + + @Inject(method = "setRecipeOC", at = @At("HEAD"), cancellable = true, remap = false) + public void setRecipeOC(int button, boolean isShiftClick, CallbackInfo ci) { + if (this.recipe.recipeType.isScanner()) ci.cancel(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/gui/MachineModeFancyConfiguratorMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/gui/MachineModeFancyConfiguratorMixin.java new file mode 100644 index 000000000..988de4f40 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/gui/MachineModeFancyConfiguratorMixin.java @@ -0,0 +1,53 @@ +package org.gtlcore.gtlcore.mixin.gtm.gui; + +import org.gtlcore.gtlcore.api.gui.MachineModeConfigurator; + +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.api.gui.fancy.FancyMachineUIWidget; +import com.gregtechceu.gtceu.api.machine.fancyconfigurator.MachineModeFancyConfigurator; +import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; + +import com.lowdragmc.lowdraglib.gui.editor.ColorPattern; +import com.lowdragmc.lowdraglib.gui.texture.*; +import com.lowdragmc.lowdraglib.gui.widget.*; + +import org.spongepowered.asm.mixin.*; + +@Mixin(MachineModeFancyConfigurator.class) +public class MachineModeFancyConfiguratorMixin { + + @Shadow(remap = false) + protected IRecipeLogicMachine machine; + + /** + * @author . + * @reason 配方类型选择界面调整 + */ + @Overwrite(remap = false) + public Widget createMainPage(FancyMachineUIWidget widget) { + int length = machine.getRecipeTypes().length; + var group = new MachineModeConfigurator(0, 0, 140, 20 * Math.min(length, 6) + 4, machine); + group.setBackground(GuiTextures.BACKGROUND_INVERSE); + if (length > 6) { + DraggableScrollableWidgetGroup widgetGroup = new DraggableScrollableWidgetGroup(2, 2, 136, 6 * 20); + addWidgets(widgetGroup, length); + group.addWidget(widgetGroup); + } else addWidgets(group, length); + return group; + } + + private void addWidgets(WidgetGroup group, int length) { + for (int i = 0; i < length; i++) { + int finalI = i; + group.addWidget(new ButtonWidget(length < 7 ? 2 : 0, (length < 7 ? 2 : 0) + i * 20, 136, 20, IGuiTexture.EMPTY, + cd -> machine.setActiveRecipeType(finalI))); + group.addWidget(new ImageWidget(length < 7 ? 2 : 0, (length < 7 ? 2 : 0) + i * 20, 136, 20, + () -> new GuiTextureGroup( + ResourceBorderTexture.BUTTON_COMMON.copy() + .setColor(machine.getActiveRecipeType() == finalI ? ColorPattern.CYAN.color : -1), + new TextTexture(machine.getRecipeTypes()[finalI].registryName.toLanguageKey()).setWidth(136) + .setType(TextTexture.TextType.ROLL)))); + + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/gui/PatternPreviewWidgetMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/gui/PatternPreviewWidgetMixin.java index c07872336..f713c72d6 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/gui/PatternPreviewWidgetMixin.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/gui/PatternPreviewWidgetMixin.java @@ -1,16 +1,99 @@ package org.gtlcore.gtlcore.mixin.gtm.gui; +import org.gtlcore.gtlcore.api.gui.ExtendPatternPreviewWidget; + import com.gregtechceu.gtceu.api.gui.widget.PatternPreviewWidget; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; +import com.gregtechceu.gtceu.api.pattern.*; + +import com.lowdragmc.lowdraglib.utils.*; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.state.BlockState; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.Constant; -import org.spongepowered.asm.mixin.injection.ModifyConstant; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.*; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.lang.reflect.InvocationTargetException; +import java.util.*; +import java.util.stream.Collectors; + +import static com.gregtechceu.gtceu.api.gui.widget.PatternPreviewWidget.locateNextRegion; +import static org.gtlcore.gtlcore.api.gui.ExtendPatternPreviewWidget.gatherBlockDrops; @Mixin(PatternPreviewWidget.class) public class PatternPreviewWidgetMixin { + @Shadow(remap = false) + private static TrackedDummyWorld LEVEL; + + @Shadow(remap = false) + private void loadControllerFormed(Collection poses, IMultiController controllerBase) {} + @ModifyConstant(method = "setPage", remap = false, constant = @Constant(intValue = 18, ordinal = 0)) private int modifyContainer(int constant) { return 36; } + + @Inject(method = "initializePattern", at = @At("HEAD"), remap = false, cancellable = true) + private void initializePattern(MultiblockShapeInfo shapeInfo, HashSet blockDrops, CallbackInfoReturnable cir) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + Map blockMap = new Object2ObjectOpenHashMap<>(); + IMultiController controllerBase = null; + BlockPos multiPos = locateNextRegion(500); + + BlockInfo[][][] blocks = shapeInfo.getBlocks(); + for (int x = 0; x < blocks.length; x++) { + BlockInfo[][] aisle = blocks[x]; + for (int y = 0; y < aisle.length; y++) { + BlockInfo[] column = aisle[y]; + for (int z = 0; z < column.length; z++) { + var block = column[z]; + if (block == null) continue; + BlockState blockState = block.getBlockState(); + BlockPos pos = multiPos.offset(x, y, z); + if (column[z].getBlockEntity(pos) instanceof IMachineBlockEntity holder && + holder.getMetaMachine() instanceof IMultiController controller) { + holder.getSelf().setLevel(LEVEL); + controllerBase = controller; + } + blockMap.put(pos, BlockInfo.fromBlockState(blockState)); + } + } + } + + LEVEL.addBlocks(blockMap); + if (controllerBase != null) { + LEVEL.setInnerBlockEntity(controllerBase.self().holder.getSelf()); + } + + Map parts = gatherBlockDrops(blockMap, LEVEL); + blockDrops.addAll(parts.keySet()); + + Map predicateMap = new Object2ObjectOpenHashMap<>(); + if (controllerBase != null) { + loadControllerFormed(predicateMap.keySet(), controllerBase); + predicateMap = controllerBase.getMultiblockState().getMatchContext().get("predicates"); + } + + if (controllerBase == null) cir.setReturnValue(null); + + Class MEclass = Class.forName("com.gregtechceu.gtceu.api.gui.widget.PatternPreviewWidget$MBPattern"); + var con = MEclass.getConstructor(Map.class, List.class, Map.class, IMultiController.class); + con.setAccessible(true); + cir.setReturnValue(con.newInstance(blockMap, parts.values().stream().sorted((one, two) -> { + if (one.isController) return -1; + if (two.isController) return +1; + if (one.isTile && !two.isTile) return -1; + if (two.isTile && !one.isTile) return +1; + if (one.blockId != two.blockId) return two.blockId - one.blockId; + return two.amount - one.amount; + }).map(ExtendPatternPreviewWidget.PartInfo::getItemStack) + .filter(list -> !list.isEmpty()).collect(Collectors.toList()), + predicateMap, + controllerBase)); + } } diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/ActiveTransformerMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/ActiveTransformerMachineMixin.java new file mode 100644 index 000000000..573173f9e --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/ActiveTransformerMachineMixin.java @@ -0,0 +1,77 @@ +package org.gtlcore.gtlcore.mixin.gtm.machine; + +import org.gtlcore.gtlcore.api.capability.IInt128EnergyContainer; + +import com.gregtechceu.gtceu.api.capability.IEnergyContainer; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.multiblock.WorkableElectricMultiblockMachine; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.ActiveTransformerMachine; +import com.gregtechceu.gtceu.config.ConfigHolder; +import com.gregtechceu.gtceu.utils.FormattingUtil; + +import net.minecraft.network.chat.Component; + +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.List; + +@Mixin(ActiveTransformerMachine.class) +public abstract class ActiveTransformerMachineMixin extends WorkableElectricMultiblockMachine { + + @Shadow(remap = false) + private IEnergyContainer powerOutput; + @Shadow(remap = false) + private IEnergyContainer powerInput; + + public ActiveTransformerMachineMixin(IMachineBlockEntity holder, Object... args) { + super(holder, args); + } + + /** + * @author Dragons + * @reason Fix overflow + */ + @Overwrite(remap = false) + protected boolean isSubscriptionActive() { + if (!isFormed()) return false; + + if (powerInput == null || powerInput.getEnergyStored() <= 0) return false; + if (powerOutput == null) return false; + return ((IInt128EnergyContainer) powerOutput).getInt128EnergyStored().compareTo(((IInt128EnergyContainer) powerOutput).getInt128EnergyCapacity()) < 0; + } + + @Override + public void addDisplayText(@NotNull List textList) { + // super.addDisplayText(textList); idek what it does stop doing what you do for a minute pls + // Assume That the Structure is ALWAYS formed, and has at least 1 In and 1 Out, there is never a case where this + // does not occur. + if (isFormed()) { + if (!isWorkingEnabled()) { + textList.add(Component.translatable("gtceu.multiblock.work_paused")); + } else if (isActive()) { + textList.add(Component.translatable("gtceu.multiblock.running")); + textList.add(Component + .translatable("gtceu.multiblock.active_transformer.max_input", + FormattingUtil.formatNumbers( + Math.abs(powerInput.getInputVoltage() * powerInput.getInputAmperage())))); + textList.add(Component + .translatable("gtceu.multiblock.active_transformer.max_output", + FormattingUtil.formatNumbers( + Math.abs(powerOutput.getOutputVoltage() * powerOutput.getOutputAmperage())))); + textList.add(Component + .translatable("gtceu.multiblock.active_transformer.average_in", ((IInt128EnergyContainer) powerInput).getInt128InputPerSec().divide(20).toFormattedString())); + textList.add(Component + .translatable("gtceu.multiblock.active_transformer.average_out", ((IInt128EnergyContainer) powerOutput).getInt128OutputPerSec().divide(20).toFormattedString())); + if (!ConfigHolder.INSTANCE.machines.harmlessActiveTransformers) { + textList.add(Component + .translatable("gtceu.multiblock.active_transformer.danger_enabled")); + } + } else { + textList.add(Component.translatable("gtceu.multiblock.idling")); + } + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/DataAccessHatchMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/DataAccessHatchMachineMixin.java new file mode 100644 index 000000000..5c1f61bc0 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/DataAccessHatchMachineMixin.java @@ -0,0 +1,57 @@ +package org.gtlcore.gtlcore.mixin.gtm.machine; + +import org.gtlcore.gtlcore.api.recipe.RecipeResult; + +import com.gregtechceu.gtceu.api.capability.IDataAccessHatch; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; +import com.gregtechceu.gtceu.api.machine.multiblock.part.TieredPartMachine; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.research.DataBankMachine; +import com.gregtechceu.gtceu.common.machine.multiblock.part.DataAccessHatchMachine; +import com.gregtechceu.gtceu.common.recipe.condition.ResearchCondition; + +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.*; + +import java.util.Collection; +import java.util.Set; + +@Mixin(DataAccessHatchMachine.class) +public abstract class DataAccessHatchMachineMixin extends TieredPartMachine implements IDataAccessHatch { + + @Mutable + @Final + @Shadow(remap = false) + private final Set recipes; + + @Shadow(remap = false) + public abstract boolean isCreative(); + + @Shadow(remap = false) + public abstract GTRecipe modifyRecipe(GTRecipe recipe); + + public DataAccessHatchMachineMixin(IMachineBlockEntity holder, int tier, Set recipes) { + super(holder, tier); + this.recipes = recipes; + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public boolean isRecipeAvailable(@NotNull GTRecipe recipe, @NotNull Collection seen) { + seen.add(this); + if (recipe.conditions.stream().noneMatch(ResearchCondition.class::isInstance) || + this.recipes.contains(recipe)) + return true; + else { + for (var c : this.getControllers()) { + if (c instanceof DataBankMachine) continue; + RecipeResult.of((IRecipeLogicMachine) c, RecipeResult.FAIL_NO_FIND_RESEARCHED); + } + return false; + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/FluidHatchPartMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/FluidHatchPartMachineMixin.java index 158ee0a73..815a3154b 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/FluidHatchPartMachineMixin.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/FluidHatchPartMachineMixin.java @@ -22,8 +22,8 @@ public FluidHatchPartMachineMixin(IMachineBlockEntity holder, int tier, IO io) { } /** - * @author - * @reason + * @author . + * @reason . */ @Overwrite(remap = false) public static long getTankCapacity(long initialCapacity, int tier) { diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/FusionReactorMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/FusionReactorMachineMixin.java index 5924cd5b9..713c97642 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/FusionReactorMachineMixin.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/FusionReactorMachineMixin.java @@ -1,12 +1,22 @@ package org.gtlcore.gtlcore.mixin.gtm.machine; +import org.gtlcore.gtlcore.api.recipe.RecipeResult; + import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.machine.multiblock.WorkableElectricMultiblockMachine; import com.gregtechceu.gtceu.api.machine.trait.NotifiableEnergyContainer; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.OverclockingLogic; +import com.gregtechceu.gtceu.api.recipe.RecipeHelper; +import com.gregtechceu.gtceu.api.recipe.logic.OCParams; +import com.gregtechceu.gtceu.api.recipe.logic.OCResult; import com.gregtechceu.gtceu.common.machine.multiblock.electric.FusionReactorMachine; import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.*; import java.util.List; @@ -21,11 +31,41 @@ public class FusionReactorMachineMixin extends WorkableElectricMultiblockMachine @Shadow(remap = false) protected final NotifiableEnergyContainer energyContainer; + @Shadow(remap = false) + protected void updatePreHeatSubscription() {} + public FusionReactorMachineMixin(IMachineBlockEntity holder, NotifiableEnergyContainer energyContainer, Object... args) { super(holder, args); this.energyContainer = energyContainer; } + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public static @Nullable GTRecipe recipeModifier(MetaMachine machine, @NotNull GTRecipe recipe, @NotNull OCParams params, @NotNull OCResult result) { + if (machine instanceof FusionReactorMachineMixin fm) { + if (RecipeHelper.getRecipeEUtTier(recipe) <= fm.getTier() && recipe.data.contains("eu_to_start")) { + if (recipe.data.getLong("eu_to_start") <= fm.energyContainer.getEnergyCapacity()) { + long heatDiff = recipe.data.getLong("eu_to_start") - fm.heat; + if (heatDiff <= 0L) { + return RecipeHelper.applyOverclock(new OverclockingLogic(0.5F, 2.0F, false), recipe, fm.getMaxVoltage(), params, result); + } else if (fm.energyContainer.getEnergyStored() < heatDiff) { + return null; + } else { + fm.energyContainer.removeEnergy(heatDiff); + fm.heat += heatDiff; + fm.updatePreHeatSubscription(); + return RecipeHelper.applyOverclock(new OverclockingLogic(0.5F, 2.0F, false), recipe, fm.getMaxVoltage(), params, result); + } + } + RecipeResult.of(fm, RecipeResult.fail(Component.translatable("gtceu.recipe.fail.no.enough.cache.energy"))); + } + } + return null; + } + /** * @author . * @reason . diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/HugeBusPartMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/HugeBusPartMachineMixin.java deleted file mode 100644 index 516b5ebc6..000000000 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/HugeBusPartMachineMixin.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.gtlcore.gtlcore.mixin.gtm.machine; - -import org.gtlcore.gtlcore.api.machine.trait.IDistinctMachine; - -import com.gregtechceu.gtceu.api.capability.recipe.IO; -import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; -import com.gregtechceu.gtceu.api.machine.multiblock.part.TieredIOPartMachine; - -import com.hepdd.gtmthings.common.block.machine.multiblock.part.HugeBusPartMachine; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(HugeBusPartMachine.class) -public class HugeBusPartMachineMixin extends TieredIOPartMachine { - - public HugeBusPartMachineMixin(IMachineBlockEntity holder, int tier, IO io) { - super(holder, tier, io); - } - - @Inject(method = "setDistinct", at = @At("RETURN"), remap = false) - public void setDistinct(boolean isDistinct, CallbackInfo ci) { - for (var controller : this.getControllers()) { - if (controller instanceof IDistinctMachine iDistinctMachine) { - iDistinctMachine.upDate(); - } - } - } -} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/ItemBusPartMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/ItemBusPartMachineMixin.java index 75d82fa29..7f37041a6 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/ItemBusPartMachineMixin.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/ItemBusPartMachineMixin.java @@ -1,6 +1,6 @@ package org.gtlcore.gtlcore.mixin.gtm.machine; -import org.gtlcore.gtlcore.api.machine.trait.IDistinctMachine; +import org.gtlcore.gtlcore.api.machine.trait.IRecipeCapabilityMachine; import org.gtlcore.gtlcore.api.machine.trait.NotifiableCircuitItemStackHandler; import com.gregtechceu.gtceu.api.capability.recipe.IO; @@ -25,8 +25,8 @@ public ItemBusPartMachineMixin(IMachineBlockEntity holder, int tier, IO io) { } /** - * @author - * @reason + * @author . + * @reason . */ @Overwrite(remap = false) protected int getInventorySize() { @@ -52,8 +52,8 @@ protected void createCircuitItemHandler(Object[] args, CallbackInfoReturnable itemstack.is(GTItems.TURBINE_ROTOR.asItem())); + } + + @Override + public GTRecipe modifyRecipe(GTRecipe recipe) { + GTRecipe modifiedRecipe = this.isFrontFaceFree() && this.hasRotor() ? recipe : null; + if (modifiedRecipe != null) return modifiedRecipe; + else if (this.getRotorStack().isEmpty()) { + RecipeResult.of((IRecipeLogicMachine) this.getControllers().get(0), + RecipeResult.fail(Component.translatable("gtceu.recipe.fail.rotor.isEmpty"))); + } else { + RecipeResult.of((IRecipeLogicMachine) this.getControllers().get(0), + RecipeResult.fail(Component.translatable("gtceu.multiblock.universal.rotor_obstructed"))); + } + return null; + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public Widget createUIWidget() { + WidgetGroup group = new WidgetGroup(0, 0, 34, 34); + WidgetGroup container = new WidgetGroup(4, 4, 26, 26); + container.addWidget((new SlotWidget(this.inventory.storage, 0, 4, 4, true, true)) + .setBackground(GuiTextures.SLOT, GuiTextures.TURBINE_OVERLAY) + .setHoverTooltips(Component.translatable("tooltip.gtlcore.turbine_rotor_only"))); + container.setBackground(GuiTextures.BACKGROUND_INVERSE); + group.addWidget(container); + return group; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/SimpleSteamMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/SimpleSteamMachineMixin.java new file mode 100644 index 000000000..22849b989 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/machine/SimpleSteamMachineMixin.java @@ -0,0 +1,48 @@ +package org.gtlcore.gtlcore.mixin.gtm.machine; + +import org.gtlcore.gtlcore.api.recipe.RecipeResult; + +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.steam.SimpleSteamMachine; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.RecipeHelper; +import com.gregtechceu.gtceu.api.recipe.logic.OCParams; +import com.gregtechceu.gtceu.api.recipe.logic.OCResult; +import com.gregtechceu.gtceu.common.recipe.condition.VentCondition; + +import net.minecraft.network.chat.Component; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +@Mixin(SimpleSteamMachine.class) +public class SimpleSteamMachineMixin { + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public static @Nullable GTRecipe recipeModifier(MetaMachine machine, @NotNull GTRecipe recipe, @NotNull OCParams params, @NotNull OCResult result) { + if (machine instanceof SimpleSteamMachine steamMachine) { + if (RecipeHelper.getRecipeEUtTier(recipe) <= 1 && steamMachine.checkVenting()) { + GTRecipe modified = recipe.copy(); + modified.conditions.add(VentCondition.INSTANCE); + if (steamMachine.isHighPressure) { + result.init(RecipeHelper.getInputEUt(recipe) * 2L, modified.duration, params.getOcAmount()); + } else { + result.init(RecipeHelper.getInputEUt(recipe), modified.duration * 2, params.getOcAmount()); + } + return modified; + } + if (!steamMachine.checkVenting()) { + RecipeResult.of(steamMachine, RecipeResult.fail(Component.translatable("gtceu.recipe.fail.no.venting"))); + } else if (RecipeHelper.getRecipeEUtTier(recipe) > 1) { + RecipeResult.of(steamMachine, RecipeResult.FAIL_VOLTAGE_TIER); + } + } + return null; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/recipe/GTRecipeJSMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/recipe/GTRecipeJSMixin.java new file mode 100644 index 000000000..864b09c84 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/recipe/GTRecipeJSMixin.java @@ -0,0 +1,50 @@ +package org.gtlcore.gtlcore.mixin.gtm.recipe; + +import com.gregtechceu.gtceu.integration.kjs.recipe.GTRecipeSchema; + +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + +import dev.latvian.mods.kubejs.item.InputItem; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +@Mixin(GTRecipeSchema.GTRecipeJS.class) +public abstract class GTRecipeJSMixin { + + @Shadow(remap = false) + public int chance; + + /** + * Special handling for kubejs:ingot_field_shape: convert quantity from 64 to 8 + */ + @ModifyArg( + method = "notConsumable(Ldev/latvian/mods/kubejs/item/InputItem;)Lcom/gregtechceu/gtceu/integration/kjs/recipe/GTRecipeSchema$GTRecipeJS;", + at = @At(value = "INVOKE", target = "Lcom/gregtechceu/gtceu/integration/kjs/recipe/GTRecipeSchema$GTRecipeJS;inputItems([Ldev/latvian/mods/kubejs/item/InputItem;)Lcom/gregtechceu/gtceu/integration/kjs/recipe/GTRecipeSchema$GTRecipeJS;"), + index = 0, + remap = false) + private InputItem[] modifyInputItemInNotConsumable(InputItem[] inputs) { + // Check if we have exactly one input item and it needs modification + if (inputs.length == 1) { + InputItem itemStack = inputs[0]; + if (itemStack.count == 64) { + Ingredient ingredient = itemStack.ingredient; + ItemStack[] items = ingredient.getItems(); + + if (items.length > 0) { + String itemId = BuiltInRegistries.ITEM.getKey(items[0].getItem()).toString(); + if ("kubejs:ingot_field_shape".equals(itemId)) { + // Return a new array with modified InputItem (count 8 instead of 64) + return new InputItem[] { InputItem.of(ingredient, 8) }; + } + } + } + } + + // Return the original inputs array if no modification needed + return inputs; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/recipe/Ingredient/IntProviderIngredientAccessor.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/recipe/Ingredient/IntProviderIngredientAccessor.java new file mode 100644 index 000000000..5b99aa5ee --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/recipe/Ingredient/IntProviderIngredientAccessor.java @@ -0,0 +1,18 @@ +package org.gtlcore.gtlcore.mixin.gtm.recipe.Ingredient; + +import com.gregtechceu.gtceu.api.recipe.ingredient.IntProviderIngredient; + +import net.minecraft.world.item.ItemStack; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(IntProviderIngredient.class) +public interface IntProviderIngredientAccessor { + + @Accessor(value = "itemStacks", remap = false) + ItemStack[] getItemStack(); + + @Accessor(value = "sampledCount", remap = false) + Integer getSampledCount(); +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/recipe/MachineRecipeLoaderMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/recipe/MachineRecipeLoaderMixin.java new file mode 100644 index 000000000..42d5b5d78 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/recipe/MachineRecipeLoaderMixin.java @@ -0,0 +1,127 @@ +package org.gtlcore.gtlcore.mixin.gtm.recipe; + +import org.gtlcore.gtlcore.common.data.GTLMachines; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.data.recipe.VanillaRecipeHelper; +import com.gregtechceu.gtceu.data.recipe.misc.MachineRecipeLoader; + +import net.minecraft.data.recipes.FinishedRecipe; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +import java.util.Locale; +import java.util.function.Consumer; + +import static com.gregtechceu.gtceu.api.GTValues.*; +import static com.gregtechceu.gtceu.common.data.GTMachines.*; + +@Mixin(MachineRecipeLoader.class) +public class MachineRecipeLoaderMixin { + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + private static void registerHatchConversion(Consumer provider) { + for (int i = 0; i < FLUID_IMPORT_HATCH.length; i++) { + if (FLUID_IMPORT_HATCH[i] != null && FLUID_EXPORT_HATCH[i] != null) { + + VanillaRecipeHelper.addShapedRecipe(provider, + "fluid_hatch_output_to_input_" + FLUID_IMPORT_HATCH[i].getTier(), + FLUID_IMPORT_HATCH[i].asStack(), + "d", "B", 'B', FLUID_EXPORT_HATCH[i].asStack()); + VanillaRecipeHelper.addShapedRecipe(provider, + "fluid_hatch_input_to_output_" + FLUID_EXPORT_HATCH[i].getTier(), + FLUID_EXPORT_HATCH[i].asStack(), + "d", "B", 'B', FLUID_IMPORT_HATCH[i].asStack()); + } + } + for (int i = 0; i < ITEM_IMPORT_BUS.length; i++) { + if (ITEM_IMPORT_BUS[i] != null && ITEM_EXPORT_BUS[i] != null) { + + VanillaRecipeHelper.addShapedRecipe(provider, + "item_bus_output_to_input_" + ITEM_IMPORT_BUS[i].getTier(), ITEM_IMPORT_BUS[i].asStack(), + "d", "B", 'B', ITEM_EXPORT_BUS[i].asStack()); + VanillaRecipeHelper.addShapedRecipe(provider, + "item_bus_input_to_output_" + ITEM_EXPORT_BUS[i].getTier(), ITEM_EXPORT_BUS[i].asStack(), + "d", "B", 'B', ITEM_IMPORT_BUS[i].asStack()); + } + } + + for (int tier : GTValues.tiersBetween(EV, MAX)) { + var tierName = VN[tier].toLowerCase(Locale.ROOT); + + var importHatch4x = FLUID_IMPORT_HATCH_4X[tier]; + var exportHatch4x = FLUID_EXPORT_HATCH_4X[tier]; + var importHatch9x = FLUID_IMPORT_HATCH_9X[tier]; + var exportHatch9x = FLUID_EXPORT_HATCH_9X[tier]; + + VanillaRecipeHelper.addShapedRecipe( + provider, "fluid_hatch_4x_output_to_input_" + tierName, + importHatch4x.asStack(), "d", "B", + 'B', exportHatch4x.asStack()); + VanillaRecipeHelper.addShapedRecipe( + provider, "fluid_hatch_4x_input_to_output_" + tierName, + exportHatch4x.asStack(), "d", "B", + 'B', importHatch4x.asStack()); + + VanillaRecipeHelper.addShapedRecipe( + provider, "fluid_hatch_9x_output_to_input_" + tierName, + importHatch9x.asStack(), "d", "B", + 'B', exportHatch9x.asStack()); + VanillaRecipeHelper.addShapedRecipe( + provider, "fluid_hatch_9x_input_to_output_" + tierName, + exportHatch9x.asStack(), "d", "B", + 'B', importHatch9x.asStack()); + } + + for (int tier : GTValues.tiersBetween(LuV, MAX)) { + var tierName = VN[tier].toLowerCase(Locale.ROOT); + + var inputBuffer = DUAL_IMPORT_HATCH[tier]; + var outputBuffer = DUAL_EXPORT_HATCH[tier]; + + VanillaRecipeHelper.addShapedRecipe( + provider, + "dual_hatch_output_to_input_" + tierName, + inputBuffer.asStack(), + "d", + "B", + 'B', + outputBuffer.asStack()); + VanillaRecipeHelper.addShapedRecipe( + provider, + "dual_hatch_input_to_output_" + tierName, + outputBuffer.asStack(), + "d", + "B", + 'B', + inputBuffer.asStack()); + } + + // Steam + VanillaRecipeHelper.addShapedRecipe(provider, "steam_bus_output_to_input", STEAM_EXPORT_BUS.asStack(), + "d", "B", 'B', STEAM_IMPORT_BUS.asStack()); + VanillaRecipeHelper.addShapedRecipe(provider, "steam_bus_input_to_output", STEAM_IMPORT_BUS.asStack(), + "d", "B", 'B', STEAM_EXPORT_BUS.asStack()); + + if (GTCEu.isAE2Loaded()) { + VanillaRecipeHelper.addShapedRecipe(provider, "me_fluid_hatch_output_to_input", + GTLMachines.GTAEMachines.FLUID_IMPORT_HATCH_ME.asStack(), "d", "B", 'B', + GTLMachines.GTAEMachines.FLUID_EXPORT_HATCH_ME.asStack()); + VanillaRecipeHelper.addShapedRecipe(provider, "me_fluid_hatch_input_to_output", + GTLMachines.GTAEMachines.FLUID_EXPORT_HATCH_ME.asStack(), "d", "B", 'B', + GTLMachines.GTAEMachines.FLUID_IMPORT_HATCH_ME.asStack()); + VanillaRecipeHelper.addShapedRecipe(provider, "me_item_bus_output_to_input", + GTLMachines.GTAEMachines.ITEM_IMPORT_BUS_ME.asStack(), "d", "B", 'B', + GTLMachines.GTAEMachines.ITEM_EXPORT_BUS_ME.asStack()); + VanillaRecipeHelper.addShapedRecipe(provider, "me_item_bus_input_to_output", + GTLMachines.GTAEMachines.ITEM_EXPORT_BUS_ME.asStack(), "d", "B", 'B', + GTLMachines.GTAEMachines.ITEM_IMPORT_BUS_ME.asStack()); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/recipe/MetaTileEntityMachineRecipeLoaderMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/recipe/MetaTileEntityMachineRecipeLoaderMixin.java new file mode 100644 index 000000000..10698293d --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/recipe/MetaTileEntityMachineRecipeLoaderMixin.java @@ -0,0 +1,350 @@ +package org.gtlcore.gtlcore.mixin.gtm.recipe; + +import org.gtlcore.gtlcore.GTLCore; +import org.gtlcore.gtlcore.common.data.GTLMachines; +import org.gtlcore.gtlcore.common.data.GTLRecipeTypes; +import org.gtlcore.gtlcore.utils.Registries; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.GTCEuAPI; +import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.api.data.chemical.ChemicalHelper; +import com.gregtechceu.gtceu.api.data.chemical.material.Material; +import com.gregtechceu.gtceu.api.data.chemical.material.stack.UnificationEntry; +import com.gregtechceu.gtceu.api.data.tag.TagPrefix; +import com.gregtechceu.gtceu.api.fluids.store.FluidStorageKeys; +import com.gregtechceu.gtceu.api.machine.MachineDefinition; +import com.gregtechceu.gtceu.api.machine.multiblock.CleanroomType; +import com.gregtechceu.gtceu.common.data.*; +import com.gregtechceu.gtceu.data.recipe.CraftingComponent; +import com.gregtechceu.gtceu.data.recipe.CustomTags; +import com.gregtechceu.gtceu.data.recipe.VanillaRecipeHelper; +import com.gregtechceu.gtceu.data.recipe.misc.MetaTileEntityMachineRecipeLoader; + +import net.minecraft.data.recipes.FinishedRecipe; +import net.minecraft.world.item.ItemStack; + +import appeng.core.definitions.*; +import com.hepdd.gtmthings.data.CustomMachines; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.function.Consumer; + +import static com.gregtechceu.gtceu.api.GTValues.*; +import static com.gregtechceu.gtceu.api.data.tag.TagPrefix.*; +import static com.gregtechceu.gtceu.common.data.GTItems.*; +import static com.gregtechceu.gtceu.common.data.GTMachines.*; +import static com.gregtechceu.gtceu.common.data.GTMaterials.*; +import static com.gregtechceu.gtceu.common.data.GTRecipeTypes.*; +import static com.hepdd.gtmthings.data.CustomMachines.HUGE_INPUT_DUAL_HATCH; +import static org.gtlcore.gtlcore.common.data.GTLMaterials.*; + +@Mixin(MetaTileEntityMachineRecipeLoader.class) +public abstract class MetaTileEntityMachineRecipeLoaderMixin { + + @Shadow(remap = false) + private static void registerLaserRecipes(Consumer provider) {} + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public static void init(Consumer provider) { + CraftingComponent.initializeComponents(); + ASSEMBLER_RECIPES.recipeBuilder("reservoir_hatch").inputItems(GTItems.COVER_INFINITE_WATER).inputItems(GTMachines.FLUID_IMPORT_HATCH[4]).inputItems(GTItems.ELECTRIC_PUMP_EV).outputItems(GTMachines.RESERVOIR_HATCH).duration(300).EUt(GTValues.VA[4]).save(provider); + registerLaserRecipes(provider); + VanillaRecipeHelper.addShapedRecipe(provider, true, "dynamo_hatch_ulv", GTMachines.ENERGY_OUTPUT_HATCH[0].asStack(), " V ", "SHS", " ", 'S', new UnificationEntry(TagPrefix.spring, GTMaterials.Lead), 'V', GTItems.VOLTAGE_COIL_ULV.asStack(), 'H', GTMachines.HULL[0].asStack()); + ASSEMBLER_RECIPES.recipeBuilder("dynamo_hatch_ulv").inputItems(GTMachines.HULL[0]).inputItems(TagPrefix.spring, GTMaterials.Lead, 2).inputItems(GTItems.VOLTAGE_COIL_ULV).outputItems(GTMachines.ENERGY_OUTPUT_HATCH[0]).duration(200).EUt(GTValues.VA[0]).save(provider); + VanillaRecipeHelper.addShapedRecipe(provider, true, "dynamo_hatch_lv", GTMachines.ENERGY_OUTPUT_HATCH[1].asStack(), " V ", "SHS", " ", 'S', new UnificationEntry(TagPrefix.spring, GTMaterials.Tin), 'V', GTItems.VOLTAGE_COIL_LV.asStack(), 'H', GTMachines.HULL[1].asStack()); + ASSEMBLER_RECIPES.recipeBuilder("dynamo_hatch_lv").inputItems(GTMachines.HULL[1]).inputItems(TagPrefix.spring, GTMaterials.Tin, 2).inputItems(GTItems.VOLTAGE_COIL_LV).outputItems(GTMachines.ENERGY_OUTPUT_HATCH[1]).duration(200).EUt(GTValues.VA[1]).save(provider); + VanillaRecipeHelper.addShapedRecipe(provider, true, "dynamo_hatch_mv", GTMachines.ENERGY_OUTPUT_HATCH[2].asStack(), " V ", "SHS", " P ", 'P', GTItems.ULTRA_LOW_POWER_INTEGRATED_CIRCUIT.asStack(), 'S', new UnificationEntry(TagPrefix.spring, GTMaterials.Copper), 'V', GTItems.VOLTAGE_COIL_MV.asStack(), 'H', GTMachines.HULL[2].asStack()); + ASSEMBLER_RECIPES.recipeBuilder("dynamo_hatch_mv").inputItems(GTMachines.HULL[2]).inputItems(TagPrefix.spring, GTMaterials.Copper, 2).inputItems(GTItems.ULTRA_LOW_POWER_INTEGRATED_CIRCUIT).inputItems(GTItems.VOLTAGE_COIL_MV).outputItems(GTMachines.ENERGY_OUTPUT_HATCH[2]).duration(200).EUt(GTValues.VA[2]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder("dynamo_hatch_hv").inputItems(GTMachines.HULL[3]).inputItems(TagPrefix.spring, GTMaterials.Gold, 2).inputItems(GTItems.LOW_POWER_INTEGRATED_CIRCUIT, 2).inputItems(GTItems.VOLTAGE_COIL_HV).inputFluids(GTMaterials.SodiumPotassium.getFluid(1000L)).outputItems(GTMachines.ENERGY_OUTPUT_HATCH[3]).duration(200).EUt(GTValues.VA[3]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder("dynamo_hatch_ev").inputItems(GTMachines.HULL[4]).inputItems(TagPrefix.spring, GTMaterials.Aluminium, 2).inputItems(GTItems.POWER_INTEGRATED_CIRCUIT, 2).inputItems(GTItems.VOLTAGE_COIL_EV).inputFluids(GTMaterials.SodiumPotassium.getFluid(2000L)).outputItems(GTMachines.ENERGY_OUTPUT_HATCH[4]).duration(200).EUt(GTValues.VA[4]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder("dynamo_hatch_iv").inputItems(GTMachines.HULL[5]).inputItems(TagPrefix.spring, GTMaterials.Tungsten, 2).inputItems(GTItems.HIGH_POWER_INTEGRATED_CIRCUIT, 2).inputItems(GTItems.VOLTAGE_COIL_IV).inputFluids(GTMaterials.SodiumPotassium.getFluid(3000L)).outputItems(GTMachines.ENERGY_OUTPUT_HATCH[5]).duration(200).EUt(GTValues.VA[5]).save(provider); + ASSEMBLY_LINE_RECIPES.recipeBuilder("dynamo_hatch_luv").inputItems(GTMachines.HULL[6]).inputItems(TagPrefix.spring, GTMaterials.NiobiumTitanium, 4).inputItems(GTItems.HIGH_POWER_INTEGRATED_CIRCUIT, 2).inputItems(CustomTags.LuV_CIRCUITS).inputItems(GTItems.VOLTAGE_COIL_LuV, 2).inputFluids(GTMaterials.SodiumPotassium.getFluid(6000L)).inputFluids(GTMaterials.SolderingAlloy.getFluid(720L)).outputItems(GTMachines.ENERGY_OUTPUT_HATCH[6]).duration(400).EUt(GTValues.VA[6]).save(provider); + ASSEMBLY_LINE_RECIPES.recipeBuilder("dynamo_hatch_zpm").inputItems(GTMachines.HULL[7]).inputItems(TagPrefix.spring, GTMaterials.VanadiumGallium, 4).inputItems(GTItems.ULTRA_HIGH_POWER_INTEGRATED_CIRCUIT, 2).inputItems(CustomTags.ZPM_CIRCUITS).inputItems(GTItems.VOLTAGE_COIL_ZPM, 2).inputFluids(GTMaterials.SodiumPotassium.getFluid(8000L)).inputFluids(GTMaterials.SolderingAlloy.getFluid(1440L)).outputItems(GTMachines.ENERGY_OUTPUT_HATCH[7]).stationResearch((b) -> b.researchStack(GTMachines.ENERGY_OUTPUT_HATCH[6].asStack()).CWUt(8)).duration(600).EUt(GTValues.VA[7]).save(provider); + ASSEMBLY_LINE_RECIPES.recipeBuilder("dynamo_hatch_uv").inputItems(GTMachines.HULL[8]).inputItems(TagPrefix.spring, GTMaterials.YttriumBariumCuprate, 4).inputItems(GTItems.ULTRA_HIGH_POWER_INTEGRATED_CIRCUIT, 2).inputItems(CustomTags.UV_CIRCUITS).inputItems(GTItems.VOLTAGE_COIL_UV, 2).inputFluids(GTMaterials.SodiumPotassium.getFluid(10000L)).inputFluids(GTMaterials.SolderingAlloy.getFluid(2880L)).outputItems(GTMachines.ENERGY_OUTPUT_HATCH[8]).stationResearch((b) -> b.researchStack(GTMachines.ENERGY_OUTPUT_HATCH[7].asStack()).CWUt(64).EUt(GTValues.VA[7])).duration(800).EUt(GTValues.VA[8]).save(provider); + ASSEMBLY_LINE_RECIPES.recipeBuilder("dynamo_hatch_uhv").inputItems(GTMachines.HULL[9]).inputItems(TagPrefix.spring, GTMaterials.Europium, 4).inputItems(GTItems.ULTRA_HIGH_POWER_INTEGRATED_CIRCUIT, 2).inputItems(CustomTags.UHV_CIRCUITS).inputItems(TagPrefix.wireGtDouble, GTMaterials.RutheniumTriniumAmericiumNeutronate, 2).inputFluids(GTMaterials.SodiumPotassium.getFluid(12000L)).inputFluids(GTMaterials.SolderingAlloy.getFluid(5760L)).outputItems(GTMachines.ENERGY_OUTPUT_HATCH[9]).stationResearch((b) -> b.researchStack(GTMachines.ENERGY_OUTPUT_HATCH[8].asStack()).CWUt(128).EUt(GTValues.VA[8])).duration(1000).EUt(GTValues.VA[9]).save(provider); + VanillaRecipeHelper.addShapedRecipe(provider, true, "energy_hatch_ulv", GTMachines.ENERGY_INPUT_HATCH[0].asStack(), " V ", "CHC", " ", 'C', new UnificationEntry(TagPrefix.cableGtSingle, GTMaterials.RedAlloy), 'V', GTItems.VOLTAGE_COIL_ULV.asStack(), 'H', GTMachines.HULL[0].asStack()); + ASSEMBLER_RECIPES.recipeBuilder("energy_hatch_ulv").inputItems(GTMachines.HULL[0]).inputItems(TagPrefix.cableGtSingle, GTMaterials.RedAlloy, 2).inputItems(GTItems.VOLTAGE_COIL_ULV).outputItems(GTMachines.ENERGY_INPUT_HATCH[0]).duration(200).EUt(GTValues.VA[0]).save(provider); + VanillaRecipeHelper.addShapedRecipe(provider, true, "energy_hatch_lv", GTMachines.ENERGY_INPUT_HATCH[1].asStack(), " V ", "CHC", " ", 'C', new UnificationEntry(TagPrefix.cableGtSingle, GTMaterials.Tin), 'V', GTItems.VOLTAGE_COIL_LV.asStack(), 'H', GTMachines.HULL[1].asStack()); + ASSEMBLER_RECIPES.recipeBuilder("energy_hatch_lv").inputItems(GTMachines.HULL[1]).inputItems(TagPrefix.cableGtSingle, GTMaterials.Tin, 2).inputItems(GTItems.VOLTAGE_COIL_LV).outputItems(GTMachines.ENERGY_INPUT_HATCH[1]).duration(200).EUt(GTValues.VA[1]).save(provider); + VanillaRecipeHelper.addShapedRecipe(provider, true, "energy_hatch_mv", GTMachines.ENERGY_INPUT_HATCH[2].asStack(), " V ", "CHC", " P ", 'C', new UnificationEntry(TagPrefix.cableGtSingle, GTMaterials.Copper), 'P', GTItems.ULTRA_LOW_POWER_INTEGRATED_CIRCUIT.asStack(), 'V', GTItems.VOLTAGE_COIL_MV.asStack(), 'H', GTMachines.HULL[2].asStack()); + ASSEMBLER_RECIPES.recipeBuilder("energy_hatch_mv").inputItems(GTMachines.HULL[2]).inputItems(TagPrefix.cableGtSingle, GTMaterials.Copper, 2).inputItems(GTItems.ULTRA_LOW_POWER_INTEGRATED_CIRCUIT).inputItems(GTItems.VOLTAGE_COIL_MV).outputItems(GTMachines.ENERGY_INPUT_HATCH[2]).duration(200).EUt(GTValues.VA[2]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder("energy_hatch_hv").inputItems(GTMachines.HULL[3]).inputItems(TagPrefix.cableGtSingle, GTMaterials.Gold, 2).inputItems(GTItems.LOW_POWER_INTEGRATED_CIRCUIT, 2).inputItems(GTItems.VOLTAGE_COIL_HV).inputFluids(GTMaterials.SodiumPotassium.getFluid(1000L)).outputItems(GTMachines.ENERGY_INPUT_HATCH[3]).duration(200).EUt(GTValues.VA[3]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder("energy_hatch_ev").inputItems(GTMachines.HULL[4]).inputItems(TagPrefix.cableGtSingle, GTMaterials.Aluminium, 2).inputItems(GTItems.POWER_INTEGRATED_CIRCUIT, 2).inputItems(GTItems.VOLTAGE_COIL_EV).inputFluids(GTMaterials.SodiumPotassium.getFluid(2000L)).outputItems(GTMachines.ENERGY_INPUT_HATCH[4]).duration(200).EUt(GTValues.VA[4]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder("energy_hatch_iv").inputItems(GTMachines.HULL[5]).inputItems(TagPrefix.cableGtSingle, GTMaterials.Tungsten, 2).inputItems(GTItems.HIGH_POWER_INTEGRATED_CIRCUIT, 2).inputItems(GTItems.VOLTAGE_COIL_IV).inputFluids(GTMaterials.SodiumPotassium.getFluid(3000L)).outputItems(GTMachines.ENERGY_INPUT_HATCH[5]).duration(200).EUt(GTValues.VA[5]).save(provider); + ASSEMBLY_LINE_RECIPES.recipeBuilder("energy_hatch_luv").inputItems(GTMachines.HULL[6]).inputItems(TagPrefix.cableGtSingle, GTMaterials.NiobiumTitanium, 4).inputItems(GTItems.HIGH_POWER_INTEGRATED_CIRCUIT, 2).inputItems(CustomTags.LuV_CIRCUITS).inputItems(GTItems.VOLTAGE_COIL_LuV, 2).inputFluids(GTMaterials.SodiumPotassium.getFluid(6000L)).inputFluids(GTMaterials.SolderingAlloy.getFluid(720L)).outputItems(GTMachines.ENERGY_INPUT_HATCH[6]).scannerResearch((b) -> b.researchStack(GTMachines.ENERGY_INPUT_HATCH[5].asStack()).EUt(GTValues.VA[4])).duration(400).EUt(GTValues.VA[6]).save(provider); + ASSEMBLY_LINE_RECIPES.recipeBuilder("energy_hatch_zpm").inputItems(GTMachines.HULL[7]).inputItems(TagPrefix.cableGtSingle, GTMaterials.VanadiumGallium, 4).inputItems(GTItems.ULTRA_HIGH_POWER_INTEGRATED_CIRCUIT, 2).inputItems(CustomTags.ZPM_CIRCUITS).inputItems(GTItems.VOLTAGE_COIL_ZPM, 2).inputFluids(GTMaterials.SodiumPotassium.getFluid(8000L)).inputFluids(GTMaterials.SolderingAlloy.getFluid(1440L)).outputItems(GTMachines.ENERGY_INPUT_HATCH[7]).stationResearch((b) -> b.researchStack(GTMachines.ENERGY_INPUT_HATCH[6].asStack()).CWUt(8)).duration(600).EUt(GTValues.VA[7]).save(provider); + ASSEMBLY_LINE_RECIPES.recipeBuilder("energy_hatch_uv").inputItems(GTMachines.HULL[8]).inputItems(TagPrefix.cableGtSingle, GTMaterials.YttriumBariumCuprate, 4).inputItems(GTItems.ULTRA_HIGH_POWER_INTEGRATED_CIRCUIT, 2).inputItems(CustomTags.UV_CIRCUITS).inputItems(GTItems.VOLTAGE_COIL_UV, 2).inputFluids(GTMaterials.SodiumPotassium.getFluid(10000L)).inputFluids(GTMaterials.SolderingAlloy.getFluid(2880L)).outputItems(GTMachines.ENERGY_INPUT_HATCH[8]).stationResearch((b) -> b.researchStack(GTMachines.ENERGY_INPUT_HATCH[7].asStack()).CWUt(64).EUt(GTValues.VA[7])).duration(800).EUt(GTValues.VA[8]).save(provider); + ASSEMBLY_LINE_RECIPES.recipeBuilder("energy_hatch_uhv").inputItems(GTMachines.HULL[9]).inputItems(TagPrefix.cableGtSingle, GTMaterials.Europium, 4).inputItems(GTItems.ULTRA_HIGH_POWER_INTEGRATED_CIRCUIT, 2).inputItems(CustomTags.UHV_CIRCUITS).inputItems(TagPrefix.wireGtDouble, GTMaterials.RutheniumTriniumAmericiumNeutronate, 2).inputFluids(GTMaterials.SodiumPotassium.getFluid(12000L)).inputFluids(GTMaterials.SolderingAlloy.getFluid(5760L)).outputItems(GTMachines.ENERGY_INPUT_HATCH[9]).stationResearch((b) -> b.researchStack(GTMachines.ENERGY_INPUT_HATCH[8].asStack()).CWUt(128).EUt(GTValues.VA[8])).duration(1000).EUt(GTValues.VA[9]).save(provider); + + for (int tier = 0; tier < GTMachines.POWER_TRANSFORMER.length; ++tier) { + MachineDefinition hatch = GTMachines.POWER_TRANSFORMER[tier]; + if (hatch != null) { + Material materialPrime = ChemicalHelper.getMaterial(CraftingComponent.CABLE_HEX.getIngredient(tier)).material(); + Material materialSecond = ChemicalHelper.getMaterial(CraftingComponent.CABLE_TIER_UP_OCT.getIngredient(tier)).material(); + String var10001 = GTValues.VN[tier]; + ASSEMBLER_RECIPES.recipeBuilder(var10001.toLowerCase() + "_power_transformer").inputItems(GTMachines.HI_AMP_TRANSFORMER_4A[tier]).inputItems(CraftingComponent.PUMP.getIngredient(tier / 2 + 1)).inputItems(CraftingComponent.CABLE_TIER_UP_OCT.getIngredient(tier)).inputItems(CraftingComponent.CABLE_HEX.getIngredient(tier)).inputItems(TagPrefix.springSmall, materialPrime).inputItems(TagPrefix.spring, materialSecond).inputFluids(GTMaterials.Lubricant.getFluid(2000L)).outputItems(hatch).duration(100).EUt(GTValues.VA[tier]).save(provider); + } + } + + for (int tier = 0; tier < GTMachines.ENERGY_INPUT_HATCH_4A.length; ++tier) { + MachineDefinition hatch = GTMachines.ENERGY_INPUT_HATCH_4A[tier]; + if (hatch != null) { + String var23 = GTValues.VN[tier]; + ASSEMBLER_RECIPES.recipeBuilder("energy_hatch_4a_" + var23.toLowerCase()).inputItems(GTMachines.ENERGY_INPUT_HATCH[tier]).inputItems(CraftingComponent.WIRE_QUAD.getIngredient(tier), 2).inputItems(CraftingComponent.PLATE.getIngredient(tier), 2).outputItems(hatch).duration(100).EUt(GTValues.VA[tier]).save(provider); + } + } + + for (int tier = 0; tier < GTMachines.ENERGY_INPUT_HATCH_16A.length; ++tier) { + MachineDefinition hatch = GTMachines.ENERGY_INPUT_HATCH_16A[tier]; + if (hatch != null) { + MachineDefinition transformer; + if (tier == (GTCEuAPI.isHighTier() ? 14 : 9)) { + transformer = GTMachines.HI_AMP_TRANSFORMER_4A[tier - 1]; + } else { + transformer = GTMachines.TRANSFORMER[tier]; + } + + String var24 = GTValues.VN[tier]; + ASSEMBLER_RECIPES.recipeBuilder("energy_hatch_16a_" + var24.toLowerCase()).inputItems(transformer).inputItems(GTMachines.ENERGY_INPUT_HATCH_4A[tier]).inputItems(CraftingComponent.WIRE_OCT.getIngredient(tier), 2).inputItems(CraftingComponent.PLATE.getIngredient(tier), 4).outputItems(hatch).duration(200).EUt(GTValues.VA[tier]).save(provider); + } + } + + for (int tier = 0; tier < GTMachines.SUBSTATION_ENERGY_INPUT_HATCH.length; ++tier) { + MachineDefinition hatch = GTMachines.SUBSTATION_ENERGY_INPUT_HATCH[tier]; + if (hatch != null) { + MachineDefinition transformer; + if (tier == (GTCEuAPI.isHighTier() ? 14 : 9)) { + transformer = GTMachines.POWER_TRANSFORMER[tier - 1]; + } else { + transformer = GTMachines.POWER_TRANSFORMER[tier]; + } + + String var25 = GTValues.VN[tier]; + ASSEMBLER_RECIPES.recipeBuilder("substation_energy_hatch_" + var25.toLowerCase()).inputItems(transformer).inputItems(GTMachines.ENERGY_INPUT_HATCH_16A[tier]).inputItems(CraftingComponent.WIRE_HEX.getIngredient(tier), 2).inputItems(CraftingComponent.PLATE.getIngredient(tier), 6).outputItems(hatch).duration(400).EUt(GTValues.VA[tier]).save(provider); + } + } + + for (int tier = 0; tier < GTMachines.ENERGY_OUTPUT_HATCH_4A.length; ++tier) { + MachineDefinition hatch = GTMachines.ENERGY_OUTPUT_HATCH_4A[tier]; + if (hatch != null) { + String var26 = GTValues.VN[tier]; + ASSEMBLER_RECIPES.recipeBuilder("dynamo_hatch_4a_" + var26.toLowerCase()).inputItems(GTMachines.ENERGY_OUTPUT_HATCH[tier]).inputItems(CraftingComponent.WIRE_QUAD.getIngredient(tier), 2).inputItems(CraftingComponent.PLATE.getIngredient(tier), 2).outputItems(hatch).duration(100).EUt(GTValues.VA[tier - 1]).save(provider); + } + } + + for (int tier = 0; tier < GTMachines.ENERGY_OUTPUT_HATCH_16A.length; ++tier) { + MachineDefinition hatch = GTMachines.ENERGY_OUTPUT_HATCH_16A[tier]; + if (hatch != null) { + MachineDefinition transformer; + if (tier == (GTCEuAPI.isHighTier() ? 14 : 9)) { + transformer = GTMachines.HI_AMP_TRANSFORMER_4A[tier - 1]; + } else { + transformer = GTMachines.TRANSFORMER[tier]; + } + + String var27 = GTValues.VN[tier]; + ASSEMBLER_RECIPES.recipeBuilder("dynamo_hatch_16a_" + var27.toLowerCase()).inputItems(transformer).inputItems(GTMachines.ENERGY_OUTPUT_HATCH_4A[tier]).inputItems(CraftingComponent.WIRE_OCT.getIngredient(tier), 2).inputItems(CraftingComponent.PLATE.getIngredient(tier), 4).outputItems(hatch).duration(200).EUt(GTValues.VA[tier]).save(provider); + } + } + + for (int tier = 0; tier < GTMachines.SUBSTATION_ENERGY_OUTPUT_HATCH.length; ++tier) { + MachineDefinition hatch = GTMachines.SUBSTATION_ENERGY_OUTPUT_HATCH[tier]; + if (hatch != null) { + MachineDefinition transformer; + if (tier == (GTCEuAPI.isHighTier() ? 14 : 9)) { + transformer = GTMachines.POWER_TRANSFORMER[tier - 1]; + } else { + transformer = GTMachines.POWER_TRANSFORMER[tier]; + } + + if (transformer != null) { + String var28 = GTValues.VN[tier]; + ASSEMBLER_RECIPES.recipeBuilder("substation_dynamo_hatch_" + var28.toLowerCase()).inputItems(transformer).inputItems(GTMachines.ENERGY_OUTPUT_HATCH_16A[tier]).inputItems(CraftingComponent.WIRE_HEX.getIngredient(tier), 2).inputItems(CraftingComponent.PLATE.getIngredient(tier), 6).outputItems(hatch).duration(400).EUt(GTValues.VA[tier]).save(provider); + } + } + } + + ASSEMBLER_RECIPES.recipeBuilder("maintenance_hatch").inputItems(GTMachines.HULL[1]).circuitMeta(8).outputItems(GTMachines.MAINTENANCE_HATCH).duration(100).EUt(GTValues.VA[1]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder("ev_large_miner").inputItems(GTMachines.HULL[4]).inputItems(TagPrefix.frameGt, GTMaterials.Titanium, 4).inputItems(CustomTags.IV_CIRCUITS, 4).inputItems(GTItems.ELECTRIC_MOTOR_EV, 4).inputItems(GTItems.ELECTRIC_PUMP_EV, 4).inputItems(GTItems.CONVEYOR_MODULE_EV, 4).inputItems(TagPrefix.gear, GTMaterials.Tungsten, 4).circuitMeta(2).outputItems(GTMachines.LARGE_MINER[4]).duration(400).EUt(GTValues.VA[4]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder("iv_large_miner").inputItems(GTMachines.HULL[5]).inputItems(TagPrefix.frameGt, GTMaterials.TungstenSteel, 4).inputItems(CustomTags.IV_CIRCUITS, 4).inputItems(GTItems.ELECTRIC_MOTOR_IV, 4).inputItems(GTItems.ELECTRIC_PUMP_IV, 4).inputItems(GTItems.CONVEYOR_MODULE_IV, 4).inputItems(TagPrefix.gear, GTMaterials.Iridium, 4).circuitMeta(2).outputItems(GTMachines.LARGE_MINER[5]).duration(400).EUt(GTValues.VA[5]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder("luv_large_miner").inputItems(GTMachines.HULL[6]).inputItems(TagPrefix.frameGt, GTMaterials.HSSS, 4).inputItems(CustomTags.LuV_CIRCUITS, 4).inputItems(GTItems.ELECTRIC_MOTOR_LuV, 4).inputItems(GTItems.ELECTRIC_PUMP_LuV, 4).inputItems(GTItems.CONVEYOR_MODULE_LuV, 4).inputItems(TagPrefix.gear, GTMaterials.Ruridit, 4).circuitMeta(2).outputItems(GTMachines.LARGE_MINER[6]).duration(400).EUt(GTValues.VA[6]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder("mv_fluid_drilling_rig").inputItems(GTMachines.HULL[2]).inputItems(TagPrefix.frameGt, GTMaterials.Steel, 4).inputItems(CustomTags.MV_CIRCUITS, 4).inputItems(GTItems.ELECTRIC_MOTOR_MV, 4).inputItems(GTItems.ELECTRIC_PUMP_MV, 4).inputItems(TagPrefix.gear, GTMaterials.VanadiumSteel, 4).circuitMeta(2).outputItems(GTMachines.FLUID_DRILLING_RIG[2]).duration(400).EUt(GTValues.VA[2]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder("hv_fluid_drilling_rig").inputItems(GTMachines.HULL[4]).inputItems(TagPrefix.frameGt, GTMaterials.Titanium, 4).inputItems(CustomTags.EV_CIRCUITS, 4).inputItems(GTItems.ELECTRIC_MOTOR_EV, 4).inputItems(GTItems.ELECTRIC_PUMP_EV, 4).inputItems(TagPrefix.gear, GTMaterials.TungstenCarbide, 4).circuitMeta(2).outputItems(GTMachines.FLUID_DRILLING_RIG[3]).duration(400).EUt(GTValues.VA[4]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder("ev_fluid_drilling_rig").inputItems(GTMachines.HULL[6]).inputItems(TagPrefix.frameGt, GTMaterials.TungstenSteel, 4).inputItems(CustomTags.LuV_CIRCUITS, 4).inputItems(GTItems.ELECTRIC_MOTOR_LuV, 4).inputItems(GTItems.ELECTRIC_PUMP_LuV, 4).inputItems(TagPrefix.gear, GTMaterials.Osmiridium, 4).circuitMeta(2).outputItems(GTMachines.FLUID_DRILLING_RIG[4]).duration(400).EUt(GTValues.VA[6]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder("long_distance_item_endpoint").inputItems(TagPrefix.pipeLargeItem, GTMaterials.Tin, 2).inputItems(TagPrefix.plate, GTMaterials.Steel, 8).inputItems(TagPrefix.gear, GTMaterials.Steel, 2).circuitMeta(1).inputFluids(GTMaterials.SolderingAlloy.getFluid(72L)).outputItems(GTMachines.LONG_DIST_ITEM_ENDPOINT, 2).duration(400).EUt(16L).save(provider); + ASSEMBLER_RECIPES.recipeBuilder("long_distance_fluid_endpoint").inputItems(TagPrefix.pipeLargeFluid, GTMaterials.Bronze, 2).inputItems(TagPrefix.plate, GTMaterials.Steel, 8).inputItems(TagPrefix.gear, GTMaterials.Steel, 2).circuitMeta(1).inputFluids(GTMaterials.SolderingAlloy.getFluid(72L)).outputItems(GTMachines.LONG_DIST_FLUID_ENDPOINT, 2).duration(400).EUt(16L).save(provider); + ASSEMBLER_RECIPES.recipeBuilder("long_distance_item_pipe").inputItems(TagPrefix.pipeLargeItem, GTMaterials.Tin, 2).inputItems(TagPrefix.plate, GTMaterials.Steel, 8).circuitMeta(2).inputFluids(GTMaterials.SolderingAlloy.getFluid(72L)).outputItems(GTBlocks.LD_ITEM_PIPE, 64).duration(600).EUt(24L).save(provider); + ASSEMBLER_RECIPES.recipeBuilder("long_distance_fluid_pipe").inputItems(TagPrefix.pipeLargeFluid, GTMaterials.Bronze, 2).inputItems(TagPrefix.plate, GTMaterials.Steel, 8).circuitMeta(2).inputFluids(GTMaterials.SolderingAlloy.getFluid(72L)).outputItems(GTBlocks.LD_FLUID_PIPE, 64).duration(600).EUt(24L).save(provider); + + if (GTCEu.isAE2Loaded()) { + + ItemStack meInterface = AEParts.INTERFACE.stack(1); + ItemStack accelerationCard = AEItems.SPEED_CARD.stack(2); + ItemStack capacityCard = AEItems.CAPACITY_CARD.stack(1); + + ASSEMBLER_RECIPES.recipeBuilder("me_export_hatch") + .inputItems(FLUID_EXPORT_HATCH[EV]) + .inputItems(meInterface.copy()) + .inputItems(accelerationCard.copy()) + .outputItems(GTLMachines.GTAEMachines.FLUID_EXPORT_HATCH_ME) + .duration(300).EUt(VA[HV]) + .save(provider); + + ASSEMBLER_RECIPES.recipeBuilder("me_import_hatch") + .inputItems(FLUID_IMPORT_HATCH[EV]) + .inputItems(meInterface.copy()) + .inputItems(accelerationCard.copy()) + .outputItems(GTLMachines.GTAEMachines.FLUID_IMPORT_HATCH_ME) + .duration(300).EUt(VA[HV]) + .save(provider); + + ASSEMBLER_RECIPES.recipeBuilder("me_export_bus") + .inputItems(ITEM_EXPORT_BUS[EV]) + .inputItems(meInterface.copy()) + .inputItems(accelerationCard.copy()) + .outputItems(GTLMachines.GTAEMachines.ITEM_EXPORT_BUS_ME) + .duration(300).EUt(VA[HV]) + .save(provider); + + ASSEMBLER_RECIPES.recipeBuilder("me_import_bus") + .inputItems(ITEM_IMPORT_BUS[EV]) + .inputItems(meInterface.copy()) + .inputItems(accelerationCard.copy()) + .outputItems(GTLMachines.GTAEMachines.ITEM_IMPORT_BUS_ME) + .duration(300).EUt(VA[HV]) + .save(provider); + + ASSEMBLER_RECIPES.recipeBuilder("me_stocking_import_bus") + .inputItems(ITEM_IMPORT_BUS[IV]) + .inputItems(meInterface.copy()) + .inputItems(CONVEYOR_MODULE_IV) + .inputItems(SENSOR_IV) + .inputItems(accelerationCard.copyWithCount(4)) + .outputItems(GTLMachines.GTAEMachines.STOCKING_IMPORT_BUS_ME) + .duration(300).EUt(VA[IV]) + .save(provider); + + ASSEMBLER_RECIPES.recipeBuilder("me_stocking_import_hatch") + .inputItems(FLUID_IMPORT_HATCH[IV]) + .inputItems(meInterface.copy()) + .inputItems(ELECTRIC_PUMP_IV) + .inputItems(SENSOR_IV) + .inputItems(accelerationCard.copyWithCount(4)) + .outputItems(GTLMachines.GTAEMachines.STOCKING_IMPORT_HATCH_ME) + .duration(300).EUt(VA[IV]) + .save(provider); + + ASSEMBLY_LINE_RECIPES.recipeBuilder(GTLCore.id("me_mini_pattern_buffer")) + .inputItems(HUGE_INPUT_DUAL_HATCH[IV], 1) + .inputItems(EMITTER_IV, 1) + .inputItems(CustomTags.LuV_CIRCUITS, 4) + .inputItems(AEBlocks.PATTERN_PROVIDER.asItem(), 3) + .inputItems(AEBlocks.INTERFACE.asItem(), 3) + .inputItems(AEItems.SPEED_CARD.asItem(), 3) + .inputItems(AEItems.CAPACITY_CARD.asItem(), 4) + .inputItems(plateDouble, Trinium, 2) + .inputItems(wireGtSingle, UraniumTriplatinum, 24) + .inputFluids(SolderingAlloy.getFluid(L * 4)) + .inputFluids(Lubricant.getFluid(500)) + .outputItems(GTLMachines.GTAEMachines.ME_MINI_PATTERN_BUFFER) + .duration(600).EUt(VA[IV]) + .scannerResearch(b -> b.researchStack(HUGE_INPUT_DUAL_HATCH[IV].asStack()) + .duration(600) + .EUt(VA[IV])) + .save(provider); + + ASSEMBLY_LINE_RECIPES.recipeBuilder(GTLCore.id("me_extend_pattern_buffer")) + .inputItems(HUGE_INPUT_DUAL_HATCH[UV], 1) + .inputItems(EMITTER_UV, 1) + .inputItems(CustomTags.UHV_CIRCUITS, 4) + .inputItems(AEBlocks.PATTERN_PROVIDER.asItem(), 3) + .inputItems(AEBlocks.INTERFACE.asItem(), 3) + .inputItems(AEItems.SPEED_CARD.asItem(), 6) + .inputItems(AEItems.CAPACITY_CARD.asItem(), 4) + .inputItems(plate, Neutronium, 8) + .inputItems(plate, Neutronium, 8) + .inputItems(wireFine, EnrichedNaquadahTriniumEuropiumDuranide, 32) + .inputItems(wireFine, EnrichedNaquadahTriniumEuropiumDuranide, 32) + .inputItems(wireFine, EnrichedNaquadahTriniumEuropiumDuranide, 32) + .inputFluids(SolderingAlloy.getFluid(L * 4)) + .inputFluids(Lubricant.getFluid(500)) + .inputFluids(Highurabilityompoundteel.getFluid(L * 4)) + .outputItems(GTLMachines.GTAEMachines.ME_EXTEND_PATTERN_BUFFER) + .stationResearch(b -> b.researchStack(HUGE_INPUT_DUAL_HATCH[UV].asStack()) + .CWUt(96)) + .duration(600).EUt(VA[UV]).save(provider); + + ASSEMBLY_LINE_RECIPES.recipeBuilder(GTLCore.id("me_final_pattern_buffer")) + .inputItems(HUGE_INPUT_DUAL_HATCH[UEV], 1) + .inputItems(EMITTER_UEV, 2) + .inputItems(CustomTags.UIV_CIRCUITS, 4) + .inputItems(AEBlocks.PATTERN_PROVIDER.asItem(), 3) + .inputItems(AEBlocks.INTERFACE.asItem(), 3) + .inputItems(AEItems.SPEED_CARD.asItem(), 6) + .inputItems(AEItems.CAPACITY_CARD.asItem(), 4) + .inputItems(plate, DegenerateRhenium, 12) + .inputItems(plate, DegenerateRhenium, 12) + .inputItems(wireFine, Enderite, 48) + .inputItems(wireFine, Enderite, 48) + .inputItems(wireFine, Enderite, 48) + .inputFluids(MutatedLivingSolder.getFluid(L * 4)) + .inputFluids(Lubricant.getFluid(500)) + .inputFluids(Zylon.getFluid(L * 4)) + .outputItems(GTLMachines.GTAEMachines.ME_FINAL_PATTERN_BUFFER) + .stationResearch(b -> b.researchStack(HUGE_INPUT_DUAL_HATCH[UEV].asStack()) + .CWUt(288)) + .duration(600).EUt(VA[UEV]).save(provider); + + ASSEMBLY_LINE_RECIPES.recipeBuilder(GTLCore.id("me_pattern_buffer_proxy")) + .inputItems(HULL[UV], 1) + .inputItems(SENSOR_UV, 2) + .inputItems(CustomTags.UHV_CIRCUITS, 1) + .inputItems(Registries.getItemStack("kubejs:entangled_singularity", 2)) + .inputItems(AEBlocks.QUANTUM_LINK.asItem(), 1) + .inputItems(AEBlocks.QUANTUM_RING.asItem(), 8) + .inputItems(wireFine, EnrichedNaquadahTriniumEuropiumDuranide, 32) + .inputItems(wireFine, EnrichedNaquadahTriniumEuropiumDuranide, 32) + .inputFluids(SolderingAlloy.getFluid(L * 4)) + .inputFluids(Lubricant.getFluid(500)) + .outputItems(GTLMachines.GTAEMachines.ME_PATTERN_BUFFER_PROXY) + .stationResearch(b -> b.researchStack(DUAL_IMPORT_HATCH[UV].asStack()) + .CWUt(48)) + .duration(600).EUt(VA[UV]).save(provider); + + GTLRecipeTypes.PRECISION_ASSEMBLER_RECIPES.recipeBuilder(GTLCore.id("me_extended_export_buffer")) + .inputItems(CustomMachines.ME_EXPORT_BUFFER, 4) + .inputItems(GTBlocks.MACHINE_CASING_UHV, 4) + .inputItems(capacityCard, 64) + .inputItems(accelerationCard, 48) + .inputFluids(SolderingAlloy.getFluid(1440)) + .inputFluids(Mithril.getFluid(576)) + .inputFluids(Iron.getFluid(FluidStorageKeys.PLASMA, 576)) + .inputFluids(SterileGrowthMedium.getFluid(576)) + .outputItems(GTLMachines.GTAEMachines.ME_EXTENDED_EXPORT_BUFFER) + .EUt(VA[UV]).duration(400) + .cleanroom(CleanroomType.CLEANROOM) + .save(provider); + + GTLRecipeTypes.PRECISION_ASSEMBLER_RECIPES.recipeBuilder(GTLCore.id("me_extended_async_export_buffer")) + .inputItems(CustomMachines.ME_EXPORT_BUFFER, 4) + .inputItems(GTBlocks.MACHINE_CASING_UHV, 4) + .inputItems(capacityCard, 64) + .inputItems(accelerationCard, 48) + .inputFluids(SolderingAlloy.getFluid(1440)) + .inputFluids(Orichalcum.getFluid(576)) + .inputFluids(AbyssalAlloy.getFluid(576)) + .inputFluids(SterileGrowthMedium.getFluid(576)) + .outputItems(GTLMachines.GTAEMachines.ME_EXTENDED_ASYNC_EXPORT_BUFFER) + .EUt(VA[UV]).duration(400) + .cleanroom(CleanroomType.CLEANROOM) + .save(provider); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/registry/GTMachinesMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/registry/GTMachinesMixin.java index ba281d41d..f455bbe3d 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/registry/GTMachinesMixin.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/registry/GTMachinesMixin.java @@ -26,7 +26,6 @@ import com.gregtechceu.gtceu.client.renderer.machine.RotorHolderMachineRenderer; import com.gregtechceu.gtceu.client.renderer.machine.SimpleGeneratorMachineRenderer; import com.gregtechceu.gtceu.common.data.*; -import com.gregtechceu.gtceu.common.data.machines.GTAEMachines; import com.gregtechceu.gtceu.common.data.machines.GTResearchMachines; import com.gregtechceu.gtceu.common.machine.multiblock.part.*; import com.gregtechceu.gtceu.common.machine.storage.BufferMachine; @@ -68,7 +67,6 @@ public class GTMachinesMixin { private static void init(CallbackInfo ci) { GCyMMachines.init(); GTResearchMachines.init(); - GTAEMachines.init(); ModLoader.get().postEvent(new GTCEuAPI.RegisterEvent<>(GTRegistries.MACHINES, MachineDefinition.class)); GTRegistries.MACHINES.freeze(); ci.cancel(); diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/registry/GTRecipeBuilderMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/registry/GTRecipeBuilderMixin.java index afddd59bb..137a3493a 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/registry/GTRecipeBuilderMixin.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/registry/GTRecipeBuilderMixin.java @@ -5,9 +5,13 @@ import com.gregtechceu.gtceu.api.recipe.GTRecipeType; import com.gregtechceu.gtceu.common.data.GTRecipeTypes; import com.gregtechceu.gtceu.data.recipe.builder.GTRecipeBuilder; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.nbt.CompoundTag; import com.google.gson.JsonObject; import lombok.experimental.Accessors; +import org.jetbrains.annotations.NotNull; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -16,6 +20,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import static org.gtlcore.gtlcore.common.data.GTLRecipeTypes.*; + @Mixin(GTRecipeBuilder.class) @Accessors(chain = true, fluent = true) public class GTRecipeBuilderMixin { @@ -26,6 +32,9 @@ public class GTRecipeBuilderMixin { @Shadow(remap = false) public int duration; + @Shadow(remap = false) + public @NotNull CompoundTag data = new CompoundTag(); + @Shadow(remap = false) public GTRecipeBuilder duration(int duration) { return null; @@ -37,6 +46,7 @@ public GTRecipeBuilder duration(int duration) { @Inject(method = "EUt(J)Lcom/gregtechceu/gtceu/data/recipe/builder/GTRecipeBuilder;", at = @At("HEAD"), remap = false) private void eu(long eu, CallbackInfoReturnable cir) { gTLCore$eut = eu; + this.data.putInt("euTier", GTUtil.getTierByVoltage(eu > 0 ? eu : -eu)); } @Unique @@ -48,7 +58,9 @@ private void eu(long eu, CallbackInfoReturnable cir) { recipeType == GTRecipeTypes.get("slaughterhouse") || recipeType == GTRecipeTypes.get("dyson_sphere") || recipeType == GTRecipeTypes.get("space_elevator") || - recipeType == GTRecipeTypes.get("annihilate_generator")) { + recipeType == GTRecipeTypes.get("annihilate_generator") || + recipeType == CREATE_AGGREGATION_RECIPES || + recipeType == DOOR_OF_CREATE_RECIPES) { return Math.abs(duration); } return (int) Math.min(Integer.MAX_VALUE, Math.max(1, Math.abs(duration * ConfigHolder.INSTANCE.durationMultiplier))); diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/registry/GTRecipeJSMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/registry/GTRecipeJSMixin.java new file mode 100644 index 000000000..d98ca985e --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/registry/GTRecipeJSMixin.java @@ -0,0 +1,22 @@ +package org.gtlcore.gtlcore.mixin.gtm.registry; + +import com.gregtechceu.gtceu.integration.kjs.recipe.GTRecipeSchema; +import com.gregtechceu.gtceu.utils.GTUtil; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(GTRecipeSchema.GTRecipeJS.class) +public abstract class GTRecipeJSMixin { + + @Shadow(remap = false) + public abstract GTRecipeSchema.GTRecipeJS addData(String key, int data); + + @Inject(method = "EUt", at = @At("HEAD"), remap = false) + public void EUt(long eu, CallbackInfoReturnable cir) { + this.addData("euTier", GTUtil.getTierByVoltage(eu > 0 ? eu : -eu)); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/registry/GTRecipeModifiersMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/registry/GTRecipeModifiersMixin.java new file mode 100644 index 000000000..5cf579ebf --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/registry/GTRecipeModifiersMixin.java @@ -0,0 +1,91 @@ +package org.gtlcore.gtlcore.mixin.gtm.registry; + +import org.gtlcore.gtlcore.api.machine.trait.IRecipeCapabilityMachine; +import org.gtlcore.gtlcore.api.recipe.RecipeResult; +import org.gtlcore.gtlcore.api.recipe.RecipeRunnerHelper; + +import com.gregtechceu.gtceu.api.capability.recipe.IRecipeCapabilityHolder; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; +import com.gregtechceu.gtceu.api.machine.multiblock.CoilWorkableElectricMultiblockMachine; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.OverclockingLogic; +import com.gregtechceu.gtceu.api.recipe.RecipeHelper; +import com.gregtechceu.gtceu.api.recipe.content.ContentModifier; +import com.gregtechceu.gtceu.api.recipe.logic.OCParams; +import com.gregtechceu.gtceu.api.recipe.logic.OCResult; +import com.gregtechceu.gtceu.api.recipe.modifier.ParallelLogic; +import com.gregtechceu.gtceu.common.data.GTRecipeModifiers; + +import com.mojang.datafixers.util.Pair; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +@Mixin(GTRecipeModifiers.class) +public class GTRecipeModifiersMixin { + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public static GTRecipe ebfOverclock(MetaMachine machine, @NotNull GTRecipe recipe, @NotNull OCParams params, @NotNull OCResult result) { + if (machine instanceof CoilWorkableElectricMultiblockMachine coilMachine) { + int blastFurnaceTemperature = coilMachine.getCoilType().getCoilTemperature() + 100 * Math.max(0, coilMachine.getTier() - 2); + if (recipe.data.contains("ebf_temp")) { + if (recipe.data.getInt("ebf_temp") <= blastFurnaceTemperature) { + return RecipeHelper.getRecipeEUtTier(recipe) > coilMachine.getTier() ? null : + RecipeHelper.applyOverclock(new OverclockingLogic((p, r, maxVoltage) -> OverclockingLogic.heatingCoilOC( + params, result, maxVoltage, blastFurnaceTemperature, recipe.data.contains("ebf_temp") ? recipe.data.getInt("ebf_temp") : 0)), + recipe, coilMachine.getOverclockVoltage(), params, result); + } else { + RecipeResult.of((IRecipeLogicMachine) machine, RecipeResult.FAIL_NO_ENOUGH_TEMPERATURE); + return null; + } + } else return null; + } else return null; + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public static GTRecipe hatchParallel(MetaMachine machine, @NotNull GTRecipe recipe, boolean modifyDuration, + @NotNull OCParams params, @NotNull OCResult result) { + if (machine instanceof IMultiController controller && controller instanceof IRecipeCapabilityMachine) { + if (controller.isFormed()) { + var hatch = ((IRecipeCapabilityMachine) controller).getParallelHatch(); + if (hatch != null) { + long recipeEU = RecipeHelper.getInputEUt(recipe); + var parallelRecipe = ParallelLogic.applyParallel(machine, recipe, hatch.getCurrentParallel(), modifyDuration); + if (parallelRecipe.getSecond() == 0) return null; + result.init(recipeEU, recipe.duration, parallelRecipe.getSecond(), params.getOcAmount()); + return parallelRecipe.getFirst(); + } + } + } + return recipe; + } + + /** + * @author Dragons + * @reason 适配me增广输出 + */ + @Overwrite(remap = false) + public static Pair fastParallel(MetaMachine machine, @NotNull GTRecipe recipe, int maxParallel, + boolean modifyDuration) { + if (machine instanceof IRecipeCapabilityHolder holder) { + while (maxParallel > 0) { + var copied = recipe.copy(ContentModifier.multiplier(maxParallel), modifyDuration); + if (RecipeRunnerHelper.matchRecipe(holder, copied) && copied.matchTickRecipe(holder).isSuccess()) { + return Pair.of(copied, maxParallel); + } + maxParallel /= 2; + } + } + return Pair.of(recipe, 1); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/registry/GTRecipeTypeMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/registry/GTRecipeTypeMixin.java index b956ba649..abd921c92 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtm/registry/GTRecipeTypeMixin.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtm/registry/GTRecipeTypeMixin.java @@ -1,16 +1,11 @@ package org.gtlcore.gtlcore.mixin.gtm.registry; -import org.gtlcore.gtlcore.api.recipe.RecipeRunnerHelper; import org.gtlcore.gtlcore.common.data.GTLMaterials; import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.capability.recipe.FluidRecipeCapability; -import com.gregtechceu.gtceu.api.capability.recipe.IRecipeCapabilityHolder; -import com.gregtechceu.gtceu.api.recipe.GTRecipe; import com.gregtechceu.gtceu.api.recipe.GTRecipeType; -import com.gregtechceu.gtceu.api.recipe.lookup.GTRecipeLookup; -import com.gregtechceu.gtceu.api.recipe.lookup.RecipeIterator; import com.gregtechceu.gtceu.common.data.GTMaterials; import com.gregtechceu.gtceu.data.recipe.builder.GTRecipeBuilder; import com.gregtechceu.gtceu.utils.GTUtil; @@ -18,7 +13,6 @@ import net.minecraft.data.recipes.FinishedRecipe; import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.*; import java.util.*; @@ -123,69 +117,4 @@ public GTRecipeType onRecipeBuild(BiConsumer searchFuelRecipe(IRecipeCapabilityHolder holder) { - return holder.hasProxies() && this.isFuelRecipeType() ? this.getLookup().getRecipeIterator(holder, (recipe) -> recipe.isFuel && RecipeRunnerHelper.matchRecipe(holder, recipe) && recipe.matchTickRecipe(holder).isSuccess()) : null; - } - - /** - * @author . - * @reason . - */ - @Overwrite(remap = false) - public Iterator searchRecipe(IRecipeCapabilityHolder holder) { - if (!holder.hasProxies()) { - return null; - } else { - RecipeIterator iterator = this.getLookup().getRecipeIterator(holder, - (recipex) -> !recipex.isFuel && RecipeRunnerHelper.matchRecipe(holder, recipex) && recipex.matchTickRecipe(holder).isSuccess()); - boolean any = false; - GTRecipe recipe = null; - while (iterator.hasNext()) { - recipe = iterator.next(); - if (recipe != null) { - any = true; - break; - } - } - if (any) { - iterator.reset(); - return Collections.singleton(recipe).iterator(); - } else { - Iterator var7 = this.customRecipeLogicRunners.iterator(); - do { - if (!var7.hasNext()) { - return Collections.emptyIterator(); - } - GTRecipeType.ICustomRecipeLogic logic = (GTRecipeType.ICustomRecipeLogic) var7.next(); - recipe = logic.createCustomRecipe(holder); - } while (recipe == null); - return Collections.singleton(recipe).iterator(); - } - } - } - - public GTRecipeTypeMixin(List customRecipeLogicRunners) { - this.customRecipeLogicRunners = customRecipeLogicRunners; - } - - @Mutable - @Final - @Shadow(remap = false) - private final List customRecipeLogicRunners; - - @Shadow(remap = false) - public GTRecipeLookup getLookup() { - return null; - } - - @Shadow(remap = false) - public boolean isFuelRecipeType() { - return false; - } } diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtmt/CreativeEnergyHatchPartMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtmt/CreativeEnergyHatchPartMachineMixin.java new file mode 100644 index 000000000..23b68a084 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtmt/CreativeEnergyHatchPartMachineMixin.java @@ -0,0 +1,184 @@ +package org.gtlcore.gtlcore.mixin.gtmt; + +import org.gtlcore.gtlcore.integration.gtmt.InfinityEnergyContainer; +import org.gtlcore.gtlcore.integration.gtmt.NewGTValues; +import org.gtlcore.gtlcore.utils.NumberUtils; + +import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.multiblock.part.TieredIOPartMachine; +import com.gregtechceu.gtceu.api.machine.trait.NotifiableEnergyContainer; +import com.gregtechceu.gtceu.api.pattern.MultiblockWorldSavedData; +import com.gregtechceu.gtceu.utils.GTUtil; + +import com.lowdragmc.lowdraglib.gui.editor.ColorPattern; +import com.lowdragmc.lowdraglib.gui.modular.ModularUI; +import com.lowdragmc.lowdraglib.gui.texture.GuiTextureGroup; +import com.lowdragmc.lowdraglib.gui.texture.ResourceBorderTexture; +import com.lowdragmc.lowdraglib.gui.texture.TextTexture; +import com.lowdragmc.lowdraglib.gui.widget.ButtonWidget; +import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; +import com.lowdragmc.lowdraglib.gui.widget.SelectorWidget; +import com.lowdragmc.lowdraglib.gui.widget.TextFieldWidget; +import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.player.Player; + +import com.hepdd.gtmthings.common.block.machine.multiblock.part.CreativeEnergyHatchPartMachine; +import org.apache.commons.lang3.ArrayUtils; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.*; + +import java.util.Arrays; + +@Mixin(CreativeEnergyHatchPartMachine.class) +public abstract class CreativeEnergyHatchPartMachineMixin extends TieredIOPartMachine { + + @Shadow(remap = false) + @Final + public NotifiableEnergyContainer energyContainer; + + @Shadow(remap = false) + private long voltage; + + @Persisted + @Shadow(remap = false) + private Long maxEnergy; + + @Shadow(remap = false) + private int setTier; + + @Shadow(remap = false) + private int amps; + + public CreativeEnergyHatchPartMachineMixin(IMachineBlockEntity holder, int tier, IO io) { + super(holder, tier, io); + } + + /** + * @author liansishen + * @reason Lastest GTMT + */ + @Overwrite(remap = false) + protected NotifiableEnergyContainer createEnergyContainer() { + this.setTier = 30; + this.voltage = GTValues.VEX[setTier]; + this.maxEnergy = NumberUtils.saturatedMultiply(this.voltage, this.amps); + return new InfinityEnergyContainer(this, this.maxEnergy, this.voltage, this.amps, 0L, 0L); + } + + /** + * @author liansishen + * @reason Lastest GTMT + */ + @Overwrite(remap = false) + public ModularUI createUI(Player entityPlayer) { + return new ModularUI(176, 136, this, entityPlayer) + .background(GuiTextures.BACKGROUND) + .widget(new LabelWidget(7, 32, "gtceu.creative.energy.voltage")) + .widget(new TextFieldWidget(9, 47, 152, 16, () -> String.valueOf(voltage), + value -> { + gTLCore$setVoltage(Long.parseLong(value)); + setTier = GTUtil.getTierByVoltage(this.voltage); + }).setNumbersOnly(8L, Long.MAX_VALUE)) + .widget(new LabelWidget(7, 74, "gtceu.creative.energy.amperage")) + .widget(new ButtonWidget(7, 87, 20, 20, + new GuiTextureGroup(ResourceBorderTexture.BUTTON_COMMON, new TextTexture("-")), + cd -> gTLCore$setAmps(--amps == -1 ? 0 : amps))) + .widget(new TextFieldWidget(31, 89, 114, 16, () -> String.valueOf(amps), + value -> gTLCore$setAmps(Integer.parseInt(value))).setNumbersOnly(1, 67108864)) + .widget(new ButtonWidget(149, 87, 20, 20, + new GuiTextureGroup(ResourceBorderTexture.BUTTON_COMMON, new TextTexture("+")), + cd -> { + if (amps < Integer.MAX_VALUE) { + gTLCore$setAmps(++amps); + } + })) + + .widget(new SelectorWidget(7, 7, 50, 20, Arrays.stream(NewGTValues.VNF).toList(), -1) + .setOnChanged(tier -> { + setTier = ArrayUtils.indexOf(NewGTValues.VNF, tier); + gTLCore$setVoltage(GTValues.VEX[setTier]); + }) + .setSupplier(() -> NewGTValues.VNF[setTier]) + .setButtonBackground(ResourceBorderTexture.BUTTON_COMMON) + .setBackground(ColorPattern.BLACK.rectTexture()) + .setValue(NewGTValues.VNF[setTier])); + } + + @Override + public void loadCustomPersistedData(@NotNull CompoundTag tag) { + super.loadCustomPersistedData(tag); + gTLCore$updateEnergyContainer(); + } + + @Unique + private void gTLCore$setVoltage(long voltage) { + this.voltage = voltage; + this.maxEnergy = NumberUtils.saturatedMultiply(this.voltage, this.amps); + gTLCore$updateMachine(); + } + + @Unique + private void gTLCore$setAmps(int amps) { + this.amps = amps; + this.maxEnergy = NumberUtils.saturatedMultiply(this.voltage, this.amps); + gTLCore$updateMachine(); + } + + @Unique + private void gTLCore$updateEnergyContainer() { + this.energyContainer.resetBasicInfo(this.maxEnergy, this.voltage, this.amps, 0, 0); + this.energyContainer.setEnergyStored(this.maxEnergy); + } + + @Unique + private void gTLCore$updateMachine() { + gTLCore$updateEnergyContainer(); + if (getLevel() instanceof ServerLevel serverLevel) { + serverLevel.getServer().execute(() -> { + for (var c : getControllers()) { + if (c.isFormed()) { + c.getPatternLock().lock(); + try { + c.onStructureInvalid(); + var mwsd = MultiblockWorldSavedData.getOrCreate(serverLevel); + mwsd.removeMapping(c.getMultiblockState()); + mwsd.addAsyncLogic(c); + } finally { + c.getPatternLock().unlock(); + } + } + } + }); + } + } + + /** + * @author Dragons + * @reason Dont use + */ + @Overwrite(remap = false) + protected void addEnergy() {} + + /** + * @author Dragons + * @reason Dont use + */ + @Overwrite(remap = false) + protected void InfinityEnergySubscription() {} + + @Override + public void onLoad() { + super.onLoad(); + } + + @Override + public void onUnload() { + super.onUnload(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtmt/CreativeLaserHatchPartMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtmt/CreativeLaserHatchPartMachineMixin.java new file mode 100644 index 000000000..673a6d5bb --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtmt/CreativeLaserHatchPartMachineMixin.java @@ -0,0 +1,191 @@ +package org.gtlcore.gtlcore.mixin.gtmt; + +import org.gtlcore.gtlcore.integration.gtmt.InfinityLaserContainer; +import org.gtlcore.gtlcore.integration.gtmt.NewGTValues; +import org.gtlcore.gtlcore.utils.NumberUtils; + +import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.multiblock.part.TieredIOPartMachine; +import com.gregtechceu.gtceu.api.machine.trait.NotifiableLaserContainer; +import com.gregtechceu.gtceu.api.pattern.MultiblockWorldSavedData; +import com.gregtechceu.gtceu.utils.GTUtil; + +import com.lowdragmc.lowdraglib.gui.editor.ColorPattern; +import com.lowdragmc.lowdraglib.gui.modular.ModularUI; +import com.lowdragmc.lowdraglib.gui.texture.GuiTextureGroup; +import com.lowdragmc.lowdraglib.gui.texture.ResourceBorderTexture; +import com.lowdragmc.lowdraglib.gui.texture.TextTexture; +import com.lowdragmc.lowdraglib.gui.widget.ButtonWidget; +import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; +import com.lowdragmc.lowdraglib.gui.widget.SelectorWidget; +import com.lowdragmc.lowdraglib.gui.widget.TextFieldWidget; +import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.player.Player; + +import com.hepdd.gtmthings.common.block.machine.multiblock.part.CreativeLaserHatchPartMachine; +import org.apache.commons.lang3.ArrayUtils; +import org.jetbrains.annotations.NotNull; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.*; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.Arrays; + +@Mixin(CreativeLaserHatchPartMachine.class) +public abstract class CreativeLaserHatchPartMachineMixin extends TieredIOPartMachine { + + @Shadow(remap = false) + private NotifiableLaserContainer buffer; + + @Shadow(remap = false) + private long voltage; + + @Shadow(remap = false) + private int setTier; + + @Shadow(remap = false) + private int amps; + + @Persisted + @Shadow(remap = false) + private Long maxEnergy; + + public CreativeLaserHatchPartMachineMixin(IMachineBlockEntity holder, int tier, IO io) { + super(holder, tier, io); + } + + @Redirect(method = "", at = @At(value = "FIELD", target = "Lcom/hepdd/gtmthings/common/block/machine/multiblock/part/CreativeLaserHatchPartMachine;maxEnergy:Ljava/lang/Long;", opcode = org.objectweb.asm.Opcodes.PUTFIELD), remap = false) + private void redirectMaxEnergyAssignment(CreativeLaserHatchPartMachine instance, Long value) { + this.maxEnergy = NumberUtils.saturatedMultiply(this.voltage, this.amps); + } + + @Redirect(method = "", at = @At(value = "FIELD", target = "Lcom/hepdd/gtmthings/common/block/machine/multiblock/part/CreativeLaserHatchPartMachine;voltage:J", opcode = Opcodes.PUTFIELD), remap = false) + private void redirectVoltageAssignment(CreativeLaserHatchPartMachine instance, long value) { + this.setTier = 30; + this.voltage = GTValues.VEX[this.setTier]; + } + + @Redirect(method = "", at = @At(value = "INVOKE", target = "Lcom/gregtechceu/gtceu/api/machine/trait/NotifiableLaserContainer;receiverContainer(Lcom/gregtechceu/gtceu/api/machine/MetaMachine;JJJ)Lcom/gregtechceu/gtceu/api/machine/trait/NotifiableLaserContainer;"), remap = false) + private NotifiableLaserContainer redirectReceiverContainer(MetaMachine machine, long maxCapacity, long maxInputVoltage, long maxInputAmperage) { + return new InfinityLaserContainer(this, this.maxEnergy, this.voltage, this.amps, 0L, 0L); + } + + /** + * @author liansishen + * @reason Lastest GTMT + */ + @Overwrite(remap = false) + public ModularUI createUI(Player entityPlayer) { + return new ModularUI(176, 136, this, entityPlayer) + .background(GuiTextures.BACKGROUND) + .widget(new LabelWidget(7, 32, "gtceu.creative.energy.voltage")) + .widget(new TextFieldWidget(9, 47, 152, 16, () -> String.valueOf(voltage), + value -> { + gTLCore$setVoltage(Long.parseLong(value)); + setTier = GTUtil.getTierByVoltage(voltage); + }).setNumbersOnly(8192L, Long.MAX_VALUE)) + .widget(new LabelWidget(7, 74, "gtceu.creative.energy.amperage")) + .widget(new ButtonWidget(7, 87, 20, 20, + new GuiTextureGroup(ResourceBorderTexture.BUTTON_COMMON, new TextTexture("-")), + cd -> gTLCore$setAmps(--amps == -1 ? 0 : amps))) + .widget(new TextFieldWidget(31, 89, 114, 16, () -> String.valueOf(amps), + value -> gTLCore$setAmps(Integer.parseInt(value))).setNumbersOnly(1, 67108864)) + .widget(new ButtonWidget(149, 87, 20, 20, + new GuiTextureGroup(ResourceBorderTexture.BUTTON_COMMON, new TextTexture("+")), + cd -> { + if (amps < Integer.MAX_VALUE) { + gTLCore$setAmps(++amps); + } + })) + + .widget(new SelectorWidget(7, 7, 30, 20, Arrays.stream(NewGTValues.LASER_VNF).toList(), -1) + .setOnChanged(tier -> { + setTier = ArrayUtils.indexOf(NewGTValues.LASER_VNF, tier) + 5; + gTLCore$setVoltage(GTValues.VEX[setTier]); + }) + .setSupplier(() -> NewGTValues.LASER_VNF[setTier - 5]) + .setButtonBackground(ResourceBorderTexture.BUTTON_COMMON) + .setBackground(ColorPattern.BLACK.rectTexture()) + .setValue(NewGTValues.LASER_VNF[setTier - 5])); + } + + @Override + public void loadCustomPersistedData(@NotNull CompoundTag tag) { + super.loadCustomPersistedData(tag); + gTLCore$updateEnergyContainer(); + } + + @Unique + private void gTLCore$setVoltage(long voltage) { + this.voltage = voltage; + this.maxEnergy = NumberUtils.saturatedMultiply(this.voltage, this.amps); + gTLCore$updateMachine(); + } + + @Unique + private void gTLCore$setAmps(int amps) { + this.amps = amps; + this.maxEnergy = NumberUtils.saturatedMultiply(this.voltage, this.amps); + gTLCore$updateMachine(); + } + + @Unique + private void gTLCore$updateEnergyContainer() { + this.buffer.resetBasicInfo(this.maxEnergy, this.voltage, this.amps, 0, 0); + this.buffer.setEnergyStored(this.maxEnergy); + } + + @Unique + private void gTLCore$updateMachine() { + gTLCore$updateEnergyContainer(); + if (getLevel() instanceof ServerLevel serverLevel) { + serverLevel.getServer().execute(() -> { + for (var c : getControllers()) { + if (c.isFormed()) { + c.getPatternLock().lock(); + try { + c.onStructureInvalid(); + var mwsd = MultiblockWorldSavedData.getOrCreate(serverLevel); + mwsd.removeMapping(c.getMultiblockState()); + mwsd.addAsyncLogic(c); + } finally { + c.getPatternLock().unlock(); + } + } + } + }); + } + } + + /** + * @author Dragons + * @reason Dont use + */ + @Overwrite(remap = false) + protected void AddEngerySubscription() {} + + /** + * @author Dragons + * @reason Dont use + */ + @Overwrite(remap = false) + protected void addEng() {} + + @Override + public void onLoad() { + super.onLoad(); + } + + @Override + public void onUnload() { + super.onUnload(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtmt/GTMTRecipeMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtmt/GTMTRecipeMixin.java new file mode 100644 index 000000000..c73e433e7 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtmt/GTMTRecipeMixin.java @@ -0,0 +1,123 @@ +package org.gtlcore.gtlcore.mixin.gtmt; + +import org.gtlcore.gtlcore.common.data.GTLMachines; + +import com.gregtechceu.gtceu.api.GTCEuAPI; +import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.api.data.chemical.material.stack.UnificationEntry; +import com.gregtechceu.gtceu.api.data.tag.TagPrefix; +import com.gregtechceu.gtceu.api.item.ComponentItem; +import com.gregtechceu.gtceu.api.machine.multiblock.CleanroomType; +import com.gregtechceu.gtceu.common.data.*; +import com.gregtechceu.gtceu.common.data.machines.GTResearchMachines; +import com.gregtechceu.gtceu.data.recipe.CustomTags; +import com.gregtechceu.gtceu.data.recipe.VanillaRecipeHelper; + +import net.minecraft.data.recipes.FinishedRecipe; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; + +import com.hepdd.gtmthings.GTMThings; +import com.hepdd.gtmthings.data.CustomItems; +import com.hepdd.gtmthings.data.CustomMachines; +import com.hepdd.gtmthings.data.GTMTRecipe; +import com.hepdd.gtmthings.data.WirelessMachines; +import com.tterrag.registrate.util.entry.ItemEntry; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +import java.util.List; +import java.util.function.Consumer; + +import static com.gregtechceu.gtceu.common.data.GTRecipeTypes.ASSEMBLER_RECIPES; + +@Mixin(GTMTRecipe.class) +public class GTMTRecipeMixin { + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public static void init(Consumer provider) { + ASSEMBLER_RECIPES.recipeBuilder("cover_maintenance_detector").inputItems(GTItems.EMITTER_LV).inputItems(TagPrefix.plate, GTMaterials.Steel).circuitMeta(1).inputFluids(GTMaterials.SolderingAlloy.getFluid(72L)).outputItems(GTItems.COVER_MAINTENANCE_DETECTOR).EUt(16L).duration(100).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_monitor")).inputItems(GTMachines.HULL[1].asStack()).inputItems(GTItems.COVER_SCREEN.asStack()).inputItems(Items.ENDER_PEARL, 16).inputItems(GTItems.TERMINAL.asStack()).inputItems(CustomTags.LV_CIRCUITS, 4).inputItems(TagPrefix.foil, GTMaterials.Steel, 16).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(WirelessMachines.WIRELESS_ENERGY_MONITOR.asStack()).duration(400).EUt(GTValues.VA[1]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_interface")).inputItems(GTMachines.ENERGY_INPUT_HATCH[1].asStack()).inputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_LV.asStack()).inputItems(Items.ENDER_PEARL, 16).inputItems(CustomTags.LV_CIRCUITS, 4).inputItems(TagPrefix.spring, GTMaterials.Iron, 4).inputItems(TagPrefix.foil, GTMaterials.Steel, 16).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(WirelessMachines.WIRELESS_ENERGY_INTERFACE.asStack()).duration(400).EUt(GTValues.VA[1]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_item_transfer_cover")).inputItems(GTItems.SENSOR_LV.asStack()).inputItems(GTItems.EMITTER_LV.asStack()).inputItems(GTItems.ROBOT_ARM_LV.asStack()).inputItems(CustomTags.LV_CIRCUITS, 2).inputItems(TagPrefix.plate, GTMaterials.EnderPearl, 2).inputItems(TagPrefix.plateDouble, GTMaterials.Steel, 2).inputFluids(GTMaterials.Polyethylene.getFluid(288L)).outputItems(CustomItems.WIRELESS_ITEM_TRANSFER_COVER.asStack()).duration(200).EUt(GTValues.VA[1]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_fluid_transfer_cover")).inputItems(GTItems.SENSOR_LV.asStack()).inputItems(GTItems.EMITTER_LV.asStack()).inputItems(GTItems.FLUID_REGULATOR_LV.asStack()).inputItems(CustomTags.LV_CIRCUITS, 2).inputItems(TagPrefix.plate, GTMaterials.EnderPearl, 2).inputItems(TagPrefix.plateDouble, GTMaterials.Steel, 2).inputFluids(GTMaterials.Polyethylene.getFluid(288L)).outputItems(CustomItems.WIRELESS_FLUID_TRANSFER_COVER.asStack()).duration(200).EUt(GTValues.VA[1]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_receive_cover_lv")).inputItems(GTItems.SENSOR_LV.asStack()).inputItems(GTItems.EMITTER_LV.asStack()).inputItems(TagPrefix.plate, GTMaterials.EnderPearl, 4).inputItems(CustomTags.LV_CIRCUITS, 2).inputItems(GTItems.VOLTAGE_COIL_LV.asStack()).inputItems(TagPrefix.spring, GTMaterials.Tin, 1).inputItems(TagPrefix.cableGtSingle, GTMaterials.Tin, 2).inputItems(TagPrefix.cableGtSingle, GTMaterials.RedAlloy, 2).inputItems(TagPrefix.plate, GTMaterials.Steel, 4).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_LV.asStack()).duration(200).EUt(GTValues.VA[1]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_receive_cover_lv_4a")).inputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_LV.asStack(2)).inputItems(GTItems.INDUCTOR.asStack(4)).inputItems(TagPrefix.cableGtQuadruple, GTMaterials.Tin, 4).inputItems(GTItems.VOLTAGE_COIL_LV.asStack(2)).inputItems(TagPrefix.plateDouble, GTMaterials.BatteryAlloy, 2).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_LV_4A.asStack()).duration(200).EUt(GTValues.VA[1]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_receive_cover_mv")).inputItems(GTItems.SENSOR_MV.asStack()).inputItems(GTItems.EMITTER_MV.asStack()).inputItems(TagPrefix.plate, GTMaterials.EnderPearl, 4).inputItems(CustomTags.MV_CIRCUITS, 2).inputItems(GTItems.VOLTAGE_COIL_MV.asStack()).inputItems(GTItems.ULTRA_LOW_POWER_INTEGRATED_CIRCUIT.asStack()).inputItems(TagPrefix.cableGtSingle, GTMaterials.Copper, 2).inputItems(TagPrefix.cableGtSingle, GTMaterials.RedAlloy, 2).inputItems(TagPrefix.plate, GTMaterials.Aluminium, 4).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_MV.asStack()).duration(200).EUt(GTValues.VA[2]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_receive_cover_mv_4a")).inputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_MV.asStack(2)).inputItems(GTItems.INDUCTOR.asStack(8)).inputItems(TagPrefix.cableGtQuadruple, GTMaterials.Copper, 4).inputItems(GTItems.VOLTAGE_COIL_MV.asStack(2)).inputItems(TagPrefix.plateDouble, GTMaterials.BatteryAlloy, 2).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_MV_4A.asStack()).duration(200).EUt(GTValues.VA[2]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_receive_cover_hv")).inputItems(GTItems.SENSOR_HV.asStack()).inputItems(GTItems.EMITTER_HV.asStack()).inputItems(TagPrefix.plate, GTMaterials.EnderPearl, 4).inputItems(CustomTags.HV_CIRCUITS, 2).inputItems(GTItems.VOLTAGE_COIL_HV.asStack()).inputItems(GTItems.LOW_POWER_INTEGRATED_CIRCUIT.asStack()).inputItems(TagPrefix.cableGtSingle, GTMaterials.Gold, 2).inputItems(TagPrefix.cableGtSingle, GTMaterials.RedAlloy, 2).inputItems(TagPrefix.plate, GTMaterials.StainlessSteel, 4).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_HV.asStack()).duration(200).EUt(GTValues.VA[3]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_receive_cover_hv_4a")).inputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_HV.asStack(2)).inputItems(GTItems.SMD_INDUCTOR.asStack(4)).inputItems(TagPrefix.cableGtQuadruple, GTMaterials.Gold, 4).inputItems(GTItems.VOLTAGE_COIL_HV.asStack(2)).inputItems(TagPrefix.plateDouble, GTMaterials.BatteryAlloy, 2).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_HV_4A.asStack()).duration(200).EUt(GTValues.VA[3]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_receive_cover_ev")).inputItems(GTItems.SENSOR_EV.asStack()).inputItems(GTItems.EMITTER_EV.asStack()).inputItems(TagPrefix.plate, GTMaterials.EnderPearl, 4).inputItems(CustomTags.EV_CIRCUITS, 2).inputItems(GTItems.VOLTAGE_COIL_EV.asStack()).inputItems(GTItems.POWER_INTEGRATED_CIRCUIT.asStack()).inputItems(TagPrefix.cableGtSingle, GTMaterials.Aluminium, 2).inputItems(TagPrefix.cableGtSingle, GTMaterials.RedAlloy, 2).inputItems(TagPrefix.plate, GTMaterials.Titanium, 4).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_EV.asStack()).duration(200).EUt(GTValues.VA[4]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_receive_cover_ev_4a")).inputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_EV.asStack(2)).inputItems(GTItems.SMD_INDUCTOR.asStack(8)).inputItems(TagPrefix.cableGtQuadruple, GTMaterials.Aluminium, 4).inputItems(GTItems.VOLTAGE_COIL_EV.asStack(2)).inputItems(TagPrefix.plateDouble, GTMaterials.BatteryAlloy, 2).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_EV_4A.asStack()).duration(200).EUt(GTValues.VA[4]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_receive_cover_iv")).inputItems(GTItems.SENSOR_IV.asStack()).inputItems(GTItems.EMITTER_IV.asStack()).inputItems(TagPrefix.plate, GTMaterials.EnderPearl, 4).inputItems(CustomTags.IV_CIRCUITS, 2).inputItems(GTItems.VOLTAGE_COIL_IV.asStack()).inputItems(GTItems.HIGH_POWER_INTEGRATED_CIRCUIT.asStack()).inputItems(TagPrefix.cableGtSingle, GTMaterials.Platinum, 2).inputItems(TagPrefix.cableGtSingle, GTMaterials.RedAlloy, 2).inputItems(TagPrefix.plate, GTMaterials.TungstenSteel, 4).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_IV.asStack()).duration(200).EUt(GTValues.VA[5]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_receive_cover_iv_4a")).inputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_IV.asStack(2)).inputItems(GTItems.ADVANCED_SMD_INDUCTOR.asStack(4)).inputItems(TagPrefix.cableGtQuadruple, GTMaterials.Platinum, 4).inputItems(GTItems.VOLTAGE_COIL_IV.asStack(2)).inputItems(TagPrefix.plateDouble, GTMaterials.BatteryAlloy, 2).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_IV_4A.asStack()).duration(200).EUt(GTValues.VA[5]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_receive_cover_luv")).inputItems(GTItems.SENSOR_LuV.asStack()).inputItems(GTItems.EMITTER_LuV.asStack()).inputItems(TagPrefix.plate, GTMaterials.EnderPearl, 4).inputItems(CustomTags.LuV_CIRCUITS, 2).inputItems(GTItems.VOLTAGE_COIL_LuV.asStack()).inputItems(GTItems.HIGH_POWER_INTEGRATED_CIRCUIT.asStack(2)).inputItems(TagPrefix.cableGtSingle, GTMaterials.NiobiumTitanium, 2).inputItems(TagPrefix.cableGtSingle, GTMaterials.RedAlloy, 2).inputItems(TagPrefix.plate, GTMaterials.RhodiumPlatedPalladium, 4).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_LUV.asStack()).duration(200).EUt(GTValues.VA[6]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_receive_cover_luv_4a")).inputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_LUV.asStack(2)).inputItems(GTItems.ADVANCED_SMD_INDUCTOR.asStack(8)).inputItems(TagPrefix.cableGtQuadruple, GTMaterials.NiobiumTitanium, 4).inputItems(GTItems.VOLTAGE_COIL_LuV.asStack(2)).inputItems(TagPrefix.plateDouble, GTMaterials.BatteryAlloy, 2).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_LUV_4A.asStack()).duration(200).EUt(GTValues.VA[6]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_receive_cover_zpm")).inputItems(GTItems.SENSOR_ZPM.asStack()).inputItems(GTItems.EMITTER_ZPM.asStack()).inputItems(TagPrefix.plate, GTMaterials.EnderPearl, 4).inputItems(CustomTags.ZPM_CIRCUITS, 2).inputItems(GTItems.VOLTAGE_COIL_ZPM.asStack()).inputItems(GTItems.ULTRA_HIGH_POWER_INTEGRATED_CIRCUIT.asStack()).inputItems(TagPrefix.cableGtSingle, GTMaterials.VanadiumGallium, 2).inputItems(TagPrefix.cableGtSingle, GTMaterials.RedAlloy, 2).inputItems(TagPrefix.plate, GTMaterials.NaquadahAlloy, 4).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_ZPM.asStack()).duration(200).EUt(GTValues.VA[7]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_receive_cover_zpm_4a")).inputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_ZPM.asStack(2)).inputItems(GTItems.ADVANCED_SMD_INDUCTOR.asStack(16)).inputItems(TagPrefix.cableGtQuadruple, GTMaterials.VanadiumGallium, 4).inputItems(GTItems.VOLTAGE_COIL_ZPM.asStack(2)).inputItems(TagPrefix.plateDouble, GTMaterials.BatteryAlloy, 2).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_ZPM_4A.asStack()).duration(200).EUt(GTValues.VA[7]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_receive_cover_uv")).inputItems(GTItems.SENSOR_UV.asStack()).inputItems(GTItems.EMITTER_UV.asStack()).inputItems(TagPrefix.plate, GTMaterials.EnderPearl, 4).inputItems(CustomTags.UV_CIRCUITS, 2).inputItems(GTItems.VOLTAGE_COIL_UV.asStack()).inputItems(GTItems.ULTRA_HIGH_POWER_INTEGRATED_CIRCUIT.asStack(2)).inputItems(TagPrefix.cableGtSingle, GTMaterials.YttriumBariumCuprate, 2).inputItems(TagPrefix.cableGtSingle, GTMaterials.RedAlloy, 2).inputItems(TagPrefix.plate, GTMaterials.Darmstadtium, 4).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_UV.asStack()).duration(200).EUt(GTValues.VA[8]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_receive_cover_uv_4a")).inputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_UV.asStack(2)).inputItems(GTItems.ADVANCED_SMD_INDUCTOR.asStack(32)).inputItems(TagPrefix.cableGtQuadruple, GTMaterials.YttriumBariumCuprate, 4).inputItems(GTItems.VOLTAGE_COIL_UV.asStack(2)).inputItems(TagPrefix.plateDouble, GTMaterials.BatteryAlloy, 2).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_UV_4A.asStack()).duration(200).EUt(GTValues.VA[8]).save(provider); + List> WIRELESS_ENERGY_RECEIVE_COVER = List.of(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_LV, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_MV, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_HV, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_EV, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_IV, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_LUV, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_ZPM, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_UV, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_UHV, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_UEV, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_UIV, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_UXV, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_OPV); + List> WIRELESS_ENERGY_RECEIVE_COVER_4A = List.of(CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_LV_4A, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_MV_4A, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_HV_4A, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_EV_4A, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_IV_4A, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_LUV_4A, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_ZPM_4A, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_UV_4A, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_UHV_4A, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_UEV_4A, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_UIV_4A, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_UXV_4A, CustomItems.WIRELESS_ENERGY_RECEIVE_COVER_OPV_4A); + + for (int tier : GTValues.tiersBetween(1, GTCEuAPI.isHighTier() ? 13 : 9)) { + String var10001 = GTValues.VN[tier]; + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_input_hatch_" + var10001.toLowerCase())).inputItems(GTMachines.ENERGY_INPUT_HATCH[tier].asStack()).inputItems(WIRELESS_ENERGY_RECEIVE_COVER.get(tier - 1).asStack()).inputItems(GTItems.COVER_ENERGY_DETECTOR_ADVANCED.asStack()).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(WirelessMachines.WIRELESS_ENERGY_INPUT_HATCH[tier].asStack()).duration(200).EUt(GTValues.VA[tier]).save(provider); + var10001 = GTValues.VN[tier]; + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_output_hatch_" + var10001.toLowerCase())).inputItems(GTMachines.ENERGY_OUTPUT_HATCH[tier].asStack()).inputItems(WIRELESS_ENERGY_RECEIVE_COVER.get(tier - 1).asStack()).inputItems(GTItems.COVER_ENERGY_DETECTOR_ADVANCED.asStack()).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(WirelessMachines.WIRELESS_ENERGY_OUTPUT_HATCH[tier].asStack()).duration(200).EUt(GTValues.VA[tier]).save(provider); + } + + for (int tier : GTValues.tiersBetween(4, GTCEuAPI.isHighTier() ? 13 : 9)) { + String var24 = GTValues.VN[tier]; + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_input_hatch_" + var24.toLowerCase() + "_4a")).inputItems(GTMachines.ENERGY_INPUT_HATCH_4A[tier].asStack()).inputItems(WIRELESS_ENERGY_RECEIVE_COVER.get(tier - 1).asStack(2)).inputItems(GTItems.COVER_ENERGY_DETECTOR_ADVANCED.asStack(1)).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(WirelessMachines.WIRELESS_ENERGY_INPUT_HATCH_4A[tier].asStack()).duration(200).EUt(GTValues.VA[tier]).save(provider); + var24 = GTValues.VN[tier]; + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_input_hatch_" + var24.toLowerCase() + "_16a")).inputItems(GTMachines.ENERGY_INPUT_HATCH_16A[tier].asStack()).inputItems(WIRELESS_ENERGY_RECEIVE_COVER_4A.get(tier - 1).asStack(2)).inputItems(GTItems.COVER_ENERGY_DETECTOR_ADVANCED.asStack(1)).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(WirelessMachines.WIRELESS_ENERGY_INPUT_HATCH_16A[tier].asStack()).duration(200).EUt(GTValues.VA[tier]).save(provider); + var24 = GTValues.VN[tier]; + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_output_hatch_" + var24.toLowerCase() + "_4a")).inputItems(GTMachines.ENERGY_OUTPUT_HATCH_4A[tier].asStack()).inputItems(WIRELESS_ENERGY_RECEIVE_COVER.get(tier - 1).asStack(2)).inputItems(GTItems.COVER_ENERGY_DETECTOR_ADVANCED.asStack(1)).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(WirelessMachines.WIRELESS_ENERGY_OUTPUT_HATCH_4A[tier].asStack()).duration(200).EUt(GTValues.VA[tier]).save(provider); + var24 = GTValues.VN[tier]; + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_output_hatch_" + var24.toLowerCase() + "_16a")).inputItems(GTMachines.ENERGY_OUTPUT_HATCH_16A[tier].asStack()).inputItems(WIRELESS_ENERGY_RECEIVE_COVER_4A.get(tier - 1).asStack(2)).inputItems(GTItems.COVER_ENERGY_DETECTOR_ADVANCED.asStack(1)).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(WirelessMachines.WIRELESS_ENERGY_OUTPUT_HATCH_16A[tier].asStack()).duration(200).EUt(GTValues.VA[tier]).save(provider); + } + + for (int tier : GTValues.tiersBetween(5, GTCEuAPI.isHighTier() ? 13 : 9)) { + String var28 = GTValues.VN[tier]; + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_input_hatch_" + var28.toLowerCase() + "_256a")).inputItems(GTMachines.LASER_INPUT_HATCH_256[tier].asStack()).inputItems(WIRELESS_ENERGY_RECEIVE_COVER_4A.get(tier - 1).asStack(4)).inputItems(GTMachines.ACTIVE_TRANSFORMER.asStack()).inputItems(GTBlocks.SUPERCONDUCTING_COIL.asStack()).inputItems(GTItems.COVER_ENERGY_DETECTOR_ADVANCED.asStack(1)).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(WirelessMachines.WIRELESS_ENERGY_INPUT_HATCH_256A[tier].asStack()).duration(200).EUt(GTValues.VA[tier]).save(provider); + var28 = GTValues.VN[tier]; + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_input_hatch_" + var28.toLowerCase() + "_1024a")).inputItems(GTMachines.LASER_INPUT_HATCH_1024[tier].asStack()).inputItems(WIRELESS_ENERGY_RECEIVE_COVER_4A.get(tier - 1).asStack(8)).inputItems(GTMachines.ACTIVE_TRANSFORMER.asStack()).inputItems(GTBlocks.SUPERCONDUCTING_COIL.asStack()).inputItems(GTItems.COVER_ENERGY_DETECTOR_ADVANCED.asStack(1)).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(WirelessMachines.WIRELESS_ENERGY_INPUT_HATCH_1024A[tier].asStack()).duration(200).EUt(GTValues.VA[tier]).save(provider); + var28 = GTValues.VN[tier]; + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_input_hatch_" + var28.toLowerCase() + "_4096a")).inputItems(GTMachines.LASER_INPUT_HATCH_4096[tier].asStack()).inputItems(WIRELESS_ENERGY_RECEIVE_COVER_4A.get(tier - 1).asStack(16)).inputItems(GTMachines.ACTIVE_TRANSFORMER.asStack()).inputItems(GTBlocks.SUPERCONDUCTING_COIL.asStack()).inputItems(GTItems.COVER_ENERGY_DETECTOR_ADVANCED.asStack(1)).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(WirelessMachines.WIRELESS_ENERGY_INPUT_HATCH_4096A[tier].asStack()).duration(200).EUt(GTValues.VA[tier]).save(provider); + var28 = GTValues.VN[tier]; + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_output_hatch_" + var28.toLowerCase() + "_256a")).inputItems(GTMachines.LASER_OUTPUT_HATCH_256[tier].asStack()).inputItems(WIRELESS_ENERGY_RECEIVE_COVER_4A.get(tier - 1).asStack(4)).inputItems(GTMachines.ACTIVE_TRANSFORMER.asStack()).inputItems(GTBlocks.SUPERCONDUCTING_COIL.asStack()).inputItems(GTItems.COVER_ENERGY_DETECTOR_ADVANCED.asStack(1)).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(WirelessMachines.WIRELESS_ENERGY_OUTPUT_HATCH_256A[tier].asStack()).duration(200).EUt(GTValues.VA[tier]).save(provider); + var28 = GTValues.VN[tier]; + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_output_hatch_" + var28.toLowerCase() + "_1024a")).inputItems(GTMachines.LASER_OUTPUT_HATCH_1024[tier].asStack()).inputItems(WIRELESS_ENERGY_RECEIVE_COVER_4A.get(tier - 1).asStack(8)).inputItems(GTMachines.ACTIVE_TRANSFORMER.asStack()).inputItems(GTBlocks.SUPERCONDUCTING_COIL.asStack()).inputItems(GTItems.COVER_ENERGY_DETECTOR_ADVANCED.asStack(1)).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(WirelessMachines.WIRELESS_ENERGY_OUTPUT_HATCH_1024A[tier].asStack()).duration(200).EUt(GTValues.VA[tier]).save(provider); + var28 = GTValues.VN[tier]; + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_energy_output_hatch_" + var28.toLowerCase() + "_4096a")).inputItems(GTMachines.LASER_OUTPUT_HATCH_4096[tier].asStack()).inputItems(WIRELESS_ENERGY_RECEIVE_COVER_4A.get(tier - 1).asStack(16)).inputItems(GTMachines.ACTIVE_TRANSFORMER.asStack()).inputItems(GTBlocks.SUPERCONDUCTING_COIL.asStack()).inputItems(GTItems.COVER_ENERGY_DETECTOR_ADVANCED.asStack(1)).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(WirelessMachines.WIRELESS_ENERGY_OUTPUT_HATCH_4096A[tier].asStack()).duration(200).EUt(GTValues.VA[tier]).save(provider); + } + + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("lv_digital_miner")).inputItems(GTMachines.MINER[1].asStack()).inputItems(GTItems.CONVEYOR_MODULE_LV.asStack(2)).inputItems(GTItems.ROBOT_ARM_LV.asStack(2)).inputItems(GTItems.EMITTER_LV.asStack(1)).inputItems(GTItems.SENSOR_LV.asStack(1)).inputItems(CustomTags.MV_CIRCUITS, 2).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomMachines.DIGITAL_MINER[1].asStack()).duration(200).EUt(GTValues.VA[1]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("mv_digital_miner")).inputItems(GTMachines.MINER[2].asStack()).inputItems(GTItems.CONVEYOR_MODULE_MV.asStack(2)).inputItems(GTItems.ROBOT_ARM_MV.asStack(2)).inputItems(GTItems.EMITTER_MV.asStack(1)).inputItems(GTItems.SENSOR_MV.asStack(1)).inputItems(CustomTags.HV_CIRCUITS, 2).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomMachines.DIGITAL_MINER[2].asStack()).duration(200).EUt(GTValues.VA[2]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("hv_digital_miner")).inputItems(GTMachines.MINER[3].asStack()).inputItems(GTItems.CONVEYOR_MODULE_HV.asStack(2)).inputItems(GTItems.ROBOT_ARM_HV.asStack(2)).inputItems(GTItems.EMITTER_HV.asStack(1)).inputItems(GTItems.SENSOR_HV.asStack(1)).inputItems(CustomTags.EV_CIRCUITS, 2).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomMachines.DIGITAL_MINER[3].asStack()).duration(200).EUt(GTValues.VA[3]).save(provider); + + for (int tier : GTValues.tiersBetween(1, GTCEuAPI.isHighTier() ? 13 : 9)) { + String var34 = GTValues.VN[tier]; + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("huge_item_import_bus_" + var34.toLowerCase())).inputItems(GTMachines.ITEM_IMPORT_BUS[tier].asStack()).inputItems(tier > 4 ? GTMachines.QUANTUM_CHEST[tier] : GTMachines.SUPER_CHEST[tier]).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomMachines.HUGE_ITEM_IMPORT_BUS[tier].asStack()).duration(200).EUt(GTValues.VA[tier]).save(provider); + var34 = GTValues.VN[tier]; + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("huge_item_export_bus_" + var34.toLowerCase())).inputItems(GTMachines.ITEM_EXPORT_BUS[tier].asStack()).inputItems(tier > 4 ? GTMachines.QUANTUM_CHEST[tier] : GTMachines.SUPER_CHEST[tier]).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomMachines.HUGE_ITEM_EXPORT_BUS[tier].asStack()).duration(200).EUt(GTValues.VA[tier]).save(provider); + } + + for (int tier : GTValues.tiersBetween(1, GTCEuAPI.isHighTier() ? 13 : 9)) { + String var36 = GTValues.VN[tier]; + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("huge_dual_hatch_" + var36.toLowerCase())).inputItems(CustomMachines.HUGE_ITEM_IMPORT_BUS[tier].asStack()).inputItems(tier > 4 ? GTMachines.QUANTUM_TANK[tier] : GTMachines.SUPER_TANK[tier]).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomMachines.HUGE_INPUT_DUAL_HATCH[tier].asStack()).duration(200).EUt(GTValues.VA[tier]).save(provider); + } + + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("me_export_buffer")).inputItems(GTLMachines.GTAEMachines.ITEM_EXPORT_BUS_ME.asStack()).inputItems(GTLMachines.GTAEMachines.FLUID_EXPORT_HATCH_ME.asStack()).inputFluids(GTMaterials.SolderingAlloy.getFluid(144L)).outputItems(CustomMachines.ME_EXPORT_BUFFER.asStack()).duration(400).EUt(GTValues.VA[3]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_computation_transmitter_hatch")).inputItems(GTResearchMachines.COMPUTATION_HATCH_TRANSMITTER.asStack()).inputItems(CustomTags.ZPM_CIRCUITS).inputItems(GTItems.SENSOR_ZPM).inputFluids(GTMaterials.Polybenzimidazole.getFluid(288L)).outputItems(WirelessMachines.WIRELESS_COMPUTATION_HATCH_TRANSMITTER).cleanroom(CleanroomType.CLEANROOM).duration(200).EUt(GTValues.VA[7]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("wireless_computation_receiver_hatch")).inputItems(GTResearchMachines.COMPUTATION_HATCH_TRANSMITTER.asStack()).inputItems(CustomTags.ZPM_CIRCUITS).inputItems(GTItems.EMITTER_ZPM).inputFluids(GTMaterials.Polybenzimidazole.getFluid(288L)).outputItems(WirelessMachines.WIRELESS_COMPUTATION_HATCH_RECEIVER).cleanroom(CleanroomType.CLEANROOM).duration(200).EUt(GTValues.VA[7]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("advanced_wireless_item_transfer_cover")).inputItems(CustomItems.WIRELESS_ITEM_TRANSFER_COVER.asStack()).inputItems(CustomTags.MV_CIRCUITS).outputItems(CustomItems.ADVANCED_WIRELESS_ITEM_TRANSFER_COVER).inputFluids(GTMaterials.SolderingAlloy.getFluid(72L)).duration(100).EUt(GTValues.VA[1]).save(provider); + ASSEMBLER_RECIPES.recipeBuilder(GTMThings.id("advanced_wireless_fluid_transfer_cover")).inputItems(CustomItems.WIRELESS_FLUID_TRANSFER_COVER.asStack()).inputItems(CustomTags.MV_CIRCUITS).outputItems(CustomItems.ADVANCED_WIRELESS_FLUID_TRANSFER_COVER).inputFluids(GTMaterials.SolderingAlloy.getFluid(72L)).duration(100).EUt(GTValues.VA[1]).save(provider); + VanillaRecipeHelper.addShapedRecipe(provider, true, "advanced_terminal", CustomItems.ADVANCED_TERMINAL.asStack(), "SGS", "PBP", "PWP", 'S', new UnificationEntry(TagPrefix.screw, GTMaterials.Steel), 'G', net.minecraftforge.common.Tags.Items.GLASS_PANES, 'B', new ItemStack(Items.BOOK), 'P', new UnificationEntry(TagPrefix.plate, GTMaterials.Steel), 'W', new UnificationEntry(TagPrefix.wireGtSingle, GTMaterials.Tin)); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtmt/HugeBusPartMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtmt/HugeBusPartMachineMixin.java index 9ee06a750..9b67072d0 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/gtmt/HugeBusPartMachineMixin.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtmt/HugeBusPartMachineMixin.java @@ -1,16 +1,36 @@ package org.gtlcore.gtlcore.mixin.gtmt; +import org.gtlcore.gtlcore.api.machine.trait.IRecipeCapabilityMachine; + +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.multiblock.part.TieredIOPartMachine; + import com.hepdd.gtmthings.common.block.machine.multiblock.part.HugeBusPartMachine; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(HugeBusPartMachine.class) -public class HugeBusPartMachineMixin { +public class HugeBusPartMachineMixin extends TieredIOPartMachine { + + public HugeBusPartMachineMixin(IMachineBlockEntity holder, int tier, IO io) { + super(holder, tier, io); + } @Inject(method = "getInventorySize", at = @At("RETURN"), remap = false, cancellable = true) protected void getInventorySize(CallbackInfoReturnable cir) { cir.setReturnValue(cir.getReturnValue() - 1); } + + @Inject(method = "setDistinct", at = @At("RETURN"), remap = false) + public void setDistinct(boolean isDistinct, CallbackInfo ci) { + for (var controller : this.getControllers()) { + if (controller instanceof IRecipeCapabilityMachine machine && !machine.isDistinct()) { + machine.upDate(); + } + } + } } diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtmt/MEOutputPartMachineMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtmt/MEOutputPartMachineMixin.java new file mode 100644 index 000000000..cfe6e1509 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtmt/MEOutputPartMachineMixin.java @@ -0,0 +1,152 @@ +package org.gtlcore.gtlcore.mixin.gtmt; + +import org.gtlcore.gtlcore.api.machine.trait.MEPart.IMEOutputPart; + +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.gui.fancy.ConfiguratorPanel; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.trait.NotifiableFluidTank; +import com.gregtechceu.gtceu.api.machine.trait.NotifiableItemStackHandler; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; +import com.gregtechceu.gtceu.common.machine.multiblock.part.DualHatchPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; +import com.gregtechceu.gtceu.integration.ae2.utils.KeyStorage; + +import com.lowdragmc.lowdraglib.misc.FluidStorage; +import com.lowdragmc.lowdraglib.misc.ItemStackTransfer; + +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.crafting.Ingredient; + +import appeng.api.networking.IGrid; +import appeng.api.networking.IManagedGridNode; +import appeng.api.networking.security.IActionSource; +import com.hepdd.gtmthings.common.block.machine.multiblock.part.appeng.MEOutputPartMachine; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.*; + +import java.util.EnumSet; +import java.util.List; +import java.util.function.Function; + +@SuppressWarnings("all") +@Mixin(MEOutputPartMachine.class) +public abstract class MEOutputPartMachineMixin extends DualHatchPartMachine implements IMEOutputPart, IGridConnectedMachine { + + @Unique + private byte gTLCore$time; + + @Shadow(remap = false) + private KeyStorage internalBuffer; + @Shadow(remap = false) + private KeyStorage internalTankBuffer; + @Mutable + @Final + @Shadow(remap = false) + protected final IActionSource actionSource; + + @Shadow(remap = false) + public abstract IManagedGridNode getMainNode(); + + public MEOutputPartMachineMixin(IMachineBlockEntity holder, int tier, IO io, IActionSource actionSource, Object... args) { + super(holder, tier, io, args); + this.actionSource = actionSource; + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + protected void autoIO() { + if (this.isReturn()) { + this.gTLCore$time++; + if (this.updateMEStatus()) { + IGrid grid = this.getMainNode().getGrid(); + if (grid != null) { + if (!this.internalBuffer.isEmpty()) { + this.internalBuffer.insertInventory(grid.getStorageService().getInventory(), this.actionSource); + } + if (!this.internalTankBuffer.isEmpty()) { + this.internalTankBuffer.insertInventory(grid.getStorageService().getInventory(), this.actionSource); + } + } + this.updateInventorySubscription(); + } + } else this.gTLCore$time++; + } + + @Override + public void attachConfigurators(@NotNull ConfiguratorPanel configuratorPanel) { + super.attachConfigurators(configuratorPanel); + IMEOutputPart.attachRecipeLockable(configuratorPanel, this); + } + + @Override + public void loadCustomPersistedData(@NotNull CompoundTag tag) { + super.loadCustomPersistedData(tag); + if (tag.getCompound("ForgeData").getBoolean("isAllFacing")) { + getMainNode().setExposedOnSides(EnumSet.allOf(Direction.class)); + } + } + + @Override + public void returnStorage() { + this.gTLCore$time = 0; + } + + @Override + public byte getTime() { + return gTLCore$time; + } + + @Override + public void setTime(byte time) { + gTLCore$time = time; + } + + @Mixin(targets = "com.hepdd.gtmthings.common.block.machine.multiblock.part.appeng.MEOutputPartMachine$InaccessibleInfiniteTank", remap = false) + public static class InaccessibleInfiniteTankMixin extends NotifiableFluidTank { + + @Shadow(remap = false) + FluidStorage storage; + + public InaccessibleInfiniteTankMixin(MetaMachine machine, int slots, long capacity, IO io, IO capabilityIO) { + super(machine, slots, capacity, io, capabilityIO); + } + + @Override + public List handleRecipeInner(IO io, GTRecipe recipe, List left, @Nullable String slotName, boolean simulate) { + if (io != IO.OUT) return left; + for (var fluid : left) { + if (fluid.isEmpty()) continue; + for (var f : fluid.getStacks()) { + if (f != null) this.storage.fill(f, simulate); + } + } + return null; + } + } + + @Mixin(targets = "com.hepdd.gtmthings.common.block.machine.multiblock.part.appeng.MEOutputPartMachine$InaccessibleInfiniteHandler", remap = false) + private static class InaccessibleInfiniteHandlerMixin extends NotifiableItemStackHandler { + + public InaccessibleInfiniteHandlerMixin(MetaMachine machine, int slots, @NotNull IO handlerIO, @NotNull IO capabilityIO, Function transferFactory) { + super(machine, slots, handlerIO, capabilityIO, transferFactory); + } + + @Override + public List handleRecipeInner(IO io, GTRecipe recipe, List left, @Nullable String slotName, boolean simulate) { + if (io != IO.OUT) return left; + for (var item : left) { + if (item.isEmpty()) continue; + for (var i : item.getItems()) if (i != null) storage.insertItem(0, i, simulate); + } + return null; + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtmt/trait/CatalystFluidStackHandlerMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtmt/trait/CatalystFluidStackHandlerMixin.java new file mode 100644 index 000000000..9b393b59d --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtmt/trait/CatalystFluidStackHandlerMixin.java @@ -0,0 +1,87 @@ +package org.gtlcore.gtlcore.mixin.gtmt.trait; + +import org.gtlcore.gtlcore.api.recipe.ingredient.CacheHashStrategies; + +import com.gregtechceu.gtceu.api.capability.recipe.FluidRecipeCapability; +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.trait.NotifiableFluidTank; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.content.Content; +import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; +import com.gregtechceu.gtceu.utils.FluidStackHashStrategy; + +import com.lowdragmc.lowdraglib.misc.FluidStorage; +import com.lowdragmc.lowdraglib.side.fluid.FluidStack; + +import com.hepdd.gtmthings.common.block.machine.trait.CatalystFluidStackHandler; +import it.unimi.dsi.fastutil.objects.Object2LongOpenCustomHashMap; +import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet; +import it.unimi.dsi.fastutil.objects.ObjectSet; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Unique; + +import java.util.List; + +@Mixin(CatalystFluidStackHandler.class) +public abstract class CatalystFluidStackHandlerMixin extends NotifiableFluidTank { + + @Unique + private final Object2LongOpenCustomHashMap gTLCore$fluidCatalystInventory = new Object2LongOpenCustomHashMap<>(FluidStackHashStrategy.comparingAllButAmount()); + + public CatalystFluidStackHandlerMixin(MetaMachine machine, int slots, long capacity, IO io, IO capabilityIO) { + super(machine, slots, capacity, io, capabilityIO); + } + + /** + * @author Dragons + * @reason 性能优化 + */ + @Overwrite(remap = false) + public List handleRecipeInner(IO io, GTRecipe recipe, List left, @Nullable String slotName, boolean simulate) { + if (!simulate) return left; + + ObjectSet catalysts = new ObjectOpenCustomHashSet<>(CacheHashStrategies.FluidIngredientHashStrategy.INSTANCE); + for (Content content : recipe.getInputContents(FluidRecipeCapability.CAP)) { + FluidIngredient fluidIngredient = (FluidIngredient) content.getContent(); + if (content.chance <= 0) { + for (FluidStack fluidStack : fluidIngredient.getStacks()) { + if (gTLCore$fluidCatalystInventory.getLong(fluidStack) >= fluidStack.getAmount()) catalysts.add(fluidIngredient); + } + } else { + for (FluidStack fluidStack : fluidIngredient.getStacks()) { + if (gTLCore$fluidCatalystInventory.containsKey(fluidStack)) return left; + } + } + } + + left.removeIf(catalysts::contains); + return left.isEmpty() ? null : left; + } + + @Override + public void onContentsChanged() { + super.onContentsChanged(); + if (machine.isRemote()) return; + gTLCore$rebuildMap(); + } + + @Override + public void onMachineLoad() { + super.onMachineLoad(); + if (machine.isRemote()) return; + gTLCore$rebuildMap(); + notifyListeners(); + } + + @Unique + private void gTLCore$rebuildMap() { + gTLCore$fluidCatalystInventory.clear(); + for (FluidStorage storage : this.getStorages()) { + final FluidStack fluidStack = storage.getFluid(); + if (!fluidStack.isEmpty()) gTLCore$fluidCatalystInventory.addTo(fluidStack, fluidStack.getAmount()); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/gtmt/trait/CatalystItemStackHandlerMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/gtmt/trait/CatalystItemStackHandlerMixin.java new file mode 100644 index 000000000..dd8c77c47 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/gtmt/trait/CatalystItemStackHandlerMixin.java @@ -0,0 +1,88 @@ +package org.gtlcore.gtlcore.mixin.gtmt.trait; + +import org.gtlcore.gtlcore.api.recipe.ingredient.CacheHashStrategies; + +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.trait.NotifiableItemStackHandler; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.content.Content; +import com.gregtechceu.gtceu.utils.ItemStackHashStrategy; + +import com.lowdragmc.lowdraglib.misc.ItemStackTransfer; + +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + +import com.hepdd.gtmthings.common.block.machine.trait.CatalystItemStackHandler; +import it.unimi.dsi.fastutil.objects.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Unique; + +import java.util.List; +import java.util.function.Function; + +@Mixin(CatalystItemStackHandler.class) +public abstract class CatalystItemStackHandlerMixin extends NotifiableItemStackHandler { + + @Unique + private final Object2IntOpenCustomHashMap gTLCore$itemCatalystInventory = new Object2IntOpenCustomHashMap<>(ItemStackHashStrategy.comparingAllButCount()); + + public CatalystItemStackHandlerMixin(MetaMachine machine, int slots, @NotNull IO handlerIO, @NotNull IO capabilityIO, Function transferFactory) { + super(machine, slots, handlerIO, capabilityIO, transferFactory); + } + + /** + * @author Dragons + * @reason 性能优化 + */ + @Overwrite(remap = false) + public List handleRecipeInner(IO io, GTRecipe recipe, List left, @Nullable String slotName, boolean simulate) { + if (!simulate) return left; + + ObjectSet catalysts = new ObjectOpenCustomHashSet<>(CacheHashStrategies.IngredientHashStrategy.INSTANCE); + for (Content content : recipe.getInputContents(ItemRecipeCapability.CAP)) { + Ingredient ingredient = (Ingredient) content.getContent(); + if (content.chance <= 0) { + for (ItemStack item : ingredient.getItems()) { + if (gTLCore$itemCatalystInventory.getInt(item) >= item.getCount()) catalysts.add(ingredient); + } + } else { + for (ItemStack item : ingredient.getItems()) { + if (gTLCore$itemCatalystInventory.containsKey(item)) return left; + } + } + } + + left.removeIf(catalysts::contains); + return left.isEmpty() ? null : left; + } + + @Override + public void onContentsChanged() { + super.onContentsChanged(); + if (machine.isRemote()) return; + gTLCore$rebuildMap(); + } + + @Override + public void onMachineLoad() { + super.onMachineLoad(); + if (machine.isRemote()) return; + gTLCore$rebuildMap(); + notifyListeners(); + } + + @Unique + private void gTLCore$rebuildMap() { + gTLCore$itemCatalystInventory.clear(); + for (int i = 0; i < this.storage.getSlots(); i++) { + final ItemStack itemStack = this.storage.getStackInSlot(i); + if (!itemStack.isEmpty()) gTLCore$itemCatalystInventory.addTo(itemStack, itemStack.getCount()); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ldlib/AsyncThreadDataMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ldlib/AsyncThreadDataMixin.java new file mode 100644 index 000000000..5aba90bc5 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ldlib/AsyncThreadDataMixin.java @@ -0,0 +1,20 @@ +package org.gtlcore.gtlcore.mixin.ldlib; + +import com.lowdragmc.lowdraglib.async.AsyncThreadData; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +@Mixin(AsyncThreadData.class) +public class AsyncThreadDataMixin { + + @ModifyArg(method = "createExecutorService", + at = @At(value = "INVOKE", + target = "Ljava/util/concurrent/ScheduledExecutorService;scheduleAtFixedRate(Ljava/lang/Runnable;JJLjava/util/concurrent/TimeUnit;)Ljava/util/concurrent/ScheduledFuture;"), + index = 2, + remap = false) + private long modifyDelay(long delay) { + return 500L; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ldlib/ClickDataMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/ldlib/ClickDataMixin.java new file mode 100644 index 000000000..07a58daae --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ldlib/ClickDataMixin.java @@ -0,0 +1,33 @@ +package org.gtlcore.gtlcore.mixin.ldlib; + +import org.gtlcore.gtlcore.client.gui.widget.IExtendedClickData; + +import com.lowdragmc.lowdraglib.gui.util.ClickData; + +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; + +import java.util.UUID; + +@Implements(@Interface( + prefix = "gTLCore$", + iface = IExtendedClickData.class)) +@Mixin(ClickData.class) +public abstract class ClickDataMixin { + + @Unique + private @Nullable UUID gTLCore$uuid; + + @Unique + public void gTLCore$setUUID(@Nullable UUID uuid) { + this.gTLCore$uuid = uuid; + } + + @Nullable + public UUID gTLCore$getUUID() { + return gTLCore$uuid; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/ldlib/CustomBakedModelFixed.java b/src/main/java/org/gtlcore/gtlcore/mixin/ldlib/CustomBakedModelFixed.java index c89cd9b65..e21e5ca0e 100644 --- a/src/main/java/org/gtlcore/gtlcore/mixin/ldlib/CustomBakedModelFixed.java +++ b/src/main/java/org/gtlcore/gtlcore/mixin/ldlib/CustomBakedModelFixed.java @@ -1,44 +1,130 @@ package org.gtlcore.gtlcore.mixin.ldlib; -import com.lowdragmc.lowdraglib.client.model.custommodel.Connections; -import com.lowdragmc.lowdraglib.client.model.custommodel.CustomBakedModel; +import com.lowdragmc.lowdraglib.client.bakedpipeline.Quad; +import com.lowdragmc.lowdraglib.client.bakedpipeline.Submap; +import com.lowdragmc.lowdraglib.client.model.ModelFactory; +import com.lowdragmc.lowdraglib.client.model.custommodel.*; import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.BakedModel; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.util.RandomSource; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.state.BlockState; -import com.google.common.collect.Table; -import com.llamalad7.mixinextras.sugar.Local; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.*; -import java.util.List; -import java.util.Objects; +import java.util.*; +import java.util.concurrent.*; + +import javax.annotation.Nonnull; /** * @author EasterFG on 2025/3/2 */ @Mixin(CustomBakedModel.class) -public abstract class CustomBakedModelFixed { +public abstract class CustomBakedModelFixed implements BakedModel { - @Shadow(remap = false) + @Mutable @Final - private Table> sideCache; - - @Inject(method = "getCustomQuads", at = @At(value = "INVOKE", target = "Ljava/util/Objects;requireNonNull(Ljava/lang/Object;)Ljava/lang/Object;"), remap = false, cancellable = true) - public void getCustomQuadsHook(BlockAndTintGetter level, BlockPos pos, BlockState state, Direction side, RandomSource rand, CallbackInfoReturnable> cir, @Local Connections connections) { - var cache = sideCache.get(side, connections); - if (cache == null) { - cache = sideCache.values().stream().filter(Objects::nonNull) - .findAny().orElse(List.of()); + @Shadow(remap = false) + private final BakedModel parent; + protected final ConcurrentMap>> sideCache = new ConcurrentHashMap<>(); + protected final List noSideCache = new ObjectArrayList<>(); + + protected CustomBakedModelFixed(BakedModel parent) { + this.parent = parent; + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + @Nonnull + @Deprecated + public List getQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource rand) { + return parent.getQuads(state, side, rand); + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + @Nonnull + public List getCustomQuads(BlockAndTintGetter level, BlockPos pos, @Nonnull BlockState state, @Nullable Direction side, RandomSource rand) { + var connections = Connections.checkConnections(level, pos, state, side); + if (side == null) { + if (noSideCache.isEmpty()) { + synchronized (noSideCache) { + if (noSideCache.isEmpty()) { + noSideCache.addAll(buildCustomQuads(connections, parent.getQuads(state, null, rand), 0.0f)); + } + } + } + return noSideCache; + } + return sideCache + .computeIfAbsent(side, key -> new ConcurrentHashMap<>()) + .computeIfAbsent(connections, key -> buildCustomQuads(key, parent.getQuads(state, side, rand), 0.0f)); + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public static List reBakeCustomQuads(List quads, BlockAndTintGetter level, BlockPos pos, @Nonnull BlockState state, @Nullable Direction side, float offset) { + return buildCustomQuads(Connections.checkConnections(level, pos, state, side), quads, offset); + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + public static List buildCustomQuads(Connections connections, List base, float offset) { + List result = new LinkedList<>(); + for (BakedQuad bakedQuad : base) { + var section = LDLMetadataSection.getMetadata(bakedQuad.getSprite()); + TextureAtlasSprite connection = section.connection == null ? null : ModelFactory.getBlockSprite(section.connection); + if (connection == null) { + result.add(makeQuad(bakedQuad, section, offset).rebake()); + continue; + } + + Quad quad = makeQuad(bakedQuad, section, offset).derotate(); + Quad[] quads = quad.subdivide(4); + + int[] ctm = connections.getSubmapIndices(); + + for (int j = 0; j < quads.length; j++) { + Quad q = quads[j]; + if (q != null) { + int ctmid = q.getUvs().normalize().getQuadrant(); + quads[j] = q.grow().transformUVs(ctm[ctmid] > 15 ? bakedQuad.getSprite() : connection, Submap.uvs[ctm[ctmid]]); + } + } + result.addAll(Arrays.stream(quads).filter(Objects::nonNull).map(Quad::rebake).toList()); + } + return result; + } + + /** + * @author . + * @reason . + */ + @Overwrite(remap = false) + protected static Quad makeQuad(BakedQuad bq, LDLMetadataSection section, float offset) { + Quad q = Quad.from(bq, offset); + if (section.emissive) { + q = q.setLight(15, 15); } - cir.setReturnValue(cache); + return q; } } diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/mc/GuiGraphicsMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/mc/GuiGraphicsMixin.java new file mode 100644 index 000000000..21e6c9162 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/mc/GuiGraphicsMixin.java @@ -0,0 +1,36 @@ +package org.gtlcore.gtlcore.mixin.mc; + +import com.gregtechceu.gtceu.utils.FormattingUtil; + +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.world.item.ItemStack; + +import com.llamalad7.mixinextras.sugar.Local; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import static org.gtlcore.gtlcore.utils.NumberUtils.UNITS; + +@Mixin(GuiGraphics.class) +public class GuiGraphicsMixin { + + @Redirect(method = "renderItemDecorations(Lnet/minecraft/client/gui/Font;Lnet/minecraft/world/item/ItemStack;IILjava/lang/String;)V", + at = @At(value = "INVOKE", + target = "Ljava/lang/String;valueOf(I)Ljava/lang/String;")) + public String renderItemDecorations(int i, @Local(ordinal = 0, argsOnly = true) ItemStack stack) { + return core$format(stack.getCount()); + } + + @Unique + private String core$format(int number) { + double temp = number; + int unitIndex = 0; + while (temp >= 1000 && unitIndex < UNITS.length - 1) { + temp /= 1000; + unitIndex++; + } + return FormattingUtil.DECIMAL_FORMAT_0F.format(temp) + UNITS[unitIndex]; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/mc/LevelChunkMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/mc/LevelChunkMixin.java new file mode 100644 index 000000000..202a06e3d --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/mc/LevelChunkMixin.java @@ -0,0 +1,36 @@ +package org.gtlcore.gtlcore.mixin.mc; + +import org.gtlcore.gtlcore.common.util.BlockStateWatcher; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.LevelChunk; + +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(LevelChunk.class) +public abstract class LevelChunkMixin { + + @Final + @Shadow + Level level; + + @Inject(method = "setBlockState", + at = @At(value = "INVOKE", + opcode = Opcodes.GETFIELD, + target = "Lnet/minecraft/world/level/block/state/BlockState;onPlace(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Z)V")) + private void notifyBlockStateWatchers(BlockPos pos, BlockState state, boolean isMoving, + CallbackInfoReturnable cir) { + if (level instanceof ServerLevel serverLevel) { + serverLevel.getServer().execute(() -> BlockStateWatcher.notifyWatchersInternal(serverLevel, pos, state)); + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/mixin/meRequester/NumberFieldMixin.java b/src/main/java/org/gtlcore/gtlcore/mixin/meRequester/NumberFieldMixin.java new file mode 100644 index 000000000..32482a4b0 --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/mixin/meRequester/NumberFieldMixin.java @@ -0,0 +1,17 @@ +package org.gtlcore.gtlcore.mixin.meRequester; + +import com.almostreliable.merequester.client.widgets.NumberField; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.ModifyConstant; + +@Mixin(NumberField.class) +public class NumberFieldMixin { + + @ModifyConstant( + method = "", + constant = @Constant(intValue = 7)) + private int replaceMaxLength(int seven) { + return 15; + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/utils/GTLUtil.java b/src/main/java/org/gtlcore/gtlcore/utils/GTLUtil.java index c1580c01b..8be03447e 100644 --- a/src/main/java/org/gtlcore/gtlcore/utils/GTLUtil.java +++ b/src/main/java/org/gtlcore/gtlcore/utils/GTLUtil.java @@ -1,26 +1,26 @@ package org.gtlcore.gtlcore.utils; import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.recipe.*; import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.Tag; +import net.minecraft.nbt.*; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.material.Fluid; +import org.jetbrains.annotations.Nullable; + /** * @author EasterFG on 2025/3/21 */ public class GTLUtil { - @SuppressWarnings("deprecation") public static String getItemId(Item item) { return BuiltInRegistries.ITEM.getKey(item).toString(); } - @SuppressWarnings("deprecation") public static String getFluidId(Fluid fluid) { return BuiltInRegistries.FLUID.getKey(fluid).toString(); } @@ -46,4 +46,27 @@ public static ItemStack loadItemStack(CompoundTag compoundTag) { return ItemStack.EMPTY; } } + + /** + * 代码参考自gto + * @line ... + */ + + public static Tag serializeNBT(GTRecipe recipe) { + CompoundTag tag = new CompoundTag(); + tag.putString("id", recipe.id.toString()); + tag.put("recipe", GTRecipeSerializer.CODEC.encodeStart(NbtOps.INSTANCE, recipe).result().orElse(new CompoundTag())); + return tag; + } + + public static @Nullable GTRecipe deserializeNBT(Tag tag) { + if (tag instanceof CompoundTag ctag) { + var id = ResourceLocation.tryParse(ctag.getString("id")); + var recipe = GTRecipeSerializer.CODEC.parse(NbtOps.INSTANCE, ctag.get("recipe")).result().orElse(null); + if (recipe == null || id == null) return null; + recipe.setId(id); + return recipe; + } + return null; + } } diff --git a/src/main/java/org/gtlcore/gtlcore/utils/MachineIO.java b/src/main/java/org/gtlcore/gtlcore/utils/MachineIO.java index ea6ac6b6e..1f0c93b1e 100644 --- a/src/main/java/org/gtlcore/gtlcore/utils/MachineIO.java +++ b/src/main/java/org/gtlcore/gtlcore/utils/MachineIO.java @@ -1,72 +1,38 @@ package org.gtlcore.gtlcore.utils; -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.machine.multiblock.WorkableMultiblockMachine; -import com.gregtechceu.gtceu.api.recipe.GTRecipe; -import com.gregtechceu.gtceu.api.recipe.ingredient.IntCircuitIngredient; -import com.gregtechceu.gtceu.common.data.GTRecipeTypes; -import com.gregtechceu.gtceu.data.recipe.builder.GTRecipeBuilder; import com.lowdragmc.lowdraglib.side.fluid.FluidStack; import net.minecraft.world.item.ItemStack; -import net.minecraftforge.registries.ForgeRegistries; - -import java.util.Objects; public class MachineIO { public static boolean inputItem(WorkableMultiblockMachine machine, ItemStack item) { - GTRecipe recipe = new GTRecipeBuilder(item.kjs$getIdLocation(), GTRecipeTypes.DUMMY_RECIPES).inputItems(item).buildRawRecipe(); - if (recipe.matchRecipe(machine).isSuccess()) { - return recipe.handleRecipeIO(IO.IN, machine, machine.recipeLogic.getChanceCaches()); - } - return false; + return MachineUtil.inputItem(machine, item); } public static boolean outputItem(WorkableMultiblockMachine machine, ItemStack item) { - if (!item.isEmpty()) { - GTRecipe recipe = new GTRecipeBuilder(item.kjs$getIdLocation(), GTRecipeTypes.DUMMY_RECIPES).outputItems(item).buildRawRecipe(); - if (recipe.matchRecipe(machine).isSuccess()) { - return recipe.handleRecipeIO(IO.OUT, machine, machine.recipeLogic.getChanceCaches()); - } - } - return false; + return MachineUtil.outputItem(machine, item); } public static boolean notConsumableItem(WorkableMultiblockMachine machine, ItemStack item) { - return new GTRecipeBuilder(item.kjs$getIdLocation(), GTRecipeTypes.DUMMY_RECIPES).inputItems(item).buildRawRecipe().matchRecipe(machine).isSuccess(); + return MachineUtil.notConsumableItem(machine, item); } public static boolean notConsumableCircuit(WorkableMultiblockMachine machine, int configuration) { - return new GTRecipeBuilder(GTCEu.id(String.valueOf(configuration)), GTRecipeTypes.DUMMY_RECIPES).inputItems(IntCircuitIngredient.circuitInput(configuration)).buildRawRecipe() - .matchRecipe(machine).isSuccess(); + return MachineUtil.notConsumableCircuit(machine, configuration); } public static boolean inputFluid(WorkableMultiblockMachine machine, FluidStack fluid) { - GTRecipe recipe = new GTRecipeBuilder(Objects.requireNonNull(ForgeRegistries.FLUIDS.getKey(fluid.getFluid())), GTRecipeTypes.DUMMY_RECIPES).inputFluids(fluid).buildRawRecipe(); - if (recipe.matchRecipe(machine).isSuccess()) { - return recipe.handleRecipeIO(IO.IN, machine, machine.recipeLogic.getChanceCaches()); - } - return false; + return MachineUtil.inputFluid(machine, fluid); } public static boolean outputFluid(WorkableMultiblockMachine machine, FluidStack fluid) { - if (!fluid.isEmpty()) { - GTRecipe recipe = new GTRecipeBuilder(Objects.requireNonNull(ForgeRegistries.FLUIDS.getKey(fluid.getFluid())), GTRecipeTypes.DUMMY_RECIPES).outputFluids(fluid).buildRawRecipe(); - if (recipe.matchRecipe(machine).isSuccess()) { - return recipe.handleRecipeIO(IO.OUT, machine, machine.recipeLogic.getChanceCaches()); - } - } - return false; + return MachineUtil.outputFluid(machine, fluid); } public static boolean inputEU(WorkableMultiblockMachine machine, long eu) { - GTRecipe recipe = new GTRecipeBuilder(GTCEu.id(String.valueOf(eu)), GTRecipeTypes.DUMMY_RECIPES).inputEU(eu).buildRawRecipe(); - if (recipe.matchRecipe(machine).isSuccess()) { - return recipe.handleRecipeIO(IO.IN, machine, machine.recipeLogic.getChanceCaches()); - } - return false; + return MachineUtil.inputEU(machine, eu); } } diff --git a/src/main/java/org/gtlcore/gtlcore/utils/MachineUtil.java b/src/main/java/org/gtlcore/gtlcore/utils/MachineUtil.java index 42aa5dab6..bcbf46d0b 100644 --- a/src/main/java/org/gtlcore/gtlcore/utils/MachineUtil.java +++ b/src/main/java/org/gtlcore/gtlcore/utils/MachineUtil.java @@ -1,5 +1,7 @@ package org.gtlcore.gtlcore.utils; +import org.gtlcore.gtlcore.api.recipe.RecipeResult; + import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.machine.multiblock.WorkableMultiblockMachine; @@ -12,13 +14,18 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; import net.minecraftforge.registries.ForgeRegistries; import java.util.Objects; +import static org.gtlcore.gtlcore.api.recipe.RecipeRunnerHelper.*; + public class MachineUtil { + public static final BlockPos[] EMPTY_POS_ARRAY = new BlockPos[0]; + private MachineUtil() { throw new IllegalAccessError(); } @@ -36,22 +43,26 @@ public static BlockPos getOffsetPos(int a, int b, Direction facing, BlockPos pos public static boolean inputItem(WorkableMultiblockMachine machine, ItemStack item) { GTRecipe recipe = new GTRecipeBuilder(item.kjs$getIdLocation(), GTRecipeTypes.DUMMY_RECIPES).inputItems(item).buildRawRecipe(); - if (recipe.matchRecipe(machine).isSuccess()) { - return recipe.handleRecipeIO(IO.IN, machine, machine.recipeLogic.getChanceCaches()); - } + if (matchRecipeInputNocache(machine, recipe)) { + return handleRecipeInputNocache(machine, recipe); + } else RecipeResult.of(machine, RecipeResult.fail(Component.translatable("gtceu.recipe.fail.no.input.item", item.getDisplayName()))); return false; } public static boolean outputItem(WorkableMultiblockMachine machine, ItemStack item) { if (!item.isEmpty()) { GTRecipe recipe = new GTRecipeBuilder(item.kjs$getIdLocation(), GTRecipeTypes.DUMMY_RECIPES).outputItems(item).buildRawRecipe(); - if (recipe.matchRecipe(machine).isSuccess()) { - return recipe.handleRecipeIO(IO.OUT, machine, machine.recipeLogic.getChanceCaches()); + if (matchRecipeOutput(machine, recipe)) { + return handleRecipeOutput(machine, recipe); } } return false; } + public static boolean notConsumableItem(WorkableMultiblockMachine machine, ItemStack item) { + return new GTRecipeBuilder(item.kjs$getIdLocation(), GTRecipeTypes.DUMMY_RECIPES).inputItems(item).buildRawRecipe().matchRecipe(machine).isSuccess(); + } + public static boolean notConsumableCircuit(WorkableMultiblockMachine machine, int configuration) { return new GTRecipeBuilder(GTCEu.id(String.valueOf(configuration)), GTRecipeTypes.DUMMY_RECIPES).inputItems(IntCircuitIngredient.circuitInput(configuration)).buildRawRecipe() .matchRecipe(machine).isSuccess(); @@ -59,8 +70,26 @@ public static boolean notConsumableCircuit(WorkableMultiblockMachine machine, in public static boolean inputFluid(WorkableMultiblockMachine machine, FluidStack fluid) { GTRecipe recipe = new GTRecipeBuilder(Objects.requireNonNull(ForgeRegistries.FLUIDS.getKey(fluid.getFluid())), GTRecipeTypes.DUMMY_RECIPES).inputFluids(fluid).buildRawRecipe(); - if (recipe.matchRecipe(machine).isSuccess()) { - return recipe.handleRecipeIO(IO.IN, machine, machine.recipeLogic.getChanceCaches()); + if (matchRecipeInputNocache(machine, recipe)) { + return handleRecipeInputNocache(machine, recipe); + } else RecipeResult.of(machine, RecipeResult.fail(Component.translatable("gtceu.recipe.fail.no.input.fluid", fluid.getDisplayName()))); + return false; + } + + public static boolean outputFluid(WorkableMultiblockMachine machine, FluidStack fluid) { + if (!fluid.isEmpty()) { + GTRecipe recipe = new GTRecipeBuilder(Objects.requireNonNull(ForgeRegistries.FLUIDS.getKey(fluid.getFluid())), GTRecipeTypes.DUMMY_RECIPES).outputFluids(fluid).buildRawRecipe(); + if (matchRecipeOutput(machine, recipe)) { + return handleRecipeOutput(machine, recipe); + } + } + return false; + } + + public static boolean inputEU(WorkableMultiblockMachine machine, long eu) { + GTRecipe recipe = new GTRecipeBuilder(GTCEu.id(String.valueOf(eu)), GTRecipeTypes.DUMMY_RECIPES).inputEU(eu).buildRawRecipe(); + if (recipe.matchTickRecipe(machine).isSuccess()) { + return recipe.handleTickRecipeIO(IO.IN, machine, machine.recipeLogic.getChanceCaches()); } return false; } diff --git a/src/main/java/org/gtlcore/gtlcore/utils/NumberUtils.java b/src/main/java/org/gtlcore/gtlcore/utils/NumberUtils.java index 067354afe..b7e647348 100644 --- a/src/main/java/org/gtlcore/gtlcore/utils/NumberUtils.java +++ b/src/main/java/org/gtlcore/gtlcore/utils/NumberUtils.java @@ -5,11 +5,32 @@ import com.google.common.math.LongMath; +import java.math.BigInteger; import java.text.DecimalFormat; public class NumberUtils { - private static final String[] UNITS = { "", "K", "M", "G", "T", "P", "E", "B" }; + private static final double LN_095 = Math.log(0.95); + + private static final double[] TABLE = new double[128]; + + public static final String[] UNITS = { "", "K", "M", "G", "T", "P", "E", "Z", "Y", "B", "N", "D" }; + + public static final int[] NEAREST = { 1, 2, 4, 4, 4, 8, 8, 8, 8, 8, 8, 16, 16, 16, 16, 16 }; + + public static final BigInteger BIG_INTEGER_MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE); + + static { + double p = 1.0; + for (int i = 0; i < TABLE.length; i++) { + TABLE[i] = p; + p *= 0.95; + } + } + + public static double pow95(int n) { + return (n < TABLE.length) ? TABLE[n] : Math.exp(n * LN_095); + } public static String formatLong(long number) { DecimalFormat df = new DecimalFormat("#.##"); @@ -22,21 +43,83 @@ public static String formatLong(long number) { return df.format(temp) + UNITS[unitIndex]; } + public static String formatDouble(double number) { + DecimalFormat df = new DecimalFormat("#.##"); + double temp = number; + int unitIndex = 0; + while (temp >= 1000 && unitIndex < UNITS.length - 1) { + temp /= 1000; + unitIndex++; + } + return df.format(temp) + UNITS[unitIndex]; + } + + public static MutableComponent numberText(double number) { + return Component.literal(formatDouble(number)); + } + public static MutableComponent numberText(long number) { return Component.literal(formatLong(number)); } + public static long getLongValue(BigInteger bigInt) { + return bigInt.compareTo(BIG_INTEGER_MAX_LONG) > 0 ? Long.MAX_VALUE : bigInt.longValue(); + } + public static int getFakeVoltageTier(long voltage) { - long a = voltage; - int b = 0; - while (a / 4L >= 8L) { - b++; - a /= 4L; - } - return b; + return voltage < 32L ? 0 : 1 + ((Long.SIZE - 1 - Long.numberOfLeadingZeros(voltage >> 5)) >> 1); } public static long getVoltageFromFakeTier(int tier) { return LongMath.pow(4L, tier + 1) * 2; } + + public static int nearestPow2Lookup(int x) { + if (x < 1) return 1; + if (x > 16) return 16; + return NEAREST[x - 1]; + } + + // actualConsumeParallel > 1 + public static int getAdditionalTier(double durationFactor, double actualConsumeParallel) { + // 移除边界检查以提升性能 + // if (!(durationFactor > 0.0 && durationFactor < 1.0)) { + // throw new IllegalArgumentException("require 0 < durationFactor < 1"); + // } + + return (int) Math.ceil(Math.log(actualConsumeParallel) / (-Math.log(durationFactor))); + } + + // ======================================== + // Saturate + // ======================================== + + public static long saturatedAdd(long a, long b) { + long naiveSum = a + b; + if ((a ^ b) < 0 | (a ^ naiveSum) >= 0) { + // If a and b have different signs or a has the same sign as the result then there was no + // overflow, return. + return naiveSum; + } + // we did over/under flow, if the sign is negative we should return MAX otherwise MIN + return Long.MAX_VALUE + ((naiveSum >>> (Long.SIZE - 1)) ^ 1); + } + + public static long saturatedMultiply(long a, long b) { + int leadingZeros = Long.numberOfLeadingZeros(a) + Long.numberOfLeadingZeros(~a) + Long.numberOfLeadingZeros(b) + Long.numberOfLeadingZeros(~b); + if (leadingZeros > Long.SIZE + 1) { + return a * b; + } + // the return value if we will overflow (which we calculate by overflowing a long :) ) + long limit = Long.MAX_VALUE + ((a ^ b) >>> (Long.SIZE - 1)); + if (leadingZeros < Long.SIZE | (a < 0 & b == Long.MIN_VALUE)) { + // overflow + return limit; + } + long result = a * b; + if (a == 0 || result / a == b) { + return result; + } + return limit; + } } diff --git a/src/main/java/org/gtlcore/gtlcore/utils/StorageManager.java b/src/main/java/org/gtlcore/gtlcore/utils/StorageManager.java index 621fb9ca7..e47fbaf96 100755 --- a/src/main/java/org/gtlcore/gtlcore/utils/StorageManager.java +++ b/src/main/java/org/gtlcore/gtlcore/utils/StorageManager.java @@ -8,22 +8,23 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.saveddata.SavedData; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.jetbrains.annotations.NotNull; -import java.util.HashMap; import java.util.Map; import java.util.UUID; public class StorageManager extends SavedData { - private final Map disks; + private final Object2ObjectMap disks; public StorageManager() { - disks = new HashMap<>(); + disks = new Object2ObjectOpenHashMap<>(); this.setDirty(); } - private StorageManager(Map disks) { + private StorageManager(Object2ObjectMap disks) { this.disks = disks; this.setDirty(); } @@ -32,10 +33,14 @@ private StorageManager(Map disks) { public @NotNull CompoundTag save(@NotNull CompoundTag nbt) { ListTag diskList = new ListTag(); for (Map.Entry entry : disks.entrySet()) { - CompoundTag disk = new CompoundTag(); + UUID uuid = entry.getKey(); + InfinityCellDataStorage storage = entry.getValue(); + + if (uuid == null || storage == null) continue; - disk.putUUID("diskuuid", entry.getKey()); - disk.put("diskdata", entry.getValue().toNbt()); + CompoundTag disk = new CompoundTag(); + disk.putUUID("diskuuid", uuid); + disk.put("diskdata", storage.toNbt()); diskList.add(disk); } @@ -44,7 +49,7 @@ private StorageManager(Map disks) { } public static StorageManager readNbt(CompoundTag nbt) { - Map disks = new HashMap<>(); + Object2ObjectMap disks = new Object2ObjectOpenHashMap<>(); ListTag diskList = nbt.getList("disklist", CompoundTag.TAG_COMPOUND); for (int i = 0; i < diskList.size(); i++) { CompoundTag disk = diskList.getCompound(i); @@ -63,20 +68,20 @@ public void removeDisk(UUID uuid) { setDirty(); } - public InfinityCellDataStorage getOrCreateDisk(UUID uuid) { + public InfinityCellDataStorage getOrCreateDisk(UUID uuid, boolean isFastCell) { if (!disks.containsKey(uuid)) { - updateDisk(uuid, new InfinityCellDataStorage()); + updateDisk(uuid, new InfinityCellDataStorage(isFastCell)); } return disks.get(uuid); } - public void modifyDisk(UUID diskID, ListTag stackKeys, long[] stackAmounts, long itemCount) { - InfinityCellDataStorage diskToModify = getOrCreateDisk(diskID); - if (stackKeys != null && stackAmounts != null) { + public void modifyDisk(UUID diskID, ListTag stackKeys, ListTag amounts, double totalAmount, boolean isFastCell) { + InfinityCellDataStorage diskToModify = getOrCreateDisk(diskID, isFastCell); + if (stackKeys != null && amounts != null) { diskToModify.stackKeys = stackKeys; - diskToModify.stackAmounts = stackAmounts; + diskToModify.amounts = amounts; } - diskToModify.itemCount = itemCount; + diskToModify.totalAmount = totalAmount; updateDisk(diskID, diskToModify); } diff --git a/src/main/java/org/gtlcore/gtlcore/utils/TextUtil.java b/src/main/java/org/gtlcore/gtlcore/utils/TextUtil.java index 2e1ac622c..f04e2c83c 100644 --- a/src/main/java/org/gtlcore/gtlcore/utils/TextUtil.java +++ b/src/main/java/org/gtlcore/gtlcore/utils/TextUtil.java @@ -8,6 +8,24 @@ public class TextUtil { + public static final ChatFormatting[] GTL_CORE$VC = { + DARK_GRAY, + GRAY, + AQUA, + GOLD, + DARK_PURPLE, + BLUE, + LIGHT_PURPLE, + RED, + DARK_AQUA, + DARK_RED, + GREEN, + DARK_GREEN, + YELLOW, + BLUE, + RED + }; + public static String formatting(String input, ChatFormatting[] colours, double delay) { StringBuilder sb = new StringBuilder(input.length() * 3); if (delay <= 0.0D) diff --git a/src/main/java/org/gtlcore/gtlcore/utils/datastructure/FirstFlagRecipePartSet.java b/src/main/java/org/gtlcore/gtlcore/utils/datastructure/FirstFlagRecipePartSet.java new file mode 100644 index 000000000..ce27ec29f --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/utils/datastructure/FirstFlagRecipePartSet.java @@ -0,0 +1,43 @@ +package org.gtlcore.gtlcore.utils.datastructure; + +import org.gtlcore.gtlcore.api.machine.trait.IRecipeHandlePart; + +import it.unimi.dsi.fastutil.objects.ObjectIterator; +import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet; +import it.unimi.dsi.fastutil.objects.ReferenceSet; +import it.unimi.dsi.fastutil.objects.ReferenceSortedSet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class FirstFlagRecipePartSet { + + private final ReferenceSortedSet<@NotNull IRecipeHandlePart> handlers = new ReferenceLinkedOpenHashSet<>(); + + public void addOrSetActive(@NotNull IRecipeHandlePart handler) { + if (handlers.size() > 1) handlers.remove(handler); + handlers.add(handler); + } + + @NotNull + public ReferenceSet<@NotNull IRecipeHandlePart> getAll() { + return handlers; + } + + @Nullable + public IRecipeHandlePart getActive() { + return handlers.isEmpty() ? null : handlers.last(); + } + + @NotNull + public ObjectIterator<@NotNull IRecipeHandlePart> getReverseIterator() { + return ReverseIteratorBuilder.build(handlers); + } + + public boolean isEmpty() { + return handlers.isEmpty(); + } + + public void clear() { + handlers.clear(); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/utils/datastructure/GTRecipe2IntBiMultiMap.java b/src/main/java/org/gtlcore/gtlcore/utils/datastructure/GTRecipe2IntBiMultiMap.java new file mode 100644 index 000000000..ceb4366ff --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/utils/datastructure/GTRecipe2IntBiMultiMap.java @@ -0,0 +1,92 @@ +package org.gtlcore.gtlcore.utils.datastructure; + +import com.gregtechceu.gtceu.api.recipe.GTRecipe; + +import it.unimi.dsi.fastutil.ints.*; +import it.unimi.dsi.fastutil.objects.*; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; +import java.util.function.BiConsumer; + +public class GTRecipe2IntBiMultiMap { + + private final Object2ReferenceMap keyToValues = new Object2ReferenceOpenHashMap<>(); + private final Int2ReferenceMap> valueToKeys = new Int2ReferenceOpenHashMap<>(); + + public void put(@NotNull GTRecipe key, int value) { + keyToValues.computeIfAbsent(key, k -> new IntArraySet()).add(value); + valueToKeys.computeIfAbsent(value, v -> new ObjectArraySet<>()).add(key); + } + + public IntSet getValues(GTRecipe key) { + return keyToValues.getOrDefault(key, IntSets.emptySet()); + } + + public ObjectSet getKeys(int value) { + return valueToKeys.getOrDefault(value, ObjectSets.emptySet()); + } + + public void remove(GTRecipe key, int value) { + Optional.ofNullable(keyToValues.get(key)).ifPresent(set -> { + set.remove(value); + if (set.isEmpty()) keyToValues.remove(key); + }); + Optional.ofNullable(valueToKeys.get(value)).ifPresent(set -> { + set.remove(key); + if (set.isEmpty()) valueToKeys.remove(value); + }); + } + + public void removeByKey(GTRecipe key) { + IntSet values = keyToValues.remove(key); + if (values != null) { + for (int v : values) { + ObjectSet ks = valueToKeys.get(v); + if (ks != null) { + ks.remove(key); + if (ks.isEmpty()) valueToKeys.remove(v); + } + } + } + } + + public void removeByValue(int value) { + ObjectSet keys = valueToKeys.remove(value); + if (keys != null) { + for (GTRecipe k : keys) { + IntSet vs = keyToValues.get(k); + if (vs != null) { + vs.remove(value); + if (vs.isEmpty()) keyToValues.remove(k); + } + } + } + } + + public ObjectSet keySet() { + return ObjectSets.unmodifiable(keyToValues.keySet()); + } + + public IntSet valueSet() { + return IntSets.unmodifiable(valueToKeys.keySet()); + } + + public void clear() { + keyToValues.clear(); + valueToKeys.clear(); + } + + public int size() { + return keyToValues.values().stream().mapToInt(IntSet::size).sum(); + } + + public void forEach(BiConsumer action) { + for (var entry : keyToValues.entrySet()) { + GTRecipe key = entry.getKey(); + for (int value : entry.getValue()) { + action.accept(key, value); + } + } + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/utils/datastructure/Int128.java b/src/main/java/org/gtlcore/gtlcore/utils/datastructure/Int128.java new file mode 100644 index 000000000..89269c55c --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/utils/datastructure/Int128.java @@ -0,0 +1,810 @@ +package org.gtlcore.gtlcore.utils.datastructure; + +import com.google.common.primitives.Ints; +import lombok.Getter; +import org.jetbrains.annotations.NotNull; + +import java.math.BigInteger; + +/** + * 高性能128位有符号整数实现,使用两个long组成 + * 避免频繁创建新对象,支持原地修改 + */ +@SuppressWarnings({ "unused", "DuplicatedCode", "UnusedReturnValue" }) +@Getter +public final class Int128 extends Number implements Comparable { + + // 高64位和低64位 + private long high; + private long low; + + public static Int128 ZERO() { + return new Int128(0, 0); + } + + public static Int128 ONE() { + return new Int128(0, 1); + } + + public static Int128 NEGATIVE_ONE() { + return new Int128(-1L, -1L); + } + + public static final Int128 MAX_VALUE = new Int128(0x7FFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL); + public static final Int128 MIN_VALUE = new Int128(0x8000000000000000L, 0); + + public Int128() { + this.high = 0; + this.low = 0; + } + + @Override + public int intValue() { + return Ints.saturatedCast(longValue()); + } + + @Override + public long longValue() { + // 正常范围内的数字 + if ((high == 0 && low >= 0) || (high == -1L && low < 0)) { + return low; + } + + // 超出范围,饱和到边界值 + return isNegative() ? Long.MIN_VALUE : Long.MAX_VALUE; + } + + @Override + public float floatValue() { + return (float) toDouble(); + } + + @Override + public double doubleValue() { + return toDouble(); + } + + public Int128(long high, long low) { + this.high = high; + this.low = low; + } + + public Int128(long value) { + this.high = value < 0 ? -1L : 0; + this.low = value; + } + + public Int128 set(long high, long low) { + this.high = high; + this.low = low; + return this; + } + + public Int128 set(Int128 other) { + this.high = other.high; + this.low = other.low; + return this; + } + + // =================== 算术运算 =================== + + public Int128 add(long other) { + long newLow = this.low + other; + long newHigh = this.high; + + if (Long.compareUnsigned(newLow, this.low) < 0) { + newHigh++; // 发生无符号进位 + } + + if (other < 0) { + newHigh--; + } + + this.low = newLow; + this.high = newHigh; + return this; + } + + public Int128 add(Int128 other) { + long newLow = this.low + other.low; + long newHigh = this.high + other.high; + + if (Long.compareUnsigned(newLow, this.low) < 0) { + newHigh++; + } + + this.low = newLow; + this.high = newHigh; + return this; + } + + public static Int128 add(Int128 a, Int128 b, Int128 result) { + long newLow = a.low + b.low; + long newHigh = a.high + b.high; + + if (Long.compareUnsigned(newLow, a.low) < 0) { + newHigh++; + } + + result.low = newLow; + result.high = newHigh; + return result; + } + + public Int128 subtract(Int128 other) { + long newLow = this.low - other.low; + long newHigh = this.high - other.high; + + // 处理无符号借位 + if (Long.compareUnsigned(this.low, other.low) < 0) { + newHigh--; + } + + this.low = newLow; + this.high = newHigh; + return this; + } + + public static Int128 subtract(Int128 a, Int128 b, Int128 result) { + long newLow = a.low - b.low; + long newHigh = a.high - b.high; + + if (Long.compareUnsigned(a.low, b.low) < 0) { + newHigh--; + } + + result.low = newLow; + result.high = newHigh; + return result; + } + + public Int128 multiply(Int128 other) { + long a0 = this.low & 0xFFFFFFFFL; + long a1 = this.low >>> 32; + long a2 = this.high & 0xFFFFFFFFL; + long a3 = this.high >>> 32; + + long b0 = other.low & 0xFFFFFFFFL; + long b1 = other.low >>> 32; + long b2 = other.high & 0xFFFFFFFFL; + long b3 = other.high >>> 32; + + long p0 = a0 * b0; + long p1 = a0 * b1 + a1 * b0; + long p2 = a0 * b2 + a1 * b1 + a2 * b0; + long p3 = a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0; + + p1 += p0 >>> 32; + p2 += p1 >>> 32; + p3 += p2 >>> 32; + + this.low = (p1 << 32) | (p0 & 0xFFFFFFFFL); + this.high = (p3 << 32) | (p2 & 0xFFFFFFFFL); + + return this; + } + + public static Int128 multiply(Int128 a, Int128 b, Int128 result) { + long a0 = a.low & 0xFFFFFFFFL; + long a1 = a.low >>> 32; + long a2 = a.high & 0xFFFFFFFFL; + long a3 = a.high >>> 32; + + long b0 = b.low & 0xFFFFFFFFL; + long b1 = b.low >>> 32; + long b2 = b.high & 0xFFFFFFFFL; + long b3 = b.high >>> 32; + + long p0 = a0 * b0; + long p1 = a0 * b1 + a1 * b0; + long p2 = a0 * b2 + a1 * b1 + a2 * b0; + long p3 = a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0; + + p1 += p0 >>> 32; + p2 += p1 >>> 32; + p3 += p2 >>> 32; + + result.low = (p1 << 32) | (p0 & 0xFFFFFFFFL); + result.high = (p3 << 32) | (p2 & 0xFFFFFFFFL); + + return result; + } + + public Int128 multiply(long multiplier) { + long a0 = this.low & 0xFFFFFFFFL; + long a1 = this.low >>> 32; + long a2 = this.high & 0xFFFFFFFFL; + long a3 = this.high >>> 32; + + long m0 = multiplier & 0xFFFFFFFFL; + long m1 = multiplier >>> 32; + + long p0 = a0 * m0; + long p1 = a0 * m1 + a1 * m0; + long p2 = a1 * m1 + a2 * m0; + long p3 = a2 * m1 + a3 * m0; + + p1 += p0 >>> 32; + p2 += p1 >>> 32; + p3 += p2 >>> 32; + + this.low = (p1 << 32) | (p0 & 0xFFFFFFFFL); + this.high = (p3 << 32) | (p2 & 0xFFFFFFFFL); + + return this; + } + + public static Int128 multiply(Int128 a, long multiplier, Int128 result) { + result.set(a.high, a.low); + return result.multiply(multiplier); + } + + public Int128 divide(Int128 divisor, Int128 remainder) { + if (divisor.isZero()) { + throw new ArithmeticException("Division by zero"); + } + + if (this.isZero()) { + remainder.set(0, 0); + return this.set(0, 0); + } + + boolean negativeResult = (this.isNegative() != divisor.isNegative()); + + Int128 dividend = new Int128(this.high, this.low); + Int128 div = new Int128(divisor.high, divisor.low); + + if (dividend.isNegative()) dividend.negate(); + if (div.isNegative()) div.negate(); + + Int128 quotient = new Int128(); + Int128 temp = new Int128(); + + for (int i = 127; i >= 0; i--) { + temp.shiftLeft(1); + if (dividend.getBit(i)) { + temp.low |= 1; + } + + if (temp.compareTo(div) >= 0) { + temp.subtract(div); + quotient.setBit(i, true); + } + } + + remainder.set(temp); + + this.set(quotient); + if (negativeResult) { + this.negate(); + } + + return this; + } + + public Int128 divide(long divisor) { + if (divisor == 0) { + throw new ArithmeticException("Division by zero"); + } + + boolean neg = (this.isNegative() != (divisor < 0)); + + if (this.isNegative()) this.negate(); + if (divisor < 0) divisor = -divisor; + + long rem = 0; + long resultHigh = 0; + long resultLow = 0; + + if (high != 0) { + resultHigh = Long.divideUnsigned(high, divisor); + rem = Long.remainderUnsigned(high, divisor); + } + + if (rem != 0) { + long combined = (rem << 32) | (low >>> 32); + long q1 = Long.divideUnsigned(combined, divisor); + rem = Long.remainderUnsigned(combined, divisor); + + combined = (rem << 32) | (low & 0xFFFFFFFFL); + long q0 = Long.divideUnsigned(combined, divisor); + + resultLow = (q1 << 32) | q0; + } else { + resultLow = Long.divideUnsigned(low, divisor); + } + + this.high = resultHigh; + this.low = resultLow; + + if (neg) this.negate(); + + return this; + } + + public Int128 divideNew(long divisor) { + if (divisor == 0) { + throw new ArithmeticException("Division by zero"); + } + + // 创建副本进行计算 + Int128 result = new Int128(this.high, this.low); + boolean neg = (result.isNegative() != (divisor < 0)); + + if (result.isNegative()) result.negate(); + long absDivisor = divisor < 0 ? -divisor : divisor; + + long rem = 0; + long resultHigh = 0; + long resultLow = 0; + + if (result.high != 0) { + resultHigh = Long.divideUnsigned(result.high, absDivisor); + rem = Long.remainderUnsigned(result.high, absDivisor); + } + + if (rem != 0) { + long combined = (rem << 32) | (result.low >>> 32); + long q1 = Long.divideUnsigned(combined, absDivisor); + rem = Long.remainderUnsigned(combined, absDivisor); + + combined = (rem << 32) | (result.low & 0xFFFFFFFFL); + long q0 = Long.divideUnsigned(combined, absDivisor); + + resultLow = (q1 << 32) | q0; + } else { + resultLow = Long.divideUnsigned(result.low, absDivisor); + } + + result.high = resultHigh; + result.low = resultLow; + + if (neg) result.negate(); + + return result; + } + + // =================== 位运算 =================== + + public Int128 shiftLeft(int n) { + n &= 127; // 限制在0-127范围 + + if (n >= 64) { + this.high = this.low << (n - 64); + this.low = 0; + } else if (n > 0) { + this.high = (this.high << n) | (this.low >>> (64 - n)); + this.low = this.low << n; + } + + return this; + } + + public Int128 shiftRight(int n) { + n &= 127; + + if (n >= 64) { + this.low = this.high >> (n - 64); + this.high = this.high >> 63; // 符号扩展 + } else if (n > 0) { + this.low = (this.low >>> n) | (this.high << (64 - n)); + this.high = this.high >> n; + } + + return this; + } + + public Int128 shiftRightUnsigned(int n) { + n &= 127; + + if (n >= 64) { + this.low = this.high >>> (n - 64); + this.high = 0; + } else if (n > 0) { + this.low = (this.low >>> n) | (this.high << (64 - n)); + this.high = this.high >>> n; + } + + return this; + } + + public Int128 negate() { + this.low = ~this.low; + this.high = ~this.high; + + // 加1 + this.low++; + if (this.low == 0) { + this.high++; + } + + return this; + } + + // =================== 比较运算 =================== + + @Override + public int compareTo(Int128 other) { + boolean thisNeg = this.isNegative(); + boolean otherNeg = other.isNegative(); + + if (thisNeg != otherNeg) { + return thisNeg ? -1 : 1; + } + + if (this.high != other.high) { + return Long.compare(this.high, other.high); + } + + return Long.compareUnsigned(this.low, other.low); + } + + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Int128 other)) return false; + return this.high == other.high && this.low == other.low; + } + + // =================== 工具方法 =================== + + public boolean isZero() { + return high == 0 && low == 0; + } + + public boolean isNegative() { + return high < 0; + } + + public boolean isPositive() { + return !isNegative() && !isZero(); + } + + public boolean getBit(int index) { + if (index < 64) { + return (low & (1L << index)) != 0; + } else { + return (high & (1L << (index - 64))) != 0; + } + } + + public void setBit(int index, boolean value) { + if (index < 64) { + if (value) { + low |= (1L << index); + } else { + low &= ~(1L << index); + } + } else { + if (value) { + high |= (1L << (index - 64)); + } else { + high &= ~(1L << (index - 64)); + } + } + } + + public long toLong() { + return low; + } + + public double toDouble() { + return high * Math.pow(2, 64) + (low & 0x7FFFFFFFFFFFFFFFL) + (low < 0 ? Math.pow(2, 63) : 0); + } + + public BigInteger toBigInteger() { + if (isZero()) { + return BigInteger.ZERO; + } + + byte[] bytes = new byte[16]; + + for (int i = 0; i < 8; i++) { + bytes[i] = (byte) (high >>> (56 - i * 8)); + } + + for (int i = 0; i < 8; i++) { + bytes[i + 8] = (byte) (low >>> (56 - i * 8)); + } + + return new BigInteger(bytes); + } + + public static Int128 fromBigInteger(BigInteger value) { + if (value == null) { + return ZERO(); + } + + if (value.bitLength() > 127) { + throw new ArithmeticException("BigInteger too large for Int128: " + value); + } + + if (value.equals(BigInteger.ZERO)) { + return new Int128(0, 0); + } + if (value.equals(BigInteger.ONE)) { + return new Int128(0, 1); + } + + byte[] bytes = value.toByteArray(); + + long high = 0, low = 0; + + int len = bytes.length; + + for (int i = 0; i < Math.min(8, len); i++) { + int byteIndex = len - 1 - i; + if (byteIndex >= 0) { + low |= ((long) (bytes[byteIndex] & 0xFF)) << (i * 8); + } + } + + for (int i = 8; i < Math.min(16, len); i++) { + int byteIndex = len - 1 - i; + if (byteIndex >= 0) { + high |= ((long) (bytes[byteIndex] & 0xFF)) << ((i - 8) * 8); + } + } + + if (value.signum() < 0 && len < 16) { + if (len <= 8) { + if (len < 8) { + low |= (-1L << (len * 8)); + } + high = -1L; + } else { + high |= (-1L << ((len - 8) * 8)); + } + } + + return new Int128(high, low); + } + + @Override + public String toString() { + if (isZero()) return "0"; + + if (high == 0 || (high == -1 && low < 0)) { + return Long.toString(low); + } + + return toStringFast(); + } + + private String toStringFast() { + boolean negative = isNegative(); + + long workHigh = negative ? ~high : high; + long workLow = negative ? ~low + 1 : low; + if (negative && workLow == 0) workHigh++; + + if (workHigh == 0) { + return negative ? "-" + workLow : Long.toString(workLow); + } + + char[] digits = new char[40]; + int pos = digits.length; + + final long BILLION = 1_000_000_000L; + + while (workHigh != 0 || workLow != 0) { + long quotientHigh, quotientLow, remainder; + + if (workHigh == 0) { + quotientHigh = 0; + quotientLow = workLow / BILLION; + remainder = workLow % BILLION; + } else { + long temp = workHigh % BILLION; + quotientHigh = workHigh / BILLION; + + long combined = (temp << 32) | (workLow >>> 32); + long q1 = combined / BILLION; + temp = combined % BILLION; + + combined = (temp << 32) | (workLow & 0xFFFFFFFFL); + long q0 = combined / BILLION; + remainder = combined % BILLION; + + quotientLow = (q1 << 32) | q0; + } + + for (int i = 0; i < 9 && (remainder != 0 || workHigh != 0 || workLow != quotientLow * BILLION + remainder); i++) { + digits[--pos] = (char) ('0' + (remainder % 10)); + remainder /= 10; + } + + workHigh = quotientHigh; + workLow = quotientLow; + } + + if (negative) { + digits[--pos] = '-'; + } + + return new String(digits, pos, digits.length - pos); + } + + public static Int128 fromString(@NotNull String str) { + str = str.trim(); + if (str.isEmpty()) { + throw new NumberFormatException("empty string"); + } + + return fromDecimalString(str); + } + + public static Int128 fromString(@NotNull String str, Int128 defaultValue) { + try { + return fromString(str); + } catch (Exception e) { + return defaultValue; + } + } + + private static Int128 fromDecimalString(@NotNull String str) { + boolean negative = false; + int start = 0; + + if (str.charAt(0) == '-') { + negative = true; + start = 1; + } else if (str.charAt(0) == '+') { + start = 1; + } + + if (start >= str.length()) { + throw new NumberFormatException("no digits"); + } + + if (str.length() - start <= 18) { + try { + long value = Long.parseLong(str.substring(start)); + return new Int128(negative ? -value : value); + } catch (NumberFormatException ignored) {} + } + + Int128 result = new Int128(); + Int128 ten = new Int128(10); + + for (int i = start; i < str.length(); i++) { + char c = str.charAt(i); + if (c < '0' || c > '9') { + throw new NumberFormatException("invalid digit: " + c); + } + + // result = result * 10 + digit + result.multiply(ten); + result.add(new Int128(c - '0')); + } + + if (negative) { + result.negate(); + } + + return result; + } + + public static Int128 sum(Int128 a, Int128 b) { + return a.add(b); + } + + public String toHexString() { + return String.format("%016X%016X", high, low); + } + + /** + * 格式化数字的toString版本,使用千分位分隔符 + * + * @param separator 分隔符,通常为 "," 或 " " + * @return 格式化后的字符串 + */ + public String toFormattedString(String separator) { + if (separator == null) separator = ","; + + String baseStr = this.toString(); + if (baseStr.length() <= 3) { + return baseStr; + } + + boolean negative = baseStr.startsWith("-"); + String digits = negative ? baseStr.substring(1) : baseStr; + + StringBuilder formatted = new StringBuilder(); + int len = digits.length(); + + for (int i = 0; i < len; i++) { + if (i > 0 && (len - i) % 3 == 0) { + formatted.append(separator); + } + formatted.append(digits.charAt(i)); + } + + if (negative) { + formatted.insert(0, "-"); + } + + return formatted.toString(); + } + + public String toFormattedString() { + return toFormattedString(","); + } + + /** + * 紧凑格式,使用科学计数法显示大数字 + * + * @return 紧凑格式的字符串,如 "1.23E+15" + */ + public String toCompactString() { + if (isZero()) return "0"; + + String str = this.toString(); + boolean negative = str.startsWith("-"); + String digits = negative ? str.substring(1) : str; + + if (digits.length() <= 6) { + return str; + } + + char firstDigit = digits.charAt(0); + + String mantissa = String.valueOf(firstDigit) + '.' + digits.substring(1, 4); + + int exponent = digits.length() - 1; + String result = mantissa + "E+" + exponent; + + return negative ? "-" + result : result; + } + + /** + * 人类可读的格式,使用单位后缀 + * + * @return 人类可读的字符串,如 "1.23K", "4.56M", "7.89B" + */ + public String toHumanReadableString() { + if (isZero()) return "0"; + + String[] units = { "", "K", "M", "B", "T", "P", "E", "Z", "Y" }; + + String str = this.toString(); + boolean negative = str.startsWith("-"); + String digits = negative ? str.substring(1) : str; + + if (digits.length() <= 3) { + return str; + } + + int unitIndex = (digits.length() - 1) / 3; + if (unitIndex >= units.length) { + return toCompactString(); + } + + int significantDigits = digits.length() - (unitIndex * 3); + String integerPart = digits.substring(0, significantDigits); + + StringBuilder result = new StringBuilder(); + if (negative) result.append("-"); + + result.append(integerPart); + + int remainingDigits = digits.length() - significantDigits; + if (remainingDigits > 0 && integerPart.length() < 3) { + result.append("."); + int decimalPlaces = Math.min(2, Math.min(remainingDigits, 3 - integerPart.length())); + result.append(digits, significantDigits, significantDigits + decimalPlaces); + } + + result.append(units[unitIndex]); + + return result.toString(); + } + + @Override + public int hashCode() { + return Long.hashCode(high) * 31 + Long.hashCode(low); + } + + public Int128 copy() { + return new Int128(this.high, this.low); + } +} diff --git a/src/main/java/org/gtlcore/gtlcore/utils/datastructure/ReverseIteratorBuilder.java b/src/main/java/org/gtlcore/gtlcore/utils/datastructure/ReverseIteratorBuilder.java new file mode 100644 index 000000000..64d39b10a --- /dev/null +++ b/src/main/java/org/gtlcore/gtlcore/utils/datastructure/ReverseIteratorBuilder.java @@ -0,0 +1,42 @@ +package org.gtlcore.gtlcore.utils.datastructure; + +import it.unimi.dsi.fastutil.objects.*; +import org.jetbrains.annotations.NotNull; + +import java.util.NoSuchElementException; + +public class ReverseIteratorBuilder { + + @NotNull + public static ObjectIterator build(@NotNull ReferenceSortedSet<@NotNull T> set) { + return set.isEmpty() ? ObjectIterators.emptyIterator() : set.size() == 1 ? ObjectIterators.singleton(set.first()) : new ReverseIterator<>(set); + } + + private static class ReverseIterator implements ObjectIterator { + + private final ObjectBidirectionalIterator delegate; + + // never use empty set + public ReverseIterator(ReferenceSortedSet set) { + this.delegate = set.iterator(set.last()); + } + + @Override + public boolean hasNext() { + return delegate.hasPrevious(); + } + + @Override + public T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return delegate.previous(); + } + + @Override + public void remove() { + delegate.remove(); + } + } +} diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 7d144436c..721988552 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -34,7 +34,7 @@ side = "BOTH" [[dependencies.${mod_id}]] modId = "ae2" mandatory = true -versionRange = "[15.2.,)" +versionRange = "[15.4.10,)" ordering = "AFTER" side = "BOTH" @@ -69,6 +69,6 @@ side = "BOTH" [[dependencies.${mod_id}]] modId = "extendedae" mandatory = false -versionRange = "[1.1,)" +versionRange = "[1.4,8,)" ordering = "AFTER" side = "BOTH" \ No newline at end of file diff --git a/src/main/resources/assets/gtceu/lang/en_us.json b/src/main/resources/assets/gtceu/lang/en_us.json index fe56f7a89..68d91c841 100644 --- a/src/main/resources/assets/gtceu/lang/en_us.json +++ b/src/main/resources/assets/gtceu/lang/en_us.json @@ -1,1651 +1,1740 @@ { - "block.gtceu.a_mass_fabricator": "进阶质量发生器", - "block.gtceu.advanced_assembly_line": "进阶装配线", - "block.gtceu.advanced_hyper_reactor": "进阶超能反应堆", - "block.gtceu.advanced_infinite_driller": "进阶无尽钻机", - "block.gtceu.advanced_integrated_ore_processor": "进阶集成矿石处理厂", - "block.gtceu.advanced_multi_smelter": "进阶熔炉", - "block.gtceu.advanced_neutron_activator": "中子漩涡", - "block.gtceu.advanced_rare_earth_centrifugal": "进阶稀土离心机", - "block.gtceu.advanced_vacuum_drying_furnace": "进阶真空干燥炉", - "block.gtceu.advanced_sps_crafting": "进阶超临界合成机", - "block.gtceu.aggregation_device": "聚合装置", - "block.gtceu.annihilate_generator": "人造恒星", - "block.gtceu.assemble_plant": "通用组装厂", - "block.gtceu.assembler_module": "太空电梯组装模块", - "block.gtceu.atomic_energy_excitation_plant": "原子能激发工厂", - "block.gtceu.auto_configuration_maintenance_hatch": "可配置自动维护仓", - "block.gtceu.bedrock_drilling_rig": "基岩钻机", - "block.gtceu.blaze_blast_furnace": "烈焰高炉", - "block.gtceu.block_bus": "方块总线", - "block.gtceu.tag_filter_me_stock_bus_part_machine": "标签过滤ME库存输入总线", - "block.gtceu.me_dual_hatch_stock_part_machine": "ME库存输入总成", - "block.gtceu.block_conversion_room": "方块转换室", - "block.gtceu.chemical_distort": "深度化学扭曲仪", - "block.gtceu.chemical_energy_devourer": "化学能吞噬者", - "block.gtceu.chemical_plant": "化工厂", - "block.gtceu.circuit_assembly_line": "电路装配线", - "block.gtceu.cleaning_configuration_maintenance_hatch": "超净可配置维护仓", - "block.gtceu.cold_ice_freezer": "寒冰冷冻机", - "block.gtceu.component_assembly_line": "部件装配线", - "block.gtceu.cooling_tower": "冷却塔", - "block.gtceu.create_aggregation": "创造聚合仪", - "block.gtceu.create_computation": "创造计算机", - "block.gtceu.crystalline_infinity": "无限晶胞", - "block.gtceu.decay_hastener": "衰变加速器", - "block.gtceu.desulfurizer": "脱硫机", - "block.gtceu.digestion_tank": "煮解池", - "block.gtceu.dimensional_focus_engraving_array": "维度聚焦激光蚀刻阵列", - "block.gtceu.dimensionally_transcendent_dirt_forge": "超维度等泥土锻炉", - "block.gtceu.dimensionally_transcendent_mixer": "超维度搅拌机", - "block.gtceu.dimensionally_transcendent_plasma_forge": "超维度等离子锻炉", - "block.gtceu.dimensionally_transcendent_steam_boiler": "超维度等蒸汽锅炉", - "block.gtceu.dimensionally_transcendent_steam_oven": "超维度蒸汽熔炉", - "block.gtceu.disassembly": "拆解机", - "block.gtceu.dissolving_tank": "溶解罐", - "block.gtceu.door_of_create": "创造之门", - "block.gtceu.dragon_egg_copier": "龙蛋复制机", - "block.gtceu.dyson_sphere": "戴森球控制系统", - "block.gtceu.electric_implosion_compressor": "电力聚爆压缩机", - "block.gtceu.element_copying": "元素复制机", - "block.gtceu.engraving_laser_plant": "激光蚀刻工厂", - "block.gtceu.ev_buffer": "§5进阶缓存器 III§r", - "block.gtceu.ev_dehydrator": "§5高级脱水机 III§r", - "block.gtceu.ev_dual_input_hatch": "§5EV§r输入总成", - "block.gtceu.ev_dual_output_hatch": "§5EV§r输出总成", - "block.gtceu.ev_huge_input_hatch": "§5EV§r巨型输入仓", - "block.gtceu.ev_huge_output_hatch": "§5EV§r巨型输出仓", - "block.gtceu.ev_lightning_processor": "§5高级闪电处理机 III§r", - "block.gtceu.ev_lightning_rod": "§5基础避雷针§r", - "block.gtceu.ev_neutron_accelerator": "§5EV 中子加速器§r", - "block.gtceu.ev_rocket_engine": "§5基础火箭引擎§r", - "block.gtceu.ev_world_data_scanner": "§5进阶世界信息扫描仪 III§r", - "block.gtceu.eye_of_harmony": "鸿蒙之眼", - "block.gtceu.field_extruder_factory": "力场压模工厂", - "block.gtceu.fishing_ground": "渔场", - "block.gtceu.fission_reactor": "裂变反应堆", - "block.gtceu.flotation_cell_regulator": "工业浮选机", - "block.gtceu.gas_mega_turbine": "特大燃气涡轮", - "block.gtceu.generator_array": "发电阵列", - "block.gtceu.gravitation_shockburst": "引力震爆器", - "block.gtceu.gravity_hatch": "重力控制仓", - "block.gtceu.cleaning_gravity_maintenance_hatch": "重力超净维护仓", - "block.gtceu.sterile_cleaning_gravity_maintenance_hatch": "重力无菌维护仓", - "block.gtceu.law_cleaning_gravity_maintenance_hatch": "重力绝对洁净维护仓", - "block.gtceu.gravity_configuration_hatch": "可配置重力控制仓", - "block.gtceu.cleaning_gravity_configuration_maintenance_hatch": "可配置重力超净维护仓", - "block.gtceu.sterile_cleaning_gravity_configuration_maintenance_hatch": "可配置重力无菌维护仓", - "block.gtceu.law_cleaning_gravity_configuration_maintenance_hatch": "可配置重力绝对洁净维护仓", - "block.gtceu.greenhouse": "温室", - "block.gtceu.heat_exchanger": "热交换机", - "block.gtceu.holy_separator": "神圣分离者", - "block.gtceu.huge_incubator": "微生物支配者", - "block.gtceu.hv_dehydrator": "§6高级脱水机 II§r", - "block.gtceu.hv_dual_input_hatch": "§6HV§r输入总成", - "block.gtceu.hv_dual_output_hatch": "§6HV§r输出总成", - "block.gtceu.hv_energy_input_hatch_16a": "16安§4HV§r能源仓", - "block.gtceu.hv_energy_input_hatch_4a": "4安§4HV§r能源仓 ", - "block.gtceu.hv_energy_output_hatch": "§4HV§r动力仓", - "block.gtceu.hv_energy_output_hatch_16a": "16安§4HV§r动力仓", - "block.gtceu.hv_energy_output_hatch_4a": "4安§4HV§r动力仓", - "block.gtceu.hv_huge_input_hatch": "§6HV§r巨型输入仓", - "block.gtceu.hv_huge_output_hatch": "§6HV§r巨型输出仓", - "block.gtceu.hv_lightning_processor": "§6高级闪电处理机 II§r", - "block.gtceu.hv_neutron_accelerator": "§6HV 中子加速器§r", - "block.gtceu.hv_semi_fluid": "§6进阶半流质发电机 II§r", - "block.gtceu.hv_world_data_scanner": "§6进阶世界信息扫描仪 II§r", - "block.gtceu.hyper_reactor": "超能反应堆", - "block.gtceu.incubator": "培养缸", - "block.gtceu.integrated_ore_processor": "集成矿石处理厂", - "block.gtceu.isa_mill": "艾萨研磨机", - "block.gtceu.iv_1048576a_laser_source_hatch": "1048576安§9IV§r激光源仓", - "block.gtceu.iv_1048576a_laser_target_hatch": "1048576安§9IV§r激光靶仓", - "block.gtceu.iv_16384a_laser_source_hatch": "16384安§9IV§r激光源仓", - "block.gtceu.iv_16384a_laser_target_hatch": "16384安§9IV§r激光靶仓", - "block.gtceu.iv_16777216a_laser_source_hatch": "16777216安§9IV§r激光源仓", - "block.gtceu.iv_16777216a_laser_target_hatch": "16777216安§9IV§r激光靶仓", - "block.gtceu.iv_262144a_laser_source_hatch": "262144安§9IV§r激光源仓", - "block.gtceu.iv_262144a_laser_target_hatch": "262144安§9IV§r激光靶仓", - "block.gtceu.iv_4194304a_laser_source_hatch": "4194304安§9IV§r激光源仓", - "block.gtceu.iv_4194304a_laser_target_hatch": "4194304安§9IV§r激光靶仓", - "block.gtceu.iv_65536a_laser_source_hatch": "65536安§9IV§r激光源仓", - "block.gtceu.iv_65536a_laser_target_hatch": "65536安§9IV§r激光靶仓", - "block.gtceu.iv_67108864a_laser_source_hatch": "67108864安§9IV§r激光源仓", - "block.gtceu.iv_67108864a_laser_target_hatch": "67108864安§9IV§r激光靶仓", - "block.gtceu.iv_buffer": "§9精英缓存器§r", - "block.gtceu.iv_dehydrator": "§9精英脱水机§r", - "block.gtceu.iv_dual_input_hatch": "§9IV§r输入总成", - "block.gtceu.iv_dual_output_hatch": "§9IV§r输出总成", - "block.gtceu.iv_huge_input_hatch": "§9IV§r巨型输入仓", - "block.gtceu.iv_huge_output_hatch": "§9IV§r巨型输出仓", - "block.gtceu.iv_lightning_processor": "§9精英闪电处理机§r", - "block.gtceu.iv_lightning_rod": "§9进阶避雷针§r", - "block.gtceu.iv_naquadah_reactor": "§9基础硅岩反应堆§r", - "block.gtceu.iv_neutron_accelerator": "§9IV 中子加速器§r", - "block.gtceu.iv_rocket_engine": "§9进阶火箭引擎§r", - "block.gtceu.iv_world_data_scanner": "§9精英世界信息扫描仪§r", - "block.gtceu.large_block_conversion_room": "大型方块转换室", - "block.gtceu.large_chemical_plant": "大型化工厂", - "block.gtceu.large_cracker": "大型裂化机", - "block.gtceu.large_gas_collector": "大型集气室", - "block.gtceu.large_greenhouse": "大型温室", - "block.gtceu.large_incubator": "大型培养缸", - "block.gtceu.large_naquadah_reactor": "大型硅岩反应堆", - "block.gtceu.large_pyrolyse_oven": "大型热解炉", - "block.gtceu.large_recycler": "回收机", - "block.gtceu.large_rock_crusher": "大型碎岩机", - "block.gtceu.large_semi_fluid_generator": "大型半流质发电机", - "block.gtceu.large_steam_bath": "大型蒸汽浸洗机", - "block.gtceu.large_steam_centrifuge": "大型蒸汽离心机", - "block.gtceu.large_steam_circuit_assembler": "大型蒸汽电路组装机", - "block.gtceu.large_steam_input_hatch": "大型蒸汽输入仓", - "block.gtceu.large_steam_macerator": "大型蒸汽研磨机", - "block.gtceu.large_steam_mixer": "大型蒸汽搅拌机", - "block.gtceu.large_steam_ore_washer": "大型蒸汽洗矿机", - "block.gtceu.large_steam_thermal_centrifuge": "大型蒸汽热力离心机", - "block.gtceu.large_void_miner": "大型虚空采矿厂", - "block.gtceu.lava_furnace": "熔岩炉", - "block.gtceu.law_cleaning_maintenance_hatch": "绝对洁净维护仓", - "block.gtceu.law_configuration_cleaning_maintenance_hatch": "绝对洁净可配置维护仓", - "block.gtceu.luv_1048576a_laser_source_hatch": "1048576安§dLuV§r激光源仓", - "block.gtceu.luv_1048576a_laser_target_hatch": "1048576安§dLuV§r激光靶仓", - "block.gtceu.luv_16384a_laser_source_hatch": "16384安§dLuV§r激光源仓", - "block.gtceu.luv_16384a_laser_target_hatch": "16384安§dLuV§r激光靶仓", - "block.gtceu.luv_16777216a_laser_source_hatch": "16777216安§dLuV§r激光源仓", - "block.gtceu.luv_16777216a_laser_target_hatch": "16777216安§dLuV§r激光靶仓", - "block.gtceu.luv_262144a_laser_source_hatch": "262144安§dLuV§r激光源仓", - "block.gtceu.luv_262144a_laser_target_hatch": "262144安§dLuV§r激光靶仓", - "block.gtceu.luv_4194304a_laser_source_hatch": "4194304安§dLuV§r激光源仓", - "block.gtceu.luv_4194304a_laser_target_hatch": "4194304安§dLuV§r激光靶仓", - "block.gtceu.luv_65536a_laser_source_hatch": "65536安§dLuV§r激光源仓", - "block.gtceu.luv_65536a_laser_target_hatch": "65536安§dLuV§r激光靶仓", - "block.gtceu.luv_67108864a_laser_source_hatch": "67108864安§dLuV§r激光源仓", - "block.gtceu.luv_67108864a_laser_target_hatch": "67108864安§dLuV§r激光靶仓", - "block.gtceu.luv_buffer": "§d精英缓存器 II§r", - "block.gtceu.luv_compressed_fusion_reactor": "压缩核聚变反应堆控制电脑 MK-I", - "block.gtceu.luv_dehydrator": "§d精英脱水机 II§r", - "block.gtceu.luv_huge_input_hatch": "§dLuV§r巨型输入仓", - "block.gtceu.luv_huge_output_hatch": "§dLuV§r巨型输出仓", - "block.gtceu.luv_lightning_processor": "§d精英闪电处理机 II§r", - "block.gtceu.luv_lightning_rod": "§d精英避雷针§r", - "block.gtceu.luv_naquadah_reactor": "§d进阶硅岩反应堆§r", - "block.gtceu.luv_neutron_accelerator": "§dLuV 中子加速器§r", - "block.gtceu.luv_rocket_engine": "§d精英火箭引擎§r", - "block.gtceu.luv_world_data_scanner": "§d精英世界信息扫描仪 II§r", - "block.gtceu.lv_dehydrator": "基础脱水机", - "block.gtceu.lv_dual_input_hatch": "§7LV§r输入总成", - "block.gtceu.lv_dual_output_hatch": "§7LV§r输出总成", - "block.gtceu.lv_energy_input_hatch_16a": "16安§7LV§r能源仓", - "block.gtceu.lv_energy_input_hatch_4a": "4安§7LV§r能源仓 ", - "block.gtceu.lv_energy_output_hatch": "§7LV§r动力仓", - "block.gtceu.lv_energy_output_hatch_16a": "16安§7LV§r动力仓", - "block.gtceu.lv_energy_output_hatch_4a": "4安§7LV§r动力仓", - "block.gtceu.lv_huge_input_hatch": "§7LV§r巨型输入仓", - "block.gtceu.lv_huge_output_hatch": "§7LV§r巨型输出仓", - "block.gtceu.lv_lightning_processor": "基础闪电处理机", - "block.gtceu.lv_neutron_accelerator": "§7LV 中子加速器§r", - "block.gtceu.lv_primitive_magic_energy": "低压原始魔法能源吸收器", - "block.gtceu.lv_semi_fluid": "基础半流质发电机§r", - "block.gtceu.lv_world_data_scanner": "基础世界信息扫描仪", - "block.gtceu.mage_assembler": "巨型组装厂", - "block.gtceu.magic_manufacturer": "魔法制造机", - "block.gtceu.mass_fabricator": "质量发生器", - "block.gtceu.matter_fabricator": "物质生成机", - "block.gtceu.max_1024a_laser_source_hatch": "1024安§c§lMAX§r激光源仓", - "block.gtceu.max_1024a_laser_target_hatch": "1024安§c§lMAX§r激光靶仓", - "block.gtceu.max_1048576a_laser_source_hatch": "1048576安§c§lMAX§r激光源仓", - "block.gtceu.max_1048576a_laser_target_hatch": "1048576安§c§lMAX§r激光靶仓", - "block.gtceu.max_16384a_laser_source_hatch": "16384安§c§lMAX§r激光源仓", - "block.gtceu.max_16384a_laser_target_hatch": "16384安§c§lMAX§r激光靶仓", - "block.gtceu.max_16777216a_laser_source_hatch": "16777216安§c§lMAX§r激光源仓", - "block.gtceu.max_16777216a_laser_target_hatch": "16777216安§c§lMAX§r激光靶仓", - "block.gtceu.max_256a_laser_source_hatch": "256安§c§lMAX§r激光源仓", - "block.gtceu.max_256a_laser_target_hatch": "256安§c§lMAX§r激光靶仓", - "block.gtceu.max_262144a_laser_source_hatch": "262144安§c§lMAX§r激光源仓", - "block.gtceu.max_262144a_laser_target_hatch": "262144安§c§lMAX§r激光靶仓", - "block.gtceu.max_4096a_laser_source_hatch": "4096安§c§lMAX§r激光源仓", - "block.gtceu.max_4096a_laser_target_hatch": "4096安§c§lMAX§r激光靶仓", - "block.gtceu.max_4194304a_laser_source_hatch": "4194304安§c§lMAX§r激光源仓", - "block.gtceu.max_4194304a_laser_target_hatch": "4194304安§c§lMAX§r激光靶仓", - "block.gtceu.max_65536a_laser_source_hatch": "65536安§c§lMAX§r激光源仓", - "block.gtceu.max_65536a_laser_target_hatch": "65536安§c§lMAX§r激光靶仓", - "block.gtceu.max_67108864a_laser_source_hatch": "67108864安§c§lMAX§r激光源仓", - "block.gtceu.max_67108864a_laser_target_hatch": "67108864安§c§lMAX§r激光靶仓", - "block.gtceu.max_buffer": "§c§l终焉缓存器§r", - "block.gtceu.max_energy_input_hatch_16a": "16安§c§lMAX§r能源仓", - "block.gtceu.max_energy_input_hatch_4a": "4安§c§lMAX§r能源仓", - "block.gtceu.max_energy_output_hatch_16a": "16安§c§lMAX§r动力仓", - "block.gtceu.max_energy_output_hatch_4a": "4安§c§lMAX§r动力仓", - "block.gtceu.max_huge_input_hatch": "§c§lMAX§r巨型输入仓", - "block.gtceu.max_huge_output_hatch": "§c§lMAX§r巨型输出仓", - "block.gtceu.max_neutron_accelerator": "§c§lMAX 中子加速器§r", - "block.gtceu.max_neutron_compressor": "中子态素压缩机", - "block.gtceu.max_parallel_hatch": "§c§lMAX§r并行控制仓", - "block.gtceu.max_substation_input_hatch_64a": "64安§c§lMAX§r变电能源仓", - "block.gtceu.max_substation_output_hatch_64a": "64安§c§lMAX§r变电动力仓", - "block.gtceu.mega_alloy_blast_smelter": "巨型合金冶炼炉", - "block.gtceu.mega_canner": "特大装罐机", - "block.gtceu.mega_distillery": "超级蒸馏塔", - "block.gtceu.mega_extractor": "特大相变仪", - "block.gtceu.mega_fluid_heater": "超级流体加热器", - "block.gtceu.mega_presser": "特大机床", - "block.gtceu.mega_steam_input_hatch": "特大蒸汽输入仓", - "block.gtceu.mega_steam_output_hatch": "特大蒸汽输出仓", - "block.gtceu.mega_wiremill": "特大线材轧机", - "block.gtceu.mixed_plant": "通用混合厂", - "block.gtceu.mv_dehydrator": "§b高级脱水机 §r", - "block.gtceu.mv_dual_input_hatch": "§bMV§r输入总成", - "block.gtceu.mv_dual_output_hatch": "§bMV§r输出总成", - "block.gtceu.mv_energy_input_hatch_16a": "16安§bMV§r能源仓", - "block.gtceu.mv_energy_input_hatch_4a": "4安§bMV§r能源仓 ", - "block.gtceu.mv_energy_output_hatch": "§bMV§r动力仓", - "block.gtceu.mv_energy_output_hatch_16a": "16安§bMV§r动力仓", - "block.gtceu.mv_energy_output_hatch_4a": "4安§bMV§r动力仓", - "block.gtceu.mv_huge_input_hatch": "§bMV§r巨型输入仓", - "block.gtceu.mv_huge_output_hatch": "§bMV§r巨型输出仓", - "block.gtceu.mv_lightning_processor": "§b高级闪电处理机 §r", - "block.gtceu.mv_neutron_accelerator": "§bMV 中子加速器§r", - "block.gtceu.mv_semi_fluid": "§b进阶半流质发电机§r", - "block.gtceu.mv_world_data_scanner": "§b进阶世界信息扫描仪§r", - "block.gtceu.nano_core": "纳米核心", - "block.gtceu.nano_forge_1": "1阶纳米锻炉", - "block.gtceu.nano_forge_2": "2阶纳米锻炉", - "block.gtceu.nano_forge_3": "3阶纳米锻炉", - "block.gtceu.naquadah_alloy_crate": "硅岩合金板条箱", - "block.gtceu.naquadah_alloy_drum": "硅岩合金桶", - "block.gtceu.neutron_activator": "中子活化器", - "block.gtceu.neutron_sensor": "中子传感器", - "block.gtceu.heat_sensor": "温度传感器", - "block.gtceu.neutronium_crate": "中子素板条箱", - "block.gtceu.neutronium_drum": "中子素桶", - "block.gtceu.opv_1048576a_laser_source_hatch": "1048576安§9§lOpV§r激光源仓", - "block.gtceu.opv_1048576a_laser_target_hatch": "1048576安§9§lOpV§r激光靶仓", - "block.gtceu.opv_16384a_laser_source_hatch": "16384安§9§lOpV§r激光源仓", - "block.gtceu.opv_16384a_laser_target_hatch": "16384安§9§lOpV§r激光靶仓", - "block.gtceu.opv_16777216a_laser_source_hatch": "16777216安§9§lOpV§r激光源仓", - "block.gtceu.opv_16777216a_laser_target_hatch": "16777216安§9§lOpV§r激光靶仓", - "block.gtceu.opv_262144a_laser_source_hatch": "262144安§9§lOpV§r激光源仓", - "block.gtceu.opv_262144a_laser_target_hatch": "262144安§9§lOpV§r激光靶仓", - "block.gtceu.opv_4194304a_laser_source_hatch": "4194304安§9§lOpV§r激光源仓", - "block.gtceu.opv_4194304a_laser_target_hatch": "4194304安§9§lOpV§r激光靶仓", - "block.gtceu.opv_65536a_laser_source_hatch": "65536安§9§lOpV§r激光源仓", - "block.gtceu.opv_65536a_laser_target_hatch": "65536安§9§lOpV§r激光靶仓", - "block.gtceu.opv_67108864a_laser_source_hatch": "67108864安§9§lOpV§r激光源仓", - "block.gtceu.opv_67108864a_laser_target_hatch": "67108864安§9§lOpV§r激光靶仓", - "block.gtceu.opv_buffer": "§9§l传奇缓存器§r", - "block.gtceu.opv_dehydrator": "§9§l传奇脱水机§r", - "block.gtceu.opv_energy_input_hatch_16a": "16安§9§lOpV§r能源仓", - "block.gtceu.opv_energy_input_hatch_4a": "4安§9§lOpV§r能源仓", - "block.gtceu.opv_energy_output_hatch_16a": "16安§9§lOpV§r动力仓", - "block.gtceu.opv_energy_output_hatch_4a": "4安§9§lOpV§r动力仓", - "block.gtceu.opv_huge_input_hatch": "§9§lOpV§r巨型输入仓", - "block.gtceu.opv_huge_output_hatch": "§9§lOpV§r巨型输出仓", - "block.gtceu.opv_lightning_processor": "§9§l传奇闪电处理机§r", - "block.gtceu.opv_neutron_accelerator": "§9§lOpV 中子加速器§r", - "block.gtceu.opv_parallel_hatch": "§9§lOpV§r并行控制仓", - "block.gtceu.opv_substation_input_hatch_64a": "64安§9§lOpV§r变电能源仓", - "block.gtceu.opv_substation_output_hatch_64a": "64安§9§lOpV§r变电动力仓", - "block.gtceu.opv_world_data_scanner": "§9§l传奇世界信息扫描仪§r", - "block.gtceu.pcb_factory": "PCB工厂", - "block.gtceu.petrochemical_plant": "石化工厂", - "block.gtceu.plasma_condenser": "等离子冷凝器", - "block.gtceu.plasma_mega_turbine": "特大等离子涡轮", - "block.gtceu.precision_assembler": "精密组装机", - "block.gtceu.primitive_void_ore": "原始虚空采矿机", - "block.gtceu.processing_plant": "通用加工厂", - "block.gtceu.qft": "量子操纵者", - "block.gtceu.rare_earth_centrifugal": "稀土离心机", - "block.gtceu.resource_collection": "太空电梯资源采集模块", - "block.gtceu.rhodium_plated_palladium_crate": "镀铑钯板条箱", - "block.gtceu.rhodium_plated_palladium_drum": "镀铑钯桶", - "block.gtceu.rocket_large_turbine": "大型火箭引擎涡轮", - "block.gtceu.rocket_mega_turbine": "特大火箭引擎涡轮", - "block.gtceu.rotor_hatch": "转子仓", - "block.gtceu.separated_plant": "通用分离厂", - "block.gtceu.slaughterhouse": "工业屠宰场", - "block.gtceu.space_elevator": "太空电梯", - "block.gtceu.space_probe_surface_reception": "宇宙探测器地面接收单元", - "block.gtceu.space_cosmic_probe_receivers": "天基宇宙探测器接收器", - "block.gtceu.sps_crafting": "超临界合成机", - "block.gtceu.star_ultimate_material_forge_factory": "恒星终极物质锻造工厂", - "block.gtceu.steam_bath": "蒸汽浸洗机", - "block.gtceu.steam_foundry": "蒸汽铸造炉", - "block.gtceu.steam_mega_turbine": "特大蒸汽涡轮", - "block.gtceu.steam_mixer": "蒸汽搅拌机", - "block.gtceu.steam_ore_washer": "蒸汽洗矿机", - "block.gtceu.steam_piston_hammer": "蒸汽活塞锤", - "block.gtceu.steam_pressor": "蒸汽挤压机", - "block.gtceu.stellar_forge": "恒星炎炀锻炉", - "block.gtceu.sterile_cleaning_maintenance_hatch": "无菌维护仓", - "block.gtceu.sterile_configuration_cleaning_maintenance_hatch": "无菌可配置维护仓", - "block.gtceu.super_blast_smelter": "超级冶炼炉", - "block.gtceu.super_computation": "超级计算机", - "block.gtceu.super_particle_collider": "超级粒子对撞机", - "block.gtceu.superconducting_electromagnetism": "超导电磁工厂", - "block.gtceu.supercritical_mega_steam_turbine": "特大超临界蒸汽涡轮", - "block.gtceu.supercritical_steam_turbine": "超临界蒸汽涡轮", - "block.gtceu.suprachronal_assembly_line": "超时空装配线", - "block.gtceu.suprachronal_assembly_line_module": "超时空装配线拓展模块", - "block.gtceu.simulation_machine": "多方块模拟机", - "block.gtceu.uev_1048576a_laser_source_hatch": "1048576安§aUEV§r激光源仓", - "block.gtceu.uev_1048576a_laser_target_hatch": "1048576安§aUEV§r激光靶仓", - "block.gtceu.uev_16384a_laser_source_hatch": "16384安§aUEV§r激光源仓", - "block.gtceu.uev_16384a_laser_target_hatch": "16384安§aUEV§r激光靶仓", - "block.gtceu.uev_16777216a_laser_source_hatch": "16777216安§aUEV§r激光源仓", - "block.gtceu.uev_16777216a_laser_target_hatch": "16777216安§aUEV§r激光靶仓", - "block.gtceu.uev_262144a_laser_source_hatch": "262144安§aUEV§r激光源仓", - "block.gtceu.uev_262144a_laser_target_hatch": "262144安§aUEV§r激光靶仓", - "block.gtceu.uev_4194304a_laser_source_hatch": "4194304安§aUEV§r激光源仓", - "block.gtceu.uev_4194304a_laser_target_hatch": "4194304安§aUEV§r激光靶仓", - "block.gtceu.uev_65536a_laser_source_hatch": "65536安§aUEV§r激光源仓", - "block.gtceu.uev_65536a_laser_target_hatch": "65536安§aUEV§r激光靶仓", - "block.gtceu.uev_67108864a_laser_source_hatch": "67108864安§aUEV§r激光源仓", - "block.gtceu.uev_67108864a_laser_target_hatch": "67108864安§aUEV§r激光靶仓", - "block.gtceu.uev_buffer": "§a史诗缓存器 II§r", - "block.gtceu.uev_compressed_fusion_reactor": "压缩核聚变反应堆控制电脑 MK-V", - "block.gtceu.uev_dehydrator": "§a史诗脱水机 II§r", - "block.gtceu.uev_energy_input_hatch_16a": "16安§aUEV§r能源仓", - "block.gtceu.uev_energy_input_hatch_4a": "4安§aUEV§r能源仓", - "block.gtceu.uev_energy_output_hatch_16a": "16安§aUEV§r动力仓", - "block.gtceu.uev_energy_output_hatch_4a": "4安§aUEV§r动力仓", - "block.gtceu.uev_fusion_reactor": "核聚变反应堆控制电脑 MK-V", - "block.gtceu.uev_huge_input_hatch": "§aUEV§r巨型输入仓", - "block.gtceu.uev_huge_output_hatch": "§aUEV§r巨型输出仓", - "block.gtceu.uev_lightning_processor": "§a史诗闪电处理机 II§r", - "block.gtceu.uev_neutron_accelerator": "§aUEV 中子加速器§r", - "block.gtceu.uev_parallel_hatch": "§aUEV§r并行控制仓", - "block.gtceu.uev_rotor_holder": "§aUEV§r转子支架", - "block.gtceu.uev_substation_input_hatch_64a": "64安§aUEV§r变电能源仓", - "block.gtceu.uev_substation_output_hatch_64a": "64安§aUEV§r变电动力仓", - "block.gtceu.uev_world_data_scanner": "§a史诗世界信息扫描仪II§r", - "block.gtceu.uhv_1048576a_laser_source_hatch": "1048576安§4UHV§r激光源仓", - "block.gtceu.uhv_1048576a_laser_target_hatch": "1048576安§4UHV§r激光靶仓", - "block.gtceu.uhv_16384a_laser_source_hatch": "16384安§4UHV§r激光源仓", - "block.gtceu.uhv_16384a_laser_target_hatch": "16384安§4UHV§r激光靶仓", - "block.gtceu.uhv_16777216a_laser_source_hatch": "16777216安§4UHV§r激光源仓", - "block.gtceu.uhv_16777216a_laser_target_hatch": "16777216安§4UHV§r激光靶仓", - "block.gtceu.uhv_262144a_laser_source_hatch": "262144安§4UHV§r激光源仓", - "block.gtceu.uhv_262144a_laser_target_hatch": "262144安§4UHV§r激光靶仓", - "block.gtceu.uhv_4194304a_laser_source_hatch": "4194304安§4UHV§r激光源仓", - "block.gtceu.uhv_4194304a_laser_target_hatch": "4194304安§4UHV§r激光靶仓", - "block.gtceu.uhv_65536a_laser_source_hatch": "65536安§4UHV§r激光源仓", - "block.gtceu.uhv_65536a_laser_target_hatch": "65536安§4UHV§r激光靶仓", - "block.gtceu.uhv_67108864a_laser_source_hatch": "67108864安§4UHV§r激光源仓", - "block.gtceu.uhv_67108864a_laser_target_hatch": "67108864安§4UHV§r激光靶仓", - "block.gtceu.uhv_buffer": "§4史诗缓存器§r", - "block.gtceu.uhv_compressed_fusion_reactor": "压缩核聚变反应堆控制电脑 MK-IV", - "block.gtceu.uhv_dehydrator": "§4史诗脱水机§r", - "block.gtceu.uhv_fusion_reactor": "核聚变反应堆控制电脑 MK-IV", - "block.gtceu.uhv_huge_input_hatch": "§4UHV§r巨型输入仓", - "block.gtceu.uhv_huge_output_hatch": "§4UHV§r巨型输出仓", - "block.gtceu.uhv_lightning_processor": "§4史诗闪电处理机§r", - "block.gtceu.uhv_neutron_accelerator": "§4UHV 中子加速器§r", - "block.gtceu.uhv_parallel_hatch": "§4UHV§r并行控制仓", - "block.gtceu.uhv_rotor_holder": "§4UHV§r转子支架", - "block.gtceu.uhv_world_data_scanner": "§4史诗世界信息扫描仪§r", - "block.gtceu.uiv_1048576a_laser_source_hatch": "1048576安§2UIV§r激光源仓", - "block.gtceu.uiv_1048576a_laser_target_hatch": "1048576安§2UIV§r激光靶仓", - "block.gtceu.uiv_16384a_laser_source_hatch": "16384安§2UIV§r激光源仓", - "block.gtceu.uiv_16384a_laser_target_hatch": "16384安§2UIV§r激光靶仓", - "block.gtceu.uiv_16777216a_laser_source_hatch": "16777216安§2UIV§r激光源仓", - "block.gtceu.uiv_16777216a_laser_target_hatch": "16777216安§2UIV§r激光靶仓", - "block.gtceu.uiv_262144a_laser_source_hatch": "262144安§2UIV§r激光源仓", - "block.gtceu.uiv_262144a_laser_target_hatch": "262144安§2UIV§r激光靶仓", - "block.gtceu.uiv_4194304a_laser_source_hatch": "4194304安§2UIV§r激光源仓", - "block.gtceu.uiv_4194304a_laser_target_hatch": "4194304安§2UIV§r激光靶仓", - "block.gtceu.uiv_65536a_laser_source_hatch": "65536安§2UIV§r激光源仓", - "block.gtceu.uiv_65536a_laser_target_hatch": "65536安§2UIV§r激光靶仓", - "block.gtceu.uiv_67108864a_laser_source_hatch": "67108864安§2UIV§r激光源仓", - "block.gtceu.uiv_67108864a_laser_target_hatch": "67108864安§2UIV§r激光靶仓", - "block.gtceu.uiv_buffer": "§2史诗缓存器 III§r", - "block.gtceu.uiv_dehydrator": "§2史诗脱水机 III§r", - "block.gtceu.uiv_energy_input_hatch_16a": "16安§2UIV§r能源仓", - "block.gtceu.uiv_energy_input_hatch_4a": "4安§2UIV§r能源仓", - "block.gtceu.uiv_energy_output_hatch_16a": "16安§2UIV§r动力仓", - "block.gtceu.uiv_energy_output_hatch_4a": "4安§2UIV§r动力仓", - "block.gtceu.uiv_huge_input_hatch": "§2UIV§r巨型输入仓", - "block.gtceu.uiv_huge_output_hatch": "§2UIV§r巨型输出仓", - "block.gtceu.uiv_lightning_processor": "§2史诗闪电处理机 III§r", - "block.gtceu.uiv_neutron_accelerator": "§2UIV 中子加速器§r", - "block.gtceu.uiv_parallel_hatch": "§2UIV§r并行控制仓", - "block.gtceu.uiv_substation_input_hatch_64a": "64安§2UIV§r变电能源仓", - "block.gtceu.uiv_substation_output_hatch_64a": "64安§2UIV§r变电动力仓", - "block.gtceu.uiv_world_data_scanner": "§2史诗世界信息扫描仪 III§r", - "block.gtceu.ulv_huge_input_hatch": "§8ULV§r巨型输入仓", - "block.gtceu.ulv_huge_output_hatch": "§8ULV§r巨型输出仓", - "block.gtceu.ulv_neutron_accelerator": "§8ULV 中子加速器§r", - "block.gtceu.ulv_primitive_magic_energy": "超低压原始魔法能源吸收器", - "block.gtceu.uv_1048576a_laser_source_hatch": "1048576安§3UV§r激光源仓", - "block.gtceu.uv_1048576a_laser_target_hatch": "1048576安§3UV§r激光靶仓", - "block.gtceu.uv_16384a_laser_source_hatch": "16384安§3UV§r激光源仓", - "block.gtceu.uv_16384a_laser_target_hatch": "16384安§3UV§r激光靶仓", - "block.gtceu.uv_16777216a_laser_source_hatch": "16777216安§3UV§r激光源仓", - "block.gtceu.uv_16777216a_laser_target_hatch": "16777216安§3UV§r激光靶仓", - "block.gtceu.uv_262144a_laser_source_hatch": "262144安§3UV§r激光源仓", - "block.gtceu.uv_262144a_laser_target_hatch": "262144安§3UV§r激光靶仓", - "block.gtceu.uv_4194304a_laser_source_hatch": "4194304安§3UV§r激光源仓", - "block.gtceu.uv_4194304a_laser_target_hatch": "4194304安§3UV§r激光靶仓", - "block.gtceu.uv_65536a_laser_source_hatch": "65536安§3UV§r激光源仓", - "block.gtceu.uv_65536a_laser_target_hatch": "65536安§3UV§r激光靶仓", - "block.gtceu.uv_67108864a_laser_source_hatch": "67108864安§3UV§r激光源仓", - "block.gtceu.uv_67108864a_laser_target_hatch": "67108864安§3UV§r激光靶仓", - "block.gtceu.uv_buffer": "§3终极缓存器§r", - "block.gtceu.uv_compressed_fusion_reactor": "压缩核聚变反应堆控制电脑 MK-III", - "block.gtceu.uv_dehydrator": "§3终极脱水机§r", - "block.gtceu.uv_huge_input_hatch": "§3UV§r巨型输入仓", - "block.gtceu.uv_huge_output_hatch": "§3UV§r巨型输出仓", - "block.gtceu.uv_lightning_processor": "§3终极闪电处理机§r", - "block.gtceu.uv_neutron_accelerator": "§3UV 中子加速器§r", - "block.gtceu.uv_world_data_scanner": "§3终极世界信息扫描仪§r§r", - "block.gtceu.uxv_1048576a_laser_source_hatch": "1048576安§eUXV§r激光源仓", - "block.gtceu.uxv_1048576a_laser_target_hatch": "1048576安§eUXV§r激光靶仓", - "block.gtceu.uxv_16384a_laser_source_hatch": "16384安§eUXV§r激光源仓", - "block.gtceu.uxv_16384a_laser_target_hatch": "16384安§eUXV§r激光靶仓", - "block.gtceu.uxv_16777216a_laser_source_hatch": "16777216安§eUXV§r激光源仓", - "block.gtceu.uxv_16777216a_laser_target_hatch": "16777216安§eUXV§r激光靶仓", - "block.gtceu.uxv_262144a_laser_source_hatch": "262144安§eUXV§r激光源仓", - "block.gtceu.uxv_262144a_laser_target_hatch": "262144安§eUXV§r激光靶仓", - "block.gtceu.uxv_4194304a_laser_source_hatch": "4194304安§eUXV§r激光源仓", - "block.gtceu.uxv_4194304a_laser_target_hatch": "4194304安§eUXV§r激光靶仓", - "block.gtceu.uxv_65536a_laser_source_hatch": "65536安§eUXV§r激光源仓", - "block.gtceu.uxv_65536a_laser_target_hatch": "65536安§eUXV§r激光靶仓", - "block.gtceu.uxv_67108864a_laser_source_hatch": "67108864安§eUXV§r激光源仓", - "block.gtceu.uxv_67108864a_laser_target_hatch": "67108864安§eUXV§r激光靶仓", - "block.gtceu.uxv_buffer": "§e史诗缓存器 IV§r", - "block.gtceu.uxv_dehydrator": "§e史诗脱水机 IV§r", - "block.gtceu.uxv_energy_input_hatch_16a": "16安§eUXV§r能源仓", - "block.gtceu.uxv_energy_input_hatch_4a": "4安§eUXV§r能源仓", - "block.gtceu.uxv_energy_output_hatch_16a": "16安§eUXV§r动力仓", - "block.gtceu.uxv_energy_output_hatch_4a": "4安§eUXV§r动力仓", - "block.gtceu.uxv_huge_input_hatch": "§eUXV§r巨型输入仓", - "block.gtceu.uxv_huge_output_hatch": "§eUXV§r巨型输出仓", - "block.gtceu.uxv_lightning_processor": "§e史诗闪电处理机 IV§r", - "block.gtceu.uxv_neutron_accelerator": "§eUXV 中子加速器§r", - "block.gtceu.uxv_parallel_hatch": "§eUXV§r并行控制仓", - "block.gtceu.uxv_substation_input_hatch_64a": "64安§eUXV§r变电能源仓", - "block.gtceu.uxv_substation_output_hatch_64a": "64安§eUXV§r变电动力仓", - "block.gtceu.uxv_world_data_scanner": "§e史诗世界信息扫描仪 IV§r", - "block.gtceu.vacuum_drying_furnace": "真空干燥炉", - "block.gtceu.void_fluid_drilling_rig": "虚空流体钻机", - "block.gtceu.void_miner": "虚空采矿机", - "block.gtceu.weather_control": "天气控制器", - "block.gtceu.wireless_data_transmitter_hatch": "无线光学数据源仓", - "block.gtceu.wireless_data_receiver_hatch": "无线光学数据靶仓", - "block.gtceu.wood_distillation": "木化工厂", - "block.gtceu.zpm_1048576a_laser_source_hatch": "1048576安§cZPM§r激光源仓", - "block.gtceu.zpm_1048576a_laser_target_hatch": "1048576安§cZPM§r激光靶仓", - "block.gtceu.zpm_16384a_laser_source_hatch": "16384安§cZPM§r激光源仓", - "block.gtceu.zpm_16384a_laser_target_hatch": "16384安§cZPM§r激光靶仓", - "block.gtceu.zpm_16777216a_laser_source_hatch": "16777216安§cZPM§r激光源仓", - "block.gtceu.zpm_16777216a_laser_target_hatch": "16777216安§cZPM§r激光靶仓", - "block.gtceu.zpm_262144a_laser_source_hatch": "262144安§cZPM§r激光源仓", - "block.gtceu.zpm_262144a_laser_target_hatch": "262144安§cZPM§r激光靶仓", - "block.gtceu.zpm_4194304a_laser_source_hatch": "4194304安§cZPM§r激光源仓", - "block.gtceu.zpm_4194304a_laser_target_hatch": "4194304安§cZPM§r激光靶仓", - "block.gtceu.zpm_65536a_laser_source_hatch": "65536安§cZPM§r激光源仓", - "block.gtceu.zpm_65536a_laser_target_hatch": "65536安§cZPM§r激光靶仓", - "block.gtceu.zpm_67108864a_laser_source_hatch": "67108864安§cZPM§r激光源仓", - "block.gtceu.zpm_67108864a_laser_target_hatch": "67108864安§cZPM§r激光靶仓", - "block.gtceu.zpm_buffer": "§c精英缓存器 III§r", - "block.gtceu.zpm_compressed_fusion_reactor": "压缩核聚变反应堆控制电脑 MK-II", - "block.gtceu.zpm_dehydrator": "§c精英脱水机 III§r", - "block.gtceu.zpm_fluid_drilling_rig": "§c无尽流体钻机§r", - "block.gtceu.zpm_huge_input_hatch": "§cZPM§r巨型输入仓", - "block.gtceu.zpm_huge_output_hatch": "§cZPM§r巨型输出仓", - "block.gtceu.zpm_lightning_processor": "§c精英闪电处理机 III§r", - "block.gtceu.zpm_naquadah_reactor": "§c精英硅岩反应堆§r", - "block.gtceu.zpm_neutron_accelerator": "§cZPM 中子加速器§r", - "block.gtceu.zpm_world_data_scanner": "§c精英世界信息扫描仪 III§r", - "gtceu.advanced_hyper_reactor": "进阶超能反应", - "gtceu.aggregation_device": "聚合装置", - "gtceu.annihilate_generator": "湮灭发电机", - "gtceu.assembler_module": "太空组装", - "gtceu.atomic_energy_excitation": "原子能激发", - "gtceu.bedrock_drilling_rig": "基岩钻机", - "gtceu.block_conversion": "方块转换", - "gtceu.casings.tier": "等级:%s", - "gtceu.circuit_assembly_line": "电路装配线", - "gtceu.circuit_printer": "编程电路复制", - "gtceu.component_assembly_line": "部件装配", - "gtceu.cosmos_simulation": "宇宙模拟", - "gtceu.create_aggregation": "创造聚合", - "gtceu.decay_hastener": "衰变加速", - "gtceu.dehydrator": "脱水机", - "gtceu.desulfurizer": "脱硫", - "gtceu.digestion_treatment": "煮解", - "gtceu.dimensional_focus_engraving_array": "维度聚焦激光蚀刻", - "gtceu.dimensionally_transcendent_mixer": "超维度搅拌", - "gtceu.dimensionally_transcendent_plasma_forge": "超维度熔炼", - "gtceu.disassembly": "拆解", - "gtceu.dissolution_treatment": "溶解", - "gtceu.distort": "深度化学扭曲仪", - "gtceu.door_of_create": "创造之门", - "gtceu.dragon_egg_copier": "龙蛋复制", - "gtceu.drilling_module": "太空钻井", - "gtceu.dyson_sphere": "戴森球", - "gtceu.electric_implosion_compressor": "电力聚爆压缩机", - "gtceu.element_copying": "元素复制机", - "gtceu.fishing_ground": "渔场", - "gtceu.fission_reactor": "裂变反应堆", - "gtceu.flotating_beneficiation": "浮游选矿", - "gtceu.fuel_refining": "燃料精炼", - "gtceu.gravitation_shockburst": "强引力震爆", - "gtceu.greenhouse": "温室", - "gtceu.heat_exchanger": "热交换机", - "gtceu.hyper_reactor": "超能反应", - "gtceu.incubator": "培养缸", - "gtceu.integrated_ore_processor": "集成矿石处理", - "gtceu.isa_mill": "湿法碾磨", - "gtceu.jei.bedrock_fluid.benzene_deposit": "苯矿藏", - "gtceu.jei.bedrock_fluid.ceres_krypton_deposit": "氪矿藏", - "gtceu.jei.bedrock_fluid.ceres_neon_deposit": "氖矿藏", - "gtceu.jei.bedrock_fluid.ceres_radon_deposit": "氡矿藏", - "gtceu.jei.bedrock_fluid.ceres_xenon_deposit": "氙矿藏", - "gtceu.jei.bedrock_fluid.charcoal_byproducts": "木炭副产矿藏", - "gtceu.jei.bedrock_fluid.chlorine": "氯矿藏", - "gtceu.jei.bedrock_fluid.coal_gas_deposit": "煤气矿藏", - "gtceu.jei.bedrock_fluid.deuterium_deposit": "氘矿藏", - "gtceu.jei.bedrock_fluid.flat_heavy_oil_deposit": "超平坦重油矿藏", - "gtceu.jei.bedrock_fluid.flat_light_oil_deposit": "超平坦轻油矿藏", - "gtceu.jei.bedrock_fluid.flat_natural_gas_deposit": "超平坦天然气矿藏", - "gtceu.jei.bedrock_fluid.flat_oil_deposit": "超平坦石油矿藏", - "gtceu.jei.bedrock_fluid.flat_raw_oil_deposit": "超平坦原油矿藏", - "gtceu.jei.bedrock_fluid.flat_salt_water_deposit": "超平坦盐水矿藏", - "gtceu.jei.bedrock_fluid.fluorine": "氟矿藏", - "gtceu.jei.bedrock_fluid.helium3_deposit": "氦-3矿藏", - "gtceu.jei.bedrock_fluid.helium_deposit": "氦矿藏", - "gtceu.jei.bedrock_fluid.hydrochloric_acid_deposit": "盐酸矿藏", - "gtceu.jei.bedrock_fluid.methane_deposit": "甲烷矿藏", - "gtceu.jei.bedrock_fluid.nitric_acid_deposit": "硝酸矿藏", - "gtceu.jei.bedrock_fluid.radon_deposit": "氡矿藏", - "gtceu.jei.bedrock_fluid.sulfuric_acid_deposit": "硫酸矿藏", - "gtceu.jei.bedrock_fluid.unknowwater": "不明液体矿藏", - "gtceu.jei.bedrock_fluid.void_heavy_oil_deposit": "虚空重油矿藏", - "gtceu.jei.bedrock_fluid.void_light_oil_deposit": "虚空轻油矿藏", - "gtceu.jei.bedrock_fluid.void_natural_gas_deposit": "虚空天然气矿藏", - "gtceu.jei.bedrock_fluid.void_oil_deposit": "虚空石油矿藏", - "gtceu.jei.bedrock_fluid.void_raw_oil_deposit": "虚空原油矿藏", - "gtceu.jei.bedrock_fluid.void_salt_water_deposit": "虚空盐水矿藏", - "gtceu.jei.ore_vein.apatite_vein_ad": "磷矿脉", - "gtceu.jei.ore_vein.bauxite_vein_ad": "金红石矿脉", - "gtceu.jei.ore_vein.beryllium_vein_aw": "绿宝石矿脉", - "gtceu.jei.ore_vein.calorite_vein_ad": "耐热矿脉", - "gtceu.jei.ore_vein.celestine_vein_ad": "天青石矿脉", - "gtceu.jei.ore_vein.certus_quartz_vein_aw": "AE2矿脉", - "gtceu.jei.ore_vein.desh_vein_ad": "戴斯矿脉", - "gtceu.jei.ore_vein.molybdenum_vein_aw": "钼矿脉", - "gtceu.jei.ore_vein.monazite_vein_ad": "群居石矿脉", - "gtceu.jei.ore_vein.naquadah_vein_ad": "硅岩矿脉", - "gtceu.jei.ore_vein.nickel_vein_ad": "镍矿脉", - "gtceu.jei.ore_vein.olivine_vein_ad": "橄榄石矿脉", - "gtceu.jei.ore_vein.ostrum_vein_ad": "紫金矿脉", - "gtceu.jei.ore_vein.pitchblende_vein_ad": "铀矿脉", - "gtceu.jei.ore_vein.plutonium_vein_ad": "钚矿脉", - "gtceu.jei.ore_vein.quartzite_vein_aw": "石英岩矿脉", - "gtceu.jei.ore_vein.saltpeter_vein_aw": "硝石矿脉", - "gtceu.jei.ore_vein.scheelite_vein_ad": "钨矿脉", - "gtceu.jei.ore_vein.sheldonite_vein_ad": "铂系矿脉", - "gtceu.jei.ore_vein.stibnite_vein_aw": "锑辉矿脉", - "gtceu.jei.ore_vein.sulfur_vein_ad": "硫矿脉", - "gtceu.jei.ore_vein.sulfur_vein_aw": "硫磺矿脉", - "gtceu.jei.ore_vein.topaz_vein_aw": "黄玉矿脉", - "gtceu.jei.ore_vein.zircon_vein_ad": "锆石矿脉", - "gtceu.large_gas_collector": "大型集气室", - "gtceu.large_naquadah_reactor": "大型硅岩反应", - "gtceu.large_recycler": "材料回收", - "gtceu.large_void_miner": "精准矿石模式", - "gtceu.lava_furnace": "熔岩炉", - "gtceu.lightning_processor": "闪电处理", - "gtceu.machine.advanced_infinite_driller.not_fluid_head": "无钻头", - "gtceu.machine.advanced_infinite_driller.no_coil": "线圈等级过低", - "gtceu.machine.advanced_infinite_driller.range": "工作范围: %s", - "gtceu.machine.advanced_infinite_driller.heat": "最大温度: %sK / 工作温度: %sK", - "gtceu.machine.advanced_infinite_driller.current_heat": "当前温度: %sK", - "gtceu.machine.advanced_infinite_driller.fast": "高速模式: %s ", - "gtceu.machine.advanced_infinite_driller.process": "损坏: %s", - "gtceu.machine.advanced_infinite_driller.drilled_fluid": "流体: %s 产量: %s", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.0": "更加高级的无尽流体钻井", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.1": "可以更加高效的抽取流体, 与此同时钻机需要一定温度来启动,可以通入液态烈焰来升温", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.2": "可放入中子素钻头(50000)/振金钻头(100000)", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.3": "基础温度: 8000K(泰坦钢线圈), 使用更高级的线圈可以提供更高的额外温度", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.4": "钻机在不同的工作温度下会产生热量,产热公式为(温度/2000)", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.5": "随着温度提升,效率也会提升.当温度超过临界值,钻头将会融毁", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.6": "可以通入不同的流体来降温, 可用冷却液如下.冷却固定消耗为200B/5t", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.7": "蒸馏水 1K ", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.8": "液态氧 2K ", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.9": "液态氦 4K", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.10": "液态烈焰消耗公式为(当前温度^1.3)", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.11": "可使用温度传感器", - "gtceu.machine.advanced_neutron_activator.tooltip.1": "工作时会消耗对应电量,自动提供中子动能", - "gtceu.machine.advanced_neutron_activator.tooltip.2": "中子动能自动转换比例为2eu -> 1ev", - "gtceu.machine.advanced_assembly_line.tooltip.0": "可拓展至64格", - "gtceu.machine.advanced_assembly_line.tooltip.1": "只能使用数据靶仓", - "gtceu.machine.advanced_hyper_reactor.tooltip.0": "提供不同等离子体获得不同并行", - "gtceu.machine.advanced_hyper_reactor.tooltip.1": "星辉:8,致密中子:16", - "gtceu.machine.advanced_integrated_ore_processor.tooltip.0": "最大并行数:2147483647", - "gtceu.machine.aggregation_device.tooltip.0": "电压等级每高出UEV一级最大并行数x2", - "gtceu.machine.assembly_line.tooltip.0": "每个装配线单元可使速度提升1%", - "gtceu.machine.available_recipe_map_10.tooltip": "可用配方类型:%s,%s,%s,%s,%s,%s,%s,%s,%s,%s", - "gtceu.machine.available_recipe_map_11.tooltip": "可用配方类型:%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s", - "gtceu.machine.available_recipe_map_5.tooltip": "可用配方类型:%s,%s,%s,%s,%s", - "gtceu.machine.available_recipe_map_6.tooltip": "可用配方类型:%s,%s,%s,%s,%s,%s", - "gtceu.machine.available_recipe_map_7.tooltip": "可用配方类型:%s,%s,%s,%s,%s,%s,%s", - "gtceu.machine.available_recipe_map_8.tooltip": "可用配方类型:%s,%s,%s,%s,%s,%s,%s,%s", - "gtceu.machine.available_recipe_map_9.tooltip": "可用配方类型:%s,%s,%s,%s,%s,%s,%s,%s,%s", - "gtceu.machine.bedrock_drilling_rig.tooltip.0": "需要基岩在钻头下方", - "gtceu.machine.bedrock_drilling_rig.tooltip.1": "每次运行都有10%的概率破坏基岩", - "gtceu.machine.blaze_blast_furnace.tooltip.0": "需每秒提供§b10x(功率÷120)^1/2§r的§e液态烈焰§r", - "gtceu.machine.blaze_blast_furnace.tooltip.1": "最大并行数固定为64", - "gtceu.machine.block_conversion_room.am": "每次转化数量:%s", - "gtceu.machine.block_conversion_room.tooltip.0": "每秒随机选取机器内部一个位置的方块进行转化,运行前需设置电路为1", - "gtceu.machine.block_conversion_room.tooltip.1": "电压等级每高出MV1级,每次转换方块数量+4,且不会重复", - "gtceu.machine.chemical_distort.tooltip.0": "线圈温度每高出配方温度100K,并行加4", - "gtceu.machine.chemical_distort.tooltip.1": "更高的电压不会提供额外温度", - "gtceu.machine.chemical_plant.tooltip.0": "线圈等级每高出白铜一级能耗与速度x5%", - "gtceu.machine.circuit_assembly_line.tooltip.0": "在主机中放入同配方的机器人可以获得对应数量x2的并行", - "gtceu.machine.cold_ice_freezer.tooltip.0": "需每秒提供§b10x电压等级^2§r的§b液态冰§r", - "gtceu.machine.create_computation.tooltip.0": "输入电压:§4§lMAX§r", - "gtceu.machine.dimensionally_transcendent_dirt_forge.tooltip.0": "拥有524288的最大并行,且直接完成配方", - "gtceu.machine.dimensionally_transcendent_mixer.tooltip.0": "运行搅拌机配方时耗时倍数为0.2", - "gtceu.machine.duration_multiplier.tooltip": "耗时倍数:%s", - "gtceu.machine.dyson_sphere.number": "发射次数:%s / 10000", - "gtceu.machine.dyson_sphere.tooltip.0": "发射戴森球模块后开始工作", - "gtceu.machine.dyson_sphere.tooltip.1": "每次运行都有(模块数量/128 + 1)%的概率损坏一次模块", - "gtceu.machine.dyson_sphere.tooltip.2": "当损坏高于60%时,输出效率随损坏值由100%逐渐降低到20%,并输出随损坏值增强的红石信号", - "gtceu.machine.dyson_sphere.tooltip.3": "当损坏达到100%时减少一次模块发射数量,并重制损坏值", - "gtceu.machine.dyson_sphere.tooltip.4": "在损坏值高于60%时发射不会增加发射次数,但会重制损坏值", - "gtceu.machine.dyson_sphere.tooltip.5": "产能功率,和需求算力由发射的模块数量决定", - "gtceu.machine.dyson_sphere.tooltip.6": "每次发射可使功率增加1A MAX", - "gtceu.machine.dyson_sphere.voltage": "最大能量输出:%s EU/t", - "gtceu.machine.efficiency.tooltip": "§7效率:§r%s", - "gtceu.machine.electric_blast_furnace.tooltip.a": "耗时倍数:max(配方温度/炉温,0.5)", - "gtceu.machine.engraving_laser_plant.tooltip.0": "精密激光模式不支持并行", - "gtceu.machine.eut_multiplier.tooltip": "耗能倍数:%s", - "gtceu.machine.eye_of_harmony.tooltip.0": "创造一个微缩宇宙,并获取里面的资源", - "gtceu.machine.eye_of_harmony.tooltip.1": "这台多方块机器需要太多EU,无法用常规手段供能", - "gtceu.machine.eye_of_harmony.tooltip.2": "由无线EU网络直接供给EU,具体数值可在GUI内查看", - "gtceu.machine.eye_of_harmony.tooltip.3": "执行特殊超频模式,每提升16倍功率提升2倍速度,超频由编程电路调节", - "gtceu.machine.eye_of_harmony.tooltip.4": "工作前需设置好电路,1号不执行超频,2-4分别执行1,2,3次超频", - "gtceu.machine.eye_of_harmony.tooltip.5": "启动需1024B的宇宙素,与1024KB的氢和氦", - "gtceu.machine.eye_of_harmony.tooltip.6": "氢和氦存储在机器内部,在机器准备工作之前会持续消耗", - "gtceu.machine.fission_reactor.cooler": "冷却组件数量:%s,相邻数:%s", - "gtceu.machine.fission_reactor.damaged": "损坏:%s", - "gtceu.machine.fission_reactor.fuel": "燃料组件数量:%s,相邻数:%s", - "gtceu.machine.fission_reactor.heat": "堆温:%s K", - "gtceu.machine.fission_reactor.tooltip.0": "反应堆在运行前会获得最大并行数量为燃料组件数量的并行", - "gtceu.machine.fission_reactor.tooltip.1": "反应堆在运行过程中会根据条件升温,每秒升温值(K)=配方产热x(1+相邻燃料棒数量)", - "gtceu.machine.fission_reactor.tooltip.10": "如果供给量是需求量的n倍,则执行特殊超频,超频次数为n", - "gtceu.machine.fission_reactor.tooltip.11": "再次消耗需求量的冷却液,并减少1秒时间,如果无法供给冷却液则中断超频,如果进度到达100%则中断超频并消耗一次需求量的冷却液使温度降低1K", - "gtceu.machine.fission_reactor.tooltip.12": "由蒸馏水作为冷却液时将产生蒸汽,产生量:消耗量xmin(160,160/(1.4^(373-温度)))", - "gtceu.machine.fission_reactor.tooltip.13": "由钠钾合金作为冷却液时产生热钠钾合金,产生量=消耗量,如果温度高于825K则产生同等量的超临界钠钾合金", - "gtceu.machine.fission_reactor.tooltip.14": "无论反应堆是否具有消耗冷却液的条件都能执行配方", - "gtceu.machine.fission_reactor.tooltip.15": "反应堆停止工作后温度将每秒降低1K", - "gtceu.machine.fission_reactor.tooltip.2": "如果温度高于1500K,反应堆将会损坏,损坏达到100%后反应堆爆炸", - "gtceu.machine.fission_reactor.tooltip.3": "在运行过程中提供冷却液并根据不同冷却液的冷却液系数来控制温度", - "gtceu.machine.fission_reactor.tooltip.4": "冷却液系数:蒸馏水:800,钠钾合金:20", - "gtceu.machine.fission_reactor.tooltip.5": "反应堆冷却有如下参数:", - "gtceu.machine.fission_reactor.tooltip.6": "最低冷却液需求量和最高冷却液供给量", - "gtceu.machine.fission_reactor.tooltip.7": "最低需求量=配方产热x实际并行数量x当前温度/1500", - "gtceu.machine.fission_reactor.tooltip.8": "最高供给量=(冷却组件-(相邻数/3))x8", - "gtceu.machine.fission_reactor.tooltip.9": "当供给量>=需求量时达到消耗冷却液条件,消耗提供的冷却液,消耗量为需求量x冷却液系数,并阻止反应堆升温", - "gtceu.machine.flotation_cell_regulator.tooltip.0": "工业级浮游选矿池", - "gtceu.machine.fusion_reactor.tooltip.a": "机器配方等级每高出机器等级1级,最大并行数x4,最高16", - "gtceu.machine.generator_array.tooltip.0": "强大的运行环境会让机器中的小发电机功率x2。", - "gtceu.machine.generator_array.tooltip.1": "可以开启无线电网模式,产生的电能会直接送入无线电网。", - "gtceu.machine.generator_array.wireless": "无线电网模式:", - "gtceu.machine.greenhouse.tooltip.0": "需要阳光才能运行", - "gtceu.machine.greenhouse.tooltip.1": "如太阳光照不足,速度就会减缓", - "gtceu.machine.heat_exchanger.tooltip.0": "每次处理全部输入的热流体", - "gtceu.machine.heat_exchanger.tooltip.1": "需要保证输入的冷却液能将流体全部冷却", - "gtceu.machine.hyper_reactor.tooltip.0": "每次运行前提供额外的1mb等离子体将获得16的并行", - "gtceu.machine.hyper_reactor.tooltip.1": "不同燃料所需的等离子体不同", - "gtceu.machine.hyper_reactor.tooltip.2": "从1-4顺序为:山铜,末影,魔金,亚稳态𬭶", - "gtceu.machine.integrated_ore_processor.tooltip.0": "一步完成矿石处理", - "gtceu.machine.integrated_ore_processor.tooltip.1": "1号电路为研磨-研磨-离心", - "gtceu.machine.integrated_ore_processor.tooltip.2": "2号电路为研磨-洗矿-热离-研磨", - "gtceu.machine.integrated_ore_processor.tooltip.3": "3号电路为研磨-洗矿-研磨-离心", - "gtceu.machine.integrated_ore_processor.tooltip.4": "4号电路为研磨-洗矿-筛选-离心", - "gtceu.machine.integrated_ore_processor.tooltip.5": "5号电路为研磨-浸洗-热离-研磨", - "gtceu.machine.integrated_ore_processor.tooltip.6": "6号电路为研磨-浸洗-研磨-离心", - "gtceu.machine.integrated_ore_processor.tooltip.7": "7号电路为研磨-浸洗-筛选-离心", - "gtceu.machine.isa_mill.tooltip.0": "工业级湿法碾磨", - "gtceu.machine.large_arc_smelter.tooltip.0": "运行闪电处理配方时耗时x4", - "gtceu.machine.large_block_conversion_room.tooltip.1": "电压等级每高出MV1级,每次转换方块数量+64,且不会重复", - "gtceu.machine.large_block_conversion_room.tooltip.2": "放入转换模拟卡会启用虚拟模式, 放入高速转换模拟卡后单次可以转化整个总线内方块", - "gtceu.machine.large_gas_collector.tooltip.0": "最大并行数:100000", - "gtceu.machine.large_greenhouse.tooltip.0": "无需要阳光就能运行", - "gtceu.machine.large_recycler.tooltip.0": "电压等级每高出EV1级,最大并行数x4", - "gtceu.machine.large_rock_crusher.tooltip.0": "需要在输入仓中放入对应流体", - "gtceu.machine.large_steam_input_hatch.tooltip.0": "将蒸汽多方块配方限制提升到MV,并解锁超频功能", - "gtceu.machine.large_void_miner.tooltip.0": "精准模式消耗精华采集指定矿脉", - "gtceu.machine.large_void_miner.tooltip.1": "随机模式消耗10KB的钻井液和更长的耗时随机采集所有矿石,随机模式注意输出空间要足够", - "gtceu.machine.lightning_rod.tooltip.0": "上方避雷针被雷击后产生大量能量", - "gtceu.machine.lightning_rod.tooltip.1": "每0.5秒只能产生一次能量,且有一定几率破坏上方避雷针", - "gtceu.machine.lightning_rod.tooltip.2": "如果存储能量已满,机器将会爆炸", - "gtceu.machine.module": "已安装的模块数量:%s", - "gtceu.machine.me.me_dual_hatch_stock.data_stick.name":"§oME库存输入总成配置数据", - "gtceu.machine.multiple_recipes.tooltip": "支持跨配方并行", - "gtceu.machine.nano_core.tooltip.0": "能够运行任意等级的纳米锻炉配方", - "gtceu.machine.nano_core.tooltip.1": "处理速度固定为20倍", - "gtceu.machine.nano_forge.tooltip.0": "放入对应的纳米蜂群才能工作,并且按蜂群数量来并行", - "gtceu.machine.nano_forge_1.tooltip.0": "并行数为蜂群数量", - "gtceu.machine.nano_forge_2.tooltip.0": "处理2阶配方时,并行数为蜂群数量", - "gtceu.machine.nano_forge_2.tooltip.1": "处理1阶配方时并行数量翻倍,超频模式改为无损超频", - "gtceu.machine.nano_forge_3.tooltip.0": "处理3阶配方时,并行数为蜂群数量", - "gtceu.machine.nano_forge_3.tooltip.1": "处理2阶配方时并行数量翻倍,超频模式改为无损超频", - "gtceu.machine.nano_forge_3.tooltip.2": "处理1阶配方时并行数翻4倍,超频模式改为每提高4倍功率获得8倍提速", - "gtceu.machine.neutron_accelerator.tooltip.0": "§6最大EU消耗:§r%s", - "gtceu.machine.neutron_accelerator.tooltip.1": "§b每点EU都会转化为§e10~20-eV§b中子动能", - "gtceu.machine.neutron_activator.efficiency": "动能消耗倍速:%s", - "gtceu.machine.neutron_activator.ev": "当前中子动能:%seV", - "gtceu.machine.neutron_activator.height": "高度:%s", - "gtceu.machine.neutron_activator.time": "耗时:%s", - "gtceu.machine.neutron_activator.tooltip.0": "§7超光速运动!", - "gtceu.machine.neutron_activator.tooltip.1": "额外的高速管道方块提供配方时间减免,同时降低中子加速器的效率", - "gtceu.machine.neutron_activator.tooltip.2": "时间减免与加速效率为0.95^额外方块数量", - "gtceu.machine.neutron_activator.tooltip.3": "没有中子加速器运行时,中子动能每秒降低§e72KeV§r中子动能", - "gtceu.machine.neutron_activator.tooltip.4": "输入石墨/铍粉可以立即吸收§e10MeV§r中子动能", - "gtceu.machine.neutron_sensor.tooltip.0": "基于§6中子动能§7输出红石信号,右键以打开GUI进行设置", - "gtceu.machine.heat_sensor.tooltip.0": "基于§6温度§7输出红石信号,右键以打开GUI进行设置", - "gtceu.machine.off": "关闭", - "gtceu.machine.on": "打开", - "gtceu.machine.parallel_hatch_mk10.tooltip": "允许同时处理至多4096个配方。", - "gtceu.machine.parallel_hatch_mk11.tooltip": "允许同时处理至多16384个配方。", - "gtceu.machine.parallel_hatch_mk12.tooltip": "允许同时处理至多65536个配方。", - "gtceu.machine.parallel_hatch_mk13.tooltip": "允许同时处理至多262144个配方。", - "gtceu.machine.parallel_hatch_mk14.tooltip": "允许同时处理至多1048576个配方。", - "gtceu.machine.parallel_hatch_mk15.tooltip": "允许同时处理至多4194304个配方。", - "gtceu.machine.parallel_hatch_mk16.tooltip": "允许同时处理至多16777216个配方。", - "gtceu.machine.parallel_hatch_mk9.tooltip": "允许同时处理至多1024个配方。", - "gtceu.machine.pcb_factory.tooltip.0": "放入纳米蜂群可获得减免", - "gtceu.machine.pcb_factory.tooltip.1": "时间倍数/每个:金:0.5%,振金:1%", - "gtceu.machine.pcb_factory.tooltip.2": "振金还可使能耗降低4倍", - "gtceu.machine.primitive_magic_energy.tooltip.0": "无尽地吸收机器上方末地水晶的能量", - "gtceu.machine.processing_plant.tooltip.0": "每种模式都需要放入一个对应电压等级的机器才能运行", - "gtceu.machine.processing_plant.tooltip.1": "电压等级每高出LV一级,最大并行数+4", - "gtceu.machine.resource_collection.tooltip.0": "最大并行数:4^(动力模块等级-1)", - "gtceu.machine.slaughterhouse.is_spawn": "实体生成:", - "gtceu.machine.slaughterhouse.tooltip.0": "电动刷怪塔,自动杀怪", - "gtceu.machine.slaughterhouse.tooltip.1": "电压等级每高出MV1级,每次处理次数+8", - "gtceu.machine.slaughterhouse.tooltip.2": "运行前需设置电路,1号电路为非敌对生物,2号为敌对生物", - "gtceu.machine.slaughterhouse.tooltip.3": "打开控制器开关实体模式,实体生成模式为玩家击杀的实际掉落,需要非和平模式", - "gtceu.machine.slaughterhouse.tooltip.4": "实体生成模式为玩家击杀的实际掉落,需要非和平模式", - "gtceu.machine.slaughterhouse.tooltip.5": "非实体生成模式为虚拟掉落,可以和平模式,由玩家击杀的掉落物无法获取", - "gtceu.machine.space_elevator.set_out": "启程", - "gtceu.machine.space_elevator.tooltip.0": "可安装最多8个拓展模块", - "gtceu.machine.space_elevator.tooltip.1": "提升电压等级可为模块提供耗时减免", - "gtceu.machine.space_elevator.tooltip.2": "更高的电压需要更多的算力来维持", - "gtceu.machine.space_probe_surface_reception.tooltip.0": "不要遮挡", - "gtceu.machine.star_ultimate_material_forge_factory.tooltip.0": "最大并行数:1000", - "gtceu.machine.super_computation.tooltip.0": "根据不同的电压等级获得算力输出", - "gtceu.machine.super_computation.tooltip.1": "且每种算力输出需要不同的电路主机8个", - "gtceu.machine.super_computation.tooltip.2": "提供§2UIV§r级电压时,需要放入§b光学处理器主机§r,并提供1024CWU/t", - "gtceu.machine.super_computation.tooltip.3": "提供§eUXV§r级电压时,需要放入§b奇异处理器主机§r,并提供2048CWU/t", - "gtceu.machine.super_computation.tooltip.4": "提供§9§lOpV§r级电压时,需要放入§b寰宇处理器主机§r,并提供4096CWU/t", - "gtceu.machine.super_computation.tooltip.5": "提供§4§lMAX§r级电压时,需要放入§b超因果处理器主机§r,并提供8192CWU/t", - "gtceu.machine.suprachronal_assembly_line.tooltip.0": "§8§l不可视之触§r", - "gtceu.machine.suprachronal_assembly_line.tooltip.1": "可在两侧拓展模块,模块与主机共享并行数", - "gtceu.machine.suprachronal_assembly_line_module.tooltip.0": "安装在超时空装配线两侧", - "gtceu.machine.simulation_machine.tooltip.0": "将主方块放入机器中, 使用终端右键可放置/预览机器内内主方块的结构", - "gtceu.machine.vacuum_drying_furnace.tooltip.0": "运行真空干燥配方时:", - "gtceu.machine.vacuum_drying_furnace.tooltip.1": "运行脱水配方时:", - "gtceu.machine.wireless_data_hatch.bind": "无线数据仓绑定完成。", - "gtceu.machine.wireless_data_transmitter_hatch.tooltip.1": "从多方块结构输出研究数据", - "gtceu.machine.wireless_data_transmitter_hatch.tooltip.2": "需要使用闪存右键无线光学数据靶仓和无线数据源仓光学进行绑定。", - "gtceu.machine.wireless_data_transmitter_hatch.to_bind": "源仓数据读取完成,请右键靶仓进行绑定。", - "gtceu.machine.wireless_data_transmitter_hatch.bind": "已绑定无线光学数据靶仓(%s)。", - "gtceu.machine.wireless_data_transmitter_hatch.unbind": "未绑定无线光学数据靶仓。", - "gtceu.machine.wireless_data_receiver_hatch.tooltip.1": "为多方块结构输入研究数据", - "gtceu.machine.wireless_data_receiver_hatch.tooltip.2": "需要使用闪存右键无线光学数据靶仓和无线数光学数据仓进行绑定。", - "gtceu.machine.wireless_data_receiver_hatch.to_bind": "靶仓数据读取完成,请右键源仓进行绑定。", - "gtceu.machine.wireless_data_receiver_hatch.bind": "已绑定无线光学数据源仓(%s)。", - "gtceu.machine.wireless_data_receiver_hatch.unbind": "未绑定无线光学数据源仓。", - "gtceu.machine.weather_control.tooltip.0": "1号电路切换晴天", - "gtceu.machine.weather_control.tooltip.1": "2号电路切换雨天", - "gtceu.machine.weather_control.tooltip.2": "3号电路切换雷暴", - "gtceu.machine.zpm_fluid_drilling_rig.tooltip": "甚至无消耗", - "gtceu.magic_manufacturer": "魔力生成", - "gtceu.mass_fabricator": "质量发生器", - "gtceu.matter_fabricator": "物质生成机", - "gtceu.miner_module": "太空采矿", - "gtceu.multiblock.coil_parallel": "线圈温度每高出900K,并行数x2", - "gtceu.multiblock.dissolving_tank.tooltip.0": "必须保证输入的流体与配方流体比例相同,否则无产物输出", - "gtceu.multiblock.large_combustion_engine.Joint_boosted": "§b联合促燃中", - "gtceu.multiblock.large_combustion_engine.supply_dinitrogen_tetroxide_to_boost": "提供四氧化二氮来联合促燃", - "gtceu.multiblock.laser.tooltip": "允许使用激光仓", - "gtceu.multiblock.mega_fluid_heater": "宿舍限电1500W,你们这是违规电器!", - "gtceu.multiblock.oc_amount": "超频次数:%s", - "gtceu.multiblock.pattern.error.tier": "§c必须使用同种等级方块§r", - "gtceu.multiblock.steam_parallel_machine.modification_oc": "修改超频次数:", - "gtceu.multiblock.steam_parallel_machine.oc": "每次超频将减少2倍耗时和增加3倍蒸汽消耗", - "gtceu.multiblock.uev_fusion_reactor.description": "核聚变反应堆MK-V是台大型多方块结构,用于融合元素形成更重的元素。它仅可使用UEV等级的能源仓。每个能源仓可增加160MEU的能量缓存,最大能量缓存为2560MEU。", - "gtceu.multiblock.uhv_fusion_reactor.description": "核聚变反应堆MK-IV是台大型多方块结构,用于融合元素形成更重的元素。它仅可使用UHV等级的能源仓。每个能源仓可增加80MEU的能量缓存,最大能量缓存为1280MEU。", - "gtceu.multiblock.fusion_reactor_energy": "EU: %dM / %dM", - "gtceu.nano_forge": "纳米蜂群工厂", - "gtceu.naquadah_reactor": "硅岩反应堆", - "gtceu.neutron_activator": "中子活化", - "gtceu.neutron_compressor": "奇点压缩", - "gtceu.pcb_factory": "PCB工厂", - "gtceu.petrochemical_plant": "石化工厂", - "gtceu.plasma_condenser": "等离子冷凝", - "gtceu.precision_assembler": "精密组装", - "gtceu.precision_laser_engraver": "精密激光蚀刻", - "gtceu.primitive_void_ore": "原始虚空采矿", - "gtceu.qft": "量子操纵者", - "gtceu.random_ore": "随机矿石模式", - "gtceu.rare_earth_centrifugal": "稀土离心", - "gtceu.recipe.ca_tier": "外壳等级:%s", - "gtceu.recipe.cleanroom_law.display_name": "绝对超净间", - "gtceu.recipe.ev_max": "最大中子动能:%s MeV", - "gtceu.recipe.ev_min": "最小中子动能:%s MeV", - "gtceu.recipe.evt": "每刻中子动能消耗:%s KeV", - "gtceu.recipe.frheat": "每秒升温:%s K", - "gtceu.recipe.grindball": "研磨球材质:%s", - "gtceu.recipe.nano_forge_tier": "纳米锻炉等级:%s", - "gtceu.recipe.sepm_tier": "需要动力模块:MK%s", - "gtceu.recipe.stellar_containment_tier": "恒星热力容器等级:%s", - "gtceu.rocket_engine": "火箭引擎", - "gtceu.semi_fluid_generator": "半流质发电机", - "gtceu.slaughterhouse": "屠宰场", - "gtceu.small_gas_collector": "小型集气室", - "gtceu.space_elevator": "太空电梯", - "gtceu.space_probe_surface_reception": "宇宙探测", - "gtceu.space_cosmic_probe_receivers": "天基宇宙探测", - "gtceu.sps_crafting": "超临界合成", - "gtceu.stellar_forge": "恒星热能熔炼", - "gtceu.super_computation": "超级计算机", - "gtceu.super_particle_collider": "粒子对撞", - "gtceu.supercritical_steam_turbine": "超临界蒸汽涡轮", - "gtceu.suprachronal_assembly_line": "超时空装配线", - "gtceu.tier.advanced": "高级", - "gtceu.tier.base": "基础", - "gtceu.tier.ultimate": "终极", - "gtceu.top.electricity": "%sA %s", - "gtceu.top.extra_output": "剩余%s项重复输出已隐藏", - "gtceu.ultimate_material_forge": "终极物质锻造", - "gtceu.universal.tooltip.ampere_out": "§b输出电流:§r%sA", - "gtceu.vacuum_drying": "真空干燥", - "gtceu.void_fluid_drilling_rig": "虚空流体钻机", - "gtceu.void_miner": "虚空采矿", - "gtceu.weather_control": "天气控制", - "gtceu.wood_distillation": "木化工厂", - "gtceu.world_data_scanner": "世界信息扫描", - "gui.gtceu.neutron_sensor.invert.disabled.0": "红石输出:普通", - "gui.gtceu.neutron_sensor.invert.disabled.1": "点击为以反转红石逻辑", - "gui.gtceu.neutron_sensor.invert.disabled.2": "中子动能介于所设定的最小值和最大值之间时传感器将发出红石信号,小于最小值时则停止发出红石信号", - "gui.gtceu.neutron_sensor.invert.enabled.0": "红石输出:反转", - "gui.gtceu.neutron_sensor.invert.enabled.1": "点击切换为普通红石逻辑", - "gui.gtceu.neutron_sensor.invert.enabled.2": "中子动能介于所设定的最小值和最大值之外时传感器将发出红石信号,小于最小值时则发出红石信号", - "item.gtceu.tool.vajra": "%s金刚杵", - "material.gtceu.1_octene": "1-辛烯", - "material.gtceu.absolute_ethanol": "绝对乙醇", - "material.gtceu.abyssalalloy": "渊狱合金", - "material.gtceu.acetaldehyde": "乙醛", - "material.gtceu.acetonitrile": "乙腈", - "material.gtceu.acetyl_chloride": "乙酰氯", - "material.gtceu.acetylating_reagent": "乙酰化试剂", - "material.gtceu.acetylene": "乙炔", - "material.gtceu.acid_leached_fluoro_carbon_lanthanide_cerium_oxide_powder": "酸浸氟碳镧铈稀土氧化物", - "material.gtceu.acidic_iridium": "酸性铱", - "material.gtceu.acidic_monazite_powder": "酸性独居石", - "material.gtceu.acidic_naquadria_caesiumfluoride": "硫酸二氟超能硅岩酸铯", - "material.gtceu.acrylic_acid": "丙烯酸", - "material.gtceu.acrylonitrile": "丙烯腈", - "material.gtceu.actinium_nitrate": "硝酸锕", - "material.gtceu.actinium_oxalate": "草酸锕", - "material.gtceu.actinium_radium_hydroxide_solution": "氢氧化锕镭溶液", - "material.gtceu.actinium_radium_nitrate_solution": "硝酸锕镭溶液", - "material.gtceu.actinium_superhydride": "超氢化锕", - "material.gtceu.actinium_trinium_hydroxides": "氢氧化锕凯金", - "material.gtceu.actinoids": "锕系元素混合物", - "material.gtceu.actinoids_1": "轻锕系元素混合物", - "material.gtceu.actinoids_2": "重锕系元素混合物", - "material.gtceu.adamantine": "精金", - "material.gtceu.adamantine_compounds": "精金化合物", - "material.gtceu.adamantium": "艾德曼合金", - "material.gtceu.alien_algae": "异星藻类渣", - "material.gtceu.alkaline": "碱金属元素混合物", - "material.gtceu.alkaline_earth": "碱土金属元素混合物", - "material.gtceu.almandine_front": "铁铝榴石矿石泡沫", - "material.gtceu.alumina": "氧化铝", - "material.gtceu.aluminium_bronze": "铝青铜", - "material.gtceu.aluminium_hydride": "氢化铝", - "material.gtceu.aluminium_trifluoride": "氟化铝", - "material.gtceu.aminated_fullerene": "胺化富勒烯", - "material.gtceu.ammonium_bifluoride": "二氟化铵", - "material.gtceu.ammonium_bifluoride_solution": "氟氢化氨", - "material.gtceu.ammonium_fluoride": "氟化铵", - "material.gtceu.ammonium_nitrate_solution": "硝酸铵溶液", - "material.gtceu.ammonium_perrhenate": "高铼酸铵", - "material.gtceu.aniline": "苯胺", - "material.gtceu.anthracene": "蒽", - "material.gtceu.antihydrogen": "反氢", - "material.gtceu.antimatter": "离散反物质", - "material.gtceu.antimony_pentafluoride": "五氟化锑", - "material.gtceu.antimony_trichloride": "三氯化锑", - "material.gtceu.antineutron": "反中子", - "material.gtceu.antiproton": "反质子", - "material.gtceu.arceusalloy2b": "阿尔宙斯合金2B", - "material.gtceu.artherium_sn": "阿瑟锡", - "material.gtceu.ash_leaching_solution": "灰烬浸出液", - "material.gtceu.astatide_solution": "砹化物溶液", - "material.gtceu.astral_silver": "星辰银", - "material.gtceu.astraltitanium": "星体钛", - "material.gtceu.atinium_hydride": "氢化锕", - "material.gtceu.attuned_tengam": "谐镃", - "material.gtceu.azafullerene": "氮杂富勒烯", - "material.gtceu.barium_chloride": "氯化钡", - "material.gtceu.barnarda_air": "巴纳德C空气", - "material.gtceu.bedrock": "基岩", - "material.gtceu.bedrock_gas": "基岩气", - "material.gtceu.bedrock_smoke": "基岩烟", - "material.gtceu.bedrock_soot_solution": "基岩烟溶液", - "material.gtceu.benzaldehyde": "苯甲醛", - "material.gtceu.benzenediazonium_tetrafluoroborate": "四氟硼酸重氮苯", - "material.gtceu.benzophenanthrenylacetonitrile": "苯并菲乙腈", - "material.gtceu.benzyl_chloride": "氯化苄", - "material.gtceu.benzylamine": "苄胺", - "material.gtceu.biohmediumsterilized": "灭菌生物培养基原液", - "material.gtceu.biomediumraw": "生物培养基原液", - "material.gtceu.bisethylenedithiotetraselenafulvalene": "双(乙烯二硫代)四硒富瓦烯", - "material.gtceu.bisethylenedithiotetraselenafulvalene_perrhenate": "高铼酸双(乙烯二硫代)四硒富瓦烯", - "material.gtceu.bismuth_germanate": "锗酸铋", - "material.gtceu.bismuth_nitrate_solution": "硝酸铋溶液", - "material.gtceu.bismuth_tellurite": "亚碲酸铋", - "material.gtceu.black_dwarf_mtter": "黑矮星物质", - "material.gtceu.black_titanium": "黑钛合金", - "material.gtceu.bloodstone": "血石", - "material.gtceu.borane_dimethyl_sulfide": "硼烷二甲基硫醚", - "material.gtceu.boric_acide": "硼酸", - "material.gtceu.borocarbide": "碳化硼混合材料", - "material.gtceu.boron_carbide": "碳化硼", - "material.gtceu.boron_fluoride": "氟化硼", - "material.gtceu.boron_francium_carbide": "硼-钫碳化物", - "material.gtceu.boron_trifluoride_acetate": "三氟化硼乙酸酯", - "material.gtceu.boron_trioxide": "氧化硼", - "material.gtceu.brominated_brine": "含溴盐水", - "material.gtceu.bromine_trifluoride": "三氟化溴", - "material.gtceu.bromo_succinimide": "N-溴代琥珀酰亚胺", - "material.gtceu.bromobutane": "溴丁烷", - "material.gtceu.bromodihydrothiine": "溴二氢硫醚", - "material.gtceu.butane_1_4_diol": "1,4-丁二醇", - "material.gtceu.butyl_lithium": "丁基锂", - "material.gtceu.cadmium_sulfide": "硫化镉", - "material.gtceu.cadmium_tungstate": "钨酸镉", - "material.gtceu.caesium_fluoride": "氟化铯", - "material.gtceu.caesium_hydroxide": "氢氧化铯", - "material.gtceu.caesium_iodide": "碘化铯", - "material.gtceu.caesium_nitrate": "硝酸铯", - "material.gtceu.caesium_xenontrioxide_fluoride": "二氟三氧氙酸铯", - "material.gtceu.calcined_rare_earth_oxide_powder": "焙烧稀土氧化物", - "material.gtceu.calcium_carbide": "电石", - "material.gtceu.calcium_fluoride": "氟化钙", - "material.gtceu.californium_cyclopentadienide": "环戊二烯化锎", - "material.gtceu.californium_trichloride": "三氯化锎", - "material.gtceu.calorite": "耐热金属", - "material.gtceu.carbon_disulfide": "二硫化碳", - "material.gtceu.carbon_nanotubes": "碳纳米管", - "material.gtceu.carbon_tetrachloride": "四氯化碳", - "material.gtceu.celestialtungsten": "天体钨", - "material.gtceu.celestine": "天青石", - "material.gtceu.cerium_chloride_powder": "氯化铈", - "material.gtceu.cerium_oxalate_powder": "草酸铈", - "material.gtceu.cerium_oxide": "氧化铈", - "material.gtceu.cerium_oxide_rare_earth_oxide_powder": "氧化铈稀土氧化物", - "material.gtceu.cerium_rich_mixture_powder": "富铈混合物", - "material.gtceu.cesium_carborane": "碳酸铯", - "material.gtceu.cesium_carborane_precursor": "铯烷预固化剂", - "material.gtceu.chalcopyrite_front": "黄铜矿矿石泡沫", - "material.gtceu.chaos": "混沌物质", - "material.gtceu.charged_caesium_cerium_cobalt_indium": "带电的铯-铈-钴-铟", - "material.gtceu.cinobite": "西诺柏", - "material.gtceu.circuit_compound": "电路化合物", - "material.gtceu.clean_bedrock_solution": "洁净基岩烟溶液", - "material.gtceu.clean_inert_residues": "纯净的惰性残渣", - "material.gtceu.clean_raw_tengam": "洁净生镃", - "material.gtceu.co_ac_ab_catalyst": "Co/AC-AB催化剂", - "material.gtceu.compressed_stone": "压缩石头", - "material.gtceu.concentrated_cerium_chloride_solution": "氯化铈浓缩液", - "material.gtceu.concentrated_monazite_rare_earth_hydroxide_powder": "浓缩独居石稀土氢氧化物", - "material.gtceu.concentrated_nitric_leachate_from_monazite": "浓缩硝酸独居石浸出溶液", - "material.gtceu.concentrated_nitride_monazite_rare_earth_solution": "浓缩氮化独居石稀土溶液", - "material.gtceu.concentrated_rare_earth_chloride_solution": "氯化稀土浓缩液", - "material.gtceu.concentration_mixing_hyper_fuel_1": "浓缩混合超能燃料 I", - "material.gtceu.concentration_mixing_hyper_fuel_2": "浓缩混合超能燃料 II", - "material.gtceu.conductive_alloy": "导电铁", - "material.gtceu.cooling_concentrated_nitric_monazite_rare_earth_powder": "冷却浓缩硝酸独居石稀土", - "material.gtceu.copernicium": "鿔", - "material.gtceu.copper76": "铜-76", - "material.gtceu.cosmic": "宇宙", - "material.gtceu.cosmic_computing_mixture": "寰宇计算混合物", - "material.gtceu.cosmic_element": "宇宙素", - "material.gtceu.cosmic_mesh": "寰宇织网", - "material.gtceu.cosmic_superconductor": "寰宇超导液", - "material.gtceu.cosmicneutronium": "宇宙中子素", - "material.gtceu.crackedradox": "裂化拉多X", - "material.gtceu.crude_hexanitrohexaaxaisowurtzitane": "粗制六硝基六氮杂异伍兹烷", - "material.gtceu.crystalline_nitric_acid": "结晶硝酸", - "material.gtceu.crystalmatrix": "水晶矩阵", - "material.gtceu.cubic_zirconia": "立方氧化锆", - "material.gtceu.cyclooctadiene": "环辛二烯", - "material.gtceu.cycloparaphenylene": "环对苯撑", - "material.gtceu.cyclopentadiene": "环戊二烯", - "material.gtceu.cyclopentadienyl_titanium_trichloride": "环戊二烯基三氯化钛", - "material.gtceu.dalisenite": "大力合金", - "material.gtceu.decaborane": "癸硼烷", - "material.gtceu.degenerate_rhenium": "简并态铼", - "material.gtceu.deglycerated_soap": "脱糖肥皂", - "material.gtceu.dense_hydrazine_fuel_mixture": "浓缩肼混合燃料", - "material.gtceu.dense_hydrazine_mixed_fuel": "浓缩肼混合燃料", - "material.gtceu.dense_neutron": "致密中子素", - "material.gtceu.desh": "戴斯", - "material.gtceu.diamagnetic_residues": "抗磁性残渣", - "material.gtceu.diaminodiphenylmethanmixture": "二氨基二苯甲烷混合物", - "material.gtceu.dibenzyltetraacetylhexaazaisowurtzitane": "二苄基四乙酰六氮杂异纤锌烷", - "material.gtceu.dibismuthhydroborat": "硼氢二铋", - "material.gtceu.diborane": "乙硼烷", - "material.gtceu.dibromoacrolein": "二溴丙烯醛", - "material.gtceu.dibromomethylbenzene": "二溴甲苯", - "material.gtceu.dichlorocyclooctadieneplatinium": "二氯环辛二烯铂", - "material.gtceu.dichlorodicyanobenzoquinone": "二氯二氰苯醌", - "material.gtceu.dichlorodicyanohydroquinone": "二氯二氰氢醌", - "material.gtceu.dichloromethane": "二氯甲烷", - "material.gtceu.diethyl_ether": "二乙醚", - "material.gtceu.diethylthiourea": "二乙基硫脲", - "material.gtceu.difluoroaniline": "二氟苯胺", - "material.gtceu.difluorobenzophenone": "二氟二苯甲酮", - "material.gtceu.dihydroiodotetracene": "二氢碘化四联苯", - "material.gtceu.diiodobiphenyl": "二碘代联苯", - "material.gtceu.dilute_hexafluorosilicic_acid": "稀六氟硅酸", - "material.gtceu.dilute_hydrofluoric_acid": "稀释氢氟酸", - "material.gtceu.diluted_fluoro_carbon_lanthanide_slurry": "稀释氟碳镧铈泥浆", - "material.gtceu.diluted_monazite_slurry": "稀释独居石稀土泥浆", - "material.gtceu.diluted_monazite_sulfate_solution": "稀释硫酸独居石溶液", - "material.gtceu.diluted_rare_earth_chloride_solution": "氯化稀土稀释液", - "material.gtceu.dilutedxenoxene": "钝化异氙", - "material.gtceu.dimensionallytranscendentcrudecatalyst": "粗制超维度催化剂", - "material.gtceu.dimensionallytranscendentexoticcatalyst": "异星超维度催化剂", - "material.gtceu.dimensionallytranscendentprosaiccatalyst": "平凡超维度催化剂", - "material.gtceu.dimensionallytranscendentresidue": "超维度残留", - "material.gtceu.dimensionallytranscendentresplendentcatalyst": "光辉超维度催化剂", - "material.gtceu.dimensionallytranscendentstellarcatalyst": "恒星超维度催化剂", - "material.gtceu.dimethoxyethane": "二甲氧基乙烷", - "material.gtceu.dimethyl_sulfide": "二甲硫醚", - "material.gtceu.dimethylether": "二甲基乙醚", - "material.gtceu.dimethylnaphthalene": "二甲基萘", - "material.gtceu.dimethylterephthalate": "对苯二甲酸二甲酯", - "material.gtceu.dinitrodipropanyloxybenzene": "二硝基二丙氧基苯", - "material.gtceu.dioxygen_difluoride": "二氟化二氧", - "material.gtceu.diphenylmethane_diisocyanate": "4,4'-二苯基甲烷二异氰酸酯", - "material.gtceu.diphenylmethanediisocyanatemixture": "二苯基甲烷二异氰酸酯混合物", - "material.gtceu.dirty_hexafluorosilicic_acid": "污浊的六氟硅酸", - "material.gtceu.ditertbutyl_dicarbonate": "二碳酸二叔丁酯", - "material.gtceu.dmap": "二甲氨基吡啶", - "material.gtceu.draconium": "龙", - "material.gtceu.draconiumawakened": "觉醒龙", - "material.gtceu.dragon_blood": "龙血", - "material.gtceu.dragon_breath": "龙息", - "material.gtceu.dragon_element": "龙素", - "material.gtceu.dried_concentrated_nitric_monazite_rare_earth_powder": "干燥浓缩硝酸独居石稀土", - "material.gtceu.dry_graphene_gel": "干石墨烯凝胶", - "material.gtceu.durene": "杜烯", - "material.gtceu.dusty_liquid_helium_iii": "污浊的液氦-3", - "material.gtceu.dysprosium_oxide": "氧化镝", - "material.gtceu.earth_crystal": "地之魔晶", - "material.gtceu.echoite": "回响合金", - "material.gtceu.eglin_steel": "埃格林钢", - "material.gtceu.enderite": "末影合金", - "material.gtceu.enderium": "末影", - "material.gtceu.energetic_alloy": "充能合金", - "material.gtceu.enriched_dragon_breath": "富集龙息", - "material.gtceu.enriched_naquadah_front": "富集硅岩矿石泡沫", - "material.gtceu.enriched_naquadah_fuel": "富集硅岩燃料", - "material.gtceu.enriched_potassium_iodide_slurry": "富集碘化钾浆液", - "material.gtceu.enriched_rare_earth_chloride_solution": "氯化稀土富集液", - "material.gtceu.enriched_xenoxene": "富集异氙", - "material.gtceu.er_lu_oxides_solution": "铒-镥氧化物溶液", - "material.gtceu.erbium_oxide": "氧化铒", - "material.gtceu.eternity": "永恒", - "material.gtceu.ethanolamine": "乙醇胺", - "material.gtceu.ethyl_acrylate": "丙烯酸乙酯", - "material.gtceu.ethyl_trifluoroacetate": "三氟乙酸乙酯", - "material.gtceu.ethylamine": "乙胺", - "material.gtceu.ethylanthrahydroquinone": "2-乙基蒽氢醌", - "material.gtceu.ethylanthraquinone": "2-乙基蒽醌", - "material.gtceu.ethylene_oxide": "环氧乙烷", - "material.gtceu.ethylene_sulfide": "乙硫酮", - "material.gtceu.ethylenediamine": "乙二胺", - "material.gtceu.ethyleneglycol": "乙二醇", - "material.gtceu.europium_oxide": "氧化铕", - "material.gtceu.euv_photoresist": "EUV光刻胶", - "material.gtceu.exciteddtec": "激发的异星超维度催化剂", - "material.gtceu.exciteddtsc": "激发的恒星超维度催化剂", - "material.gtceu.exotic_heavy_residues": "重奇异残渣", - "material.gtceu.explosivehydrazine": "爆炸性肼燃料混合物", - "material.gtceu.fall_king": "耐摔合金", - "material.gtceu.ferric_ree_chloride": "含稀土氯化铁", - "material.gtceu.ferrocene": "二茂铁", - "material.gtceu.ferromagnetic_residues": "铁磁性残渣", - "material.gtceu.filtered_fluoro_carbon_lanthanide_slurry": "过滤氟碳镧铈泥浆", - "material.gtceu.fissioned_uranium_235": "裂变铀-235", - "material.gtceu.fluorinated_samarium_concentrate_powder": "氟化钐精", - "material.gtceu.fluorine_cracked_aquadah": "加氟裂化硅岩", - "material.gtceu.fluorite": "氟石", - "material.gtceu.fluoro_benzene": "氟苯", - "material.gtceu.fluoro_carbon_lanthanide_cerium_oxide_powder": "氟碳镧铈稀土氧化物", - "material.gtceu.fluoro_carbon_lanthanide_cerium_rare_earth_oxide_powder": "氟碳镧铈罕土氧化物", - "material.gtceu.fluoro_carbon_lanthanide_cerium_rare_earth_suspension": "氟碳镧铈罕土氧化物悬浊液", - "material.gtceu.fluoro_carbon_lanthanide_cerium_solution": "氟碳镧铈稀土浊溶液", - "material.gtceu.fluoroboric_acide": "氟硼酸", - "material.gtceu.fluorocarborane": "碳氟化合物", - "material.gtceu.fluorosilicic_acid": "六氟硅酸", - "material.gtceu.fluorotoluene": "氟甲苯", - "material.gtceu.fluxed_electrum": "通流琥珀金", - "material.gtceu.flyb": "𫓧-镱", - "material.gtceu.force": "力量", - "material.gtceu.francium_caesium_cadmium_bromide": "溴化钫铯镉", - "material.gtceu.francium_carbide": "碳化钫", - "material.gtceu.free_alpha_gas": "高密度自由α粒子气", - "material.gtceu.free_electron_gas": "高密度自由电子气", - "material.gtceu.free_proton_gas": "高密度自由质子气", - "material.gtceu.fullerene": "富勒烯", - "material.gtceu.fullerene_doped_nanotubes": "富勒烯掺杂的纳米管", - "material.gtceu.fullerene_polymer_matrix_pulp": "富勒烯聚合物基体", - "material.gtceu.fuming_nitric_acid": "发烟硝酸", - "material.gtceu.gadolinium_oxide": "氧化钆", - "material.gtceu.gamma_rays_photoresist": "γ射线光刻胶", - "material.gtceu.gammabutyrolactone": "1,4-丁内酯", - "material.gtceu.germanium_ash": "锗灰", - "material.gtceu.germanium_containing_precipitate": "含锗沉淀物", - "material.gtceu.germanium_dioxide": "二氧化锗", - "material.gtceu.germanium_tetrachloride_solution": "四氯化锗", - "material.gtceu.germaniumtungstennitride": "锗-钨氮化物", - "material.gtceu.glacio_spirit": "霜原碎片", - "material.gtceu.glucose": "葡萄糖", - "material.gtceu.glucose_iron_solution": "葡萄糖铁溶液", - "material.gtceu.gluons": "胶子", - "material.gtceu.glyoxal": "乙二醛", - "material.gtceu.gold_chloride": "氯化金", - "material.gtceu.gold_cyanide": "氰化金", - "material.gtceu.gold_depleted_molybdenite": "贫金辉钼矿", - "material.gtceu.gold_trifluoride": "三氟化金", - "material.gtceu.grade_10_purified_water": "10级净化水", - "material.gtceu.grade_11_purified_water": "11级净化水", - "material.gtceu.grade_12_purified_water": "12级净化水", - "material.gtceu.grade_13_purified_water": "13级净化水", - "material.gtceu.grade_14_purified_water": "14级净化水", - "material.gtceu.grade_15_purified_water": "15级净化水", - "material.gtceu.grade_16_purified_water": "16级净化水", - "material.gtceu.grade_1_purified_water": "1级净化水", - "material.gtceu.grade_2_purified_water": "2级净化水", - "material.gtceu.grade_3_purified_water": "3级净化水", - "material.gtceu.grade_4_purified_water": "4级净化水", - "material.gtceu.grade_5_purified_water": "5级净化水", - "material.gtceu.grade_6_purified_water": "6级净化水", - "material.gtceu.grade_7_purified_water": "7级净化水", - "material.gtceu.grade_8_purified_water": "8级净化水", - "material.gtceu.grade_9_purified_water": "9级净化水", - "material.gtceu.graphene_gel_suspension": "石墨烯浆料", - "material.gtceu.graphene_oxide": "氧化石墨烯", - "material.gtceu.grisium": "灰钛合金", - "material.gtceu.grossular_front": "钙铝榴石矿石泡沫", - "material.gtceu.hafnium_chloride": "四氯化铪", - "material.gtceu.hafnium_oxide": "二氧化铪", - "material.gtceu.hassium_chloride": "氯化𬭶", - "material.gtceu.hastelloy_n": "哈斯特洛依合金-N", - "material.gtceu.hastelloy_n_75": "哈斯特洛依合金-N75", - "material.gtceu.hastelloyk_243": "哈斯特洛依合金-K243", - "material.gtceu.hastelloyx_78": "哈斯特洛依合金-X78", - "material.gtceu.heavily_fluorinated_trinium_solution": "重氟化凯金化合物溶液", - "material.gtceu.heavy_diamagnetic_residues": "重磁残渣", - "material.gtceu.heavy_ferromagnetic_residues": "重铁磁性残渣", - "material.gtceu.heavy_lepton_mixture": "重轻子混合物", - "material.gtceu.heavy_metallic_residues": "重金属残渣", - "material.gtceu.heavy_oxidized_residues": "重氧化残渣", - "material.gtceu.heavy_paramagnetic_residues": "重顺磁残渣", - "material.gtceu.heavy_quark_degenerate_matter": "重夸克简并物质", - "material.gtceu.heavy_quark_enriched_mixture": "富集重夸克混合物", - "material.gtceu.heavy_quarks": "重夸克", - "material.gtceu.heavyradox": "重拉多X", - "material.gtceu.helium_iii_hydride": "氢化氦-3", - "material.gtceu.heterogeneous_halide_monazite_rare_earth_mixture_powder": "异质卤化独居石稀土混合物", - "material.gtceu.hexabenzylhexaazaisowurtzitane": "六苄基六氮杂异伍兹烷", - "material.gtceu.hexafluoride_enriched_naquadah_solution": "六氟化富集硅岩溶液", - "material.gtceu.hexafluoride_naquadria_solution": "六氟化超能硅岩溶液", - "material.gtceu.hexafluorophosphoric_acid": "单氟磷酸", - "material.gtceu.hexamethylenetetramine": "环六亚甲基四胺", - "material.gtceu.hexanitrohexaaxaisowurtzitane": "六硝基六氮杂异伍兹烷", - "material.gtceu.high_energy_quark_gluon": "高能夸克-胶子", - "material.gtceu.high_purity_calcium_carbonate": "高纯度碳酸钙", - "material.gtceu.highenergymixture": "高能混合物", - "material.gtceu.highurabilityompoundteel": "高强度复合钢", - "material.gtceu.hikarium": "光素", - "material.gtceu.hmxexplosive": "HMX高能爆炸性化合物", - "material.gtceu.holmium_oxide": "氧化钬", - "material.gtceu.hot_oganesson": "热鿫", - "material.gtceu.hot_sodium_potassium": "热钠钾合金", - "material.gtceu.hydrazine": "肼", - "material.gtceu.hydrobromic_acid": "氢溴酸", - "material.gtceu.hydrogen_peroxide": "过氧化氢", - "material.gtceu.hydroiodic_acid": "氢碘酸", - "material.gtceu.hydroquinone": "对苯二酚", - "material.gtceu.hydroxylamine_hydrochloride": "盐酸羟胺", - "material.gtceu.hydroxylammonium_sulfate": "羟铵硫酸盐", - "material.gtceu.hyper_fuel_1": "超能燃料 I", - "material.gtceu.hyper_fuel_2": "超能燃料 II", - "material.gtceu.hyper_fuel_3": "超能燃料 III", - "material.gtceu.hyper_fuel_4": "超能燃料 IV", - "material.gtceu.hypogen": "海珀珍", - "material.gtceu.ignis_crystal": "火之魔晶", - "material.gtceu.inconel_625": "镍铬基合金-625", - "material.gtceu.inconel_792": "镍铬基合金-792", - "material.gtceu.indalloy_140": "铋铅合金-140", - "material.gtceu.inert_residues": "纯净残渣", - "material.gtceu.infinity": "无尽", - "material.gtceu.infuscolium": "魔金", - "material.gtceu.infused_gold": "注魔金", - "material.gtceu.iodine_containing_slurry": "含碘溶液", - "material.gtceu.iodine_monochloride": "氯化碘", - "material.gtceu.iridium_dioxide": "二氧化铱", - "material.gtceu.iridium_trichloride_solution": "三氯化铱溶液", - "material.gtceu.isochloropropane": "氯丙烷", - "material.gtceu.isophthaloylbis": "间苯二甲酰基二乙基硫脲", - "material.gtceu.isopropyl_alcohol": "异丙醇", - "material.gtceu.jasper": "碧玉", - "material.gtceu.kelp_slurry": "海带浆液", - "material.gtceu.kerosene": "煤油", - "material.gtceu.kevlar": "凯芙拉", - "material.gtceu.krypton_difluoride": "二氟化氪", - "material.gtceu.la_nd_oxides_solution": "镧-钕氧化物溶液", - "material.gtceu.lafium": "路菲恩", - "material.gtceu.lanthanoids_1": "轻镧系元素混合物", - "material.gtceu.lanthanoids_2": "重镧系元素混合物", - "material.gtceu.lanthanum_chloride": "氯化镧", - "material.gtceu.lanthanum_chloride_with_impurities": "含杂氯化镧", - "material.gtceu.lanthanum_embedded_fullerene": "镧-富勒烯包合物", - "material.gtceu.lanthanum_fullerene_mix": "镧-富勒烯混合物", - "material.gtceu.lanthanum_oxide": "氧化镧", - "material.gtceu.leach_residue": "铂浸出渣", - "material.gtceu.leached_turpentine": "松油浸出物", - "material.gtceu.legendarium": "传奇合金", - "material.gtceu.light_quarks": "轻夸克", - "material.gtceu.lightradox": "轻拉多X", - "material.gtceu.liquid_hydrogen": "液态氢", - "material.gtceu.liquid_starlight": "星能液", - "material.gtceu.liquidcrystalkevlar": "液晶凯芙拉", - "material.gtceu.lithium_aluminium_fluoride": "铝-锂氟化物", - "material.gtceu.lithium_aluminium_hydride": "氢化铝锂", - "material.gtceu.lithium_cyclopentadienide": "环戊二烯化锂", - "material.gtceu.lithium_fluoride": "氟化锂", - "material.gtceu.lithium_iodide": "碘化锂", - "material.gtceu.lithium_niobate_nanoparticles": "铌酸锂纳米粒子", - "material.gtceu.lithiumthiinediselenide": "二硒醚合硫锂", - "material.gtceu.lumiium": "流明", - "material.gtceu.luminessence": "流明精华", - "material.gtceu.lutetium_oxide": "氧化镥", - "material.gtceu.magmatter": "磁物质", - "material.gtceu.magnesium_chloride_bromide": "溴氯化镁", - "material.gtceu.magneto_resonatic": "共振紫晶", - "material.gtceu.magnetohydrodynamicallyconstrainedstarmatter": "磁流体约束恒星物质", - "material.gtceu.maleic_anhydride": "顺丁烯二酸酐", - "material.gtceu.mana": "魔力", - "material.gtceu.mar_m_200_steel": "MAR-M200特种钢", - "material.gtceu.metal_residue": "金属残渣", - "material.gtceu.metallic_residues": "金属残渣", - "material.gtceu.metalloid": "类金属元素混合物", - "material.gtceu.metastable_hassium": "亚稳态𬭶", - "material.gtceu.metastable_oganesson": "亚稳态鿫", - "material.gtceu.methylamine": "甲胺", - "material.gtceu.methylbenzophenanthrene": "甲基苯并菲", - "material.gtceu.mithril": "秘银", - "material.gtceu.mixed_astatide_salts": "混合砹化盐", - "material.gtceu.modulated_fluoro_carbon_lanthanide_slurry": "调制氟碳镧铈泥浆", - "material.gtceu.molten_calcium_salts": "熔融钙盐", - "material.gtceu.molybdenum_concentrate": "钼精", - "material.gtceu.molybdenum_flue": "钼烟气", - "material.gtceu.molybdenum_trioxide": "三氧化钼", - "material.gtceu.monazite_front": "独居石矿石泡沫", - "material.gtceu.monazite_rare_earth_filter_residue_powder": "独居石稀土滤渣", - "material.gtceu.monazite_rare_earth_precipitate_powder": "独居石罕土沉淀", - "material.gtceu.monazite_rare_earth_turbid_liquid": "独居石稀土混浊液", - "material.gtceu.monazite_sulfate_powder": "硫酸独居石", - "material.gtceu.monomethylhydrazine": "甲肼", - "material.gtceu.mutated_living_solder": "突变活性焊料", - "material.gtceu.n_difluorophenylpyrrole": "N-二氟苯基吡咯", - "material.gtceu.n_hydroxysuccinimide": "羟基丁二酰亚胺", - "material.gtceu.naquadah_contain_rare_earth": "含硅岩的稀土", - "material.gtceu.naquadah_contain_rare_earth_fluoride": "含硅岩的稀土氟化物", - "material.gtceu.naquadah_fuel": "硅岩燃料", - "material.gtceu.naquadah_solution": "硅岩溶液", - "material.gtceu.naquadria_caesium_xenonnonfluoride": "九氟氙酸超能硅岩铯", - "material.gtceu.naquadria_caesiumfluoride": "二氟超能硅岩酸铯", - "material.gtceu.naquadriatictaranium": "超能硅岩-塔兰金属合金", - "material.gtceu.neodymium_oxide": "氧化钕", - "material.gtceu.neodymium_rare_earth_concentrate_powder": "钕稀土精", - "material.gtceu.neutralised_red_mud": "中和赤泥", - "material.gtceu.neutralized_monazite_rare_earth_filter_residue_powder": "中和独居石稀土滤渣", - "material.gtceu.neutralized_uranium_filter_residue_powder": "中和铀滤渣", - "material.gtceu.neutronium_doped_nanotubes": "掺中子素纳米管", - "material.gtceu.nickel_front": "镍矿石泡沫", - "material.gtceu.nitrated_triniite_compound_solution": "硝化凯金化合物溶液", - "material.gtceu.nitric_leachate_from_monazite": "硝酸独居石浸出混合物", - "material.gtceu.nitrided_fluoro_carbon_lanthanide_cerium_rare_earth_oxide_solution": "氮化氟碳镧铈罕土氧化物", - "material.gtceu.nitrided_samarium_terbium_mixture_powder": "氮化钐-铽混合物", - "material.gtceu.nitrogen_pentoxide": "五氧化二氮", - "material.gtceu.nitronium_tetrafluoroborate": "四氟硝铵", - "material.gtceu.nitrosonium_octafluoroxenate": "八氟氙酸亚硝酰", - "material.gtceu.nitrosonium_tetrafluoroborate": "四氟硼酸亚硝铵", - "material.gtceu.nitrous_acid": "亚硝酸", - "material.gtceu.nitryl_fluoride": "硝酰氟", - "material.gtceu.nmethylpyrolidone": "N-甲基吡咯烷酮", - "material.gtceu.noble_gas": "稀有气体元素混合物", - "material.gtceu.not_found": "非金属元素混合物", - "material.gtceu.oganesson_breeding_base": "鿫增殖基", - "material.gtceu.orichalcum": "山铜", - "material.gtceu.ostrum": "紫金", - "material.gtceu.oxalic_acid": "草酸", - "material.gtceu.oxidized_residual_solution": "氧化残留溶液", - "material.gtceu.oxidized_residues": "氧化残渣", - "material.gtceu.oxydianiline": "二氨基二苯醚", - "material.gtceu.ozone": "臭氧", - "material.gtceu.p_nitroaniline": "对硝基苯胺", - "material.gtceu.p_phenylenediamine": "对苯二胺", - "material.gtceu.paa": "聚酰胺酸(PAA)", - "material.gtceu.palladium_fullerene_matrix": "钯-富勒烯基质", - "material.gtceu.paramagnetic_residues": "顺磁残渣", - "material.gtceu.partially_oxidized_residues": "待分离氧化金属残渣", - "material.gtceu.pcbs": "苯基-C61-丁酸苯乙烯", - "material.gtceu.pentaerythritol": "季戊四醇", - "material.gtceu.pentlandite_front": "镍黄铁矿矿石泡沫", - "material.gtceu.perditio_crystal": "混沌魔晶", - "material.gtceu.perfluorobenzene": "全氟苯", - "material.gtceu.periodicium": "錭錤錶", - "material.gtceu.phenylenedioxydiacetic_acid": "亚苯基二氧二乙酸", - "material.gtceu.phenylpentanoic_acid": "苯基戊酸", - "material.gtceu.phenylsodium": "苯基钠", - "material.gtceu.phosgene": "碳酰氯", - "material.gtceu.phosphorus_free_samarium_concentrate_powder": "脱磷钐精", - "material.gtceu.phosphorus_pentasulfide": "五硫化磷", - "material.gtceu.phosphorus_trichloride": "三氯化磷", - "material.gtceu.photopolymer": "光聚合物溶液", - "material.gtceu.photoresist": "光刻胶", - "material.gtceu.phthalic_anhydride": "邻苯二甲酸酐", - "material.gtceu.pikyonium": "皮卡优合金", - "material.gtceu.piranha_solution": "食人鱼洗液", - "material.gtceu.platinum_front": "铂矿石泡沫", - "material.gtceu.platinum_slag": "铂渣", - "material.gtceu.polycyclic_aromatic_mixture": "多环芳香烃混合物", - "material.gtceu.polyetheretherketone": "聚醚醚酮", - "material.gtceu.polyimide": "聚酰亚胺", - "material.gtceu.polyurethane": "聚氨基甲酸酯", - "material.gtceu.polyurethaneresin": "聚氨酯树脂", - "material.gtceu.poor": "贫金属元素混合物", - "material.gtceu.positive_electron": "反电子", - "material.gtceu.potassium_bisulfite": "亚硫酸氢钾", - "material.gtceu.potassium_bromide": "溴化钾", - "material.gtceu.potassium_ethylate": "乙醇钾", - "material.gtceu.potassium_ethylxanthate": "乙基黄原酸钾", - "material.gtceu.potassium_fluoride": "氟化钾", - "material.gtceu.potassium_hydroxylaminedisulfonate": "羟胺二磺酸钾", - "material.gtceu.potassium_pyrosulfate": "焦硫酸钾", - "material.gtceu.praseodymium_oxide": "氧化镨", - "material.gtceu.prasiolite": "堇云石", - "material.gtceu.pre_zylon": "预处理柴隆纤维", - "material.gtceu.primordialmatter": "流体本源物质", - "material.gtceu.promethium_oxide": "氧化钷", - "material.gtceu.propadiene": "丙二烯", - "material.gtceu.pulsating_alloy": "脉冲铁", - "material.gtceu.purified_tengam": "纯镃", - "material.gtceu.purified_xenoxene": "纯净异氙", - "material.gtceu.pyridine": "吡啶", - "material.gtceu.pyromellitic_dianhydride": "均苯二甲酸酐", - "material.gtceu.pyrope_front": "镁铝榴石矿石泡沫", - "material.gtceu.quantanium": "量子", - "material.gtceu.quantum": "量子合金", - "material.gtceu.quantum_dots": "量子点", - "material.gtceu.quantumchromodynamically_confined_matter": "量子色动力学封闭物质", - "material.gtceu.quark_gluon": "夸克-胶子", - "material.gtceu.quasifissioning": "拟裂变", - "material.gtceu.radium_nitrate": "硝酸镭", - "material.gtceu.radon_cracked_enriched_aquadah": "加氡裂化富集硅岩", - "material.gtceu.radon_difluoride": "二氟化氡", - "material.gtceu.radon_naquadria_octafluoride": "八氟超能硅岩酸氡", - "material.gtceu.radon_trioxide": "三氧化氡", - "material.gtceu.radox": "拉多X聚合物", - "material.gtceu.radox_gas": "拉多X气", - "material.gtceu.rare_earth_chlorides": "稀土氯化物", - "material.gtceu.rare_earth_hydroxides": "稀土氢氧化物", - "material.gtceu.rare_earth_metal": "稀土金属", - "material.gtceu.rare_earth_oxide": "稀土氧化物", - "material.gtceu.rareearth": "稀土元素合金", - "material.gtceu.raw_star_matter": "原始恒星混合物", - "material.gtceu.raw_tengam": "生镃", - "material.gtceu.rawradox": "粗制拉多X", - "material.gtceu.reactor_steel": "反应堆专用钒钢", - "material.gtceu.red_mud": "赤泥", - "material.gtceu.red_slurry": "赤泥浆液", - "material.gtceu.red_zircon_powder": "红锆石", - "material.gtceu.redstone_front": "红石矿石泡沫", - "material.gtceu.reprecipitated_rhodium": "再沉淀铑", - "material.gtceu.residual_triniite_solution": "残留凯金化合物溶液", - "material.gtceu.resorcinol": "间苯二酚", - "material.gtceu.rhenium_chloride": "氯化铼", - "material.gtceu.rhenium_hassium_thallium_isophtaloylbisdiethylthiourea_hexaf": "间苯二甲酰双(二乙基硫脲基)六氟磷酸铼-𬭶-铊", - "material.gtceu.rhenium_sulfuric_solution": "铼硫酸溶液", - "material.gtceu.rhodium_filter_cake": "铑滤饼", - "material.gtceu.rhodium_filter_cake_solution": "铑滤饼溶液", - "material.gtceu.rhodium_nitrate": "硝酸铑", - "material.gtceu.rhodium_rhenium_naquadah_catalyst": "铑-铼-硅岩催化剂", - "material.gtceu.rhodium_salt": "铑盐", - "material.gtceu.rhodium_salt_solution": "铑盐溶液", - "material.gtceu.rhodium_sulfate_gas": "气态硫酸铑", - "material.gtceu.rhugnor": "鲁格诺", - "material.gtceu.rocket_fuel_cn3h7o3": "火箭燃料(硝酸甲肼)", - "material.gtceu.rocket_fuel_h8n4c2o4": "火箭燃料(偏二甲肼-四氧化二氮)", - "material.gtceu.rocket_fuel_rp_1": "火箭燃料 RP-1", - "material.gtceu.roughly_rhodium_metal": "粗制铑金属", - "material.gtceu.rp_1": "RP-1混合燃料", - "material.gtceu.ruthenium_tetroxide_hot": "热四氧化钌", - "material.gtceu.ruthenium_tetroxide_lq": "四氧化钌溶液", - "material.gtceu.samarium_chloride_concentrate_solution": "氯化钐浓缩液", - "material.gtceu.samarium_chloride_sodium_chloride_mixture_powder": "氯化钐-氯化钠混合物", - "material.gtceu.samarium_chloride_with_impurities": "含杂氯化钐", - "material.gtceu.samarium_oxalate_with_impurities": "含杂草酸钐", - "material.gtceu.samarium_oxide": "氧化钐", - "material.gtceu.samarium_precipitate_powder": "钐沉淀", - "material.gtceu.samarium_rare_earth_concentrate_powder": "钐稀土精", - "material.gtceu.samarium_rare_earth_diluted_solution": "钐稀土稀释液", - "material.gtceu.samarium_rare_earth_slurry": "钐稀土泥浆", - "material.gtceu.samarium_refined_powder": "钐精", - "material.gtceu.samarium_rrare_eearth_turbid_liquid": "钐稀土浊液", - "material.gtceu.samarium_terbium_mixture_powder": "钐-铽混合物", - "material.gtceu.sarcosine": "肌氨酸", - "material.gtceu.saturated_monazite_rare_earth_powder": "饱和独居石稀土", - "material.gtceu.scandium_oxide": "氧化钪", - "material.gtceu.scandium_titanium_50_mixture": "钪-钛50混合物", - "material.gtceu.seaborgium_doped_nanotubes": "𬭳掺杂的纳米管", - "material.gtceu.seaweedbroth": "海藻基质", - "material.gtceu.selenium_oxide": "二氧化硒", - "material.gtceu.shirabon": "调律源金", - "material.gtceu.silica_alumina_gel": "硅铝凝胶", - "material.gtceu.silica_gel": "硅胶", - "material.gtceu.silica_gel_base": "硅胶基质", - "material.gtceu.silicon_carbide": "碳化硅", - "material.gtceu.siliconoil": "硅油", - "material.gtceu.silver_chloride": "氯化银", - "material.gtceu.silver_iodide": "碘化银", - "material.gtceu.silver_nitrate": "硝酸银", - "material.gtceu.silver_oxide": "氧化银", - "material.gtceu.silver_perchlorate": "高氯酸银", - "material.gtceu.silver_tetrafluoroborate": "四氟硼酸银", - "material.gtceu.sm_gd_oxides_solution": "钐-钆氧化物溶液", - "material.gtceu.soap": "肥皂", - "material.gtceu.sodium_aluminium_hydride": "氢化铝钠", - "material.gtceu.sodium_azanide": "氨基钠", - "material.gtceu.sodium_azide": "叠氮化钠", - "material.gtceu.sodium_borohydride": "硼氢化钠", - "material.gtceu.sodium_bromide": "溴化钠", - "material.gtceu.sodium_chlorate": "氯酸钠", - "material.gtceu.sodium_cyanide": "氰化钠", - "material.gtceu.sodium_ethylate": "乙醇钠", - "material.gtceu.sodium_ethylxanthate": "乙基黄原酸钠", - "material.gtceu.sodium_fluoride": "氟化钠", - "material.gtceu.sodium_fluorosilicate": "氟硅酸钠", - "material.gtceu.sodium_formate": "甲酸钠", - "material.gtceu.sodium_hexafluoroaluminate": "六氟铝酸钠", - "material.gtceu.sodium_hydride": "氢化钠", - "material.gtceu.sodium_hydroxide_solution": "氢氧化钠溶液", - "material.gtceu.sodium_hypochlorite": "次氯酸钠", - "material.gtceu.sodium_nitrate": "硝酸钠", - "material.gtceu.sodium_nitrate_solution": "硝酸钠溶液", - "material.gtceu.sodium_oxide": "氧化钠", - "material.gtceu.sodium_perchlorate": "高氯酸钠", - "material.gtceu.sodium_rutheniate": "钌酸钠", - "material.gtceu.sodium_seaborgate": "𬭳酸钠", - "material.gtceu.sodium_sulfate": "硫酸钠", - "material.gtceu.sodium_tetrafluoroborate": "四氟硼酸钠", - "material.gtceu.sodium_thiocyanate": "硫氰酸钠", - "material.gtceu.sodium_thiosulfate": "硫代硫酸钠", - "material.gtceu.sodium_toluenesulfonate": "甲苯磺酸钠", - "material.gtceu.spacetime": "时空", - "material.gtceu.spatialfluid": "扩大化空间流体", - "material.gtceu.special_ceramics": "特种陶瓷", - "material.gtceu.spessartine_front": "锰铝榴石矿石泡沫", - "material.gtceu.sphalerite_front": "闪锌矿矿石泡沫", - "material.gtceu.starlight": "星光", - "material.gtceu.starmetal": "星辉", - "material.gtceu.steam_cracked_fluoro_carbon_lanthanide_slurry": "蒸汽裂化氟碳镧铈泥浆", - "material.gtceu.steam_cracked_turpentine": "蒸汽裂化的松油浸出物", - "material.gtceu.stearic_acid": "硬脂酸", - "material.gtceu.stellar_energy_rocket_fuel": "星能火箭燃料", - "material.gtceu.stellite": "铬钴锰钛合金", - "material.gtceu.stone_dust_residue": "石头粉残渣", - "material.gtceu.strontium_europium_aluminate": "锶铕铝酸盐", - "material.gtceu.succinaldehyde": "琥珀醛", - "material.gtceu.succinic_acid": "琥珀酸", - "material.gtceu.succinic_anhydride": "丁二酸酐", - "material.gtceu.succinimide": "琥珀酰亚胺", - "material.gtceu.succinimidyl_acetate": "琥珀酰亚胺醋酸酯", - "material.gtceu.sunnarium": "阳光化合物", - "material.gtceu.super_mutated_living_solder": "超突变活性焊料", - "material.gtceu.supercritical_carbon_dioxide": "超临界二氧化碳", - "material.gtceu.supercritical_sodium_potassium": "超临界钠钾合金", - "material.gtceu.supercritical_steam": "超临界蒸汽", - "material.gtceu.superheavy_h_alloy": "超重元素-重合金", - "material.gtceu.superheavy_l_alloy": "超重元素-轻合金", - "material.gtceu.superheavyradox": "超重拉多X", - "material.gtceu.superlightradox": "超轻拉多X", - "material.gtceu.tairitsu": "对立合金", - "material.gtceu.tanmolyium": "钛钼合金β-C", - "material.gtceu.tannic": "丹宁", - "material.gtceu.tantalloy_61": "钽钨合金-61", - "material.gtceu.taranium": "塔兰", - "material.gtceu.taranium_enriched_liquid_helium_3": "浓缩塔兰金属的液氦-3", - "material.gtceu.taranium_rich_liquid_helium_4": "富塔兰金属的氦-4", - "material.gtceu.tartarite": "溶火之石", - "material.gtceu.tb_ho_oxides_solution": "铽-钬氧化物溶液", - "material.gtceu.tellurium_oxide": "二氧化碲", - "material.gtceu.temporalfluid": "富快子时间流体", - "material.gtceu.terbium_nitrate_powder": "硝酸铽", - "material.gtceu.terbium_oxide": "氧化铽", - "material.gtceu.terephthalaldehyde": "对苯二甲醛", - "material.gtceu.terephthalicacid": "对苯二甲酸", - "material.gtceu.terephthaloyl_chloride": "对苯二甲酰氯", - "material.gtceu.tert_butanol": "叔丁醇", - "material.gtceu.tertbuthylcarbonylazide": "叔丁基羰基叠氮", - "material.gtceu.tetraacetyldinitrosohexaazaisowurtzitane": "四乙酰二硝基六氮杂异戊二烯", - "material.gtceu.tetracene": "并四苯", - "material.gtceu.tetraethylammonium_bromide": "四乙基溴化铵", - "material.gtceu.tetrahydrofuran": "四氢呋喃", - "material.gtceu.thallium_chloride": "氯化铊", - "material.gtceu.thallium_thulium_doped_caesium_iodide": "铊铥掺杂的碘化铯", - "material.gtceu.thaumium": "神秘", - "material.gtceu.thionyl_chloride": "氯化亚砜", - "material.gtceu.thorite_powder": "方钍石", - "material.gtceu.thorium_phosphate_filter_cake_powder": "磷酸钍滤饼", - "material.gtceu.thorium_phosphate_refined_powder": "磷酸钍精", - "material.gtceu.thulium_oxide": "氧化铥", - "material.gtceu.titan_precision_steel": "泰坦精钢", - "material.gtceu.titanium_50": "钛-50", - "material.gtceu.titanium_50_tetrachloride": "四氯化钛-50", - "material.gtceu.titanium_50_tetrafluoride": "四氟化钛-50", - "material.gtceu.titanium_tetrafluoride": "四氟化钛", - "material.gtceu.titansteel": "泰坦钢", - "material.gtceu.titanyl_sulfate": "硫酸钛酯", - "material.gtceu.toluene_diisocyanate": "甲苯二异氰酸脂", - "material.gtceu.transcendentmetal": "超时空金属", - "material.gtceu.transition": "过渡元素合金", - "material.gtceu.transition_1": "前过渡金属元素混合物", - "material.gtceu.transition_2": "中过渡金属元素混合物", - "material.gtceu.transition_3": "后过渡金属元素混合物", - "material.gtceu.trichloroflerane": "三氯𫓧烷", - "material.gtceu.tricotylphosphine": "三辛基膦", - "material.gtceu.trifluoroacetic_phosphate_ester": "三氟乙酸对磷脂", - "material.gtceu.trimethylamine": "三甲胺", - "material.gtceu.trimethylchlorosilane": "三甲基氯硅烷", - "material.gtceu.trimethylsilane": "三甲基硅烷", - "material.gtceu.trimethyltin_chloride": "三甲基氯化锡", - "material.gtceu.trinium_compound": "凯金化合物", - "material.gtceu.trinium_tetrafluoride": "四氟化凯金", - "material.gtceu.trinium_titanium": "凯金钛合金", - "material.gtceu.tritium_hydride": "氢化氚", - "material.gtceu.tungsten_trioxide": "三氧化钨", - "material.gtceu.turbid_dragon_blood": "龙血浊液", - "material.gtceu.turpentine": "松油", - "material.gtceu.ultraacidic_residue_solution": "超酸性残渣溶液", - "material.gtceu.uncommon_residues": "精良残渣", - "material.gtceu.unfolded_fullerene": "未折叠富勒烯", - "material.gtceu.unknownnutrientagar": "未知营养琼脂", - "material.gtceu.unknowwater": "不明液体", - "material.gtceu.uranium_filter_residue_powder": "铀滤渣", - "material.gtceu.uranium_sulfate_waste_solution": "硫酸铀废液", - "material.gtceu.uruium": "乌鲁", - "material.gtceu.uu_amplifier": "UU增幅液", - "material.gtceu.vanadium_pentoxide_powder": "五氧化二钒", - "material.gtceu.vibramantium": "艾德曼振金", - "material.gtceu.vibranium": "振金", - "material.gtceu.vibranium_unstable": "不稳定振金", - "material.gtceu.vibrant_alloy": "脉冲合金", - "material.gtceu.viscoelastic_polyurethane": "粘弹性聚氨酯", - "material.gtceu.viscoelastic_polyurethane_foam": "粘弹性聚氨酯泡沫", - "material.gtceu.water_agar_mix": "琼脂水溶液", - "material.gtceu.wet_rare_earth_oxide_powder": "湿稀土氧化物", - "material.gtceu.wet_zeolite_sieving_pellets": "湿过筛沸石颗粒", - "material.gtceu.white_dwarf_mtter": "白矮星物质", - "material.gtceu.woods_glass": "伍兹玻璃", - "material.gtceu.xenic_acid": "氙酸", - "material.gtceu.xenoauric_fluoroantimonic_acid": "氟锑酸二氙", - "material.gtceu.xenon_hexafluoro_enriched_naquadate": "六氟氙酸富集硅岩", - "material.gtceu.xenon_trioxide": "三氧化氙", - "material.gtceu.xenoxene": "异氙", - "material.gtceu.xenoxene_crystal": "结晶异氙", - "material.gtceu.xenoxene_mixture": "异氙混合物", - "material.gtceu.xpjuice": "液态经验", - "material.gtceu.ytterbium_178": "镱-178", - "material.gtceu.ytterbium_oxide": "氧化镱", - "material.gtceu.yttrium_oxide": "氧化钇", - "material.gtceu.zeolite_sieving_pellets": "过筛沸石颗粒", - "material.gtceu.zinc_sulfate": "硫酸锌", - "material.gtceu.zircon": "锆石", - "material.gtceu.zircon_chlorinating_residue": "锆氯化反应残渣", - "material.gtceu.zirconiu_hafnium_oxychloride": "锆-铪氯氧化物", - "material.gtceu.zirconium_carbide": "碳化锆", - "material.gtceu.zirconium_hafnium_chloride": "锆-铪氯化物", - "material.gtceu.zirconium_oxide": "二氧化锆", - "material.gtceu.znfealcl_catalyst": "锌-铁-铝-氯混合催化剂", - "material.gtceu.zylon": "柴隆纤维", - "tagprefix.ceresstone": "谷神星%s矿石", - "tagprefix.enceladusstone": "土卫二%s矿石", - "tagprefix.ganymedestone": "木卫三%s矿石", - "tagprefix.glacio_stone": "霜原石%s矿石", - "tagprefix.iostone": "木卫一%s矿石", - "tagprefix.mars_stone": "火星石%s矿石", - "tagprefix.mercury_stone": "水星石%s矿石", - "tagprefix.milled": "碾磨%s", - "tagprefix.moon_stone": "月石%s矿石", - "tagprefix.nanoswarm": "%s纳米蜂群", - "tagprefix.plutostone": "冥王星%s矿石", - "tagprefix.titanstone": "土卫六%s矿石", - "tagprefix.venus_stone": "金星石%s矿石", - "gtceu.recipe.eu_to_starts": "启动耗能:%sMEU(MK%s)" + "block.gtceu.a_mass_fabricator": "Advanced Mass Fabricator", + "block.gtceu.advanced_assembly_line": "Advanced Assembly Line", + "block.gtceu.advanced_hyper_reactor": "Advanced Hyper Reactor", + "block.gtceu.advanced_infinite_driller": "Advanced Infinite Driller", + "block.gtceu.advanced_integrated_ore_processor": "Advanced Integrated Ore Processor", + "block.gtceu.advanced_multi_smelter": "Advanced Multi Smelter", + "block.gtceu.advanced_neutron_activator": "Neutron Vortex", + "block.gtceu.advanced_rare_earth_centrifugal": "Advanced Rare Earth Centrifuge", + "block.gtceu.advanced_sps_crafting": "Advanced Supercritical Crafting Unit", + "block.gtceu.advanced_vacuum_drying_furnace": "Advanced Vacuum Drying Furnace", + "block.gtceu.aggregation_device": "Aggregation Device", + "block.gtceu.annihilate_generator": "Artificial Star", + "block.gtceu.assemble_plant": "Universal Assembly Plant", + "block.gtceu.assembler_module": "Space Elevator Assembly Module", + "block.gtceu.atomic_energy_excitation_plant": "Atomic Energy Excitation Plant", + "block.gtceu.auto_configuration_maintenance_hatch": "Configurable Auto Maintenance Hatch", + "block.gtceu.bedrock_drilling_rig": "Bedrock Drilling Rig", + "block.gtceu.blaze_blast_furnace": "Blaze Blast Furnace", + "block.gtceu.block_bus": "Block Bus", + "block.gtceu.block_conversion_room": "Block Conversion Room", + "block.gtceu.chemical_distort": "Deep Chemical Distorter", + "block.gtceu.chemical_energy_devourer": "Chemical Energy Devourer", + "block.gtceu.chemical_plant": "Chemical Plant", + "block.gtceu.circuit_assembly_line": "Circuit Assembly Line", + "block.gtceu.cleaning_configuration_maintenance_hatch": "Sterile Configurable Maintenance Hatch", + "block.gtceu.cleaning_gravity_configuration_maintenance_hatch": "Configurable Gravity Sterile Maintenance Hatch", + "block.gtceu.cleaning_gravity_maintenance_hatch": "Gravity Sterile Maintenance Hatch", + "block.gtceu.cold_ice_freezer": "Cold Ice Freezer", + "block.gtceu.component_assembly_line": "Component Assembly Line", + "block.gtceu.cooling_tower": "Cooling Tower", + "block.gtceu.create_aggregation": "Creative Aggregator", + "block.gtceu.create_computation": "Creative Computer", + "block.gtceu.crystalline_infinity": "Crystalline Infinity", + "block.gtceu.decay_hastener": "Decay Hastener", + "block.gtceu.desulfurizer": "Desulfurizer", + "block.gtceu.digestion_tank": "Digestion Tank", + "block.gtceu.dimensional_focus_engraving_array": "Dimensional Focus Engraving Array", + "block.gtceu.dimensionally_transcendent_dirt_forge": "Dimensionally Transcendent Dirt Forge", + "block.gtceu.dimensionally_transcendent_mixer": "Dimensionally Transcendent Mixer", + "block.gtceu.dimensionally_transcendent_plasma_forge": "Dimensionally Transcendent Plasma Forge", + "block.gtceu.dimensionally_transcendent_steam_boiler": "Dimensionally Transcendent Steam Boiler", + "block.gtceu.dimensionally_transcendent_steam_oven": "Dimensionally Transcendent Steam Oven", + "block.gtceu.disassembly": "Disassembler", + "block.gtceu.dissolving_tank": "Dissolving Tank", + "block.gtceu.door_of_create": "Door of Creation", + "block.gtceu.dragon_egg_copier": "Dragon Egg Copier", + "block.gtceu.dyson_sphere": "Dyson Sphere Control System", + "block.gtceu.electric_implosion_compressor": "Electric Implosion Compressor", + "block.gtceu.element_copying": "Element Copier", + "block.gtceu.engraving_laser_plant": "Engraving Laser Plant", + "block.gtceu.ev_buffer": "§5Advanced Buffer III§r", + "block.gtceu.ev_dehydrator": "§5Advanced Dehydrator III§r", + "block.gtceu.ev_dual_input_hatch": "§5EV§r Dual Input Hatch", + "block.gtceu.ev_dual_output_hatch": "§5EV§r Dual Output Hatch", + "block.gtceu.ev_huge_input_hatch": "§5EV§r Huge Input Hatch", + "block.gtceu.ev_huge_output_hatch": "§5EV§r Huge Output Hatch", + "block.gtceu.ev_lightning_processor": "§5Advanced Lightning Processor III§r", + "block.gtceu.ev_lightning_rod": "§5Basic Lightning Rod§r", + "block.gtceu.ev_neutron_accelerator": "§5EV Neutron Accelerator§r", + "block.gtceu.ev_rocket_engine": "§5Basic Rocket Engine§r", + "block.gtceu.ev_world_data_scanner": "§5Advanced World Data Scanner III§r", + "block.gtceu.eye_of_harmony": "Eye of Harmony", + "block.gtceu.field_extruder_factory": "Field Extruder Factory", + "block.gtceu.fishing_ground": "Fishing Ground", + "block.gtceu.fission_reactor": "Fission Reactor", + "block.gtceu.flotation_cell_regulator": "Industrial Flotation Cell", + "block.gtceu.gas_mega_turbine": "Gas Mega Turbine", + "block.gtceu.generator_array": "Generator Array", + "block.gtceu.gravitation_shockburst": "Gravitation Shockburst", + "block.gtceu.gravity_configuration_hatch": "Configurable Gravity Control Hatch", + "block.gtceu.gravity_hatch": "Gravity Control Hatch", + "block.gtceu.greenhouse": "Greenhouse", + "block.gtceu.heat_exchanger": "Heat Exchanger", + "block.gtceu.heat_sensor": "Heat Sensor", + "block.gtceu.holy_separator": "Holy Separator", + "block.gtceu.huge_incubator": "Microbial Dominator", + "block.gtceu.hv_dehydrator": "§6Advanced Dehydrator II§r", + "block.gtceu.hv_dual_input_hatch": "§6HV§r Dual Input Hatch", + "block.gtceu.hv_dual_output_hatch": "§6HV§r Dual Output Hatch", + "block.gtceu.hv_energy_input_hatch_16a": "16A §4HV§r Energy Hatch", + "block.gtceu.hv_energy_input_hatch_4a": "4A §4HV§r Energy Hatch ", + "block.gtceu.hv_energy_output_hatch": "§4HV§r Dynamo Hatch", + "block.gtceu.hv_energy_output_hatch_16a": "16A §4HV§r Dynamo Hatch", + "block.gtceu.hv_energy_output_hatch_4a": "4A §4HV§r Dynamo Hatch", + "block.gtceu.hv_huge_input_hatch": "§6HV§r Huge Input Hatch", + "block.gtceu.hv_huge_output_hatch": "§6HV§r Huge Output Hatch", + "block.gtceu.hv_lightning_processor": "§6Advanced Lightning Processor II§r", + "block.gtceu.hv_neutron_accelerator": "§6HV Neutron Accelerator§r", + "block.gtceu.hv_semi_fluid": "§6Advanced Semi Fluid Generator II§r", + "block.gtceu.hv_world_data_scanner": "§6Advanced World Data Scanner II§r", + "block.gtceu.hyper_reactor": "Hyper Reactor", + "block.gtceu.incubator": "Incubator", + "block.gtceu.integrated_ore_processor": "Integrated Ore Processor", + "block.gtceu.isa_mill": "ISA Mill", + "block.gtceu.iv_1048576a_laser_source_hatch": "1048576A §9IV§r Laser Source Hatch", + "block.gtceu.iv_1048576a_laser_target_hatch": "1048576A §9IV§r Laser Target Hatch", + "block.gtceu.iv_16384a_laser_source_hatch": "16384A §9IV§r Laser Source Hatch", + "block.gtceu.iv_16384a_laser_target_hatch": "16384A §9IV§r Laser Target Hatch", + "block.gtceu.iv_16777216a_laser_source_hatch": "16777216A §9IV§r Laser Source Hatch", + "block.gtceu.iv_16777216a_laser_target_hatch": "16777216A §9IV§r Laser Target Hatch", + "block.gtceu.iv_262144a_laser_source_hatch": "262144A §9IV§r Laser Source Hatch", + "block.gtceu.iv_262144a_laser_target_hatch": "262144A §9IV§r Laser Target Hatch", + "block.gtceu.iv_4194304a_laser_source_hatch": "4194304A §9IV§r Laser Source Hatch", + "block.gtceu.iv_4194304a_laser_target_hatch": "4194304A §9IV§r Laser Target Hatch", + "block.gtceu.iv_65536a_laser_source_hatch": "65536A §9IV§r Laser Source Hatch", + "block.gtceu.iv_65536a_laser_target_hatch": "65536A §9IV§r Laser Target Hatch", + "block.gtceu.iv_67108864a_laser_source_hatch": "67108864A §9IV§r Laser Source Hatch", + "block.gtceu.iv_67108864a_laser_target_hatch": "67108864A §9IV§r Laser Target Hatch", + "block.gtceu.iv_buffer": "§9Elite Buffer§r", + "block.gtceu.iv_dehydrator": "§9Elite Dehydrator§r", + "block.gtceu.iv_dual_input_hatch": "§9IV§r Dual Input Hatch", + "block.gtceu.iv_dual_output_hatch": "§9IV§r Dual Output Hatch", + "block.gtceu.iv_huge_input_hatch": "§9IV§r Huge Input Hatch", + "block.gtceu.iv_huge_output_hatch": "§9IV§r Huge Output Hatch", + "block.gtceu.iv_lightning_processor": "§9Elite Lightning Processor§r", + "block.gtceu.iv_lightning_rod": "§9Advanced Lightning Rod§r", + "block.gtceu.iv_naquadah_reactor": "§9Basic Naquadah Reactor§r", + "block.gtceu.iv_neutron_accelerator": "§9IV Neutron Accelerator§r", + "block.gtceu.iv_rocket_engine": "§9Advanced Rocket Engine§r", + "block.gtceu.iv_world_data_scanner": "§9Elite World Data Scanner§r", + "block.gtceu.large_block_conversion_room": "Large Block Conversion Room", + "block.gtceu.large_chemical_plant": "Large Chemical Plant", + "block.gtceu.large_cracker": "Large Cracker", + "block.gtceu.large_gas_collector": "Large Gas Collector", + "block.gtceu.large_greenhouse": "Large Greenhouse", + "block.gtceu.large_incubator": "Large Incubator", + "block.gtceu.large_naquadah_reactor": "Large Naquadah Reactor", + "block.gtceu.large_pyrolyse_oven": "Large Pyrolyse Oven", + "block.gtceu.large_recycler": "Recycler", + "block.gtceu.large_rock_crusher": "Large Rock Crusher", + "block.gtceu.large_semi_fluid_generator": "Large Semi-Fluid Generator", + "block.gtceu.large_steam_bath": "Large Steam Bath", + "block.gtceu.large_steam_centrifuge": "Large Steam Centrifuge", + "block.gtceu.large_steam_circuit_assembler": "Large Steam Circuit Assembler", + "block.gtceu.large_steam_input_hatch": "Large Steam Input Hatch", + "block.gtceu.large_steam_macerator": "Large Steam Macerator", + "block.gtceu.large_steam_mixer": "Large Steam Mixer", + "block.gtceu.large_steam_ore_washer": "Large Steam Ore Washer", + "block.gtceu.large_steam_thermal_centrifuge": "Large Steam Thermal Centrifuge", + "block.gtceu.large_void_miner": "Large Void Miner", + "block.gtceu.lava_furnace": "Lava Furnace", + "block.gtceu.law_cleaning_gravity_configuration_maintenance_hatch": "Configurable Gravity Law Cleaning Maintenance Hatch", + "block.gtceu.law_cleaning_gravity_maintenance_hatch": "Gravity Law Cleaning Maintenance Hatch", + "block.gtceu.law_cleaning_maintenance_hatch": "Law Cleaning Maintenance Hatch", + "block.gtceu.law_configuration_cleaning_maintenance_hatch": "Law Configurable Cleaning Maintenance Hatch", + "block.gtceu.luv_1048576a_laser_source_hatch": "1048576A §dLuV§r Laser Source Hatch", + "block.gtceu.luv_1048576a_laser_target_hatch": "1048576A §dLuV§r Laser Target Hatch", + "block.gtceu.luv_16384a_laser_source_hatch": "16384A §dLuV§r Laser Source Hatch", + "block.gtceu.luv_16384a_laser_target_hatch": "16384A §dLuV§r Laser Target Hatch", + "block.gtceu.luv_16777216a_laser_source_hatch": "16777216A §dLuV§r Laser Source Hatch", + "block.gtceu.luv_16777216a_laser_target_hatch": "16777216A §dLuV§r Laser Target Hatch", + "block.gtceu.luv_262144a_laser_source_hatch": "262144A §dLuV§r Laser Source Hatch", + "block.gtceu.luv_262144a_laser_target_hatch": "262144A §dLuV§r Laser Target Hatch", + "block.gtceu.luv_4194304a_laser_source_hatch": "4194304A §dLuV§r Laser Source Hatch", + "block.gtceu.luv_4194304a_laser_target_hatch": "4194304A §dLuV§r Laser Target Hatch", + "block.gtceu.luv_65536a_laser_source_hatch": "65536A §dLuV§r Laser Source Hatch", + "block.gtceu.luv_65536a_laser_target_hatch": "65536A §dLuV§r Laser Target Hatch", + "block.gtceu.luv_67108864a_laser_source_hatch": "67108864A §dLuV§r Laser Source Hatch", + "block.gtceu.luv_67108864a_laser_target_hatch": "67108864A §dLuV§r Laser Target Hatch", + "block.gtceu.luv_buffer": "§dElite Buffer II§r", + "block.gtceu.luv_compressed_fusion_reactor": "Compressed Fusion Reactor Controller MK-I", + "block.gtceu.luv_dehydrator": "§dElite Dehydrator II§r", + "block.gtceu.luv_huge_input_hatch": "§dLuV§r Huge Input Hatch", + "block.gtceu.luv_huge_output_hatch": "§dLuV§r Huge Output Hatch", + "block.gtceu.luv_lightning_processor": "§dElite Lightning Processor II§r", + "block.gtceu.luv_lightning_rod": "§dElite Lightning Rod§r", + "block.gtceu.luv_naquadah_reactor": "§dAdvanced Naquadah Reactor§r", + "block.gtceu.luv_neutron_accelerator": "§dLuV Neutron Accelerator§r", + "block.gtceu.luv_rocket_engine": "§dElite Rocket Engine§r", + "block.gtceu.luv_world_data_scanner": "§dElite World Data Scanner II§r", + "block.gtceu.lv_dehydrator": "Basic Dehydrator", + "block.gtceu.lv_dual_input_hatch": "§7LV§r Dual Input Hatch", + "block.gtceu.lv_dual_output_hatch": "§7LV§r Dual Output Hatch", + "block.gtceu.lv_energy_input_hatch_16a": "16A §7LV§r Energy Hatch", + "block.gtceu.lv_energy_input_hatch_4a": "4A §7LV§r Energy Hatch ", + "block.gtceu.lv_energy_output_hatch": "§7LV§r Dynamo Hatch", + "block.gtceu.lv_energy_output_hatch_16a": "16A §7LV§r Dynamo Hatch", + "block.gtceu.lv_energy_output_hatch_4a": "4A §7LV§r Dynamo Hatch", + "block.gtceu.lv_huge_input_hatch": "§7LV§r Huge Input Hatch", + "block.gtceu.lv_huge_output_hatch": "§7LV§r Huge Output Hatch", + "block.gtceu.lv_lightning_processor": "Basic Lightning Processor", + "block.gtceu.lv_neutron_accelerator": "§7LV Neutron Accelerator§r", + "block.gtceu.lv_primitive_magic_energy": "Low Voltage Primitive Magic Energy Absorber", + "block.gtceu.lv_semi_fluid": "Basic Semi-Fluid Generator§r", + "block.gtceu.lv_world_data_scanner": "Basic World Data Scanner", + "block.gtceu.mage_assembler": "Mega Assembly Plant", + "block.gtceu.magic_manufacturer": "Magic Manufacturer", + "block.gtceu.mass_fabricator": "Mass Fabricator", + "block.gtceu.matter_fabricator": "Matter Fabricator", + "block.gtceu.max_1024a_laser_source_hatch": "1024A §c§lMAX§r Laser Source Hatch", + "block.gtceu.max_1024a_laser_target_hatch": "1024A §c§lMAX§r Laser Target Hatch", + "block.gtceu.max_1048576a_laser_source_hatch": "1048576A §c§lMAX§r Laser Source Hatch", + "block.gtceu.max_1048576a_laser_target_hatch": "1048576A §c§lMAX§r Laser Target Hatch", + "block.gtceu.max_16384a_laser_source_hatch": "16384A §c§lMAX§r Laser Source Hatch", + "block.gtceu.max_16384a_laser_target_hatch": "16384A §c§lMAX§r Laser Target Hatch", + "block.gtceu.max_16777216a_laser_source_hatch": "16777216A §c§lMAX§r Laser Source Hatch", + "block.gtceu.max_16777216a_laser_target_hatch": "16777216A §c§lMAX§r Laser Target Hatch", + "block.gtceu.max_256a_laser_source_hatch": "256A §c§lMAX§r Laser Source Hatch", + "block.gtceu.max_256a_laser_target_hatch": "256A §c§lMAX§r Laser Target Hatch", + "block.gtceu.max_262144a_laser_source_hatch": "262144A §c§lMAX§r Laser Source Hatch", + "block.gtceu.max_262144a_laser_target_hatch": "262144A §c§lMAX§r Laser Target Hatch", + "block.gtceu.max_4096a_laser_source_hatch": "4096A §c§lMAX§r Laser Source Hatch", + "block.gtceu.max_4096a_laser_target_hatch": "4096A §c§lMAX§r Laser Target Hatch", + "block.gtceu.max_4194304a_laser_source_hatch": "4194304A §c§lMAX§r Laser Source Hatch", + "block.gtceu.max_4194304a_laser_target_hatch": "4194304A §c§lMAX§r Laser Target Hatch", + "block.gtceu.max_65536a_laser_source_hatch": "65536A §c§lMAX§r Laser Source Hatch", + "block.gtceu.max_65536a_laser_target_hatch": "65536A §c§lMAX§r Laser Target Hatch", + "block.gtceu.max_67108864a_laser_source_hatch": "67108864A §c§lMAX§r Laser Source Hatch", + "block.gtceu.max_67108864a_laser_target_hatch": "67108864A §c§lMAX§r Laser Target Hatch", + "block.gtceu.max_buffer": "§c§lUltimate Buffer§r", + "block.gtceu.max_energy_input_hatch_16a": "16A §c§lMAX§r Energy Hatch", + "block.gtceu.max_energy_input_hatch_4a": "4A §c§lMAX§r Energy Hatch", + "block.gtceu.max_energy_output_hatch_16a": "16A §c§lMAX§r Dynamo Hatch", + "block.gtceu.max_energy_output_hatch_4a": "4A §c§lMAX§r Dynamo Hatch", + "block.gtceu.max_huge_input_hatch": "§c§lMAX§r Huge Input Hatch", + "block.gtceu.max_huge_output_hatch": "§c§lMAX§r Huge Output Hatch", + "block.gtceu.max_neutron_accelerator": "§c§lMAX Neutron Accelerator§r", + "block.gtceu.max_neutron_compressor": "Neutronium Compressor", + "block.gtceu.max_parallel_hatch": "§c§lMAX§r Parallel Control Hatch", + "block.gtceu.max_substation_input_hatch_64a": "64A §c§lMAX§r Substation Energy Hatch", + "block.gtceu.max_substation_output_hatch_64a": "64A §c§lMAX§r Substation Dynamo Hatch", + "block.gtceu.me_craft_parallel_core": "§3ME Crafting Parallel Core§r", + "block.gtceu.me_craft_pattern_container": "§3ME Crafting Pattern Core§r", + "block.gtceu.me_craft_speed_core": "§3ME Crafting Speed Core§r", + "block.gtceu.me_dual_hatch_stock_part_machine": "ME Dual Hatch Stock Part Machine", + "block.gtceu.me_extend_pattern_buffer": "Extended ME Pattern Buffer", + "block.gtceu.me_extended_async_export_buffer": "Extended ME Async Export Buffer", + "block.gtceu.me_extended_export_buffer": "Extended ME Export Buffer", + "block.gtceu.me_final_pattern_buffer": "Ultimate ME Pattern Buffer", + "block.gtceu.me_mini_pattern_buffer": "Mini ME Pattern Buffer", + "block.gtceu.me_molecular_assembler_io": "ME Molecular Assembler Matrix I/O", + "block.gtceu.mega_alloy_blast_smelter": "Mega Alloy Blast Smelter", + "block.gtceu.mega_canner": "Mega Canner", + "block.gtceu.mega_distillery": "Mega Distillery", + "block.gtceu.mega_extractor": "Mega Extractor", + "block.gtceu.mega_fluid_heater": "Mega Fluid Heater", + "block.gtceu.mega_presser": "Mega Presser", + "block.gtceu.mega_steam_input_hatch": "Mega Steam Input Hatch", + "block.gtceu.mega_steam_output_hatch": "Mega Steam Output Hatch", + "block.gtceu.mega_wiremill": "Mega Wiremill", + "block.gtceu.mixed_plant": "Universal Mixing Plant", + "block.gtceu.molecular_assembler_matrix": "Molecular Assembler Matrix", + "block.gtceu.mv_dehydrator": "§bAdvanced Dehydrator §r", + "block.gtceu.mv_dual_input_hatch": "§bMV§r Dual Input Hatch", + "block.gtceu.mv_dual_output_hatch": "§bMV§r Dual Output Hatch", + "block.gtceu.mv_energy_input_hatch_16a": "16A §bMV§r Energy Hatch", + "block.gtceu.mv_energy_input_hatch_4a": "4A §bMV§r Energy Hatch ", + "block.gtceu.mv_energy_output_hatch": "§bMV§r Dynamo Hatch", + "block.gtceu.mv_energy_output_hatch_16a": "16A §bMV§r Dynamo Hatch", + "block.gtceu.mv_energy_output_hatch_4a": "4A §bMV§r Dynamo Hatch", + "block.gtceu.mv_huge_input_hatch": "§bMV§r Huge Input Hatch", + "block.gtceu.mv_huge_output_hatch": "§bMV§r Huge Output Hatch", + "block.gtceu.mv_lightning_processor": "§bAdvanced Lightning Processor §r", + "block.gtceu.mv_neutron_accelerator": "§bMV Neutron Accelerator§r", + "block.gtceu.mv_semi_fluid": "§bAdvanced Semi-Fluid Generator§r", + "block.gtceu.mv_world_data_scanner": "§bAdvanced World Data Scanner§r", + "block.gtceu.nano_core": "Nano Core", + "block.gtceu.nano_forge_1": "Nano Forge Tier 1", + "block.gtceu.nano_forge_2": "Nano Forge Tier 2", + "block.gtceu.nano_forge_3": "Nano Forge Tier 3", + "block.gtceu.naquadah_alloy_crate": "Naquadah Alloy Crate", + "block.gtceu.naquadah_alloy_drum": "Naquadah Alloy Drum", + "block.gtceu.neutron_activator": "Neutron Activator", + "block.gtceu.neutron_sensor": "Neutron Sensor", + "block.gtceu.neutronium_crate": "Neutronium Crate", + "block.gtceu.neutronium_drum": "Neutronium Drum", + "block.gtceu.opv_1048576a_laser_source_hatch": "1048576A §9§lOpV§r Laser Source Hatch", + "block.gtceu.opv_1048576a_laser_target_hatch": "1048576A §9§lOpV§r Laser Target Hatch", + "block.gtceu.opv_16384a_laser_source_hatch": "16384A §9§lOpV§r Laser Source Hatch", + "block.gtceu.opv_16384a_laser_target_hatch": "16384A §9§lOpV§r Laser Target Hatch", + "block.gtceu.opv_16777216a_laser_source_hatch": "16777216A §9§lOpV§r Laser Source Hatch", + "block.gtceu.opv_16777216a_laser_target_hatch": "16777216A §9§lOpV§r Laser Target Hatch", + "block.gtceu.opv_262144a_laser_source_hatch": "262144A §9§lOpV§r Laser Source Hatch", + "block.gtceu.opv_262144a_laser_target_hatch": "262144A §9§lOpV§r Laser Target Hatch", + "block.gtceu.opv_4194304a_laser_source_hatch": "4194304A §9§lOpV§r Laser Source Hatch", + "block.gtceu.opv_4194304a_laser_target_hatch": "4194304A §9§lOpV§r Laser Target Hatch", + "block.gtceu.opv_65536a_laser_source_hatch": "65536A §9§lOpV§r Laser Source Hatch", + "block.gtceu.opv_65536a_laser_target_hatch": "65536A §9§lOpV§r Laser Target Hatch", + "block.gtceu.opv_67108864a_laser_source_hatch": "67108864A §9§lOpV§r Laser Source Hatch", + "block.gtceu.opv_67108864a_laser_target_hatch": "67108864A §9§lOpV§r Laser Target Hatch", + "block.gtceu.opv_buffer": "§9§lLegendary Buffer§r", + "block.gtceu.opv_dehydrator": "§9§lLegendary Dehydrator§r", + "block.gtceu.opv_energy_input_hatch_16a": "16A §9§lOpV§r Energy Hatch", + "block.gtceu.opv_energy_input_hatch_4a": "4A §9§lOpV§r Energy Hatch", + "block.gtceu.opv_energy_output_hatch_16a": "16A §9§lOpV§r Dynamo Hatch", + "block.gtceu.opv_energy_output_hatch_4a": "4A §9§lOpV§r Dynamo Hatch", + "block.gtceu.opv_huge_input_hatch": "§9§lOpV§r Huge Input Hatch", + "block.gtceu.opv_huge_output_hatch": "§9§lOpV§r Huge Output Hatch", + "block.gtceu.opv_lightning_processor": "§9§lLegendary Lightning Processor§r", + "block.gtceu.opv_neutron_accelerator": "§9§lOpV Neutron Accelerator§r", + "block.gtceu.opv_parallel_hatch": "§9§lOpV§r Parallel Control Hatch", + "block.gtceu.opv_substation_input_hatch_64a": "64A §9§lOpV§r Substation Energy Hatch", + "block.gtceu.opv_substation_output_hatch_64a": "64A §9§lOpV§r Substation Dynamo Hatch", + "block.gtceu.opv_world_data_scanner": "§9§lLegendary World Data Scanner§r", + "block.gtceu.pcb_factory": "PCB Factory", + "block.gtceu.petrochemical_plant": "Petrochemical Plant", + "block.gtceu.plasma_condenser": "Plasma Condenser", + "block.gtceu.plasma_mega_turbine": "Plasma Mega Turbine", + "block.gtceu.precision_assembler": "Precision Assembler", + "block.gtceu.primitive_void_ore": "Primitive Void Ore Miner", + "block.gtceu.processing_plant": "Universal Processing Plant", + "block.gtceu.qft": "Quantum Field Transformer", + "block.gtceu.rare_earth_centrifugal": "Rare Earth Centrifuge", + "block.gtceu.resource_collection": "Space Elevator Resource Collection Module", + "block.gtceu.rhodium_plated_palladium_crate": "Rhodium-Plated Palladium Crate", + "block.gtceu.rhodium_plated_palladium_drum": "Rhodium-Plated Palladium Drum", + "block.gtceu.rocket_large_turbine": "Rocket Large Turbine", + "block.gtceu.rocket_mega_turbine": "Rocket Mega Turbine", + "block.gtceu.rotor_hatch": "Rotor Hatch", + "block.gtceu.separated_plant": "Universal Separation Plant", + "block.gtceu.simulation_machine": "Multiblock Simulation Machine", + "block.gtceu.slaughterhouse": "Industrial Slaughterhouse", + "block.gtceu.space_cosmic_probe_receivers": "Space-Based Cosmic Probe Receiver", + "block.gtceu.space_elevator": "Space Elevator", + "block.gtceu.space_probe_surface_reception": "Cosmic Probe Surface Reception Unit", + "block.gtceu.sps_crafting": "Supercritical Crafting Unit", + "block.gtceu.star_ultimate_material_forge_factory": "Stellar Ultimate Material Forge Factory", + "block.gtceu.steam_bath": "Steam Bath", + "block.gtceu.steam_foundry": "Steam Foundry", + "block.gtceu.steam_mega_turbine": "Steam Mega Turbine", + "block.gtceu.steam_mixer": "Steam Mixer", + "block.gtceu.steam_ore_washer": "Steam Ore Washer", + "block.gtceu.steam_piston_hammer": "Steam Piston Hammer", + "block.gtceu.steam_pressor": "Steam Compressor", + "block.gtceu.stellar_forge": "Stellar Forge", + "block.gtceu.sterile_cleaning_gravity_configuration_maintenance_hatch": "Configurable Gravity Sterile Maintenance Hatch", + "block.gtceu.sterile_cleaning_gravity_maintenance_hatch": "Gravity Sterile Maintenance Hatch", + "block.gtceu.sterile_cleaning_maintenance_hatch": "Sterile Maintenance Hatch", + "block.gtceu.sterile_configuration_cleaning_maintenance_hatch": "Sterile Configurable Maintenance Hatch", + "block.gtceu.super_blast_smelter": "Super Blast Smelter", + "block.gtceu.super_computation": "Super Computer", + "block.gtceu.super_particle_collider": "Super Particle Collider", + "block.gtceu.superconducting_electromagnetism": "Superconducting Electromagnetic Factory", + "block.gtceu.supercritical_mega_steam_turbine": "Supercritical Mega Steam Turbine", + "block.gtceu.supercritical_steam_turbine": "Supercritical Steam Turbine", + "block.gtceu.suprachronal_assembly_line": "Suprachronal Assembly Line", + "block.gtceu.suprachronal_assembly_line_module": "Suprachronal Assembly Line Module", + "block.gtceu.tag_filter_me_stock_bus_part_machine": "Tag Filter ME Stock Bus", + "block.gtceu.uev_1048576a_laser_source_hatch": "1048576A §aUEV§r Laser Source Hatch", + "block.gtceu.uev_1048576a_laser_target_hatch": "1048576A §aUEV§r Laser Target Hatch", + "block.gtceu.uev_16384a_laser_source_hatch": "16384A §aUEV§r Laser Source Hatch", + "block.gtceu.uev_16384a_laser_target_hatch": "16384A §aUEV§r Laser Target Hatch", + "block.gtceu.uev_16777216a_laser_source_hatch": "16777216A §aUEV§r Laser Source Hatch", + "block.gtceu.uev_16777216a_laser_target_hatch": "16777216A §aUEV§r Laser Target Hatch", + "block.gtceu.uev_262144a_laser_source_hatch": "262144A §aUEV§r Laser Source Hatch", + "block.gtceu.uev_262144a_laser_target_hatch": "262144A §aUEV§r Laser Target Hatch", + "block.gtceu.uev_4194304a_laser_source_hatch": "4194304A §aUEV§r Laser Source Hatch", + "block.gtceu.uev_4194304a_laser_target_hatch": "4194304A §aUEV§r Laser Target Hatch", + "block.gtceu.uev_65536a_laser_source_hatch": "65536A §aUEV§r Laser Source Hatch", + "block.gtceu.uev_65536a_laser_target_hatch": "65536A §aUEV§r Laser Target Hatch", + "block.gtceu.uev_67108864a_laser_source_hatch": "67108864A §aUEV§r Laser Source Hatch", + "block.gtceu.uev_67108864a_laser_target_hatch": "67108864A §aUEV§r Laser Target Hatch", + "block.gtceu.uev_buffer": "§aEpic Buffer II§r", + "block.gtceu.uev_compressed_fusion_reactor": "Compressed Fusion Reactor Controller MK-V", + "block.gtceu.uev_dehydrator": "§aEpic Dehydrator II§r", + "block.gtceu.uev_energy_input_hatch_16a": "16A §aUEV§r Energy Hatch", + "block.gtceu.uev_energy_input_hatch_4a": "4A §aUEV§r Energy Hatch", + "block.gtceu.uev_energy_output_hatch_16a": "16A §aUEV§r Dynamo Hatch", + "block.gtceu.uev_energy_output_hatch_4a": "4A §aUEV§r Dynamo Hatch", + "block.gtceu.uev_fusion_reactor": "Fusion Reactor Controller MK-V", + "block.gtceu.uev_huge_input_hatch": "§aUEV§r Huge Input Hatch", + "block.gtceu.uev_huge_output_hatch": "§aUEV§r Huge Output Hatch", + "block.gtceu.uev_lightning_processor": "§aEpic Lightning Processor II§r", + "block.gtceu.uev_neutron_accelerator": "§aUEV Neutron Accelerator§r", + "block.gtceu.uev_parallel_hatch": "§aUEV§r Parallel Control Hatch", + "block.gtceu.uev_rotor_holder": "§aUEV§r Rotor Holder", + "block.gtceu.uev_substation_input_hatch_64a": "64A §aUEV§r Substation Energy Hatch", + "block.gtceu.uev_substation_output_hatch_64a": "64A §aUEV§r Substation Dynamo Hatch", + "block.gtceu.uev_world_data_scanner": "§aEpic World Data Scanner II§r", + "block.gtceu.uhv_1048576a_laser_source_hatch": "1048576A §4UHV§r Laser Source Hatch", + "block.gtceu.uhv_1048576a_laser_target_hatch": "1048576A §4UHV§r Laser Target Hatch", + "block.gtceu.uhv_16384a_laser_source_hatch": "16384A §4UHV§r Laser Source Hatch", + "block.gtceu.uhv_16384a_laser_target_hatch": "16384A §4UHV§r Laser Target Hatch", + "block.gtceu.uhv_16777216a_laser_source_hatch": "16777216A §4UHV§r Laser Source Hatch", + "block.gtceu.uhv_16777216a_laser_target_hatch": "16777216A §4UHV§r Laser Target Hatch", + "block.gtceu.uhv_262144a_laser_source_hatch": "262144A §4UHV§r Laser Source Hatch", + "block.gtceu.uhv_262144a_laser_target_hatch": "262144A §4UHV§r Laser Target Hatch", + "block.gtceu.uhv_4194304a_laser_source_hatch": "4194304A §4UHV§r Laser Source Hatch", + "block.gtceu.uhv_4194304a_laser_target_hatch": "4194304A §4UHV§r Laser Target Hatch", + "block.gtceu.uhv_65536a_laser_source_hatch": "65536A §4UHV§r Laser Source Hatch", + "block.gtceu.uhv_65536a_laser_target_hatch": "65536A §4UHV§r Laser Target Hatch", + "block.gtceu.uhv_67108864a_laser_source_hatch": "67108864A §4UHV§r Laser Source Hatch", + "block.gtceu.uhv_67108864a_laser_target_hatch": "67108864A §4UHV§r Laser Target Hatch", + "block.gtceu.uhv_buffer": "§4Epic Buffer§r", + "block.gtceu.uhv_compressed_fusion_reactor": "Compressed Fusion Reactor Controller MK-IV", + "block.gtceu.uhv_dehydrator": "§4Epic Dehydrator§r", + "block.gtceu.uhv_fusion_reactor": "Fusion Reactor Controller MK-IV", + "block.gtceu.uhv_huge_input_hatch": "§4UHV§r Huge Input Hatch", + "block.gtceu.uhv_huge_output_hatch": "§4UHV§r Huge Output Hatch", + "block.gtceu.uhv_lightning_processor": "§4Epic Lightning Processor§r", + "block.gtceu.uhv_neutron_accelerator": "§4UHV Neutron Accelerator§r", + "block.gtceu.uhv_parallel_hatch": "§4UHV§r Parallel Control Hatch", + "block.gtceu.uhv_rotor_holder": "§4UHV§r Rotor Holder", + "block.gtceu.uhv_world_data_scanner": "§4Epic World Data Scanner§r", + "block.gtceu.uiv_1048576a_laser_source_hatch": "1048576A §2UIV§r Laser Source Hatch", + "block.gtceu.uiv_1048576a_laser_target_hatch": "1048576A §2UIV§r Laser Target Hatch", + "block.gtceu.uiv_16384a_laser_source_hatch": "16384A §2UIV§r Laser Source Hatch", + "block.gtceu.uiv_16384a_laser_target_hatch": "16384A §2UIV§r Laser Target Hatch", + "block.gtceu.uiv_16777216a_laser_source_hatch": "16777216A §2UIV§r Laser Source Hatch", + "block.gtceu.uiv_16777216a_laser_target_hatch": "16777216A §2UIV§r Laser Target Hatch", + "block.gtceu.uiv_262144a_laser_source_hatch": "262144A §2UIV§r Laser Source Hatch", + "block.gtceu.uiv_262144a_laser_target_hatch": "262144A §2UIV§r Laser Target Hatch", + "block.gtceu.uiv_4194304a_laser_source_hatch": "4194304A §2UIV§r Laser Source Hatch", + "block.gtceu.uiv_4194304a_laser_target_hatch": "4194304A §2UIV§r Laser Target Hatch", + "block.gtceu.uiv_65536a_laser_source_hatch": "65536A §2UIV§r Laser Source Hatch", + "block.gtceu.uiv_65536a_laser_target_hatch": "65536A §2UIV§r Laser Target Hatch", + "block.gtceu.uiv_67108864a_laser_source_hatch": "67108864A §2UIV§r Laser Source Hatch", + "block.gtceu.uiv_67108864a_laser_target_hatch": "67108864A §2UIV§r Laser Target Hatch", + "block.gtceu.uiv_buffer": "§2Epic Buffer III§r", + "block.gtceu.uiv_dehydrator": "§2Epic Dehydrator III§r", + "block.gtceu.uiv_energy_input_hatch_16a": "16A §2UIV§r Energy Hatch", + "block.gtceu.uiv_energy_input_hatch_4a": "4A §2UIV§r Energy Hatch", + "block.gtceu.uiv_energy_output_hatch_16a": "16A §2UIV§r Dynamo Hatch", + "block.gtceu.uiv_energy_output_hatch_4a": "4A §2UIV§r Dynamo Hatch", + "block.gtceu.uiv_huge_input_hatch": "§2UIV§r Huge Input Hatch", + "block.gtceu.uiv_huge_output_hatch": "§2UIV§r Huge Output Hatch", + "block.gtceu.uiv_lightning_processor": "§2Epic Lightning Processor III§r", + "block.gtceu.uiv_neutron_accelerator": "§2UIV Neutron Accelerator§r", + "block.gtceu.uiv_parallel_hatch": "§2UIV§r Parallel Control Hatch", + "block.gtceu.uiv_substation_input_hatch_64a": "64A §2UIV§r Substation Energy Hatch", + "block.gtceu.uiv_substation_output_hatch_64a": "64A §2UIV§r Substation Dynamo Hatch", + "block.gtceu.uiv_world_data_scanner": "§2Epic World Data Scanner III§r", + "block.gtceu.ulv_huge_input_hatch": "§8ULV§r Huge Input Hatch", + "block.gtceu.ulv_huge_output_hatch": "§8ULV§r Huge Output Hatch", + "block.gtceu.ulv_neutron_accelerator": "§8ULV Neutron Accelerator§r", + "block.gtceu.ulv_primitive_magic_energy": "Ultra Low Voltage Primitive Magic Energy Absorber", + "block.gtceu.uv_1048576a_laser_source_hatch": "1048576A §3UV§r Laser Source Hatch", + "block.gtceu.uv_1048576a_laser_target_hatch": "1048576A §3UV§r Laser Target Hatch", + "block.gtceu.uv_16384a_laser_source_hatch": "16384A §3UV§r Laser Source Hatch", + "block.gtceu.uv_16384a_laser_target_hatch": "16384A §3UV§r Laser Target Hatch", + "block.gtceu.uv_16777216a_laser_source_hatch": "16777216A §3UV§r Laser Source Hatch", + "block.gtceu.uv_16777216a_laser_target_hatch": "16777216A §3UV§r Laser Target Hatch", + "block.gtceu.uv_262144a_laser_source_hatch": "262144A §3UV§r Laser Source Hatch", + "block.gtceu.uv_262144a_laser_target_hatch": "262144A §3UV§r Laser Target Hatch", + "block.gtceu.uv_4194304a_laser_source_hatch": "4194304A §3UV§r Laser Source Hatch", + "block.gtceu.uv_4194304a_laser_target_hatch": "4194304A §3UV§r Laser Target Hatch", + "block.gtceu.uv_65536a_laser_source_hatch": "65536A §3UV§r Laser Source Hatch", + "block.gtceu.uv_65536a_laser_target_hatch": "65536A §3UV§r Laser Target Hatch", + "block.gtceu.uv_67108864a_laser_source_hatch": "67108864A §3UV§r Laser Source Hatch", + "block.gtceu.uv_67108864a_laser_target_hatch": "67108864A §3UV§r Laser Target Hatch", + "block.gtceu.uv_buffer": "§3Ultimate Buffer§r", + "block.gtceu.uv_compressed_fusion_reactor": "Compressed Fusion Reactor Controller MK-III", + "block.gtceu.uv_dehydrator": "§3Ultimate Dehydrator§r", + "block.gtceu.uv_huge_input_hatch": "§3UV§r Huge Input Hatch", + "block.gtceu.uv_huge_output_hatch": "§3UV§r Huge Output Hatch", + "block.gtceu.uv_lightning_processor": "§3Ultimate Lightning Processor§r", + "block.gtceu.uv_neutron_accelerator": "§3UV Neutron Accelerator§r", + "block.gtceu.uv_world_data_scanner": "§3Ultimate World Data Scanner§r§r", + "block.gtceu.uxv_1048576a_laser_source_hatch": "1048576A §eUXV§r Laser Source Hatch", + "block.gtceu.uxv_1048576a_laser_target_hatch": "1048576A §eUXV§r Laser Target Hatch", + "block.gtceu.uxv_16384a_laser_source_hatch": "16384A §eUXV§r Laser Source Hatch", + "block.gtceu.uxv_16384a_laser_target_hatch": "16384A §eUXV§r Laser Target Hatch", + "block.gtceu.uxv_16777216a_laser_source_hatch": "16777216A §eUXV§r Laser Source Hatch", + "block.gtceu.uxv_16777216a_laser_target_hatch": "16777216A §eUXV§r Laser Target Hatch", + "block.gtceu.uxv_262144a_laser_source_hatch": "262144A §eUXV§r Laser Source Hatch", + "block.gtceu.uxv_262144a_laser_target_hatch": "262144A §eUXV§r Laser Target Hatch", + "block.gtceu.uxv_4194304a_laser_source_hatch": "4194304A §eUXV§r Laser Source Hatch", + "block.gtceu.uxv_4194304a_laser_target_hatch": "4194304A §eUXV§r Laser Target Hatch", + "block.gtceu.uxv_65536a_laser_source_hatch": "65536A §eUXV§r Laser Source Hatch", + "block.gtceu.uxv_65536a_laser_target_hatch": "65536A §eUXV§r Laser Target Hatch", + "block.gtceu.uxv_67108864a_laser_source_hatch": "67108864A §eUXV§r Laser Source Hatch", + "block.gtceu.uxv_67108864a_laser_target_hatch": "67108864A §eUXV§r Laser Target Hatch", + "block.gtceu.uxv_buffer": "§eEpic Buffer IV§r", + "block.gtceu.uxv_dehydrator": "§eEpic Dehydrator IV§r", + "block.gtceu.uxv_energy_input_hatch_16a": "16A §eUXV§r Energy Hatch", + "block.gtceu.uxv_energy_input_hatch_4a": "4A §eUXV§r Energy Hatch", + "block.gtceu.uxv_energy_output_hatch_16a": "16A §eUXV§r Dynamo Hatch", + "block.gtceu.uxv_energy_output_hatch_4a": "4A §eUXV§r Dynamo Hatch", + "block.gtceu.uxv_huge_input_hatch": "§eUXV§r Huge Input Hatch", + "block.gtceu.uxv_huge_output_hatch": "§eUXV§r Huge Output Hatch", + "block.gtceu.uxv_lightning_processor": "§eEpic Lightning Processor IV§r", + "block.gtceu.uxv_neutron_accelerator": "§eUXV Neutron Accelerator§r", + "block.gtceu.uxv_parallel_hatch": "§eUXV§r Parallel Control Hatch", + "block.gtceu.uxv_substation_input_hatch_64a": "64A §eUXV§r Substation Energy Hatch", + "block.gtceu.uxv_substation_output_hatch_64a": "64A §eUXV§r Substation Dynamo Hatch", + "block.gtceu.uxv_world_data_scanner": "§eEpic World Data Scanner IV§r", + "block.gtceu.vacuum_drying_furnace": "Vacuum Drying Furnace", + "block.gtceu.void_fluid_drilling_rig": "Void Fluid Drilling Rig", + "block.gtceu.void_miner": "Void Miner", + "block.gtceu.weather_control": "Weather Controller", + "block.gtceu.wireless_data_receiver_hatch": "Wireless Optical Data Receiver Hatch", + "block.gtceu.wireless_data_transmitter_hatch": "Wireless Optical Data Transmitter Hatch", + "block.gtceu.wood_distillation": "Wood Distillation Plant", + "block.gtceu.zpm_1048576a_laser_source_hatch": "1048576A §cZPM§r Laser Source Hatch", + "block.gtceu.zpm_1048576a_laser_target_hatch": "1048576A §cZPM§r Laser Target Hatch", + "block.gtceu.zpm_16384a_laser_source_hatch": "16384A §cZPM§r Laser Source Hatch", + "block.gtceu.zpm_16384a_laser_target_hatch": "16384A §cZPM§r Laser Target Hatch", + "block.gtceu.zpm_16777216a_laser_source_hatch": "16777216A §cZPM§r Laser Source Hatch", + "block.gtceu.zpm_16777216a_laser_target_hatch": "16777216A §cZPM§r Laser Target Hatch", + "block.gtceu.zpm_262144a_laser_source_hatch": "262144A §cZPM§r Laser Source Hatch", + "block.gtceu.zpm_262144a_laser_target_hatch": "262144A §cZPM§r Laser Target Hatch", + "block.gtceu.zpm_4194304a_laser_source_hatch": "4194304A §cZPM§r Laser Source Hatch", + "block.gtceu.zpm_4194304a_laser_target_hatch": "4194304A §cZPM§r Laser Target Hatch", + "block.gtceu.zpm_65536a_laser_source_hatch": "65536A §cZPM§r Laser Source Hatch", + "block.gtceu.zpm_65536a_laser_target_hatch": "65536A §cZPM§r Laser Target Hatch", + "block.gtceu.zpm_67108864a_laser_source_hatch": "67108864A §cZPM§r Laser Source Hatch", + "block.gtceu.zpm_67108864a_laser_target_hatch": "67108864A §cZPM§r Laser Target Hatch", + "block.gtceu.zpm_buffer": "§cElite Buffer III§r", + "block.gtceu.zpm_compressed_fusion_reactor": "Compressed Fusion Reactor Controller MK-II", + "block.gtceu.zpm_dehydrator": "§cElite Dehydrator III§r", + "block.gtceu.zpm_fluid_drilling_rig": "§cInfinite Fluid Drilling Rig§r", + "block.gtceu.zpm_huge_input_hatch": "§cZPM§r Huge Input Hatch", + "block.gtceu.zpm_huge_output_hatch": "§cZPM§r Huge Output Hatch", + "block.gtceu.zpm_lightning_processor": "§cElite Lightning Processor III§r", + "block.gtceu.zpm_naquadah_reactor": "§cElite Naquadah Reactor§r", + "block.gtceu.zpm_neutron_accelerator": "§cZPM Neutron Accelerator§r", + "block.gtceu.zpm_world_data_scanner": "§cElite World Data Scanner III§r", + "config.gtceu.option.hand.output": "Hand Output", + "config.jade.plugin_gtceu.me_molecular_assembler_io": "ME Molecular Assembler IO", + "gtceu.advanced_hyper_reactor": "Advanced Hyper Reaction", + "gtceu.aggregation_device": "Aggregation Device", + "gtceu.annihilate_generator": "Annihilation Generator", + "gtceu.assembler_module": "Space Assembly", + "gtceu.atomic_energy_excitation": "Atomic Energy Excitation", + "gtceu.bedrock_drilling_rig": "Bedrock Drilling Rig", + "gtceu.block_conversion": "Block Conversion", + "gtceu.casings.tier": "Tier: %s", + "gtceu.circuit_assembly_line": "Circuit Assembly Line", + "gtceu.circuit_printer": "Circuit Programming Copy", + "gtceu.component_assembly_line": "Component Assembly", + "gtceu.cosmos_simulation": "Cosmos Simulation", + "gtceu.create_aggregation": "Creative Aggregation", + "gtceu.decay_hastener": "Decay Hastener", + "gtceu.dehydrator": "Dehydrator", + "gtceu.desulfurizer": "Desulfurization", + "gtceu.digestion_treatment": "Digestion Treatment", + "gtceu.dimensional_focus_engraving_array": "Dimensional Focus Engraving", + "gtceu.dimensionally_transcendent_mixer": "Dimensionally Transcendent Mixing", + "gtceu.dimensionally_transcendent_plasma_forge": "Dimensionally Transcendent Smelting", + "gtceu.disassembly": "Disassembly", + "gtceu.dissolution_treatment": "Dissolution Treatment", + "gtceu.distort": "Deep Chemical Distortion", + "gtceu.door_of_create": "Door of Creation", + "gtceu.dragon_egg_copier": "Dragon Egg Copying", + "gtceu.drilling_module": "Space Drilling", + "gtceu.dyson_sphere": "Dyson Sphere", + "gtceu.electric_implosion_compressor": "Electric Implosion Compressor", + "gtceu.element_copying": "Element Copier", + "gtceu.fishing_ground": "Fishing Ground", + "gtceu.fission_reactor": "Fission Reactor", + "gtceu.flotating_beneficiation": "Flotation Beneficiation", + "gtceu.fuel_refining": "Fuel Refining", + "gtceu.gravitation_shockburst": "Gravitational Shockburst", + "gtceu.greenhouse": "Greenhouse", + "gtceu.gui.content.chance_1_in": "Consume Chance:%s%%", + "gtceu.gui.content.chance_1_logic_in": "Consume Chance:%s%% (%s)", + "gtceu.gui.content.tier_boost_fix": "Tier Chance: %s%%/tier", + "gtceu.heat_exchanger": "Heat Exchanger", + "gtceu.hyper_reactor": "Hyper Reaction", + "gtceu.incubator": "Incubator", + "gtceu.integrated_ore_processor": "Integrated Ore Processing", + "gtceu.isa_mill": "Wet Grinding", + "gtceu.jei.bedrock_fluid.benzene_deposit": "Benzene Deposit", + "gtceu.jei.bedrock_fluid.ceres_krypton_deposit": "Krypton Deposit", + "gtceu.jei.bedrock_fluid.ceres_neon_deposit": "Neon Deposit", + "gtceu.jei.bedrock_fluid.ceres_radon_deposit": "Radon Deposit", + "gtceu.jei.bedrock_fluid.ceres_xenon_deposit": "Xenon Deposit", + "gtceu.jei.bedrock_fluid.charcoal_byproducts": "Charcoal Byproduct Deposit", + "gtceu.jei.bedrock_fluid.chlorine": "Chlorine Deposit", + "gtceu.jei.bedrock_fluid.coal_gas_deposit": "Coal Gas Deposit", + "gtceu.jei.bedrock_fluid.deuterium_deposit": "Deuterium Deposit", + "gtceu.jei.bedrock_fluid.flat_heavy_oil_deposit": "Superflat Heavy Oil Deposit", + "gtceu.jei.bedrock_fluid.flat_light_oil_deposit": "Superflat Light Oil Deposit", + "gtceu.jei.bedrock_fluid.flat_natural_gas_deposit": "Superflat Natural Gas Deposit", + "gtceu.jei.bedrock_fluid.flat_oil_deposit": "Superflat Oil Deposit", + "gtceu.jei.bedrock_fluid.flat_raw_oil_deposit": "Superflat Raw Oil Deposit", + "gtceu.jei.bedrock_fluid.flat_salt_water_deposit": "Superflat Salt Water Deposit", + "gtceu.jei.bedrock_fluid.fluorine": "Fluorine Deposit", + "gtceu.jei.bedrock_fluid.helium3_deposit": "Helium-3 Deposit", + "gtceu.jei.bedrock_fluid.helium_deposit": "Helium Deposit", + "gtceu.jei.bedrock_fluid.hydrochloric_acid_deposit": "Hydrochloric Acid Deposit", + "gtceu.jei.bedrock_fluid.methane_deposit": "Methane Deposit", + "gtceu.jei.bedrock_fluid.nitric_acid_deposit": "Nitric Acid Deposit", + "gtceu.jei.bedrock_fluid.radon_deposit": "Radon Deposit", + "gtceu.jei.bedrock_fluid.sulfuric_acid_deposit": "Sulfuric Acid Deposit", + "gtceu.jei.bedrock_fluid.unknowwater": "Unknown Liquid Deposit", + "gtceu.jei.bedrock_fluid.void_heavy_oil_deposit": "Void Heavy Oil Deposit", + "gtceu.jei.bedrock_fluid.void_light_oil_deposit": "Void Light Oil Deposit", + "gtceu.jei.bedrock_fluid.void_natural_gas_deposit": "Void Natural Gas Deposit", + "gtceu.jei.bedrock_fluid.void_oil_deposit": "Void Oil Deposit", + "gtceu.jei.bedrock_fluid.void_raw_oil_deposit": "Void Raw Oil Deposit", + "gtceu.jei.bedrock_fluid.void_salt_water_deposit": "Void Salt Water Deposit", + "gtceu.jei.ore_vein.apatite_vein_ad": "Apatite Vein", + "gtceu.jei.ore_vein.bauxite_vein_ad": "Rutile Vein", + "gtceu.jei.ore_vein.beryllium_vein_aw": "Emerald Vein", + "gtceu.jei.ore_vein.calorite_vein_ad": "Heat Resistant Vein", + "gtceu.jei.ore_vein.celestine_vein_ad": "Celestite Vein", + "gtceu.jei.ore_vein.certus_quartz_vein_aw": "AE2 Vein", + "gtceu.jei.ore_vein.desh_vein_ad": "Desh Vein", + "gtceu.jei.ore_vein.molybdenum_vein_aw": "Molybdenum Vein", + "gtceu.jei.ore_vein.monazite_vein_ad": "Monazite Vein", + "gtceu.jei.ore_vein.naquadah_vein_ad": "Naquadah Vein", + "gtceu.jei.ore_vein.nickel_vein_ad": "Nickel Vein", + "gtceu.jei.ore_vein.olivine_vein_ad": "Olivine Vein", + "gtceu.jei.ore_vein.ostrum_vein_ad": "Ostrum Vein", + "gtceu.jei.ore_vein.pitchblende_vein_ad": "Uranium Vein", + "gtceu.jei.ore_vein.plutonium_vein_ad": "Plutonium Vein", + "gtceu.jei.ore_vein.quartzite_vein_aw": "Quartzite Vein", + "gtceu.jei.ore_vein.saltpeter_vein_aw": "Saltpeter Vein", + "gtceu.jei.ore_vein.scheelite_vein_ad": "Scheelite Vein", + "gtceu.jei.ore_vein.sheldonite_vein_ad": "Platiniferous Vein", + "gtceu.jei.ore_vein.stibnite_vein_aw": "Stibnite Vein", + "gtceu.jei.ore_vein.sulfur_vein_ad": "Sulfur Vein", + "gtceu.jei.ore_vein.sulfur_vein_aw": "Sulfur Vein", + "gtceu.jei.ore_vein.topaz_vein_aw": "Topaz Vein", + "gtceu.jei.ore_vein.zircon_vein_ad": "Zircon Vein", + "gtceu.large_gas_collector": "Large Gas Collector", + "gtceu.large_naquadah_reactor": "Large Naquadah Reactor", + "gtceu.large_recycler": "Material Recycler", + "gtceu.large_void_miner": "Precise Ore Mode", + "gtceu.lava_furnace": "Lava Furnace", + "gtceu.lightning_processor": "Lightning Processing", + "gtceu.machine.advanced_assembly_line.tooltip.0": "Expandable up to 64 slots", + "gtceu.machine.advanced_assembly_line.tooltip.1": "Only data hatches can be used", + "gtceu.machine.advanced_hyper_reactor.tooltip.0": "Different plasma provides different parallel processing", + "gtceu.machine.advanced_hyper_reactor.tooltip.1": "Stellar: 8, Dense Neutronium: 16", + "gtceu.machine.advanced_infinite_driller.current_heat": "Current Temperature: %sK", + "gtceu.machine.advanced_infinite_driller.drilled_fluid": "Fluid: %s Production: %s", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.0": "More advanced infinite fluid drilling rig", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.1": "Can extract fluids more efficiently, while the driller requires a certain temperature to start, can be heated by injecting liquid blaze", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.10": "Liquid blaze consumption formula: (current temperature^1.3)", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.11": "Temperature sensor can be used", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.2": "Can insert neutronium drill bits (50000)/vibranium drill bits (100000)", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.3": "Base temperature: 8000K (Titansteel coils), using higher-grade coils can provide higher additional temperature", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.4": "The driller generates heat at different working temperatures, heat generation formula: (temperature/2000)", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.5": "As temperature increases, efficiency also increases. When temperature exceeds critical value, the drill bit will melt", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.6": "Different coolant fluids can be injected to cool down, available coolants are listed below. Coolant consumption is fixed at 200B/5t", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.7": "Distilled Water 1K", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.8": "Liquid Oxygen 2K", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.9": "Liquid Helium 4K", + "gtceu.machine.advanced_infinite_driller.fast": "High Speed Mode: %s", + "gtceu.machine.advanced_infinite_driller.heat": "Max Temperature: %sK / Working Temperature: %sK", + "gtceu.machine.advanced_infinite_driller.no_coil": "Coil tier too low", + "gtceu.machine.advanced_infinite_driller.not_fluid_head": "No drill bit", + "gtceu.machine.advanced_infinite_driller.process": "Damage: %s", + "gtceu.machine.advanced_infinite_driller.range": "Working Range: %s", + "gtceu.machine.advanced_integrated_ore_processor.tooltip.0": "Max Parallel: 2147483647", + "gtceu.machine.advanced_neutron_activator.tooltip.1": "Consumes corresponding power when working, automatically provides neutron kinetic energy", + "gtceu.machine.advanced_neutron_activator.tooltip.2": "Neutron kinetic energy auto-conversion ratio is 2eu -> 1ev", + "gtceu.machine.aggregation_device.tooltip.0": "Each voltage tier above UEV doubles max parallel", + "gtceu.machine.assembly_line.tooltip.0": "Each assembly line unit increases speed by 1%", + "gtceu.machine.available_recipe_map_10.tooltip": "Available Recipe Types: %s, %s, %s, %s, %s, %s, %s, %s, %s, %s", + "gtceu.machine.available_recipe_map_11.tooltip": "Available Recipe Types: %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s", + "gtceu.machine.available_recipe_map_5.tooltip": "Available Recipe Types: %s, %s, %s, %s, %s", + "gtceu.machine.available_recipe_map_6.tooltip": "Available Recipe Types: %s, %s, %s, %s, %s, %s", + "gtceu.machine.available_recipe_map_7.tooltip": "Available Recipe Types: %s, %s, %s, %s, %s, %s, %s", + "gtceu.machine.available_recipe_map_8.tooltip": "Available Recipe Types: %s, %s, %s, %s, %s, %s, %s, %s", + "gtceu.machine.available_recipe_map_9.tooltip": "Available Recipe Types: %s, %s, %s, %s, %s, %s, %s, %s, %s", + "gtceu.machine.bedrock_drilling_rig.tooltip.0": "Requires bedrock below the drill bit", + "gtceu.machine.bedrock_drilling_rig.tooltip.1": "Each run has a 10% chance to destroy bedrock", + "gtceu.machine.blaze_blast_furnace.tooltip.0": "Requires §b10x(Power÷120)^1/2§rmb of §eLiquid Blaze§r per second", + "gtceu.machine.blaze_blast_furnace.tooltip.1": "Max parallel fixed at 64", + "gtceu.machine.block_conversion_room.am": "Conversion amount per time: %s", + "gtceu.machine.block_conversion_room.tooltip.0": "Randomly selects a block position inside the machine to convert per second", + "gtceu.machine.block_conversion_room.tooltip.1": "Each voltage tier above MV adds +4 blocks per conversion without repetition", + "gtceu.machine.chemical_distort.tooltip.0": "Each 100K coil temperature above recipe temperature adds 4 parallel", + "gtceu.machine.chemical_distort.tooltip.1": "Higher voltage does not provide additional temperature", + "gtceu.machine.chemical_plant.tooltip.0": "Each coil tier above Cupronickel reduces power consumption and increases speed by 5%", + "gtceu.machine.circuit_assembly_line.tooltip.0": "Placing robots matching the recipe in the controller provides 2x parallel for corresponding quantity", + "gtceu.machine.cold_ice_freezer.tooltip.0": "Requires §b10x voltage tier^2§rmb of §bLiquid Ice§r per second", + "gtceu.machine.create_computation.tooltip.0": "Input Voltage: §4§lMAX§r", + "gtceu.machine.dimensionally_transcendent_dirt_forge.tooltip.0": "Has a maximum parallel of 524288 and directly completes recipes", + "gtceu.machine.dimensionally_transcendent_mixer.tooltip.0": "Duration multiplier is 0.2 when running mixer recipes", + "gtceu.machine.duration_multiplier.tooltip": "Duration Multiplier: %s", + "gtceu.machine.dyson_sphere.number": "Launch Count: %s / 10000", + "gtceu.machine.dyson_sphere.tooltip.0": "Starts working after launching Dyson sphere modules", + "gtceu.machine.dyson_sphere.tooltip.1": "Each run has a (module count/128 + 1)% chance to damage one module", + "gtceu.machine.dyson_sphere.tooltip.2": "When damage exceeds 60%, output efficiency gradually decreases from 100% to 20% with damage value, and outputs redstone signal that increases with damage value", + "gtceu.machine.dyson_sphere.tooltip.3": "When damage reaches 100%, reduce module launch count by one and reset damage value", + "gtceu.machine.dyson_sphere.tooltip.4": "When damage value is above 60%, launching will not increase launch count but will reset damage value", + "gtceu.machine.dyson_sphere.tooltip.5": "Power generation and computing requirements are determined by the number of launched modules", + "gtceu.machine.dyson_sphere.tooltip.6": "Each launch increases power by 1A MAX", + "gtceu.machine.dyson_sphere.voltage": "Max Energy Output: %s EU/t", + "gtceu.machine.efficiency.tooltip": "§7Efficiency:§r %s", + "gtceu.machine.electric_blast_furnace.tooltip.a": "Duration Multiplier: max(Recipe Temperature/Furnace Temperature, 0.5)", + "gtceu.machine.engraving_laser_plant.tooltip.0": "Precision laser mode does not support parallel processing", + "gtceu.machine.eut_multiplier.tooltip": "Power Consumption Multiplier: %s", + "gtceu.machine.eye_of_harmony.tooltip.0": "Create a miniature universe and obtain resources from within", + "gtceu.machine.eye_of_harmony.tooltip.1": "This multiblock machine requires too much EU to be powered by conventional means", + "gtceu.machine.eye_of_harmony.tooltip.2": "Directly supplied with EU by wireless EU network, specific values can be viewed in GUI", + "gtceu.machine.eye_of_harmony.tooltip.3": "Executes special overclocking mode, every 16x power increase provides 2x speed, overclocking is controlled by programming circuit", + "gtceu.machine.eye_of_harmony.tooltip.4": "Circuit must be set before working, circuit 1 performs no overclocking, circuits 2-4 perform 1, 2, 3 overclocks respectively", + "gtceu.machine.eye_of_harmony.tooltip.5": "Startup requires 1024B of Cosmic Element, and 1024KB of hydrogen and helium", + "gtceu.machine.eye_of_harmony.tooltip.6": "Hydrogen and helium are stored inside the machine and will be continuously consumed before the machine is ready to work", + "gtceu.machine.eye_of_harmony.tooltip.7": "Right-click with a data stick to rebind wireless network", + "gtceu.machine.fission_reactor.cooler": "Coolant Component Count: %s, Adjacent Count: %s", + "gtceu.machine.fission_reactor.damaged": "Damage: %s", + "gtceu.machine.fission_reactor.fuel": "Fuel Component Count: %s, Adjacent Count: %s", + "gtceu.machine.fission_reactor.heat": "Reactor Temperature: %s K", + "gtceu.machine.fission_reactor.tooltip.0": "Reactor obtains maximum parallel equal to fuel component count before running", + "gtceu.machine.fission_reactor.tooltip.1": "Reactor heats up during operation based on conditions, temperature increase per second (K) = recipe heat production x (1 + adjacent fuel rod count)", + "gtceu.machine.fission_reactor.tooltip.10": "If supply is n times the demand, special overclocking is executed, overclocking count is n", + "gtceu.machine.fission_reactor.tooltip.11": "Consume demand amount of coolant again and reduce time by 1 second, if unable to supply coolant then interrupt overclocking, if progress reaches 100% then interrupt overclocking and consume one demand amount of coolant to reduce temperature by 1K", + "gtceu.machine.fission_reactor.tooltip.12": "When distilled water is used as coolant, steam is produced, production amount: consumption x min(160, 160/(1.4^(373-temperature)))", + "gtceu.machine.fission_reactor.tooltip.13": "When sodium-potassium alloy is used as coolant, hot sodium-potassium alloy is produced, production = consumption, if temperature is above 825K then same amount of supercritical sodium-potassium alloy is produced", + "gtceu.machine.fission_reactor.tooltip.14": "Recipes can be executed regardless of whether the reactor has conditions to consume coolant", + "gtceu.machine.fission_reactor.tooltip.15": "After reactor stops working, temperature decreases by 1K per second", + "gtceu.machine.fission_reactor.tooltip.2": "If temperature exceeds 1500K, reactor will be damaged, reactor explodes when damage reaches 100%", + "gtceu.machine.fission_reactor.tooltip.3": "Provide coolant during operation and control temperature based on different coolant coefficients", + "gtceu.machine.fission_reactor.tooltip.4": "Coolant coefficients: Distilled Water: 800, Sodium-Potassium Alloy: 20", + "gtceu.machine.fission_reactor.tooltip.5": "Reactor cooling has the following parameters:", + "gtceu.machine.fission_reactor.tooltip.6": "Minimum coolant demand and maximum coolant supply", + "gtceu.machine.fission_reactor.tooltip.7": "Minimum demand = recipe heat production x actual parallel count x current temperature/1500", + "gtceu.machine.fission_reactor.tooltip.8": "Maximum supply = (coolant components - (adjacent count/3)) x 8", + "gtceu.machine.fission_reactor.tooltip.9": "When supply >= demand, coolant consumption conditions are met, consume provided coolant, consumption = demand x coolant coefficient, and prevent reactor heating", + "gtceu.machine.flotation_cell_regulator.tooltip.0": "Industrial-grade flotation cell", + "gtceu.machine.fusion_reactor.tooltip.a": "Each machine recipe tier above machine tier increases max parallel by 4x, up to 16", + "gtceu.machine.generator_array.tooltip.0": "Powerful operating environment doubles the power of small generators in the machine", + "gtceu.machine.generator_array.tooltip.1": "Can enable wireless power grid mode, generated power will be directly sent to wireless power grid", + "gtceu.machine.generator_array.wireless": "Wireless Power Grid Mode:", + "gtceu.machine.greenhouse.tooltip.0": "Requires sunlight to operate", + "gtceu.machine.greenhouse.tooltip.1": "If sunlight is insufficient, speed will be reduced", + "gtceu.machine.heat_exchanger.tooltip.0": "Processes all input hot fluid each time", + "gtceu.machine.heat_exchanger.tooltip.1": "Must ensure input coolant can cool all fluid", + "gtceu.machine.heat_sensor.tooltip.0": "Outputs redstone signal based on §6Temperature§7, right-click to open GUI for settings", + "gtceu.machine.hyper_reactor.tooltip.0": "Providing additional 1mb of plasma before each run will obtain 16 parallel", + "gtceu.machine.hyper_reactor.tooltip.1": "Different fuels require different plasma", + "gtceu.machine.hyper_reactor.tooltip.2": "Order 1-4: Cupronickel, Ender, Mithril, Metastable ∫", + "gtceu.machine.integrated_ore_processor.tooltip.0": "Complete ore processing in one step", + "gtceu.machine.integrated_ore_processor.tooltip.1": "Circuit 1: Grinding-Grinding-Centrifuge", + "gtceu.machine.integrated_ore_processor.tooltip.2": "Circuit 2: Grinding-Ore Wash-Thermal Centrifuge-Grinding", + "gtceu.machine.integrated_ore_processor.tooltip.3": "Circuit 3: Grinding-Ore Wash-Grinding-Centrifuge", + "gtceu.machine.integrated_ore_processor.tooltip.4": "Circuit 4: Grinding-Ore Wash-Sifting-Centrifuge", + "gtceu.machine.integrated_ore_processor.tooltip.5": "Circuit 5: Grinding-Chemical Bath-Thermal Centrifuge-Grinding", + "gtceu.machine.integrated_ore_processor.tooltip.6": "Circuit 6: Grinding-Chemical Bath-Grinding-Centrifuge", + "gtceu.machine.integrated_ore_processor.tooltip.7": "Circuit 7: Grinding-Chemical Bath-Sifting-Centrifuge", + "gtceu.machine.isa_mill.tooltip.0": "Industrial-grade wet grinding", + "gtceu.machine.large_arc_smelter.tooltip.0": "Duration multiplied by 4 when running lightning processing recipes", + "gtceu.machine.large_block_conversion_room.tooltip.1": "Each voltage tier above MV adds +64 blocks per conversion without repetition", + "gtceu.machine.large_block_conversion_room.tooltip.2": "Inserting conversion simulation card enables virtual mode, inserting high-speed conversion simulation card allows single conversion of all blocks in buses", + "gtceu.machine.large_gas_collector.tooltip.0": "Max Parallel: 100000", + "gtceu.machine.large_greenhouse.tooltip.0": "No sunlight required to operate", + "gtceu.machine.large_recycler.tooltip.0": "Each voltage tier above EV increases max parallel by 4x", + "gtceu.machine.large_rock_crusher.tooltip.0": "Requires corresponding fluid in input hatch", + "gtceu.machine.large_steam_input_hatch.tooltip.0": "Increases steam multiblock recipe limit to MV and unlocks overclocking functionality", + "gtceu.machine.large_void_miner.tooltip.0": "Precision mode consumes essence to collect specified ore veins", + "gtceu.machine.large_void_miner.tooltip.1": "Random mode consumes 10KB drilling fluid and longer duration to randomly collect all ores, ensure sufficient output space in random mode", + "gtceu.machine.lightning_rod.tooltip.0": "Lightning rod above generates large amounts of energy when struck by lightning", + "gtceu.machine.lightning_rod.tooltip.1": "Can only generate energy once every 0.5 seconds, with a certain chance of destroying the lightning rod above", + "gtceu.machine.lightning_rod.tooltip.2": "If stored energy is full, machine will explode", + "gtceu.machine.lockrecipe.line.0": "%s x §e%s§r", + "gtceu.machine.lockrecipe.line.1": "%s x §e%s§r(Output Chance: %s%%)", + "gtceu.machine.lockrecipe.line.2": "%s x §e%s§r(§cNot Consumed§r)", + "gtceu.machine.me.me_dual_hatch_stock.data_stick.name": "§oME Stock Input Bus Configuration Data", + "gtceu.machine.me.me_extended_export_buffer.data_stick.name": "§oME Extended Export Bus Configuration Data", + "gtceu.machine.me_craft_parallel_core.tooltip.0": "Provides §64194304§f parallel", + "gtceu.machine.me_craft_pattern_core.tooltip": "Provides §6%d§f pattern capacity", + "gtceu.machine.me_craft_speed_core.tooltip.0": "Each reduces recipe duration by §68§f ticks", + "gtceu.machine.me_extended_async_export_buffer.tooltip.0": "Allows configuring §bPriority§f", + "gtceu.machine.me_extended_async_export_buffer.tooltip.1": "Uses §6Asynchronous Export§f, suitable for cross-recipe machines", + "gtceu.machine.me_extended_export_buffer.tooltip.0": "Allows configuring §6Output Filter§f and §bPriority§f", + "gtceu.machine.me_mini_pattern_buffer.desc.0": "Mini version §6provides no output§f", + "gtceu.machine.me_molecular_assembler_io.tooltip.0": "Can perform pattern management after formation", + "gtceu.machine.me_molecular_assembler_io.tooltip.1": "Products automatically flow back to AE network", + "gtceu.machine.me_pattern_buffer.desc.0": "§fAll pattern slots have §6complete isolation§f", + "gtceu.machine.me_pattern_buffer.desc.1": "§bMiddle-click§f on slot to open dedicated §6Catalyst Slot§f, left-side §6Circuit§f and §6Catalyst§f columns are shared with all patterns", + "gtceu.machine.me_pattern_buffer.desc.2": "Fill corresponding circuit in §6Pattern§f as virtual circuit, §6no consumption§f when ordering, will §6override§f shared circuit settings", + "gtceu.machine.me_pattern_buffer.desc.3": "Built-in §6Pattern Multiplication§f function, only fill basic quantity pattern, ordering will dispatch required multiplier at once", + "gtceu.machine.me_pattern_buffer.desc.4": "Built-in §6Recipe Cache§f function, single slot cache one recipe for default, support customize", + "gtceu.machine.me_pattern_buffer.desc.5": "Built-in §6ME Output§f function, no need to place additional output hatches", + "gtceu.machine.me_pattern_buffer_proxy.desc.0": "Mirror IO determined by §6bound ME Pattern Buffer§f", + "gtceu.machine.module": "Installed Module Count: %s", + "gtceu.machine.molecular_assembler_matrix.tooltip.0": "Industrial §6AE Pattern§f crafting factory", + "gtceu.machine.molecular_assembler_matrix.tooltip.1": "Supports §bCrafting Patterns§f, §bForging Patterns§f and §bStonecutting Patterns§f", + "gtceu.machine.molecular_assembler_matrix.tooltip.2": "Crafting patterns only allow §6Unbreaking§f tools, which must be placed in the catalyst slot", + "gtceu.machine.molecular_assembler_matrix.tooltip.3": "Tools §6will not be consumed§f when crafting", + "gtceu.machine.molecular_assembler_matrix.tooltip.4": "Use ME Molecular Assembler Matrix Port or Pattern Management Terminal for pattern management", + "gtceu.machine.molecular_assembler_matrix.tooltip.5": "Machine center can be filled with §bSpeed Cores§f, §bCrafting Cores§f and §bPattern Cores§f", + "gtceu.machine.molecular_assembler_matrix.tooltip.6": "Patterns are stored inside §6Pattern Cores§f and will drop when mined", + "gtceu.machine.multiple_recipes.tooltip": "Supports cross-recipe parallel processing", + "gtceu.machine.nano_core.tooltip.0": "Can run nano forge recipes of any tier", + "gtceu.machine.nano_core.tooltip.1": "Processing speed fixed at 20x", + "gtceu.machine.nano_core.tooltip.2": "Max Parallel: 8192", + "gtceu.machine.nano_forge.tooltip.0": "Requires corresponding nano swarm to work, with parallel based on swarm count", + "gtceu.machine.nano_forge_1.tooltip.0": "Parallel count equals swarm count", + "gtceu.machine.nano_forge_2.tooltip.0": "When processing Tier 2 recipes, parallel count equals swarm count", + "gtceu.machine.nano_forge_2.tooltip.1": "When processing Tier 1 recipes, parallel count doubles, overclocking mode becomes perfect overclocking", + "gtceu.machine.nano_forge_3.tooltip.0": "When processing Tier 3 recipes, parallel count equals swarm count", + "gtceu.machine.nano_forge_3.tooltip.1": "When processing Tier 2 recipes, parallel count doubles, overclocking mode becomes perfect overclocking", + "gtceu.machine.nano_forge_3.tooltip.2": "When processing Tier 1 recipes, parallel count quadruples, overclocking mode becomes 4x power for 8x speed", + "gtceu.machine.neutron_accelerator.tooltip.0": "§6Max EU Consumption:§r %s", + "gtceu.machine.neutron_accelerator.tooltip.1": "§bEach point of EU converts to §e10~20-eV§b neutron kinetic energy", + "gtceu.machine.neutron_activator.efficiency": "Kinetic Energy Consumption Rate: %s", + "gtceu.machine.neutron_activator.ev": "Current Neutron Kinetic Energy: %seV", + "gtceu.machine.neutron_activator.height": "Height: %s", + "gtceu.machine.neutron_activator.time": "Duration: %s", + "gtceu.machine.neutron_activator.tooltip.0": "§7Faster than light motion!", + "gtceu.machine.neutron_activator.tooltip.1": "Additional hyperspeed pipeline blocks provide recipe time reduction while lowering neutron accelerator efficiency", + "gtceu.machine.neutron_activator.tooltip.2": "Time reduction and acceleration efficiency: 0.95^additional block count", + "gtceu.machine.neutron_activator.tooltip.3": "When no neutron accelerator is running, neutron kinetic energy decreases by §e72KeV§r per second", + "gtceu.machine.neutron_activator.tooltip.4": "Input graphite/beryllium powder to immediately absorb §e10MeV§r neutron kinetic energy", + "gtceu.machine.neutron_sensor.tooltip.0": "Outputs redstone signal based on §6Neutron Kinetic Energy§7, right-click to open GUI for settings", + "gtceu.machine.off": "Off", + "gtceu.machine.on": "On", + "gtceu.machine.parallel_hatch_mk10.tooltip": "Allows processing up to 4096 recipes simultaneously", + "gtceu.machine.parallel_hatch_mk11.tooltip": "Allows processing up to 16384 recipes simultaneously", + "gtceu.machine.parallel_hatch_mk12.tooltip": "Allows processing up to 65536 recipes simultaneously", + "gtceu.machine.parallel_hatch_mk13.tooltip": "Allows processing up to 262144 recipes simultaneously", + "gtceu.machine.parallel_hatch_mk14.tooltip": "Allows processing up to 1048576 recipes simultaneously", + "gtceu.machine.parallel_hatch_mk15.tooltip": "Allows processing up to 4194304 recipes simultaneously", + "gtceu.machine.parallel_hatch_mk16.tooltip": "Allows processing up to 16777216 recipes simultaneously", + "gtceu.machine.parallel_hatch_mk9.tooltip": "Allows processing up to 1024 recipes simultaneously", + "gtceu.machine.pattern.recipe.cache": "Recipe Cached", + "gtceu.machine.pcb_factory.tooltip.0": "Inserting nano swarms provides reduction", + "gtceu.machine.pcb_factory.tooltip.1": "Time multiplier per swarm: Gold: 0.5%, Vibranium: 1%", + "gtceu.machine.pcb_factory.tooltip.2": "Vibranium also reduces power consumption by 4x", + "gtceu.machine.primitive_magic_energy.tooltip.0": "Endlessly absorbs energy from end crystals above the machine", + "gtceu.machine.processing_plant.tooltip.0": "Each mode requires a machine of corresponding voltage tier to operate", + "gtceu.machine.processing_plant.tooltip.1": "Each voltage tier above LV increases max parallel by 4", + "gtceu.machine.resource_collection.tooltip.0": "Max Parallel: 4^(Power Module Tier-1)", + "gtceu.machine.simulation_machine.tooltip.0": "Place main block in machine, use terminal right-click to place/preview structure of main block inside machine", + "gtceu.machine.slaughterhouse.is_spawn": "Entity Spawning:", + "gtceu.machine.slaughterhouse.tooltip.0": "Electric mob spawner, automatically kills mobs", + "gtceu.machine.slaughterhouse.tooltip.1": "Each voltage tier above MV increases processing count by 8 per run", + "gtceu.machine.slaughterhouse.tooltip.2": "Set circuit before running: Circuit 1 for passive mobs, Circuit 2 for hostile mobs", + "gtceu.machine.slaughterhouse.tooltip.3": "Turn on controller switch for entity mode, entity spawning mode provides actual player kill drops, requires non-peaceful mode", + "gtceu.machine.slaughterhouse.tooltip.4": "Entity spawning mode provides actual player kill drops, requires non-peaceful mode", + "gtceu.machine.slaughterhouse.tooltip.5": "Non-entity spawning mode provides virtual drops, works in peaceful mode, cannot obtain drops from player kills", + "gtceu.machine.space_elevator.set_out": "Departure", + "gtceu.machine.space_elevator.tooltip.0": "Can install up to 8 extension modules", + "gtceu.machine.space_elevator.tooltip.1": "Increasing voltage tier provides duration reduction for modules", + "gtceu.machine.space_elevator.tooltip.2": "Fixed consumption of 128 CWU", + "gtceu.machine.space_probe_surface_reception.tooltip.0": "Do not obstruct", + "gtceu.machine.star_ultimate_material_forge_factory.tooltip.0": "Max Parallel: 1000", + "gtceu.machine.structure_check": "Update Structure Check", + "gtceu.machine.super_computation.tooltip.0": "Obtains computational power output based on different voltage tiers", + "gtceu.machine.super_computation.tooltip.1": "Each computational power output requires 8 different circuit mainframes", + "gtceu.machine.super_computation.tooltip.2": "When providing §2UIV§r tier voltage, requires §bOptical Processor Mainframe§r and provides 1024CWU/t", + "gtceu.machine.super_computation.tooltip.3": "When providing §eUXV§r tier voltage, requires §bExotic Processor Mainframe§r and provides 2048CWU/t", + "gtceu.machine.super_computation.tooltip.4": "When providing §9§lOpV§r tier voltage, requires §bCosmic Processor Mainframe§r and provides 4096CWU/t", + "gtceu.machine.super_computation.tooltip.5": "When providing §4§lMAX§r tier voltage, requires §bSupracausal Processor Mainframe§r and provides 8192CWU/t", + "gtceu.machine.suprachronal_assembly_line.tooltip.0": "§8§lThe Unseen Touch§r", + "gtceu.machine.suprachronal_assembly_line.tooltip.1": "Can extend modules on both sides, modules share parallel count with controller", + "gtceu.machine.suprachronal_assembly_line_module.tooltip.0": "Installed on both sides of Suprachronal Assembly Line", + "gtceu.machine.vacuum_drying_furnace.tooltip.0": "When running vacuum drying recipes:", + "gtceu.machine.vacuum_drying_furnace.tooltip.1": "When running dehydration recipes:", + "gtceu.machine.weather_control.tooltip.0": "Circuit 1 switches to clear weather", + "gtceu.machine.weather_control.tooltip.1": "Circuit 2 switches to rain", + "gtceu.machine.weather_control.tooltip.2": "Circuit 3 switches to thunderstorm", + "gtceu.machine.wireless_data_hatch.bind": "Wireless data hatch binding completed", + "gtceu.machine.wireless_data_receiver_hatch.bind": "Bound to wireless optical data source hatch (%s)", + "gtceu.machine.wireless_data_receiver_hatch.to_bind": "Target hatch data reading completed, please right-click source hatch to bind", + "gtceu.machine.wireless_data_receiver_hatch.tooltip.1": "Inputs research data for multiblock structures", + "gtceu.machine.wireless_data_receiver_hatch.tooltip.2": "Requires using flash memory to right-click wireless optical data target hatch and wireless optical data hatch for binding", + "gtceu.machine.wireless_data_receiver_hatch.unbind": "Not bound to wireless optical data source hatch", + "gtceu.machine.wireless_data_transmitter_hatch.bind": "Bound to wireless optical data target hatch (%s)", + "gtceu.machine.wireless_data_transmitter_hatch.to_bind": "Source hatch data reading completed, please right-click target hatch to bind", + "gtceu.machine.wireless_data_transmitter_hatch.tooltip.1": "Outputs research data from multiblock structures", + "gtceu.machine.wireless_data_transmitter_hatch.tooltip.2": "Requires using flash memory to right-click wireless optical data target hatch and wireless optical data source hatch for binding", + "gtceu.machine.wireless_data_transmitter_hatch.unbind": "Not bound to wireless optical data target hatch", + "gtceu.machine.zpm_fluid_drilling_rig.tooltip": "Even no consumption", + "gtceu.magic_manufacturer": "Magic Generation", + "gtceu.mass_fabricator": "Mass Fabricator", + "gtceu.matter_fabricator": "Matter Fabricator", + "gtceu.miner_module": "Space Mining", + "gtceu.molecular_assembler": "Molecular Assembly", + "gtceu.multiblock.coil_parallel": "Each 900K coil temperature doubles parallel count", + "gtceu.multiblock.dissolving_tank.tooltip.0": "Must ensure input fluid matches recipe fluid ratio, otherwise no product output", + "gtceu.multiblock.fusion_reactor_energy": "EU: %dM / %dM", + "gtceu.multiblock.large_combustion_engine.Joint_boosted": "§bJoint Boosting Active", + "gtceu.multiblock.large_combustion_engine.supply_dinitrogen_tetroxide_to_boost": "Provide Dinitrogen Tetroxide for joint boosting", + "gtceu.multiblock.laser.tooltip": "Allows using laser hatches", + "gtceu.multiblock.mega_fluid_heater": "Dormitory power limit 1500W, this is an illegal electrical appliance!", + "gtceu.multiblock.oc_amount": "Overclocking Count: %s", + "gtceu.multiblock.only.laser.tooltip": "Only laser hatches can be used", + "gtceu.multiblock.pattern.error.tier": "§cMust use same tier blocks§r", + "gtceu.multiblock.steam_parallel_machine.modification_oc": "Modify Overclocking Count:", + "gtceu.multiblock.steam_parallel_machine.oc": "Each overclock reduces duration by 2x and increases steam consumption by 3x", + "gtceu.multiblock.uev_fusion_reactor.description": "Fusion Reactor MK-V is a large multiblock structure for fusing elements to form heavier elements. It can only use UEV tier energy hatches. Each energy hatch increases energy buffer by 160MEU, maximum energy buffer is 2560MEU", + "gtceu.multiblock.uhv_fusion_reactor.description": "Fusion Reactor MK-IV is a large multiblock structure for fusing elements to form heavier elements. It can only use UHV tier energy hatches. Each energy hatch increases energy buffer by 80MEU, maximum energy buffer is 1280MEU", + "gtceu.multiblock.universal.distinct.all": "Complete Isolation:", + "gtceu.multiblock.universal.rotor_obstructed": "Rotor shaft obstructed!", + "gtceu.nano_forge": "Nano Swarm Factory", + "gtceu.naquadah_reactor": "Naquadah Reactor", + "gtceu.neutron_activator": "Neutron Activation", + "gtceu.neutron_compressor": "Singularity Compression", + "gtceu.pcb_factory": "PCB Factory", + "gtceu.petrochemical_plant": "Petrochemical Plant", + "gtceu.plasma_condenser": "Plasma Condenser", + "gtceu.precision_assembler": "Precision Assembly", + "gtceu.precision_laser_engraver": "Precision Laser Engraver", + "gtceu.primitive_void_ore": "Primitive Void Mining", + "gtceu.qft": "Quantum Manipulator", + "gtceu.random_ore": "Random Ore Mode", + "gtceu.rare_earth_centrifugal": "Rare Earth Centrifuge", + "gtceu.recipe.ca_tier": "Casing Tier: %s", + "gtceu.recipe.cleanroom_law.display_name": "Absolute Cleanroom", + "gtceu.recipe.eu_to_starts": "Startup Energy Cost: %sMEU(MK%s)", + "gtceu.recipe.ev_max": "Max Neutron Kinetic Energy: %s MeV", + "gtceu.recipe.ev_min": "Min Neutron Kinetic Energy: %s MeV", + "gtceu.recipe.evt": "Neutron Kinetic Energy Consumption per Tick: %s KeV", + "gtceu.recipe.fail.Input": "Insufficient Input Materials", + "gtceu.recipe.fail.Input.Content": "Missing Input: %s", + "gtceu.recipe.fail.Output": "Insufficient output space", + "gtceu.recipe.fail.Output.Content": "Unable to Output: %s", + "gtceu.recipe.fail.block": "Block obstruction above", + "gtceu.recipe.fail.cleanroom": "%s conditions not met", + "gtceu.recipe.fail.find": "Recipe not found", + "gtceu.recipe.fail.gravity.low": "Zero gravity conditions not met", + "gtceu.recipe.fail.gravity.power": "Strong gravity conditions not met", + "gtceu.recipe.fail.nano_forge.tier": "Nano forge tier insufficient", + "gtceu.recipe.fail.no.enough.cache.energy": "Insufficient startup energy", + "gtceu.recipe.fail.no.enough.cwu.in": "Insufficient computational power input", + "gtceu.recipe.fail.no.enough.distilledwater": "Insufficient distilled water", + "gtceu.recipe.fail.no.enough.eV.consumed": "Required neutron kinetic energy too high", + "gtceu.recipe.fail.no.enough.eu.in": "Insufficient power input", + "gtceu.recipe.fail.no.enough.eu.out": "Insufficient power output space", + "gtceu.recipe.fail.no.enough.neutron": "Neutron kinetic energy too low", + "gtceu.recipe.fail.no.enough.recipe.tier": "Machine tier insufficient", + "gtceu.recipe.fail.no.enough.skylight": "Insufficient skylight", + "gtceu.recipe.fail.no.enough.temperature": "Insufficient temperature", + "gtceu.recipe.fail.no.find.researched": "Research data for this recipe not found", + "gtceu.recipe.fail.no.input.fluid": "Missing input fluid (%s)", + "gtceu.recipe.fail.no.input.item": "Missing input item (%s)", + "gtceu.recipe.fail.no.ratio": "Recipe input ratio incorrect", + "gtceu.recipe.fail.no.skylight": "Missing skylight", + "gtceu.recipe.fail.no.venting": "Vent blocked", + "gtceu.recipe.fail.processing.plant.no.input": "No machine inserted", + "gtceu.recipe.fail.processing.plant.wrong.input": "Inserted machine voltage or recipe type mismatch", + "gtceu.recipe.fail.reason": "Recipe failure reason: %s", + "gtceu.recipe.fail.rotor.different": "Rotor shaft input abnormal", + "gtceu.recipe.fail.rotor.isEmpty": "Missing turbine rotor", + "gtceu.recipe.fail.rotor_holder.tier": "The Rotor Holder tier is too low", + "gtceu.recipe.fail.too.much.neutron": "Neutron kinetic energy too high", + "gtceu.recipe.fail.voltage.tier": "Voltage tier does not meet recipe requirements", + "gtceu.recipe.frheat": "Temperature increase per second: %s K", + "gtceu.recipe.grindball": "Grinding ball material: %s", + "gtceu.recipe.nano_forge_tier": "Nano forge tier: %s", + "gtceu.recipe.sepm_tier": "Power module required: MK%s", + "gtceu.recipe.stellar_containment_tier": "Stellar containment tier: %s", + "gtceu.rocket_engine": "Rocket Engine", + "gtceu.semi_fluid_generator": "Semi-Fluid Generator", + "gtceu.slaughterhouse": "Slaughterhouse", + "gtceu.small_gas_collector": "Small Gas Collector", + "gtceu.space_cosmic_probe_receivers": "Space-Based Cosmic Probe", + "gtceu.space_elevator": "Space Elevator", + "gtceu.space_probe_surface_reception": "Cosmic Probe", + "gtceu.sps_crafting": "Supercritical Crafting", + "gtceu.stellar_forge": "Stellar Thermal Smelting", + "gtceu.super_computation": "Super Computer", + "gtceu.super_particle_collider": "Particle Collision", + "gtceu.supercritical_steam_turbine": "Supercritical Steam Turbine", + "gtceu.suprachronal_assembly_line": "Suprachronal Assembly Line", + "gtceu.tier.advanced": "Advanced", + "gtceu.tier.base": "Base", + "gtceu.tier.ultimate": "Ultimate", + "gtceu.top.buffer_not_bound": "Buffer not bound", + "gtceu.top.electricity": "%sA %s", + "gtceu.top.extra_output": "Remaining %s duplicate outputs hidden", + "gtceu.top.pending_refunds": "Pending refund list", + "gtceu.top.recipe_input": "Recipe Input:", + "gtceu.ultimate_material_forge": "Ultimate Material Forging", + "gtceu.universal.tooltip.ampere_out": "§bOutput Current:§r %sA", + "gtceu.vacuum_drying": "Vacuum Drying", + "gtceu.void_fluid_drilling_rig": "Void Fluid Drilling Rig", + "gtceu.void_miner": "Void Mining", + "gtceu.weather_control": "Weather Control", + "gtceu.wood_distillation": "Wood Chemical Plant", + "gtceu.world_data_scanner": "World Data Scanner", + "gui.gtceu.neutron_sensor.invert.disabled.0": "Redstone Output: Normal", + "gui.gtceu.neutron_sensor.invert.disabled.1": "Click to invert redstone logic", + "gui.gtceu.neutron_sensor.invert.disabled.2": "Sensor emits redstone signal when neutron kinetic energy is between set minimum and maximum values, stops emitting when below minimum", + "gui.gtceu.neutron_sensor.invert.enabled.0": "Redstone Output: Inverted", + "gui.gtceu.neutron_sensor.invert.enabled.1": "Click to switch to normal redstone logic", + "gui.gtceu.neutron_sensor.invert.enabled.2": "Sensor emits redstone signal when neutron kinetic energy is outside set minimum and maximum values, emits when below minimum", + "gui.gtlcore.recipe_lock.no_recipe": "No recipe locked", + "gui.gtlcore.recipe_lock.recipe": "Recipe locked", + "item.gtceu.tool.vajra": "%s Vajra", + "material.gtceu.1_octene": "1-Octene", + "material.gtceu.absolute_ethanol": "Absolute Ethanol", + "material.gtceu.abyssalalloy": "Abyssal Alloy", + "material.gtceu.acetaldehyde": "Acetaldehyde", + "material.gtceu.acetonitrile": "Acetonitrile", + "material.gtceu.acetyl_chloride": "Acetyl Chloride", + "material.gtceu.acetylating_reagent": "Acetylating Reagent", + "material.gtceu.acetylene": "Acetylene", + "material.gtceu.acid_leached_fluoro_carbon_lanthanide_cerium_oxide_powder": "Acid Leached Fluoro-Carbon Lanthanide Cerium Oxide Powder", + "material.gtceu.acidic_iridium": "Acidic Iridium", + "material.gtceu.acidic_monazite_powder": "Acidic Monazite Powder", + "material.gtceu.acidic_naquadria_caesiumfluoride": "Acidic Naquadria Cesium Fluoride", + "material.gtceu.acrylic_acid": "Acrylic Acid", + "material.gtceu.acrylonitrile": "Acrylonitrile", + "material.gtceu.actinium_nitrate": "Actinium Nitrate", + "material.gtceu.actinium_oxalate": "Actinium Oxalate", + "material.gtceu.actinium_radium_hydroxide_solution": "Actinium Radium Hydroxide Solution", + "material.gtceu.actinium_radium_nitrate_solution": "Actinium Radium Nitrate Solution", + "material.gtceu.actinium_superhydride": "Actinium Superhydride", + "material.gtceu.actinium_trinium_hydroxides": "Actinium Trinium Hydroxide", + "material.gtceu.actinoids": "Actinoid Mixture", + "material.gtceu.actinoids_1": "Light Actinoid Mixture", + "material.gtceu.actinoids_2": "Heavy Actinoid Mixture", + "material.gtceu.adamantine": "Adamantine", + "material.gtceu.adamantine_compounds": "Adamantine Compound", + "material.gtceu.adamantium": "Adamantium", + "material.gtceu.alien_algae": "Alien Algae Residue", + "material.gtceu.alkaline": "Alkali Metal Mixture", + "material.gtceu.alkaline_earth": "Alkaline Earth Metal Mixture", + "material.gtceu.almandine_front": "Almandine Ore Foam", + "material.gtceu.alumina": "Alumina", + "material.gtceu.aluminium_bronze": "Aluminium Bronze", + "material.gtceu.aluminium_hydride": "Aluminium Hydride", + "material.gtceu.aluminium_trifluoride": "Aluminium Trifluoride", + "material.gtceu.aminated_fullerene": "Aminated Fullerene", + "material.gtceu.ammonium_bifluoride": "Ammonium Bifluoride", + "material.gtceu.ammonium_bifluoride_solution": "Ammonium Bifluoride Solution", + "material.gtceu.ammonium_fluoride": "Ammonium Fluoride", + "material.gtceu.ammonium_nitrate_solution": "Ammonium Nitrate Solution", + "material.gtceu.ammonium_perrhenate": "Ammonium Perrhenate", + "material.gtceu.aniline": "Aniline", + "material.gtceu.anthracene": "Anthracene", + "material.gtceu.antihydrogen": "Antihydrogen", + "material.gtceu.antimatter": "Discrete Antimatter", + "material.gtceu.antimony_pentafluoride": "Antimony Pentafluoride", + "material.gtceu.antimony_trichloride": "Antimony Trichloride", + "material.gtceu.antineutron": "Antineutron", + "material.gtceu.antiproton": "Antiproton", + "material.gtceu.arceusalloy2b": "Arceus Alloy 2B", + "material.gtceu.artherium_sn": "Artherium Tin", + "material.gtceu.ash_leaching_solution": "Ash Leaching Solution", + "material.gtceu.astatide_solution": "Astatide Solution", + "material.gtceu.astral_silver": "Astral Silver", + "material.gtceu.astraltitanium": "Astral Titanium", + "material.gtceu.atinium_hydride": "Actinium Hydride", + "material.gtceu.attuned_tengam": "Attuned Tengam", + "material.gtceu.azafullerene": "Azafullerene", + "material.gtceu.barium_chloride": "Barium Chloride", + "material.gtceu.barnarda_air": "Barnard C Air", + "material.gtceu.bedrock": "Bedrock", + "material.gtceu.bedrock_gas": "Bedrock Gas", + "material.gtceu.bedrock_smoke": "Bedrock Smoke", + "material.gtceu.bedrock_soot_solution": "Bedrock Soot Solution", + "material.gtceu.benzaldehyde": "Benzaldehyde", + "material.gtceu.benzenediazonium_tetrafluoroborate": "Benzenediazonium Tetrafluoroborate", + "material.gtceu.benzophenanthrenylacetonitrile": "Benzophenanthrenylacetonitrile", + "material.gtceu.benzyl_chloride": "Benzyl Chloride", + "material.gtceu.benzylamine": "Benzylamine", + "material.gtceu.biohmediumsterilized": "Sterilized Biological Culture Medium", + "material.gtceu.biomediumraw": "Raw Biological Culture Medium", + "material.gtceu.bisethylenedithiotetraselenafulvalene": "Bis(ethylenedithio)tetraselenafulvalene", + "material.gtceu.bisethylenedithiotetraselenafulvalene_perrhenate": "Bis(ethylenedithio)tetraselenafulvalene Perrhenate", + "material.gtceu.bismuth_germanate": "Bismuth Germanate", + "material.gtceu.bismuth_nitrate_solution": "Bismuth Nitrate Solution", + "material.gtceu.bismuth_tellurite": "Bismuth Tellurite", + "material.gtceu.black_dwarf_mtter": "Black Dwarf Matter", + "material.gtceu.black_titanium": "Black Titanium", + "material.gtceu.bloodstone": "Bloodstone", + "material.gtceu.borane_dimethyl_sulfide": "Borane Dimethyl Sulfide", + "material.gtceu.boric_acide": "Boric Acid", + "material.gtceu.borocarbide": "Boron Carbide Composite", + "material.gtceu.boron_carbide": "Boron Carbide", + "material.gtceu.boron_fluoride": "Boron Fluoride", + "material.gtceu.boron_francium_carbide": "Boron Francium Carbide", + "material.gtceu.boron_trifluoride_acetate": "Boron Trifluoride Acetate", + "material.gtceu.boron_trioxide": "Boron Trioxide", + "material.gtceu.brominated_brine": "Brominated Brine", + "material.gtceu.bromine_trifluoride": "Bromine Trifluoride", + "material.gtceu.bromo_succinimide": "N-Bromosuccinimide", + "material.gtceu.bromobutane": "Bromobutane", + "material.gtceu.bromodihydrothiine": "Bromodihydrothiine", + "material.gtceu.butane_1_4_diol": "1,4-Butanediol", + "material.gtceu.butyl_lithium": "Butyl Lithium", + "material.gtceu.cadmium_sulfide": "Cadmium Sulfide", + "material.gtceu.cadmium_tungstate": "Cadmium Tungstate", + "material.gtceu.caesium_fluoride": "Cesium Fluoride", + "material.gtceu.caesium_hydroxide": "Cesium Hydroxide", + "material.gtceu.caesium_iodide": "Cesium Iodide", + "material.gtceu.caesium_nitrate": "Cesium Nitrate", + "material.gtceu.caesium_xenontrioxide_fluoride": "Cesium Xenon Trioxide Fluoride", + "material.gtceu.calcined_rare_earth_oxide_powder": "Calcined Rare Earth Oxide Powder", + "material.gtceu.calcium_carbide": "Calcium Carbide", + "material.gtceu.calcium_fluoride": "Calcium Fluoride", + "material.gtceu.californium_cyclopentadienide": "Californium Cyclopentadienide", + "material.gtceu.californium_trichloride": "Californium Trichloride", + "material.gtceu.calorite": "Calorite", + "material.gtceu.carbon_disulfide": "Carbon Disulfide", + "material.gtceu.carbon_nanotubes": "Carbon Nanotubes", + "material.gtceu.carbon_tetrachloride": "Carbon Tetrachloride", + "material.gtceu.celestialtungsten": "Celestial Tungsten", + "material.gtceu.celestine": "Celestine", + "material.gtceu.cerium_chloride_powder": "Cerium Chloride Powder", + "material.gtceu.cerium_oxalate_powder": "Cerium Oxalate Powder", + "material.gtceu.cerium_oxide": "Cerium Oxide", + "material.gtceu.cerium_oxide_rare_earth_oxide_powder": "Cerium Oxide Rare Earth Oxide Powder", + "material.gtceu.cerium_rich_mixture_powder": "Cerium Rich Mixture Powder", + "material.gtceu.cesium_carborane": "Cesium Carborane", + "material.gtceu.cesium_carborane_precursor": "Cesium Carborane Precursor", + "material.gtceu.chalcopyrite_front": "Chalcopyrite Ore Foam", + "material.gtceu.chaos": "Chaos", + "material.gtceu.charged_caesium_cerium_cobalt_indium": "Charged Cesium-Cerium-Cobalt-Indium", + "material.gtceu.cinobite": "Cinobite", + "material.gtceu.circuit_compound": "Circuit Compound", + "material.gtceu.clean_bedrock_solution": "Clean Bedrock Solution", + "material.gtceu.clean_inert_residues": "Clean Inert Residue", + "material.gtceu.clean_raw_tengam": "Clean Raw Tengam", + "material.gtceu.co_ac_ab_catalyst": "Co/AC-AB Catalyst", + "material.gtceu.compressed_stone": "Compressed Stone", + "material.gtceu.concentrated_cerium_chloride_solution": "Concentrated Cerium Chloride Solution", + "material.gtceu.concentrated_monazite_rare_earth_hydroxide_powder": "Concentrated Monazite Rare Earth Hydroxide Powder", + "material.gtceu.concentrated_nitric_leachate_from_monazite": "Concentrated Nitric Leachate from Monazite", + "material.gtceu.concentrated_nitride_monazite_rare_earth_solution": "Concentrated Nitride Monazite Rare Earth Solution", + "material.gtceu.concentrated_rare_earth_chloride_solution": "Concentrated Rare Earth Chloride Solution", + "material.gtceu.concentration_mixing_hyper_fuel_1": "Concentrated Mixing Hyper Fuel I", + "material.gtceu.concentration_mixing_hyper_fuel_2": "Concentrated Mixing Hyper Fuel II", + "material.gtceu.conductive_alloy": "Conductive Iron", + "material.gtceu.cooling_concentrated_nitric_monazite_rare_earth_powder": "Cooling Concentrated Nitric Monazite Rare Earth Powder", + "material.gtceu.copernicium": "Copernicium", + "material.gtceu.copper76": "Copper-76", + "material.gtceu.cosmic": "Cosmic", + "material.gtceu.cosmic_computing_mixture": "Cosmic Computing Mixture", + "material.gtceu.cosmic_element": "Cosmic Element", + "material.gtceu.cosmic_mesh": "Cosmic Mesh", + "material.gtceu.cosmic_superconductor": "Cosmic Superconductor", + "material.gtceu.cosmicneutronium": "Cosmic Neutronium", + "material.gtceu.crackedradox": "Cracked Radox", + "material.gtceu.crude_hexanitrohexaaxaisowurtzitane": "Crude Hexanitrohexaazaisowurtzitane", + "material.gtceu.crystalline_nitric_acid": "Crystalline Nitric Acid", + "material.gtceu.crystalmatrix": "Crystal Matrix", + "material.gtceu.cubic_zirconia": "Cubic Zirconia", + "material.gtceu.cyclooctadiene": "Cyclooctadiene", + "material.gtceu.cycloparaphenylene": "Cycloparaphenylene", + "material.gtceu.cyclopentadiene": "Cyclopentadiene", + "material.gtceu.cyclopentadienyl_titanium_trichloride": "Cyclopentadienyl Titanium Trichloride", + "material.gtceu.dalisenite": "Dalisenite", + "material.gtceu.decaborane": "Decaborane", + "material.gtceu.degenerate_rhenium": "Degenerate Rhenium", + "material.gtceu.deglycerated_soap": "Deglycerated Soap", + "material.gtceu.dense_hydrazine_fuel_mixture": "Dense Hydrazine Fuel Mixture", + "material.gtceu.dense_hydrazine_mixed_fuel": "Dense Hydrazine Mixed Fuel", + "material.gtceu.dense_neutron": "Dense Neutronium", + "material.gtceu.desh": "Desh", + "material.gtceu.diamagnetic_residues": "Diamagnetic Residue", + "material.gtceu.diaminodiphenylmethanmixture": "Diaminodiphenylmethane Mixture", + "material.gtceu.dibenzyltetraacetylhexaazaisowurtzitane": "Dibenzyltetraacetylhexaazaisowurtzitane", + "material.gtceu.dibismuthhydroborat": "Dibismuth Hydroborate", + "material.gtceu.diborane": "Diborane", + "material.gtceu.dibromoacrolein": "Dibromoacrolein", + "material.gtceu.dibromomethylbenzene": "Dibromomethylbenzene", + "material.gtceu.dichlorocyclooctadieneplatinium": "Dichlorocyclooctadiene Platinum", + "material.gtceu.dichlorodicyanobenzoquinone": "Dichlorodicyanobenzoquinone", + "material.gtceu.dichlorodicyanohydroquinone": "Dichlorodicyanohydroquinone", + "material.gtceu.dichloromethane": "Dichloromethane", + "material.gtceu.diethyl_ether": "Diethyl Ether", + "material.gtceu.diethylthiourea": "Diethylthiourea", + "material.gtceu.difluoroaniline": "Difluoroaniline", + "material.gtceu.difluorobenzophenone": "Difluorobenzophenone", + "material.gtceu.dihydroiodotetracene": "Dihydroiodotetracene", + "material.gtceu.diiodobiphenyl": "Diiodobiphenyl", + "material.gtceu.dilute_hexafluorosilicic_acid": "Dilute Hexafluorosilicic Acid", + "material.gtceu.dilute_hydrofluoric_acid": "Dilute Hydrofluoric Acid", + "material.gtceu.diluted_fluoro_carbon_lanthanide_slurry": "Diluted Fluoro-Carbon Lanthanide Slurry", + "material.gtceu.diluted_monazite_slurry": "Diluted Monazite Slurry", + "material.gtceu.diluted_monazite_sulfate_solution": "Diluted Monazite Sulfate Solution", + "material.gtceu.diluted_rare_earth_chloride_solution": "Diluted Rare Earth Chloride Solution", + "material.gtceu.dilutedxenoxene": "Diluted Xenoxene", + "material.gtceu.dimensionallytranscendentcrudecatalyst": "Dimensionally Transcendent Crude Catalyst", + "material.gtceu.dimensionallytranscendentexoticcatalyst": "Dimensionally Transcendent Exotic Catalyst", + "material.gtceu.dimensionallytranscendentprosaiccatalyst": "Dimensionally Transcendent Prosaic Catalyst", + "material.gtceu.dimensionallytranscendentresidue": "Dimensionally Transcendent Residue", + "material.gtceu.dimensionallytranscendentresplendentcatalyst": "Dimensionally Transcendent Resplendent Catalyst", + "material.gtceu.dimensionallytranscendentstellarcatalyst": "Dimensionally Transcendent Stellar Catalyst", + "material.gtceu.dimethoxyethane": "Dimethoxyethane", + "material.gtceu.dimethyl_sulfide": "Dimethyl Sulfide", + "material.gtceu.dimethylether": "Dimethyl Ether", + "material.gtceu.dimethylnaphthalene": "Dimethylnaphthalene", + "material.gtceu.dimethylterephthalate": "Dimethyl Terephthalate", + "material.gtceu.dinitrodipropanyloxybenzene": "Dinitrodipropanyloxybenzene", + "material.gtceu.dioxygen_difluoride": "Dioxygen Difluoride", + "material.gtceu.diphenylmethane_diisocyanate": "4,4'-Diphenylmethane Diisocyanate", + "material.gtceu.diphenylmethanediisocyanatemixture": "Diphenylmethane Diisocyanate Mixture", + "material.gtceu.dirty_hexafluorosilicic_acid": "Dirty Hexafluorosilicic Acid", + "material.gtceu.ditertbutyl_dicarbonate": "Di-tert-butyl Dicarbonate", + "material.gtceu.dmap": "Dimethylaminopyridine", + "material.gtceu.draconium": "Draconium", + "material.gtceu.draconiumawakened": "Awakened Draconium", + "material.gtceu.dragon_blood": "Dragon Blood", + "material.gtceu.dragon_breath": "Dragon Breath", + "material.gtceu.dragon_element": "Dragon Element", + "material.gtceu.dried_concentrated_nitric_monazite_rare_earth_powder": "Dried Concentrated Nitric Monazite Rare Earth Powder", + "material.gtceu.dry_graphene_gel": "Dry Graphene Gel", + "material.gtceu.durene": "Durene", + "material.gtceu.dusty_liquid_helium_iii": "Dusty Liquid Helium-3", + "material.gtceu.dysprosium_oxide": "Dysprosium Oxide", + "material.gtceu.earth_crystal": "Earth Crystal", + "material.gtceu.echoite": "Echoite", + "material.gtceu.eglin_steel": "Eglin Steel", + "material.gtceu.enderite": "Enderite", + "material.gtceu.enderium": "Enderium", + "material.gtceu.energetic_alloy": "Energetic Alloy", + "material.gtceu.enriched_dragon_breath": "Enriched Dragon Breath", + "material.gtceu.enriched_naquadah_front": "Enriched Naquadah Ore Foam", + "material.gtceu.enriched_naquadah_fuel": "Enriched Naquadah Fuel", + "material.gtceu.enriched_potassium_iodide_slurry": "Enriched Potassium Iodide Slurry", + "material.gtceu.enriched_rare_earth_chloride_solution": "Enriched Rare Earth Chloride Solution", + "material.gtceu.enriched_xenoxene": "Enriched Xenoxene", + "material.gtceu.er_lu_oxides_solution": "Erbium-Lutetium Oxide Solution", + "material.gtceu.erbium_oxide": "Erbium Oxide", + "material.gtceu.eternity": "Eternity", + "material.gtceu.ethanolamine": "Ethanolamine", + "material.gtceu.ethyl_acrylate": "Ethyl Acrylate", + "material.gtceu.ethyl_trifluoroacetate": "Ethyl Trifluoroacetate", + "material.gtceu.ethylamine": "Ethylamine", + "material.gtceu.ethylanthrahydroquinone": "2-Ethylanthrahydroquinone", + "material.gtceu.ethylanthraquinone": "2-Ethylanthraquinone", + "material.gtceu.ethylene_oxide": "Ethylene Oxide", + "material.gtceu.ethylene_sulfide": "Ethylene Sulfide", + "material.gtceu.ethylenediamine": "Ethylenediamine", + "material.gtceu.ethyleneglycol": "Ethylene Glycol", + "material.gtceu.europium_oxide": "Europium Oxide", + "material.gtceu.euv_photoresist": "EUV Photoresist", + "material.gtceu.exciteddtec": "Excited Dimensional Transcendent Exotic Catalyst", + "material.gtceu.exciteddtsc": "Excited Dimensional Transcendent Stellar Catalyst", + "material.gtceu.exotic_heavy_residues": "Exotic Heavy Residue", + "material.gtceu.explosivehydrazine": "Explosive Hydrazine Fuel Mixture", + "material.gtceu.fall_king": "Fall King Alloy", + "material.gtceu.ferric_ree_chloride": "Ferric REE Chloride", + "material.gtceu.ferrocene": "Ferrocene", + "material.gtceu.ferromagnetic_residues": "Ferromagnetic Residue", + "material.gtceu.filtered_fluoro_carbon_lanthanide_slurry": "Filtered Fluoro-Carbon Lanthanide Slurry", + "material.gtceu.fissioned_uranium_235": "Fissioned Uranium-235", + "material.gtceu.fluorinated_samarium_concentrate_powder": "Fluorinated Samarium Concentrate Powder", + "material.gtceu.fluorine_cracked_aquadah": "Fluorine Cracked Naquadah", + "material.gtceu.fluorite": "Fluorite", + "material.gtceu.fluoro_benzene": "Fluorobenzene", + "material.gtceu.fluoro_carbon_lanthanide_cerium_oxide_powder": "Fluoro-Carbon Lanthanide Cerium Oxide Powder", + "material.gtceu.fluoro_carbon_lanthanide_cerium_rare_earth_oxide_powder": "Fluoro-Carbon Lanthanide Cerium Rare Earth Oxide Powder", + "material.gtceu.fluoro_carbon_lanthanide_cerium_rare_earth_suspension": "Fluoro-Carbon Lanthanide Cerium Rare Earth Oxide Suspension", + "material.gtceu.fluoro_carbon_lanthanide_cerium_solution": "Fluoro-Carbon Lanthanide Cerium Rare Earth Turbid Solution", + "material.gtceu.fluoroboric_acide": "Fluoroboric Acid", + "material.gtceu.fluorocarborane": "Fluorocarborane", + "material.gtceu.fluorosilicic_acid": "Fluorosilicic Acid", + "material.gtceu.fluorotoluene": "Fluorotoluene", + "material.gtceu.fluxed_electrum": "Fluxed Electrum", + "material.gtceu.flyb": "Flyb-Ytterbium", + "material.gtceu.force": "Force", + "material.gtceu.francium_caesium_cadmium_bromide": "Francium Cesium Cadmium Bromide", + "material.gtceu.francium_carbide": "Francium Carbide", + "material.gtceu.free_alpha_gas": "Dense Free Alpha Gas", + "material.gtceu.free_electron_gas": "Dense Free Electron Gas", + "material.gtceu.free_proton_gas": "Dense Free Proton Gas", + "material.gtceu.fullerene": "Fullerene", + "material.gtceu.fullerene_doped_nanotubes": "Fullerene Doped Nanotubes", + "material.gtceu.fullerene_polymer_matrix_pulp": "Fullerene Polymer Matrix Pulp", + "material.gtceu.fuming_nitric_acid": "Fuming Nitric Acid", + "material.gtceu.gadolinium_oxide": "Gadolinium Oxide", + "material.gtceu.gamma_rays_photoresist": "Gamma Ray Photoresist", + "material.gtceu.gammabutyrolactone": "Gamma-Butyrolactone", + "material.gtceu.germanium_ash": "Germanium Ash", + "material.gtceu.germanium_containing_precipitate": "Germanium Containing Precipitate", + "material.gtceu.germanium_dioxide": "Germanium Dioxide", + "material.gtceu.germanium_tetrachloride_solution": "Germanium Tetrachloride Solution", + "material.gtceu.germaniumtungstennitride": "Germanium Tungsten Nitride", + "material.gtceu.glacio_spirit": "Glacio Spirit", + "material.gtceu.glucose": "Glucose", + "material.gtceu.glucose_iron_solution": "Glucose Iron Solution", + "material.gtceu.gluons": "Gluons", + "material.gtceu.glyoxal": "Glyoxal", + "material.gtceu.gold_chloride": "Gold Chloride", + "material.gtceu.gold_cyanide": "Gold Cyanide", + "material.gtceu.gold_depleted_molybdenite": "Gold Depleted Molybdenite", + "material.gtceu.gold_trifluoride": "Gold Trifluoride", + "material.gtceu.grade_10_purified_water": "Grade 10 Purified Water", + "material.gtceu.grade_11_purified_water": "Grade 11 Purified Water", + "material.gtceu.grade_12_purified_water": "Grade 12 Purified Water", + "material.gtceu.grade_13_purified_water": "Grade 13 Purified Water", + "material.gtceu.grade_14_purified_water": "Grade 14 Purified Water", + "material.gtceu.grade_15_purified_water": "Grade 15 Purified Water", + "material.gtceu.grade_16_purified_water": "Grade 16 Purified Water", + "material.gtceu.grade_1_purified_water": "Grade 1 Purified Water", + "material.gtceu.grade_2_purified_water": "Grade 2 Purified Water", + "material.gtceu.grade_3_purified_water": "Grade 3 Purified Water", + "material.gtceu.grade_4_purified_water": "Grade 4 Purified Water", + "material.gtceu.grade_5_purified_water": "Grade 5 Purified Water", + "material.gtceu.grade_6_purified_water": "Grade 6 Purified Water", + "material.gtceu.grade_7_purified_water": "Grade 7 Purified Water", + "material.gtceu.grade_8_purified_water": "Grade 8 Purified Water", + "material.gtceu.grade_9_purified_water": "Grade 9 Purified Water", + "material.gtceu.graphene_gel_suspension": "Graphene Gel Suspension", + "material.gtceu.graphene_oxide": "Graphene Oxide", + "material.gtceu.grisium": "Grisium", + "material.gtceu.grossular_front": "Grossular Ore Foam", + "material.gtceu.hafnium_chloride": "Hafnium Chloride", + "material.gtceu.hafnium_oxide": "Hafnium Oxide", + "material.gtceu.hassium_chloride": "Hassium Chloride", + "material.gtceu.hastelloy_n": "Hastelloy-N", + "material.gtceu.hastelloy_n_75": "Hastelloy-N 75", + "material.gtceu.hastelloyk_243": "Hastelloy-K243", + "material.gtceu.hastelloyx_78": "Hastelloy-X 78", + "material.gtceu.heavily_fluorinated_trinium_solution": "Heavily Fluorinated Trinium Solution", + "material.gtceu.heavy_diamagnetic_residues": "Heavy Diamagnetic Residue", + "material.gtceu.heavy_ferromagnetic_residues": "Heavy Ferromagnetic Residue", + "material.gtceu.heavy_lepton_mixture": "Heavy Lepton Mixture", + "material.gtceu.heavy_metallic_residues": "Heavy Metallic Residue", + "material.gtceu.heavy_oxidized_residues": "Heavy Oxidized Residue", + "material.gtceu.heavy_paramagnetic_residues": "Heavy Paramagnetic Residue", + "material.gtceu.heavy_quark_degenerate_matter": "Heavy Quark Degenerate Matter", + "material.gtceu.heavy_quark_enriched_mixture": "Heavy Quark Enriched Mixture", + "material.gtceu.heavy_quarks": "Heavy Quarks", + "material.gtceu.heavyradox": "Heavy Radox", + "material.gtceu.helium_iii_hydride": "Helium-3 Hydride", + "material.gtceu.heterogeneous_halide_monazite_rare_earth_mixture_powder": "Heterogeneous Halide Monazite Rare Earth Mixture Powder", + "material.gtceu.hexabenzylhexaazaisowurtzitane": "Hexabenzylhexaazaisowurtzitane", + "material.gtceu.hexafluoride_enriched_naquadah_solution": "Hexafluoride Enriched Naquadah Solution", + "material.gtceu.hexafluoride_naquadria_solution": "Hexafluoride Naquadria Solution", + "material.gtceu.hexafluorophosphoric_acid": "Hexafluorophosphoric Acid", + "material.gtceu.hexamethylenetetramine": "Hexamethylenetetramine", + "material.gtceu.hexanitrohexaaxaisowurtzitane": "Hexanitrohexaazaisowurtzitane", + "material.gtceu.high_energy_quark_gluon": "High Energy Quark-Gluon", + "material.gtceu.high_purity_calcium_carbonate": "High Purity Calcium Carbonate", + "material.gtceu.highenergymixture": "High Energy Mixture", + "material.gtceu.highurabilityompoundteel": "High Durability Compound Steel", + "material.gtceu.hikarium": "Hikarium", + "material.gtceu.hmxexplosive": "HMX Explosive Compound", + "material.gtceu.holmium_oxide": "Holmium Oxide", + "material.gtceu.hot_oganesson": "Hot Oganesson", + "material.gtceu.hot_sodium_potassium": "Hot Sodium Potassium Alloy", + "material.gtceu.hydrazine": "Hydrazine", + "material.gtceu.hydrobromic_acid": "Hydrobromic Acid", + "material.gtceu.hydrogen_peroxide": "Hydrogen Peroxide", + "material.gtceu.hydroiodic_acid": "Hydroiodic Acid", + "material.gtceu.hydroquinone": "Hydroquinone", + "material.gtceu.hydroxylamine_hydrochloride": "Hydroxylamine Hydrochloride", + "material.gtceu.hydroxylammonium_sulfate": "Hydroxylammonium Sulfate", + "material.gtceu.hyper_fuel_1": "Hyper Fuel I", + "material.gtceu.hyper_fuel_2": "Hyper Fuel II", + "material.gtceu.hyper_fuel_3": "Hyper Fuel III", + "material.gtceu.hyper_fuel_4": "Hyper Fuel IV", + "material.gtceu.hypogen": "Hypogen", + "material.gtceu.ignis_crystal": "Ignis Crystal", + "material.gtceu.inconel_625": "Inconel 625", + "material.gtceu.inconel_792": "Inconel 792", + "material.gtceu.indalloy_140": "Indalloy 140", + "material.gtceu.inert_residues": "Inert Residue", + "material.gtceu.infinity": "Infinity", + "material.gtceu.infuscolium": "Infuscolium", + "material.gtceu.infused_gold": "Infused Gold", + "material.gtceu.iodine_containing_slurry": "Iodine Containing Slurry", + "material.gtceu.iodine_monochloride": "Iodine Monochloride", + "material.gtceu.iridium_dioxide": "Iridium Dioxide", + "material.gtceu.iridium_trichloride_solution": "Iridium Trichloride Solution", + "material.gtceu.isochloropropane": "Isochloropropane", + "material.gtceu.isophthaloylbis": "Isophthaloylbis Diethylthiourea", + "material.gtceu.isopropyl_alcohol": "Isopropyl Alcohol", + "material.gtceu.jasper": "Jasper", + "material.gtceu.kelp_slurry": "Kelp Slurry", + "material.gtceu.kerosene": "Kerosene", + "material.gtceu.kevlar": "Kevlar", + "material.gtceu.krypton_difluoride": "Krypton Difluoride", + "material.gtceu.la_nd_oxides_solution": "Lanthanum-Neodymium Oxide Solution", + "material.gtceu.lafium": "Lafium", + "material.gtceu.lanthanoids_1": "Light Lanthanoid Mixture", + "material.gtceu.lanthanoids_2": "Heavy Lanthanoid Mixture", + "material.gtceu.lanthanum_chloride": "Lanthanum Chloride", + "material.gtceu.lanthanum_chloride_with_impurities": "Lanthanum Chloride with Impurities", + "material.gtceu.lanthanum_embedded_fullerene": "Lanthanum Embedded Fullerene", + "material.gtceu.lanthanum_fullerene_mix": "Lanthanum Fullerene Mix", + "material.gtceu.lanthanum_oxide": "Lanthanum Oxide", + "material.gtceu.leach_residue": "Platinum Leach Residue", + "material.gtceu.leached_turpentine": "Leached Turpentine", + "material.gtceu.legendarium": "Legendarium", + "material.gtceu.light_quarks": "Light Quarks", + "material.gtceu.lightradox": "Light Radox", + "material.gtceu.liquid_hydrogen": "Liquid Hydrogen", + "material.gtceu.liquid_starlight": "Liquid Starlight", + "material.gtceu.liquidcrystalkevlar": "Liquid Crystal Kevlar", + "material.gtceu.lithium_aluminium_fluoride": "Lithium Aluminium Fluoride", + "material.gtceu.lithium_aluminium_hydride": "Lithium Aluminium Hydride", + "material.gtceu.lithium_cyclopentadienide": "Lithium Cyclopentadienide", + "material.gtceu.lithium_fluoride": "Lithium Fluoride", + "material.gtceu.lithium_iodide": "Lithium Iodide", + "material.gtceu.lithium_niobate_nanoparticles": "Lithium Niobate Nanoparticles", + "material.gtceu.lithiumthiinediselenide": "Lithium Thiine Diselenide", + "material.gtceu.lumiium": "Lumiium", + "material.gtceu.luminessence": "Luminessence", + "material.gtceu.lutetium_oxide": "Lutetium Oxide", + "material.gtceu.magmatter": "Magmatter", + "material.gtceu.magnesium_chloride_bromide": "Magnesium Chloride Bromide", + "material.gtceu.magneto_resonatic": "Magneto Resonatic", + "material.gtceu.magnetohydrodynamicallyconstrainedstarmatter": "Magnetohydrodynamically Constrained Star Matter", + "material.gtceu.maleic_anhydride": "Maleic Anhydride", + "material.gtceu.mana": "Mana", + "material.gtceu.mar_m_200_steel": "Mar-M 200 Steel", + "material.gtceu.metal_residue": "Metal Residue", + "material.gtceu.metallic_residues": "Metallic Residue", + "material.gtceu.metalloid": "Metalloid Mixture", + "material.gtceu.metastable_hassium": "Metastable Hassium", + "material.gtceu.metastable_oganesson": "Metastable Oganesson", + "material.gtceu.methylamine": "Methylamine", + "material.gtceu.methylbenzophenanthrene": "Methylbenzophenanthrene", + "material.gtceu.mithril": "Mithril", + "material.gtceu.mixed_astatide_salts": "Mixed Astatide Salts", + "material.gtceu.modulated_fluoro_carbon_lanthanide_slurry": "Modulated Fluoro-Carbon Lanthanide Slurry", + "material.gtceu.molten_calcium_salts": "Molten Calcium Salts", + "material.gtceu.molybdenum_concentrate": "Molybdenum Concentrate", + "material.gtceu.molybdenum_flue": "Molybdenum Flue Gas", + "material.gtceu.molybdenum_trioxide": "Molybdenum Trioxide", + "material.gtceu.monazite_front": "Monazite Ore Foam", + "material.gtceu.monazite_rare_earth_filter_residue_powder": "Monazite Rare Earth Filter Residue Powder", + "material.gtceu.monazite_rare_earth_precipitate_powder": "Monazite Rare Earth Precipitate Powder", + "material.gtceu.monazite_rare_earth_turbid_liquid": "Monazite Rare Earth Turbid Liquid", + "material.gtceu.monazite_sulfate_powder": "Monazite Sulfate Powder", + "material.gtceu.monomethylhydrazine": "Monomethylhydrazine", + "material.gtceu.mutated_living_solder": "Mutated Living Solder", + "material.gtceu.n_difluorophenylpyrrole": "N-Difluorophenylpyrrole", + "material.gtceu.n_hydroxysuccinimide": "N-Hydroxysuccinimide", + "material.gtceu.naquadah_contain_rare_earth": "Naquadah Containing Rare Earth", + "material.gtceu.naquadah_contain_rare_earth_fluoride": "Naquadah Containing Rare Earth Fluoride", + "material.gtceu.naquadah_fuel": "Naquadah Fuel", + "material.gtceu.naquadah_solution": "Naquadah Solution", + "material.gtceu.naquadria_caesium_xenonnonfluoride": "Naquadria Cesium Xenon Nonafluoride", + "material.gtceu.naquadria_caesiumfluoride": "Naquadria Cesium Fluoride", + "material.gtceu.naquadriatictaranium": "Naquadria Taranium Alloy", + "material.gtceu.neodymium_oxide": "Neodymium Oxide", + "material.gtceu.neodymium_rare_earth_concentrate_powder": "Neodymium Rare Earth Concentrate Powder", + "material.gtceu.neutralised_red_mud": "Neutralized Red Mud", + "material.gtceu.neutralized_monazite_rare_earth_filter_residue_powder": "Neutralized Monazite Rare Earth Filter Residue Powder", + "material.gtceu.neutralized_uranium_filter_residue_powder": "Neutralized Uranium Filter Residue Powder", + "material.gtceu.neutronium_doped_nanotubes": "Neutronium Doped Nanotubes", + "material.gtceu.nickel_front": "Nickel Ore Foam", + "material.gtceu.nitrated_triniite_compound_solution": "Nitrated Trinium Compound Solution", + "material.gtceu.nitric_leachate_from_monazite": "Nitric Leachate from Monazite", + "material.gtceu.nitrided_fluoro_carbon_lanthanide_cerium_rare_earth_oxide_solution": "Nitrided Fluoro-Carbon Lanthanide Cerium Rare Earth Oxide Solution", + "material.gtceu.nitrided_samarium_terbium_mixture_powder": "Nitrided Samarium Terbium Mixture Powder", + "material.gtceu.nitrogen_pentoxide": "Nitrogen Pentoxide", + "material.gtceu.nitronium_tetrafluoroborate": "Nitronium Tetrafluoroborate", + "material.gtceu.nitrosonium_octafluoroxenate": "Nitrosonium Octafluoroxenate", + "material.gtceu.nitrosonium_tetrafluoroborate": "Nitrosonium Tetrafluoroborate", + "material.gtceu.nitrous_acid": "Nitrous Acid", + "material.gtceu.nitryl_fluoride": "Nitryl Fluoride", + "material.gtceu.nmethylpyrolidone": "N-Methylpyrrolidone", + "material.gtceu.noble_gas": "Noble Gas Mixture", + "material.gtceu.not_found": "Non-Metal Mixture", + "material.gtceu.oganesson_breeding_base": "Oganesson Breeding Base", + "material.gtceu.orichalcum": "Orichalcum", + "material.gtceu.ostrum": "Ostrum", + "material.gtceu.oxalic_acid": "Oxalic Acid", + "material.gtceu.oxidized_residual_solution": "Oxidized Residual Solution", + "material.gtceu.oxidized_residues": "Oxidized Residue", + "material.gtceu.oxydianiline": "Oxydianiline", + "material.gtceu.ozone": "Ozone", + "material.gtceu.p_nitroaniline": "p-Nitroaniline", + "material.gtceu.p_phenylenediamine": "p-Phenylenediamine", + "material.gtceu.paa": "Polyamic Acid", + "material.gtceu.palladium_fullerene_matrix": "Palladium Fullerene Matrix", + "material.gtceu.paramagnetic_residues": "Paramagnetic Residue", + "material.gtceu.partially_oxidized_residues": "Partially Oxidized Residue", + "material.gtceu.pcbs": "Phenyl-C61-Butyric Acid Methyl Ester", + "material.gtceu.pentaerythritol": "Pentaerythritol", + "material.gtceu.pentlandite_front": "Pentlandite Ore Foam", + "material.gtceu.perditio_crystal": "Perditio Crystal", + "material.gtceu.perfluorobenzene": "Perfluorobenzene", + "material.gtceu.periodicium": "Periodicium", + "material.gtceu.phenylenedioxydiacetic_acid": "Phenylenedioxydiacetic Acid", + "material.gtceu.phenylpentanoic_acid": "Phenylpentanoic Acid", + "material.gtceu.phenylsodium": "Phenylsodium", + "material.gtceu.phosgene": "Phosgene", + "material.gtceu.phosphorus_free_samarium_concentrate_powder": "Phosphorus Free Samarium Concentrate Powder", + "material.gtceu.phosphorus_pentasulfide": "Phosphorus Pentasulfide", + "material.gtceu.phosphorus_trichloride": "Phosphorus Trichloride", + "material.gtceu.photopolymer": "Photopolymer Solution", + "material.gtceu.photoresist": "Photoresist", + "material.gtceu.phthalic_anhydride": "Phthalic Anhydride", + "material.gtceu.pikyonium": "Pikyonium", + "material.gtceu.piranha_solution": "Piranha Solution", + "material.gtceu.platinum_front": "Platinum Ore Foam", + "material.gtceu.platinum_slag": "Platinum Slag", + "material.gtceu.polycyclic_aromatic_mixture": "Polycyclic Aromatic Mixture", + "material.gtceu.polyetheretherketone": "Polyetheretherketone", + "material.gtceu.polyimide": "Polyimide", + "material.gtceu.polyurethane": "Polyurethane", + "material.gtceu.polyurethaneresin": "Polyurethane Resin", + "material.gtceu.poor": "Poor Metal Mixture", + "material.gtceu.positive_electron": "Positron", + "material.gtceu.potassium_bisulfite": "Potassium Bisulfite", + "material.gtceu.potassium_bromide": "Potassium Bromide", + "material.gtceu.potassium_ethylate": "Potassium Ethylate", + "material.gtceu.potassium_ethylxanthate": "Potassium Ethylxanthate", + "material.gtceu.potassium_fluoride": "Potassium Fluoride", + "material.gtceu.potassium_hydroxylaminedisulfonate": "Potassium Hydroxylamine Disulfonate", + "material.gtceu.potassium_pyrosulfate": "Potassium Pyrosulfate", + "material.gtceu.praseodymium_oxide": "Praseodymium Oxide", + "material.gtceu.prasiolite": "Prasiolite", + "material.gtceu.pre_zylon": "Pre-Zylon", + "material.gtceu.primordialmatter": "Primordial Matter", + "material.gtceu.promethium_oxide": "Promethium Oxide", + "material.gtceu.propadiene": "Propadiene", + "material.gtceu.pulsating_alloy": "Pulsating Iron", + "material.gtceu.purified_tengam": "Purified Tengam", + "material.gtceu.purified_xenoxene": "Purified Xenoxene", + "material.gtceu.pyridine": "Pyridine", + "material.gtceu.pyromellitic_dianhydride": "Pyromellitic Dianhydride", + "material.gtceu.pyrope_front": "Pyrope Ore Foam", + "material.gtceu.quantanium": "Quantanium", + "material.gtceu.quantum": "Quantum Alloy", + "material.gtceu.quantum_dots": "Quantum Dots", + "material.gtceu.quantumchromodynamically_confined_matter": "Quantum Chromodynamically Confined Matter", + "material.gtceu.quark_gluon": "Quark-Gluon", + "material.gtceu.quasifissioning": "Quasifissioning", + "material.gtceu.radium_nitrate": "Radium Nitrate", + "material.gtceu.radon_cracked_enriched_aquadah": "Radon Cracked Enriched Naquadah", + "material.gtceu.radon_difluoride": "Radon Difluoride", + "material.gtceu.radon_naquadria_octafluoride": "Radon Naquadria Octafluoride", + "material.gtceu.radon_trioxide": "Radon Trioxide", + "material.gtceu.radox": "Radox", + "material.gtceu.radox_gas": "Radox Gas", + "material.gtceu.rare_earth_chlorides": "Rare Earth Chlorides", + "material.gtceu.rare_earth_hydroxides": "Rare Earth Hydroxides", + "material.gtceu.rare_earth_metal": "Rare Earth Metal", + "material.gtceu.rare_earth_oxide": "Rare Earth Oxide", + "material.gtceu.rareearth": "Rare Earth Element Alloy", + "material.gtceu.raw_star_matter": "Raw Star Matter", + "material.gtceu.raw_tengam": "Raw Tengam", + "material.gtceu.rawradox": "Raw Radox", + "material.gtceu.reactor_steel": "Reactor Specialized Vanadium Steel", + "material.gtceu.red_mud": "Red Mud", + "material.gtceu.red_slurry": "Red Slurry", + "material.gtceu.red_zircon_powder": "Red Zircon", + "material.gtceu.redstone_front": "Redstone Ore Foam", + "material.gtceu.reprecipitated_rhodium": "Reprecipitated Rhodium", + "material.gtceu.residual_triniite_solution": "Residual Trinium Compound Solution", + "material.gtceu.resorcinol": "Resorcinol", + "material.gtceu.rhenium_chloride": "Rhenium Chloride", + "material.gtceu.rhenium_hassium_thallium_isophtaloylbisdiethylthiourea_hexaf": "Rhenium-Hassium-Thallium Isophtaloylbis(diethylthiourea) Hexafluorophosphate", + "material.gtceu.rhenium_sulfuric_solution": "Rhenium Sulfuric Solution", + "material.gtceu.rhodium_filter_cake": "Rhodium Filter Cake", + "material.gtceu.rhodium_filter_cake_solution": "Rhodium Filter Cake Solution", + "material.gtceu.rhodium_nitrate": "Rhodium Nitrate", + "material.gtceu.rhodium_rhenium_naquadah_catalyst": "Rhodium-Rhenium-Naquadah Catalyst", + "material.gtceu.rhodium_salt": "Rhodium Salt", + "material.gtceu.rhodium_salt_solution": "Rhodium Salt Solution", + "material.gtceu.rhodium_sulfate_gas": "Gaseous Rhodium Sulfate", + "material.gtceu.rhugnor": "Rhugnor", + "material.gtceu.rocket_fuel_cn3h7o3": "Rocket Fuel (Monomethylhydrazine Nitrate)", + "material.gtceu.rocket_fuel_h8n4c2o4": "Rocket Fuel (Unsymmetrical Dimethylhydrazine-Dinitrogen Tetroxide)", + "material.gtceu.rocket_fuel_rp_1": "RP-1 Rocket Fuel", + "material.gtceu.roughly_rhodium_metal": "Rough Rhodium Metal", + "material.gtceu.rp_1": "RP-1 Mixed Fuel", + "material.gtceu.ruthenium_tetroxide_hot": "Hot Ruthenium Tetroxide", + "material.gtceu.ruthenium_tetroxide_lq": "Ruthenium Tetroxide Solution", + "material.gtceu.samarium_chloride_concentrate_solution": "Samarium Chloride Concentrate Solution", + "material.gtceu.samarium_chloride_sodium_chloride_mixture_powder": "Samarium Chloride-Sodium Chloride Mixture", + "material.gtceu.samarium_chloride_with_impurities": "Impure Samarium Chloride", + "material.gtceu.samarium_oxalate_with_impurities": "Impure Samarium Oxalate", + "material.gtceu.samarium_oxide": "Samarium Oxide", + "material.gtceu.samarium_precipitate_powder": "Samarium Precipitate", + "material.gtceu.samarium_rare_earth_concentrate_powder": "Samarium Rare Earth Concentrate", + "material.gtceu.samarium_rare_earth_diluted_solution": "Samarium Rare Earth Diluted Solution", + "material.gtceu.samarium_rare_earth_slurry": "Samarium Rare Earth Slurry", + "material.gtceu.samarium_refined_powder": "Samarium Refined Powder", + "material.gtceu.samarium_rrare_eearth_turbid_liquid": "Samarium Rare Earth Turbid Liquid", + "material.gtceu.samarium_terbium_mixture_powder": "Samarium-Terbium Mixture", + "material.gtceu.sarcosine": "Sarcosine", + "material.gtceu.saturated_monazite_rare_earth_powder": "Saturated Monazite Rare Earth", + "material.gtceu.scandium_oxide": "Scandium Oxide", + "material.gtceu.scandium_titanium_50_mixture": "Scandium-Titanium-50 Mixture", + "material.gtceu.seaborgium_doped_nanotubes": "Seaborgium-Doped Nanotubes", + "material.gtceu.seaweedbroth": "Seaweed Broth", + "material.gtceu.selenium_oxide": "Selenium Dioxide", + "material.gtceu.shirabon": "Shirabon", + "material.gtceu.silica_alumina_gel": "Silica Alumina Gel", + "material.gtceu.silica_gel": "Silica Gel", + "material.gtceu.silica_gel_base": "Silica Gel Base", + "material.gtceu.silicon_carbide": "Silicon Carbide", + "material.gtceu.siliconoil": "Silicon Oil", + "material.gtceu.silver_chloride": "Silver Chloride", + "material.gtceu.silver_iodide": "Silver Iodide", + "material.gtceu.silver_nitrate": "Silver Nitrate", + "material.gtceu.silver_oxide": "Silver Oxide", + "material.gtceu.silver_perchlorate": "Silver Perchlorate", + "material.gtceu.silver_tetrafluoroborate": "Silver Tetrafluoroborate", + "material.gtceu.sm_gd_oxides_solution": "Samarium-Gadolinium Oxides Solution", + "material.gtceu.soap": "Soap", + "material.gtceu.sodium_aluminium_hydride": "Sodium Aluminium Hydride", + "material.gtceu.sodium_azanide": "Sodium Azanide", + "material.gtceu.sodium_azide": "Sodium Azide", + "material.gtceu.sodium_borohydride": "Sodium Borohydride", + "material.gtceu.sodium_bromide": "Sodium Bromide", + "material.gtceu.sodium_chlorate": "Sodium Chlorate", + "material.gtceu.sodium_cyanide": "Sodium Cyanide", + "material.gtceu.sodium_ethylate": "Sodium Ethylate", + "material.gtceu.sodium_ethylxanthate": "Sodium Ethylxanthate", + "material.gtceu.sodium_fluoride": "Sodium Fluoride", + "material.gtceu.sodium_fluorosilicate": "Sodium Fluorosilicate", + "material.gtceu.sodium_formate": "Sodium Formate", + "material.gtceu.sodium_hexafluoroaluminate": "Sodium Hexafluoroaluminate", + "material.gtceu.sodium_hydride": "Sodium Hydride", + "material.gtceu.sodium_hydroxide_solution": "Sodium Hydroxide Solution", + "material.gtceu.sodium_hypochlorite": "Sodium Hypochlorite", + "material.gtceu.sodium_nitrate": "Sodium Nitrate", + "material.gtceu.sodium_nitrate_solution": "Sodium Nitrate Solution", + "material.gtceu.sodium_oxide": "Sodium Oxide", + "material.gtceu.sodium_perchlorate": "Sodium Perchlorate", + "material.gtceu.sodium_rutheniate": "Sodium Rutheniate", + "material.gtceu.sodium_seaborgate": "Sodium Seaborgate", + "material.gtceu.sodium_sulfate": "Sodium Sulfate", + "material.gtceu.sodium_tetrafluoroborate": "Sodium Tetrafluoroborate", + "material.gtceu.sodium_thiocyanate": "Sodium Thiocyanate", + "material.gtceu.sodium_thiosulfate": "Sodium Thiosulfate", + "material.gtceu.sodium_toluenesulfonate": "Sodium Toluenesulfonate", + "material.gtceu.spacetime": "Spacetime", + "material.gtceu.spatialfluid": "Spatial Expansion Fluid", + "material.gtceu.special_ceramics": "Special Ceramics", + "material.gtceu.spessartine_front": "Spessartine Ore Foam", + "material.gtceu.sphalerite_front": "Sphalerite Ore Foam", + "material.gtceu.starlight": "Starlight", + "material.gtceu.starmetal": "Star Metal", + "material.gtceu.steam_cracked_fluoro_carbon_lanthanide_slurry": "Steam Cracked Fluoro Carbon Lanthanide Slurry", + "material.gtceu.steam_cracked_turpentine": "Steam Cracked Turpentine", + "material.gtceu.stearic_acid": "Stearic Acid", + "material.gtceu.stellar_energy_rocket_fuel": "Stellar Energy Rocket Fuel", + "material.gtceu.stellite": "Stellite", + "material.gtceu.stone_dust_residue": "Stone Dust Residue", + "material.gtceu.strontium_europium_aluminate": "Strontium Europium Aluminate", + "material.gtceu.succinaldehyde": "Succinaldehyde", + "material.gtceu.succinic_acid": "Succinic Acid", + "material.gtceu.succinic_anhydride": "Succinic Anhydride", + "material.gtceu.succinimide": "Succinimide", + "material.gtceu.succinimidyl_acetate": "Succinimidyl Acetate", + "material.gtceu.sunnarium": "Sunnarium", + "material.gtceu.super_mutated_living_solder": "Super Mutated Living Solder", + "material.gtceu.supercritical_carbon_dioxide": "Supercritical Carbon Dioxide", + "material.gtceu.supercritical_sodium_potassium": "Supercritical Sodium Potassium", + "material.gtceu.supercritical_steam": "Supercritical Steam", + "material.gtceu.superheavy_h_alloy": "Superheavy-H Alloy", + "material.gtceu.superheavy_l_alloy": "Superheavy-L Alloy", + "material.gtceu.superheavyradox": "Superheavy Radox", + "material.gtceu.superlightradox": "Superlight Radox", + "material.gtceu.tairitsu": "Tairitsu", + "material.gtceu.tanmolyium": "Tanmolyium", + "material.gtceu.tannic": "Tannic", + "material.gtceu.tantalloy_61": "Tantalloy-61", + "material.gtceu.taranium": "Taranium", + "material.gtceu.taranium_enriched_liquid_helium_3": "Taranium Enriched Liquid Helium-3", + "material.gtceu.taranium_rich_liquid_helium_4": "Taranium Rich Liquid Helium-4", + "material.gtceu.tartarite": "Tartarite", + "material.gtceu.tb_ho_oxides_solution": "Terbium-Holmium Oxides Solution", + "material.gtceu.tellurium_oxide": "Tellurium Dioxide", + "material.gtceu.temporalfluid": "Tachyon-Rich Temporal Fluid", + "material.gtceu.terbium_nitrate_powder": "Terbium Nitrate", + "material.gtceu.terbium_oxide": "Terbium Oxide", + "material.gtceu.terephthalaldehyde": "Terephthalaldehyde", + "material.gtceu.terephthalicacid": "Terephthalic Acid", + "material.gtceu.terephthaloyl_chloride": "Terephthaloyl Chloride", + "material.gtceu.tert_butanol": "Tert-Butanol", + "material.gtceu.tertbuthylcarbonylazide": "Tert-Butylcarbonyl Azide", + "material.gtceu.tetraacetyldinitrosohexaazaisowurtzitane": "Tetraacetyldinitrosohexaazaisowurtzitane", + "material.gtceu.tetracene": "Tetracene", + "material.gtceu.tetraethylammonium_bromide": "Tetraethylammonium Bromide", + "material.gtceu.tetrahydrofuran": "Tetrahydrofuran", + "material.gtceu.thallium_chloride": "Thallium Chloride", + "material.gtceu.thallium_thulium_doped_caesium_iodide": "Thallium Thulium Doped Caesium Iodide", + "material.gtceu.thaumium": "Thaumium", + "material.gtceu.thionyl_chloride": "Thionyl Chloride", + "material.gtceu.thorite_powder": "Thorite", + "material.gtceu.thorium_phosphate_filter_cake_powder": "Thorium Phosphate Filter Cake", + "material.gtceu.thorium_phosphate_refined_powder": "Thorium Phosphate Refined", + "material.gtceu.thulium_oxide": "Thulium Oxide", + "material.gtceu.titan_precision_steel": "Titan Precision Steel", + "material.gtceu.titanium_50": "Titanium-50", + "material.gtceu.titanium_50_tetrachloride": "Titanium-50 Tetrachloride", + "material.gtceu.titanium_50_tetrafluoride": "Titanium-50 Tetrafluoride", + "material.gtceu.titanium_tetrafluoride": "Titanium Tetrafluoride", + "material.gtceu.titansteel": "Titansteel", + "material.gtceu.titanyl_sulfate": "Titanyl Sulfate", + "material.gtceu.toluene_diisocyanate": "Toluene Diisocyanate", + "material.gtceu.transcendentmetal": "Transcendent Metal", + "material.gtceu.transition": "Transition Element Alloy", + "material.gtceu.transition_1": "Early Transition Metal Mixture", + "material.gtceu.transition_2": "Middle Transition Metal Mixture", + "material.gtceu.transition_3": "Late Transition Metal Mixture", + "material.gtceu.trichloroflerane": "Trichloroflerane", + "material.gtceu.tricotylphosphine": "Trioctylphosphine", + "material.gtceu.trifluoroacetic_phosphate_ester": "Trifluoroacetic Phosphate Ester", + "material.gtceu.trimethylamine": "Trimethylamine", + "material.gtceu.trimethylchlorosilane": "Trimethylchlorosilane", + "material.gtceu.trimethylsilane": "Trimethylsilane", + "material.gtceu.trimethyltin_chloride": "Trimethyltin Chloride", + "material.gtceu.trinium_compound": "Trinium Compound", + "material.gtceu.trinium_tetrafluoride": "Trinium Tetrafluoride", + "material.gtceu.trinium_titanium": "Trinium Titanium", + "material.gtceu.tritium_hydride": "Tritium Hydride", + "material.gtceu.tungsten_trioxide": "Tungsten Trioxide", + "material.gtceu.turbid_dragon_blood": "Turbid Dragon Blood", + "material.gtceu.turpentine": "Turpentine", + "material.gtceu.ultraacidic_residue_solution": "Ultraacidic Residue Solution", + "material.gtceu.uncommon_residues": "Uncommon Residues", + "material.gtceu.unfolded_fullerene": "Unfolded Fullerene", + "material.gtceu.unknownnutrientagar": "Unknown Nutrient Agar", + "material.gtceu.unknowwater": "Unknown Liquid", + "material.gtceu.uranium_filter_residue_powder": "Uranium Filter Residue", + "material.gtceu.uranium_sulfate_waste_solution": "Uranium Sulfate Waste Solution", + "material.gtceu.uruium": "Uruium", + "material.gtceu.uu_amplifier": "UU Amplifier", + "material.gtceu.vanadium_pentoxide_powder": "Vanadium Pentoxide", + "material.gtceu.vibramantium": "Vibramantium", + "material.gtceu.vibranium": "Vibranium", + "material.gtceu.vibranium_unstable": "Unstable Vibranium", + "material.gtceu.vibrant_alloy": "Vibrant Alloy", + "material.gtceu.viscoelastic_polyurethane": "Viscoelastic Polyurethane", + "material.gtceu.viscoelastic_polyurethane_foam": "Viscoelastic Polyurethane Foam", + "material.gtceu.water_agar_mix": "Water Agar Mix", + "material.gtceu.wet_rare_earth_oxide_powder": "Wet Rare Earth Oxide", + "material.gtceu.wet_zeolite_sieving_pellets": "Wet Zeolite Sieving Pellets", + "material.gtceu.white_dwarf_mtter": "White Dwarf Matter", + "material.gtceu.woods_glass": "Woods Glass", + "material.gtceu.xenic_acid": "Xenic Acid", + "material.gtceu.xenoauric_fluoroantimonic_acid": "Xenoauric Fluoroantimonic Acid", + "material.gtceu.xenon_hexafluoro_enriched_naquadate": "Xenon Hexafluoro Enriched Naquadah", + "material.gtceu.xenon_trioxide": "Xenon Trioxide", + "material.gtceu.xenoxene": "Xenoxene", + "material.gtceu.xenoxene_crystal": "Xenoxene Crystal", + "material.gtceu.xenoxene_mixture": "Xenoxene Mixture", + "material.gtceu.xpjuice": "Liquid Experience", + "material.gtceu.ytterbium_178": "Ytterbium-178", + "material.gtceu.ytterbium_oxide": "Ytterbium Oxide", + "material.gtceu.yttrium_oxide": "Yttrium Oxide", + "material.gtceu.zeolite_sieving_pellets": "Zeolite Sieving Pellets", + "material.gtceu.zinc_sulfate": "Zinc Sulfate", + "material.gtceu.zircon": "Zircon", + "material.gtceu.zircon_chlorinating_residue": "Zircon Chlorinating Residue", + "material.gtceu.zirconiu_hafnium_oxychloride": "Zirconium-Hafnium Oxychloride", + "material.gtceu.zirconium_carbide": "Zirconium Carbide", + "material.gtceu.zirconium_hafnium_chloride": "Zirconium-Hafnium Chloride", + "material.gtceu.zirconium_oxide": "Zirconium Dioxide", + "material.gtceu.znfealcl_catalyst": "Zn-Fe-Al-Cl Mixed Catalyst", + "material.gtceu.zylon": "Zylon", + "tagprefix.ceresstone": "Ceres Stone %s Ore", + "tagprefix.enceladusstone": "Enceladus Stone %s Ore", + "tagprefix.ganymedestone": "Ganymede Stone %s Ore", + "tagprefix.glacio_stone": "Glacio Stone %s Ore", + "tagprefix.iostone": "Io Stone %s Ore", + "tagprefix.mars_stone": "Mars Stone %s Ore", + "tagprefix.mercury_stone": "Mercury Stone %s Ore", + "tagprefix.milled": "Milled %s", + "tagprefix.moon_stone": "Moon Stone %s Ore", + "tagprefix.nanoswarm": "%s Nanoswarm", + "tagprefix.plutostone": "Pluto Stone %s Ore", + "tagprefix.titanstone": "Titan Stone %s Ore", + "tagprefix.venus_stone": "Venus Stone %s Ore" } \ No newline at end of file diff --git a/src/main/resources/assets/gtceu/lang/zh_cn.json b/src/main/resources/assets/gtceu/lang/zh_cn.json index dcc56ae50..eec9842d6 100644 --- a/src/main/resources/assets/gtceu/lang/zh_cn.json +++ b/src/main/resources/assets/gtceu/lang/zh_cn.json @@ -1,1651 +1,1740 @@ { - "block.gtceu.a_mass_fabricator": "进阶质量发生器", - "block.gtceu.advanced_assembly_line": "进阶装配线", - "block.gtceu.advanced_hyper_reactor": "进阶超能反应堆", - "block.gtceu.advanced_integrated_ore_processor": "进阶集成矿石处理厂", - "block.gtceu.advanced_multi_smelter": "进阶熔炉", - "block.gtceu.advanced_sps_crafting": "进阶超临界合成机", - "block.gtceu.aggregation_device": "聚合装置", - "block.gtceu.annihilate_generator": "人造恒星", - "block.gtceu.assemble_plant": "通用组装厂", - "block.gtceu.assembler_module": "太空电梯组装模块", - "block.gtceu.atomic_energy_excitation_plant": "原子能激发工厂", - "block.gtceu.auto_configuration_maintenance_hatch": "可配置自动维护仓", - "block.gtceu.bedrock_drilling_rig": "基岩钻机", - "block.gtceu.blaze_blast_furnace": "烈焰高炉", - "block.gtceu.block_conversion_room": "方块转换室", - "block.gtceu.chemical_distort": "深度化学扭曲仪", - "block.gtceu.chemical_energy_devourer": "化学能吞噬者", - "block.gtceu.chemical_plant": "化工厂", - "block.gtceu.circuit_assembly_line": "电路装配线", - "block.gtceu.cleaning_configuration_maintenance_hatch": "超净可配置维护仓", - "block.gtceu.cold_ice_freezer": "寒冰冷冻机", - "block.gtceu.component_assembly_line": "部件装配线", - "block.gtceu.cooling_tower": "冷却塔", - "block.gtceu.create_aggregation": "创造聚合仪", - "block.gtceu.create_computation": "创造计算机", - "block.gtceu.crystalline_infinity": "无限晶胞", - "block.gtceu.decay_hastener": "衰变加速器", - "block.gtceu.desulfurizer": "脱硫机", - "block.gtceu.digestion_tank": "煮解池", - "block.gtceu.dimensional_focus_engraving_array": "维度聚焦激光蚀刻阵列", - "block.gtceu.dimensionally_transcendent_dirt_forge": "超维度等泥土锻炉", - "block.gtceu.dimensionally_transcendent_mixer": "超维度搅拌机", - "block.gtceu.dimensionally_transcendent_plasma_forge": "超维度等离子锻炉", - "block.gtceu.dimensionally_transcendent_steam_boiler": "超维度等蒸汽锅炉", - "block.gtceu.dimensionally_transcendent_steam_oven": "超维度蒸汽熔炉", - "block.gtceu.disassembly": "拆解机", - "block.gtceu.dissolving_tank": "溶解罐", - "block.gtceu.door_of_create": "创造之门", - "block.gtceu.dragon_egg_copier": "龙蛋复制机", - "block.gtceu.dyson_sphere": "戴森球控制系统", - "block.gtceu.electric_implosion_compressor": "电力聚爆压缩机", - "block.gtceu.element_copying": "元素复制机", - "block.gtceu.engraving_laser_plant": "激光蚀刻工厂", - "block.gtceu.ev_buffer": "§5进阶缓存器 III§r", - "block.gtceu.ev_dehydrator": "§5高级脱水机 III§r", - "block.gtceu.ev_dual_input_hatch": "§5EV§r输入总成", - "block.gtceu.ev_dual_output_hatch": "§5EV§r输出总成", - "block.gtceu.ev_huge_input_hatch": "§5EV§r巨型输入仓", - "block.gtceu.ev_huge_output_hatch": "§5EV§r巨型输出仓", - "block.gtceu.ev_lightning_processor": "§5高级闪电处理机 III§r", - "block.gtceu.ev_lightning_rod": "§5基础避雷针§r", - "block.gtceu.ev_neutron_accelerator": "§5EV 中子加速器§r", - "block.gtceu.ev_rocket_engine": "§5基础火箭引擎§r", - "block.gtceu.ev_world_data_scanner": "§5进阶世界信息扫描仪 III§r", - "block.gtceu.eye_of_harmony": "鸿蒙之眼", - "block.gtceu.field_extruder_factory": "力场压模工厂", - "block.gtceu.fishing_ground": "渔场", - "block.gtceu.fission_reactor": "裂变反应堆", - "block.gtceu.flotation_cell_regulator": "工业浮选机", - "block.gtceu.gas_mega_turbine": "特大燃气涡轮", - "block.gtceu.generator_array": "发电阵列", - "block.gtceu.gravitation_shockburst": "引力震爆器", - "block.gtceu.gravity_hatch": "重力控制仓", - "block.gtceu.greenhouse": "温室", - "block.gtceu.heat_exchanger": "热交换机", - "block.gtceu.holy_separator": "神圣分离者", - "block.gtceu.hv_dehydrator": "§6高级脱水机 II§r", - "block.gtceu.hv_dual_input_hatch": "§6HV§r输入总成", - "block.gtceu.hv_dual_output_hatch": "§6HV§r输出总成", - "block.gtceu.hv_energy_input_hatch_16a": "16安§4HV§r能源仓", - "block.gtceu.hv_energy_input_hatch_4a": "4安§4HV§r能源仓 ", - "block.gtceu.hv_energy_output_hatch": "§4HV§r动力仓", - "block.gtceu.hv_energy_output_hatch_16a": "16安§4HV§r动力仓", - "block.gtceu.hv_energy_output_hatch_4a": "4安§4HV§r动力仓", - "block.gtceu.hv_huge_input_hatch": "§6HV§r巨型输入仓", - "block.gtceu.hv_huge_output_hatch": "§6HV§r巨型输出仓", - "block.gtceu.hv_lightning_processor": "§6高级闪电处理机 II§r", - "block.gtceu.hv_neutron_accelerator": "§6HV 中子加速器§r", - "block.gtceu.hv_semi_fluid": "§6进阶半流质发电机 II§r", - "block.gtceu.hv_world_data_scanner": "§6进阶世界信息扫描仪 II§r", - "block.gtceu.hyper_reactor": "超能反应堆", - "block.gtceu.incubator": "培养缸", - "block.gtceu.integrated_ore_processor": "集成矿石处理厂", - "block.gtceu.isa_mill": "艾萨研磨机", - "block.gtceu.iv_1048576a_laser_source_hatch": "1048576安§9IV§r激光源仓", - "block.gtceu.iv_1048576a_laser_target_hatch": "1048576安§9IV§r激光靶仓", - "block.gtceu.iv_16384a_laser_source_hatch": "16384安§9IV§r激光源仓", - "block.gtceu.iv_16384a_laser_target_hatch": "16384安§9IV§r激光靶仓", - "block.gtceu.iv_16777216a_laser_source_hatch": "16777216安§9IV§r激光源仓", - "block.gtceu.iv_16777216a_laser_target_hatch": "16777216安§9IV§r激光靶仓", - "block.gtceu.iv_262144a_laser_source_hatch": "262144安§9IV§r激光源仓", - "block.gtceu.iv_262144a_laser_target_hatch": "262144安§9IV§r激光靶仓", - "block.gtceu.iv_4194304a_laser_source_hatch": "4194304安§9IV§r激光源仓", - "block.gtceu.iv_4194304a_laser_target_hatch": "4194304安§9IV§r激光靶仓", - "block.gtceu.iv_65536a_laser_source_hatch": "65536安§9IV§r激光源仓", - "block.gtceu.iv_65536a_laser_target_hatch": "65536安§9IV§r激光靶仓", - "block.gtceu.iv_67108864a_laser_source_hatch": "67108864安§9IV§r激光源仓", - "block.gtceu.iv_67108864a_laser_target_hatch": "67108864安§9IV§r激光靶仓", - "block.gtceu.iv_buffer": "§9精英缓存器§r", - "block.gtceu.iv_dehydrator": "§9精英脱水机§r", - "block.gtceu.iv_dual_input_hatch": "§9IV§r输入总成", - "block.gtceu.iv_dual_output_hatch": "§9IV§r输出总成", - "block.gtceu.iv_huge_input_hatch": "§9IV§r巨型输入仓", - "block.gtceu.iv_huge_output_hatch": "§9IV§r巨型输出仓", - "block.gtceu.iv_lightning_processor": "§9精英闪电处理机§r", - "block.gtceu.iv_lightning_rod": "§9进阶避雷针§r", - "block.gtceu.iv_naquadah_reactor": "§9基础硅岩反应堆§r", - "block.gtceu.iv_neutron_accelerator": "§9IV 中子加速器§r", - "block.gtceu.iv_rocket_engine": "§9进阶火箭引擎§r", - "block.gtceu.iv_world_data_scanner": "§9精英世界信息扫描仪§r", - "block.gtceu.large_block_conversion_room": "大型方块转换室", - "block.gtceu.large_chemical_plant": "大型化工厂", - "block.gtceu.large_cracker": "大型裂化机", - "block.gtceu.large_gas_collector": "大型集气室", - "block.gtceu.large_greenhouse": "大型温室", - "block.gtceu.large_incubator": "大型培养缸", - "block.gtceu.large_naquadah_reactor": "大型硅岩反应堆", - "block.gtceu.large_pyrolyse_oven": "大型热解炉", - "block.gtceu.large_recycler": "回收机", - "block.gtceu.large_rock_crusher": "大型碎岩机", - "block.gtceu.large_semi_fluid_generator": "大型半流质发电机", - "block.gtceu.large_steam_bath": "大型蒸汽浸洗机", - "block.gtceu.large_steam_centrifuge": "大型蒸汽离心机", - "block.gtceu.large_steam_circuit_assembler": "大型蒸汽电路组装机", - "block.gtceu.large_steam_input_hatch": "大型蒸汽输入仓", - "block.gtceu.large_steam_macerator": "大型蒸汽研磨机", - "block.gtceu.large_steam_mixer": "大型蒸汽搅拌机", - "block.gtceu.large_steam_ore_washer": "大型蒸汽洗矿机", - "block.gtceu.large_steam_thermal_centrifuge": "大型蒸汽热力离心机", - "block.gtceu.large_void_miner": "大型虚空采矿厂", - "block.gtceu.lava_furnace": "熔岩炉", - "block.gtceu.law_cleaning_maintenance_hatch": "绝对洁净维护仓", - "block.gtceu.law_configuration_cleaning_maintenance_hatch": "绝对洁净可配置维护仓", - "block.gtceu.luv_1048576a_laser_source_hatch": "1048576安§dLuV§r激光源仓", - "block.gtceu.luv_1048576a_laser_target_hatch": "1048576安§dLuV§r激光靶仓", - "block.gtceu.luv_16384a_laser_source_hatch": "16384安§dLuV§r激光源仓", - "block.gtceu.luv_16384a_laser_target_hatch": "16384安§dLuV§r激光靶仓", - "block.gtceu.luv_16777216a_laser_source_hatch": "16777216安§dLuV§r激光源仓", - "block.gtceu.luv_16777216a_laser_target_hatch": "16777216安§dLuV§r激光靶仓", - "block.gtceu.luv_262144a_laser_source_hatch": "262144安§dLuV§r激光源仓", - "block.gtceu.luv_262144a_laser_target_hatch": "262144安§dLuV§r激光靶仓", - "block.gtceu.luv_4194304a_laser_source_hatch": "4194304安§dLuV§r激光源仓", - "block.gtceu.luv_4194304a_laser_target_hatch": "4194304安§dLuV§r激光靶仓", - "block.gtceu.luv_65536a_laser_source_hatch": "65536安§dLuV§r激光源仓", - "block.gtceu.luv_65536a_laser_target_hatch": "65536安§dLuV§r激光靶仓", - "block.gtceu.luv_67108864a_laser_source_hatch": "67108864安§dLuV§r激光源仓", - "block.gtceu.luv_67108864a_laser_target_hatch": "67108864安§dLuV§r激光靶仓", - "block.gtceu.luv_buffer": "§d精英缓存器 II§r", - "block.gtceu.luv_compressed_fusion_reactor": "压缩核聚变反应堆控制电脑 MK-I", - "block.gtceu.luv_dehydrator": "§d精英脱水机 II§r", - "block.gtceu.luv_huge_input_hatch": "§dLuV§r巨型输入仓", - "block.gtceu.luv_huge_output_hatch": "§dLuV§r巨型输出仓", - "block.gtceu.luv_lightning_processor": "§d精英闪电处理机 II§r", - "block.gtceu.luv_lightning_rod": "§d精英避雷针§r", - "block.gtceu.luv_naquadah_reactor": "§d进阶硅岩反应堆§r", - "block.gtceu.luv_neutron_accelerator": "§dLuV 中子加速器§r", - "block.gtceu.luv_rocket_engine": "§d精英火箭引擎§r", - "block.gtceu.luv_world_data_scanner": "§d精英世界信息扫描仪 II§r", - "block.gtceu.lv_dehydrator": "基础脱水机", - "block.gtceu.lv_dual_input_hatch": "§7LV§r输入总成", - "block.gtceu.lv_dual_output_hatch": "§7LV§r输出总成", - "block.gtceu.lv_energy_input_hatch_16a": "16安§7LV§r能源仓", - "block.gtceu.lv_energy_input_hatch_4a": "4安§7LV§r能源仓 ", - "block.gtceu.lv_energy_output_hatch": "§7LV§r动力仓", - "block.gtceu.lv_energy_output_hatch_16a": "16安§7LV§r动力仓", - "block.gtceu.lv_energy_output_hatch_4a": "4安§7LV§r动力仓", - "block.gtceu.lv_huge_input_hatch": "§7LV§r巨型输入仓", - "block.gtceu.lv_huge_output_hatch": "§7LV§r巨型输出仓", - "block.gtceu.lv_lightning_processor": "基础闪电处理机", - "block.gtceu.lv_neutron_accelerator": "§7LV 中子加速器§r", - "block.gtceu.lv_primitive_magic_energy": "低压原始魔法能源吸收器", - "block.gtceu.lv_semi_fluid": "基础半流质发电机§r", - "block.gtceu.lv_world_data_scanner": "基础世界信息扫描仪", - "block.gtceu.mage_assembler": "巨型组装厂", - "block.gtceu.magic_manufacturer": "魔法制造机", - "block.gtceu.mass_fabricator": "质量发生器", - "block.gtceu.matter_fabricator": "物质生成机", - "block.gtceu.max_1024a_laser_source_hatch": "1024安§c§lMAX§r激光源仓", - "block.gtceu.max_1024a_laser_target_hatch": "1024安§c§lMAX§r激光靶仓", - "block.gtceu.max_1048576a_laser_source_hatch": "1048576安§c§lMAX§r激光源仓", - "block.gtceu.max_1048576a_laser_target_hatch": "1048576安§c§lMAX§r激光靶仓", - "block.gtceu.max_16384a_laser_source_hatch": "16384安§c§lMAX§r激光源仓", - "block.gtceu.max_16384a_laser_target_hatch": "16384安§c§lMAX§r激光靶仓", - "block.gtceu.max_16777216a_laser_source_hatch": "16777216安§c§lMAX§r激光源仓", - "block.gtceu.max_16777216a_laser_target_hatch": "16777216安§c§lMAX§r激光靶仓", - "block.gtceu.max_256a_laser_source_hatch": "256安§c§lMAX§r激光源仓", - "block.gtceu.max_256a_laser_target_hatch": "256安§c§lMAX§r激光靶仓", - "block.gtceu.max_262144a_laser_source_hatch": "262144安§c§lMAX§r激光源仓", - "block.gtceu.max_262144a_laser_target_hatch": "262144安§c§lMAX§r激光靶仓", - "block.gtceu.max_4096a_laser_source_hatch": "4096安§c§lMAX§r激光源仓", - "block.gtceu.max_4096a_laser_target_hatch": "4096安§c§lMAX§r激光靶仓", - "block.gtceu.max_4194304a_laser_source_hatch": "4194304安§c§lMAX§r激光源仓", - "block.gtceu.max_4194304a_laser_target_hatch": "4194304安§c§lMAX§r激光靶仓", - "block.gtceu.max_65536a_laser_source_hatch": "65536安§c§lMAX§r激光源仓", - "block.gtceu.max_65536a_laser_target_hatch": "65536安§c§lMAX§r激光靶仓", - "block.gtceu.max_67108864a_laser_source_hatch": "67108864安§c§lMAX§r激光源仓", - "block.gtceu.max_67108864a_laser_target_hatch": "67108864安§c§lMAX§r激光靶仓", - "block.gtceu.max_buffer": "§c§l终焉缓存器§r", - "block.gtceu.max_energy_input_hatch_16a": "16安§c§lMAX§r能源仓", - "block.gtceu.max_energy_input_hatch_4a": "4安§c§lMAX§r能源仓", - "block.gtceu.max_energy_output_hatch_16a": "16安§c§lMAX§r动力仓", - "block.gtceu.max_energy_output_hatch_4a": "4安§c§lMAX§r动力仓", - "block.gtceu.max_huge_input_hatch": "§c§lMAX§r巨型输入仓", - "block.gtceu.max_huge_output_hatch": "§c§lMAX§r巨型输出仓", - "block.gtceu.max_neutron_accelerator": "§c§lMAX 中子加速器§r", - "block.gtceu.max_neutron_compressor": "中子态素压缩机", - "block.gtceu.max_parallel_hatch": "§c§lMAX§r并行控制仓", - "block.gtceu.max_substation_input_hatch_64a": "64安§c§lMAX§r变电能源仓", - "block.gtceu.max_substation_output_hatch_64a": "64安§c§lMAX§r变电动力仓", - "block.gtceu.mega_alloy_blast_smelter": "巨型合金冶炼炉", - "block.gtceu.mega_canner": "特大装罐机", - "block.gtceu.mega_distillery": "超级蒸馏塔", - "block.gtceu.mega_extractor": "特大相变仪", - "block.gtceu.mega_fluid_heater": "超级流体加热器", - "block.gtceu.mega_presser": "特大机床", - "block.gtceu.mega_steam_input_hatch": "特大蒸汽输入仓", - "block.gtceu.mega_steam_output_hatch": "特大蒸汽输出仓", - "block.gtceu.mega_wiremill": "特大线材轧机", - "block.gtceu.mixed_plant": "通用混合厂", - "block.gtceu.mv_dehydrator": "§b高级脱水机 §r", - "block.gtceu.mv_dual_input_hatch": "§bMV§r输入总成", - "block.gtceu.mv_dual_output_hatch": "§bMV§r输出总成", - "block.gtceu.mv_energy_input_hatch_16a": "16安§bMV§r能源仓", - "block.gtceu.mv_energy_input_hatch_4a": "4安§bMV§r能源仓 ", - "block.gtceu.mv_energy_output_hatch": "§bMV§r动力仓", - "block.gtceu.mv_energy_output_hatch_16a": "16安§bMV§r动力仓", - "block.gtceu.mv_energy_output_hatch_4a": "4安§bMV§r动力仓", - "block.gtceu.mv_huge_input_hatch": "§bMV§r巨型输入仓", - "block.gtceu.mv_huge_output_hatch": "§bMV§r巨型输出仓", - "block.gtceu.mv_lightning_processor": "§b高级闪电处理机 §r", - "block.gtceu.mv_neutron_accelerator": "§bMV 中子加速器§r", - "block.gtceu.mv_semi_fluid": "§b进阶半流质发电机§r", - "block.gtceu.mv_world_data_scanner": "§b进阶世界信息扫描仪§r", - "block.gtceu.nano_core": "纳米核心", - "block.gtceu.nano_forge_1": "1阶纳米锻炉", - "block.gtceu.nano_forge_2": "2阶纳米锻炉", - "block.gtceu.nano_forge_3": "3阶纳米锻炉", - "block.gtceu.naquadah_alloy_crate": "硅岩合金板条箱", - "block.gtceu.naquadah_alloy_drum": "硅岩合金桶", - "block.gtceu.neutron_activator": "中子活化器", - "block.gtceu.neutron_sensor": "中子传感器", - "block.gtceu.neutronium_crate": "中子素板条箱", - "block.gtceu.neutronium_drum": "中子素桶", - "block.gtceu.opv_1048576a_laser_source_hatch": "1048576安§9§lOpV§r激光源仓", - "block.gtceu.opv_1048576a_laser_target_hatch": "1048576安§9§lOpV§r激光靶仓", - "block.gtceu.opv_16384a_laser_source_hatch": "16384安§9§lOpV§r激光源仓", - "block.gtceu.opv_16384a_laser_target_hatch": "16384安§9§lOpV§r激光靶仓", - "block.gtceu.opv_16777216a_laser_source_hatch": "16777216安§9§lOpV§r激光源仓", - "block.gtceu.opv_16777216a_laser_target_hatch": "16777216安§9§lOpV§r激光靶仓", - "block.gtceu.opv_262144a_laser_source_hatch": "262144安§9§lOpV§r激光源仓", - "block.gtceu.opv_262144a_laser_target_hatch": "262144安§9§lOpV§r激光靶仓", - "block.gtceu.opv_4194304a_laser_source_hatch": "4194304安§9§lOpV§r激光源仓", - "block.gtceu.opv_4194304a_laser_target_hatch": "4194304安§9§lOpV§r激光靶仓", - "block.gtceu.opv_65536a_laser_source_hatch": "65536安§9§lOpV§r激光源仓", - "block.gtceu.opv_65536a_laser_target_hatch": "65536安§9§lOpV§r激光靶仓", - "block.gtceu.opv_67108864a_laser_source_hatch": "67108864安§9§lOpV§r激光源仓", - "block.gtceu.opv_67108864a_laser_target_hatch": "67108864安§9§lOpV§r激光靶仓", - "block.gtceu.opv_buffer": "§9§l传奇缓存器§r", - "block.gtceu.opv_dehydrator": "§9§l传奇脱水机§r", - "block.gtceu.opv_energy_input_hatch_16a": "16安§9§lOpV§r能源仓", - "block.gtceu.opv_energy_input_hatch_4a": "4安§9§lOpV§r能源仓", - "block.gtceu.opv_energy_output_hatch_16a": "16安§9§lOpV§r动力仓", - "block.gtceu.opv_energy_output_hatch_4a": "4安§9§lOpV§r动力仓", - "block.gtceu.opv_huge_input_hatch": "§9§lOpV§r巨型输入仓", - "block.gtceu.opv_huge_output_hatch": "§9§lOpV§r巨型输出仓", - "block.gtceu.opv_lightning_processor": "§9§l传奇闪电处理机§r", - "block.gtceu.opv_neutron_accelerator": "§9§lOpV 中子加速器§r", - "block.gtceu.opv_parallel_hatch": "§9§lOpV§r并行控制仓", - "block.gtceu.opv_substation_input_hatch_64a": "64安§9§lOpV§r变电能源仓", - "block.gtceu.opv_substation_output_hatch_64a": "64安§9§lOpV§r变电动力仓", - "block.gtceu.opv_world_data_scanner": "§9§l传奇世界信息扫描仪§r", - "block.gtceu.pcb_factory": "PCB工厂", - "block.gtceu.petrochemical_plant": "石化工厂", - "block.gtceu.plasma_condenser": "等离子冷凝器", - "block.gtceu.plasma_mega_turbine": "特大等离子涡轮", - "block.gtceu.precision_assembler": "精密组装机", - "block.gtceu.primitive_void_ore": "原始虚空采矿机", - "block.gtceu.processing_plant": "通用加工厂", - "block.gtceu.qft": "量子操纵者", - "block.gtceu.rare_earth_centrifugal": "稀土离心机", - "block.gtceu.resource_collection": "太空电梯资源采集模块", - "block.gtceu.rhodium_plated_palladium_crate": "镀铑钯板条箱", - "block.gtceu.rhodium_plated_palladium_drum": "镀铑钯桶", - "block.gtceu.rocket_large_turbine": "大型火箭引擎涡轮", - "block.gtceu.rocket_mega_turbine": "特大火箭引擎涡轮", - "block.gtceu.rotor_hatch": "转子仓", - "block.gtceu.separated_plant": "通用分离厂", - "block.gtceu.slaughterhouse": "工业屠宰场", - "block.gtceu.space_elevator": "太空电梯", - "block.gtceu.space_probe_surface_reception": "宇宙探测器地面接收单元", - "block.gtceu.sps_crafting": "超临界合成机", - "block.gtceu.star_ultimate_material_forge_factory": "恒星终极物质锻造工厂", - "block.gtceu.steam_bath": "蒸汽浸洗机", - "block.gtceu.steam_foundry": "蒸汽铸造炉", - "block.gtceu.steam_mega_turbine": "特大蒸汽涡轮", - "block.gtceu.steam_mixer": "蒸汽搅拌机", - "block.gtceu.steam_ore_washer": "蒸汽洗矿机", - "block.gtceu.steam_piston_hammer": "蒸汽活塞锤", - "block.gtceu.steam_pressor": "蒸汽挤压机", - "block.gtceu.stellar_forge": "恒星炎炀锻炉", - "block.gtceu.sterile_cleaning_maintenance_hatch": "无菌维护仓", - "block.gtceu.sterile_configuration_cleaning_maintenance_hatch": "无菌可配置维护仓", - "block.gtceu.super_blast_smelter": "超级冶炼炉", - "block.gtceu.super_computation": "超级计算机", - "block.gtceu.super_particle_collider": "超级粒子对撞机", - "block.gtceu.superconducting_electromagnetism": "超导电磁工厂", - "block.gtceu.supercritical_mega_steam_turbine": "特大超临界蒸汽涡轮", - "block.gtceu.supercritical_steam_turbine": "超临界蒸汽涡轮", - "block.gtceu.suprachronal_assembly_line": "超时空装配线", - "block.gtceu.suprachronal_assembly_line_module": "超时空装配线拓展模块", - "block.gtceu.uev_1048576a_laser_source_hatch": "1048576安§aUEV§r激光源仓", - "block.gtceu.uev_1048576a_laser_target_hatch": "1048576安§aUEV§r激光靶仓", - "block.gtceu.uev_16384a_laser_source_hatch": "16384安§aUEV§r激光源仓", - "block.gtceu.uev_16384a_laser_target_hatch": "16384安§aUEV§r激光靶仓", - "block.gtceu.uev_16777216a_laser_source_hatch": "16777216安§aUEV§r激光源仓", - "block.gtceu.uev_16777216a_laser_target_hatch": "16777216安§aUEV§r激光靶仓", - "block.gtceu.uev_262144a_laser_source_hatch": "262144安§aUEV§r激光源仓", - "block.gtceu.uev_262144a_laser_target_hatch": "262144安§aUEV§r激光靶仓", - "block.gtceu.uev_4194304a_laser_source_hatch": "4194304安§aUEV§r激光源仓", - "block.gtceu.uev_4194304a_laser_target_hatch": "4194304安§aUEV§r激光靶仓", - "block.gtceu.uev_65536a_laser_source_hatch": "65536安§aUEV§r激光源仓", - "block.gtceu.uev_65536a_laser_target_hatch": "65536安§aUEV§r激光靶仓", - "block.gtceu.uev_67108864a_laser_source_hatch": "67108864安§aUEV§r激光源仓", - "block.gtceu.uev_67108864a_laser_target_hatch": "67108864安§aUEV§r激光靶仓", - "block.gtceu.uev_buffer": "§a史诗缓存器 II§r", - "block.gtceu.uev_compressed_fusion_reactor": "压缩核聚变反应堆控制电脑 MK-V", - "block.gtceu.uev_dehydrator": "§a史诗脱水机 II§r", - "block.gtceu.uev_energy_input_hatch_16a": "16安§aUEV§r能源仓", - "block.gtceu.uev_energy_input_hatch_4a": "4安§aUEV§r能源仓", - "block.gtceu.uev_energy_output_hatch_16a": "16安§aUEV§r动力仓", - "block.gtceu.uev_energy_output_hatch_4a": "4安§aUEV§r动力仓", - "block.gtceu.uev_fusion_reactor": "核聚变反应堆控制电脑 MK-V", - "block.gtceu.uev_huge_input_hatch": "§aUEV§r巨型输入仓", - "block.gtceu.uev_huge_output_hatch": "§aUEV§r巨型输出仓", - "block.gtceu.uev_lightning_processor": "§a史诗闪电处理机 II§r", - "block.gtceu.uev_neutron_accelerator": "§aUEV 中子加速器§r", - "block.gtceu.uev_parallel_hatch": "§aUEV§r并行控制仓", - "block.gtceu.uev_rotor_holder": "§aUEV§r转子支架", - "block.gtceu.uev_substation_input_hatch_64a": "64安§aUEV§r变电能源仓", - "block.gtceu.uev_substation_output_hatch_64a": "64安§aUEV§r变电动力仓", - "block.gtceu.uev_world_data_scanner": "§a史诗世界信息扫描仪II§r", - "block.gtceu.uhv_1048576a_laser_source_hatch": "1048576安§4UHV§r激光源仓", - "block.gtceu.uhv_1048576a_laser_target_hatch": "1048576安§4UHV§r激光靶仓", - "block.gtceu.uhv_16384a_laser_source_hatch": "16384安§4UHV§r激光源仓", - "block.gtceu.uhv_16384a_laser_target_hatch": "16384安§4UHV§r激光靶仓", - "block.gtceu.uhv_16777216a_laser_source_hatch": "16777216安§4UHV§r激光源仓", - "block.gtceu.uhv_16777216a_laser_target_hatch": "16777216安§4UHV§r激光靶仓", - "block.gtceu.uhv_262144a_laser_source_hatch": "262144安§4UHV§r激光源仓", - "block.gtceu.uhv_262144a_laser_target_hatch": "262144安§4UHV§r激光靶仓", - "block.gtceu.uhv_4194304a_laser_source_hatch": "4194304安§4UHV§r激光源仓", - "block.gtceu.uhv_4194304a_laser_target_hatch": "4194304安§4UHV§r激光靶仓", - "block.gtceu.uhv_65536a_laser_source_hatch": "65536安§4UHV§r激光源仓", - "block.gtceu.uhv_65536a_laser_target_hatch": "65536安§4UHV§r激光靶仓", - "block.gtceu.uhv_67108864a_laser_source_hatch": "67108864安§4UHV§r激光源仓", - "block.gtceu.uhv_67108864a_laser_target_hatch": "67108864安§4UHV§r激光靶仓", - "block.gtceu.uhv_buffer": "§4史诗缓存器§r", - "block.gtceu.uhv_compressed_fusion_reactor": "压缩核聚变反应堆控制电脑 MK-IV", - "block.gtceu.uhv_dehydrator": "§4史诗脱水机§r", - "block.gtceu.uhv_fusion_reactor": "核聚变反应堆控制电脑 MK-IV", - "block.gtceu.uhv_huge_input_hatch": "§4UHV§r巨型输入仓", - "block.gtceu.uhv_huge_output_hatch": "§4UHV§r巨型输出仓", - "block.gtceu.uhv_lightning_processor": "§4史诗闪电处理机§r", - "block.gtceu.uhv_neutron_accelerator": "§4UHV 中子加速器§r", - "block.gtceu.uhv_parallel_hatch": "§4UHV§r并行控制仓", - "block.gtceu.uhv_rotor_holder": "§4UHV§r转子支架", - "block.gtceu.uhv_world_data_scanner": "§4史诗世界信息扫描仪§r", - "block.gtceu.uiv_1048576a_laser_source_hatch": "1048576安§2UIV§r激光源仓", - "block.gtceu.uiv_1048576a_laser_target_hatch": "1048576安§2UIV§r激光靶仓", - "block.gtceu.uiv_16384a_laser_source_hatch": "16384安§2UIV§r激光源仓", - "block.gtceu.uiv_16384a_laser_target_hatch": "16384安§2UIV§r激光靶仓", - "block.gtceu.uiv_16777216a_laser_source_hatch": "16777216安§2UIV§r激光源仓", - "block.gtceu.uiv_16777216a_laser_target_hatch": "16777216安§2UIV§r激光靶仓", - "block.gtceu.uiv_262144a_laser_source_hatch": "262144安§2UIV§r激光源仓", - "block.gtceu.uiv_262144a_laser_target_hatch": "262144安§2UIV§r激光靶仓", - "block.gtceu.uiv_4194304a_laser_source_hatch": "4194304安§2UIV§r激光源仓", - "block.gtceu.uiv_4194304a_laser_target_hatch": "4194304安§2UIV§r激光靶仓", - "block.gtceu.uiv_65536a_laser_source_hatch": "65536安§2UIV§r激光源仓", - "block.gtceu.uiv_65536a_laser_target_hatch": "65536安§2UIV§r激光靶仓", - "block.gtceu.uiv_67108864a_laser_source_hatch": "67108864安§2UIV§r激光源仓", - "block.gtceu.uiv_67108864a_laser_target_hatch": "67108864安§2UIV§r激光靶仓", - "block.gtceu.uiv_buffer": "§2史诗缓存器 III§r", - "block.gtceu.uiv_dehydrator": "§2史诗脱水机 III§r", - "block.gtceu.uiv_energy_input_hatch_16a": "16安§2UIV§r能源仓", - "block.gtceu.uiv_energy_input_hatch_4a": "4安§2UIV§r能源仓", - "block.gtceu.uiv_energy_output_hatch_16a": "16安§2UIV§r动力仓", - "block.gtceu.uiv_energy_output_hatch_4a": "4安§2UIV§r动力仓", - "block.gtceu.uiv_huge_input_hatch": "§2UIV§r巨型输入仓", - "block.gtceu.uiv_huge_output_hatch": "§2UIV§r巨型输出仓", - "block.gtceu.uiv_lightning_processor": "§2史诗闪电处理机 III§r", - "block.gtceu.uiv_neutron_accelerator": "§2UIV 中子加速器§r", - "block.gtceu.uiv_parallel_hatch": "§2UIV§r并行控制仓", - "block.gtceu.uiv_substation_input_hatch_64a": "64安§2UIV§r变电能源仓", - "block.gtceu.uiv_substation_output_hatch_64a": "64安§2UIV§r变电动力仓", - "block.gtceu.uiv_world_data_scanner": "§2史诗世界信息扫描仪 III§r", - "block.gtceu.ulv_huge_input_hatch": "§8ULV§r巨型输入仓", - "block.gtceu.ulv_huge_output_hatch": "§8ULV§r巨型输出仓", - "block.gtceu.ulv_neutron_accelerator": "§8ULV 中子加速器§r", - "block.gtceu.ulv_primitive_magic_energy": "超低压原始魔法能源吸收器", - "block.gtceu.uv_1048576a_laser_source_hatch": "1048576安§3UV§r激光源仓", - "block.gtceu.uv_1048576a_laser_target_hatch": "1048576安§3UV§r激光靶仓", - "block.gtceu.uv_16384a_laser_source_hatch": "16384安§3UV§r激光源仓", - "block.gtceu.uv_16384a_laser_target_hatch": "16384安§3UV§r激光靶仓", - "block.gtceu.uv_16777216a_laser_source_hatch": "16777216安§3UV§r激光源仓", - "block.gtceu.uv_16777216a_laser_target_hatch": "16777216安§3UV§r激光靶仓", - "block.gtceu.uv_262144a_laser_source_hatch": "262144安§3UV§r激光源仓", - "block.gtceu.uv_262144a_laser_target_hatch": "262144安§3UV§r激光靶仓", - "block.gtceu.uv_4194304a_laser_source_hatch": "4194304安§3UV§r激光源仓", - "block.gtceu.uv_4194304a_laser_target_hatch": "4194304安§3UV§r激光靶仓", - "block.gtceu.uv_65536a_laser_source_hatch": "65536安§3UV§r激光源仓", - "block.gtceu.uv_65536a_laser_target_hatch": "65536安§3UV§r激光靶仓", - "block.gtceu.uv_67108864a_laser_source_hatch": "67108864安§3UV§r激光源仓", - "block.gtceu.uv_67108864a_laser_target_hatch": "67108864安§3UV§r激光靶仓", - "block.gtceu.uv_buffer": "§3终极缓存器§r", - "block.gtceu.uv_compressed_fusion_reactor": "压缩核聚变反应堆控制电脑 MK-III", - "block.gtceu.uv_dehydrator": "§3终极脱水机§r", - "block.gtceu.uv_huge_input_hatch": "§3UV§r巨型输入仓", - "block.gtceu.uv_huge_output_hatch": "§3UV§r巨型输出仓", - "block.gtceu.uv_lightning_processor": "§3终极闪电处理机§r", - "block.gtceu.uv_neutron_accelerator": "§3UV 中子加速器§r", - "block.gtceu.uv_world_data_scanner": "§3终极世界信息扫描仪§r§r", - "block.gtceu.uxv_1048576a_laser_source_hatch": "1048576安§eUXV§r激光源仓", - "block.gtceu.uxv_1048576a_laser_target_hatch": "1048576安§eUXV§r激光靶仓", - "block.gtceu.uxv_16384a_laser_source_hatch": "16384安§eUXV§r激光源仓", - "block.gtceu.uxv_16384a_laser_target_hatch": "16384安§eUXV§r激光靶仓", - "block.gtceu.uxv_16777216a_laser_source_hatch": "16777216安§eUXV§r激光源仓", - "block.gtceu.uxv_16777216a_laser_target_hatch": "16777216安§eUXV§r激光靶仓", - "block.gtceu.uxv_262144a_laser_source_hatch": "262144安§eUXV§r激光源仓", - "block.gtceu.uxv_262144a_laser_target_hatch": "262144安§eUXV§r激光靶仓", - "block.gtceu.uxv_4194304a_laser_source_hatch": "4194304安§eUXV§r激光源仓", - "block.gtceu.uxv_4194304a_laser_target_hatch": "4194304安§eUXV§r激光靶仓", - "block.gtceu.uxv_65536a_laser_source_hatch": "65536安§eUXV§r激光源仓", - "block.gtceu.uxv_65536a_laser_target_hatch": "65536安§eUXV§r激光靶仓", - "block.gtceu.uxv_67108864a_laser_source_hatch": "67108864安§eUXV§r激光源仓", - "block.gtceu.uxv_67108864a_laser_target_hatch": "67108864安§eUXV§r激光靶仓", - "block.gtceu.uxv_buffer": "§e史诗缓存器 IV§r", - "block.gtceu.uxv_dehydrator": "§e史诗脱水机 IV§r", - "block.gtceu.uxv_energy_input_hatch_16a": "16安§eUXV§r能源仓", - "block.gtceu.uxv_energy_input_hatch_4a": "4安§eUXV§r能源仓", - "block.gtceu.uxv_energy_output_hatch_16a": "16安§eUXV§r动力仓", - "block.gtceu.uxv_energy_output_hatch_4a": "4安§eUXV§r动力仓", - "block.gtceu.uxv_huge_input_hatch": "§eUXV§r巨型输入仓", - "block.gtceu.uxv_huge_output_hatch": "§eUXV§r巨型输出仓", - "block.gtceu.uxv_lightning_processor": "§e史诗闪电处理机 IV§r", - "block.gtceu.uxv_neutron_accelerator": "§eUXV 中子加速器§r", - "block.gtceu.uxv_parallel_hatch": "§eUXV§r并行控制仓", - "block.gtceu.uxv_substation_input_hatch_64a": "64安§eUXV§r变电能源仓", - "block.gtceu.uxv_substation_output_hatch_64a": "64安§eUXV§r变电动力仓", - "block.gtceu.uxv_world_data_scanner": "§e史诗世界信息扫描仪 IV§r", - "block.gtceu.vacuum_drying_furnace": "真空干燥炉", - "block.gtceu.void_fluid_drilling_rig": "虚空流体钻机", - "block.gtceu.void_miner": "虚空采矿机", - "block.gtceu.weather_control": "天气控制器", - "block.gtceu.wood_distillation": "木化工厂", - "block.gtceu.zpm_1048576a_laser_source_hatch": "1048576安§cZPM§r激光源仓", - "block.gtceu.zpm_1048576a_laser_target_hatch": "1048576安§cZPM§r激光靶仓", - "block.gtceu.zpm_16384a_laser_source_hatch": "16384安§cZPM§r激光源仓", - "block.gtceu.zpm_16384a_laser_target_hatch": "16384安§cZPM§r激光靶仓", - "block.gtceu.zpm_16777216a_laser_source_hatch": "16777216安§cZPM§r激光源仓", - "block.gtceu.zpm_16777216a_laser_target_hatch": "16777216安§cZPM§r激光靶仓", - "block.gtceu.zpm_262144a_laser_source_hatch": "262144安§cZPM§r激光源仓", - "block.gtceu.zpm_262144a_laser_target_hatch": "262144安§cZPM§r激光靶仓", - "block.gtceu.zpm_4194304a_laser_source_hatch": "4194304安§cZPM§r激光源仓", - "block.gtceu.zpm_4194304a_laser_target_hatch": "4194304安§cZPM§r激光靶仓", - "block.gtceu.zpm_65536a_laser_source_hatch": "65536安§cZPM§r激光源仓", - "block.gtceu.zpm_65536a_laser_target_hatch": "65536安§cZPM§r激光靶仓", - "block.gtceu.zpm_67108864a_laser_source_hatch": "67108864安§cZPM§r激光源仓", - "block.gtceu.zpm_67108864a_laser_target_hatch": "67108864安§cZPM§r激光靶仓", - "block.gtceu.zpm_buffer": "§c精英缓存器 III§r", - "block.gtceu.zpm_compressed_fusion_reactor": "压缩核聚变反应堆控制电脑 MK-II", - "block.gtceu.zpm_dehydrator": "§c精英脱水机 III§r", - "block.gtceu.zpm_fluid_drilling_rig": "§c无尽流体钻机§r", - "block.gtceu.zpm_huge_input_hatch": "§cZPM§r巨型输入仓", - "block.gtceu.zpm_huge_output_hatch": "§cZPM§r巨型输出仓", - "block.gtceu.zpm_lightning_processor": "§c精英闪电处理机 III§r", - "block.gtceu.zpm_naquadah_reactor": "§c精英硅岩反应堆§r", - "block.gtceu.zpm_neutron_accelerator": "§cZPM 中子加速器§r", - "block.gtceu.zpm_world_data_scanner": "§c精英世界信息扫描仪 III§r", - "gtceu.advanced_hyper_reactor": "进阶超能反应", - "gtceu.aggregation_device": "聚合装置", - "gtceu.annihilate_generator": "湮灭发电机", - "gtceu.assembler_module": "太空组装", - "gtceu.atomic_energy_excitation": "原子能激发", - "gtceu.bedrock_drilling_rig": "基岩钻机", - "gtceu.block_conversion": "方块转换", - "gtceu.casings.tier": "等级:%s", - "gtceu.circuit_assembly_line": "电路装配线", - "gtceu.circuit_printer": "编程电路复制", - "gtceu.component_assembly_line": "部件装配", - "gtceu.cosmos_simulation": "宇宙模拟", - "gtceu.create_aggregation": "创造聚合", - "gtceu.decay_hastener": "衰变加速", - "gtceu.dehydrator": "脱水机", - "gtceu.desulfurizer": "脱硫", - "gtceu.digestion_treatment": "煮解", - "gtceu.dimensional_focus_engraving_array": "维度聚焦激光蚀刻", - "gtceu.dimensionally_transcendent_mixer": "超维度搅拌", - "gtceu.dimensionally_transcendent_plasma_forge": "超维度熔炼", - "gtceu.disassembly": "拆解", - "gtceu.dissolution_treatment": "溶解", - "gtceu.distort": "深度化学扭曲仪", - "gtceu.door_of_create": "创造之门", - "gtceu.dragon_egg_copier": "龙蛋复制", - "gtceu.drilling_module": "太空钻井", - "gtceu.dyson_sphere": "戴森球", - "gtceu.electric_implosion_compressor": "电力聚爆压缩机", - "gtceu.element_copying": "元素复制机", - "gtceu.fishing_ground": "渔场", - "gtceu.fission_reactor": "裂变反应堆", - "gtceu.flotating_beneficiation": "浮游选矿", - "gtceu.fuel_refining": "燃料精炼", - "gtceu.gravitation_shockburst": "强引力震爆", - "gtceu.greenhouse": "温室", - "gtceu.heat_exchanger": "热交换机", - "gtceu.hyper_reactor": "超能反应", - "gtceu.incubator": "培养缸", - "gtceu.integrated_ore_processor": "集成矿石处理", - "gtceu.isa_mill": "湿法碾磨", - "gtceu.jei.bedrock_fluid.benzene_deposit": "苯矿藏", - "gtceu.jei.bedrock_fluid.ceres_krypton_deposit": "氪矿藏", - "gtceu.jei.bedrock_fluid.ceres_neon_deposit": "氖矿藏", - "gtceu.jei.bedrock_fluid.ceres_radon_deposit": "氡矿藏", - "gtceu.jei.bedrock_fluid.ceres_xenon_deposit": "氙矿藏", - "gtceu.jei.bedrock_fluid.charcoal_byproducts": "木炭副产矿藏", - "gtceu.jei.bedrock_fluid.chlorine": "氯矿藏", - "gtceu.jei.bedrock_fluid.coal_gas_deposit": "煤气矿藏", - "gtceu.jei.bedrock_fluid.deuterium_deposit": "氘矿藏", - "gtceu.jei.bedrock_fluid.flat_heavy_oil_deposit": "超平坦重油矿藏", - "gtceu.jei.bedrock_fluid.flat_light_oil_deposit": "超平坦轻油矿藏", - "gtceu.jei.bedrock_fluid.flat_natural_gas_deposit": "超平坦天然气矿藏", - "gtceu.jei.bedrock_fluid.flat_oil_deposit": "超平坦石油矿藏", - "gtceu.jei.bedrock_fluid.flat_raw_oil_deposit": "超平坦原油矿藏", - "gtceu.jei.bedrock_fluid.flat_salt_water_deposit": "超平坦盐水矿藏", - "gtceu.jei.bedrock_fluid.fluorine": "氟矿藏", - "gtceu.jei.bedrock_fluid.helium3_deposit": "氦-3矿藏", - "gtceu.jei.bedrock_fluid.helium_deposit": "氦矿藏", - "gtceu.jei.bedrock_fluid.hydrochloric_acid_deposit": "盐酸矿藏", - "gtceu.jei.bedrock_fluid.methane_deposit": "甲烷矿藏", - "gtceu.jei.bedrock_fluid.nitric_acid_deposit": "硝酸矿藏", - "gtceu.jei.bedrock_fluid.radon_deposit": "氡矿藏", - "gtceu.jei.bedrock_fluid.sulfuric_acid_deposit": "硫酸矿藏", - "gtceu.jei.bedrock_fluid.unknowwater": "不明液体矿藏", - "gtceu.jei.bedrock_fluid.void_heavy_oil_deposit": "虚空重油矿藏", - "gtceu.jei.bedrock_fluid.void_light_oil_deposit": "虚空轻油矿藏", - "gtceu.jei.bedrock_fluid.void_natural_gas_deposit": "虚空天然气矿藏", - "gtceu.jei.bedrock_fluid.void_oil_deposit": "虚空石油矿藏", - "gtceu.jei.bedrock_fluid.void_raw_oil_deposit": "虚空原油矿藏", - "gtceu.jei.bedrock_fluid.void_salt_water_deposit": "虚空盐水矿藏", - "gtceu.jei.ore_vein.apatite_vein_ad": "磷矿脉", - "gtceu.jei.ore_vein.bauxite_vein_ad": "金红石矿脉", - "gtceu.jei.ore_vein.beryllium_vein_aw": "绿宝石矿脉", - "gtceu.jei.ore_vein.calorite_vein_ad": "耐热矿脉", - "gtceu.jei.ore_vein.celestine_vein_ad": "天青石矿脉", - "gtceu.jei.ore_vein.certus_quartz_vein_aw": "AE2矿脉", - "gtceu.jei.ore_vein.desh_vein_ad": "戴斯矿脉", - "gtceu.jei.ore_vein.molybdenum_vein_aw": "钼矿脉", - "gtceu.jei.ore_vein.monazite_vein_ad": "群居石矿脉", - "gtceu.jei.ore_vein.naquadah_vein_ad": "硅岩矿脉", - "gtceu.jei.ore_vein.nickel_vein_ad": "镍矿脉", - "gtceu.jei.ore_vein.olivine_vein_ad": "橄榄石矿脉", - "gtceu.jei.ore_vein.ostrum_vein_ad": "紫金矿脉", - "gtceu.jei.ore_vein.pitchblende_vein_ad": "铀矿脉", - "gtceu.jei.ore_vein.plutonium_vein_ad": "钚矿脉", - "gtceu.jei.ore_vein.quartzite_vein_aw": "石英岩矿脉", - "gtceu.jei.ore_vein.saltpeter_vein_aw": "硝石矿脉", - "gtceu.jei.ore_vein.scheelite_vein_ad": "钨矿脉", - "gtceu.jei.ore_vein.sheldonite_vein_ad": "铂系矿脉", - "gtceu.jei.ore_vein.stibnite_vein_aw": "锑辉矿脉", - "gtceu.jei.ore_vein.sulfur_vein_ad": "硫矿脉", - "gtceu.jei.ore_vein.sulfur_vein_aw": "硫磺矿脉", - "gtceu.jei.ore_vein.topaz_vein_aw": "黄玉矿脉", - "gtceu.jei.ore_vein.zircon_vein_ad": "锆石矿脉", - "gtceu.large_gas_collector": "大型集气室", - "gtceu.large_naquadah_reactor": "大型硅岩反应", - "gtceu.large_recycler": "材料回收", - "gtceu.large_void_miner": "精准矿石模式", - "gtceu.lava_furnace": "熔岩炉", - "gtceu.lightning_processor": "闪电处理", - "gtceu.machine.advanced_assembly_line.tooltip.0": "可拓展至64格", - "gtceu.machine.advanced_assembly_line.tooltip.1": "只能使用数据靶仓", - "gtceu.machine.advanced_hyper_reactor.tooltip.0": "提供不同等离子体获得不同并行", - "gtceu.machine.advanced_hyper_reactor.tooltip.1": "星辉:8,致密中子:16", - "gtceu.machine.advanced_integrated_ore_processor.tooltip.0": "最大并行数:2147483647", - "gtceu.machine.aggregation_device.tooltip.0": "电压等级每高出UEV一级最大并行数x2", - "gtceu.machine.assembly_line.tooltip.0": "每个装配线单元可使速度提升1%", - "gtceu.machine.available_recipe_map_10.tooltip": "可用配方类型:%s,%s,%s,%s,%s,%s,%s,%s,%s,%s", - "gtceu.machine.available_recipe_map_11.tooltip": "可用配方类型:%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s", - "gtceu.machine.available_recipe_map_5.tooltip": "可用配方类型:%s,%s,%s,%s,%s", - "gtceu.machine.available_recipe_map_6.tooltip": "可用配方类型:%s,%s,%s,%s,%s,%s", - "gtceu.machine.available_recipe_map_7.tooltip": "可用配方类型:%s,%s,%s,%s,%s,%s,%s", - "gtceu.machine.available_recipe_map_8.tooltip": "可用配方类型:%s,%s,%s,%s,%s,%s,%s,%s", - "gtceu.machine.available_recipe_map_9.tooltip": "可用配方类型:%s,%s,%s,%s,%s,%s,%s,%s,%s", - "gtceu.machine.bedrock_drilling_rig.tooltip.0": "需要基岩在钻头下方", - "gtceu.machine.bedrock_drilling_rig.tooltip.1": "每次运行都有10%的概率破坏基岩", - "gtceu.machine.blaze_blast_furnace.tooltip.0": "需每秒提供§b10x(功率÷120)^1/2§r的§e液态烈焰§r", - "gtceu.machine.blaze_blast_furnace.tooltip.1": "最大并行数固定为64", - "gtceu.machine.block_conversion_room.tooltip.0": "每秒随机选取机器内部一个位置的方块进行转化", - "gtceu.machine.block_conversion_room.tooltip.1": "电压等级每高出MV1级,每次转换方块数量+4,且不会重复", - "gtceu.machine.chemical_distort.tooltip.0": "线圈温度每高出配方温度100K,并行加4", - "gtceu.machine.chemical_distort.tooltip.1": "更高的电压不会提供额外温度", - "gtceu.machine.chemical_plant.tooltip.0": "线圈等级每高出白铜一级能耗与速度x5%", - "gtceu.machine.circuit_assembly_line.tooltip.0": "在主机中放入同配方的机器人可以获得对应数量x2的并行", - "gtceu.machine.cold_ice_freezer.tooltip.0": "需每秒提供§b10x电压等级^2§r的§b液态冰§r", - "gtceu.machine.create_computation.tooltip.0": "输入电压:§4§lMAX§r", - "gtceu.machine.dimensionally_transcendent_dirt_forge.tooltip.0": "拥有524288的最大并行,且直接完成配方", - "gtceu.machine.dimensionally_transcendent_mixer.tooltip.0": "运行搅拌机配方时耗时倍数为0.2", - "gtceu.machine.duration_multiplier.tooltip": "耗时倍数:%s", - "gtceu.machine.dyson_sphere.number": "发射次数:%s / 10000", - "gtceu.machine.dyson_sphere.tooltip.0": "发射戴森球模块后开始工作", - "gtceu.machine.dyson_sphere.tooltip.1": "每次运行都有(模块数量/128 + 1)%的概率损坏一次模块", - "gtceu.machine.dyson_sphere.tooltip.2": "当损坏高于60%时,输出效率随损坏值由100%逐渐降低到20%,并输出随损坏值增强的红石信号", - "gtceu.machine.dyson_sphere.tooltip.3": "当损坏达到100%时减少一次模块发射数量,并重制损坏值", - "gtceu.machine.dyson_sphere.tooltip.4": "在损坏值高于60%时发射不会增加发射次数,但会重制损坏值", - "gtceu.machine.dyson_sphere.tooltip.5": "产能功率,和需求算力由发射的模块数量决定", - "gtceu.machine.dyson_sphere.tooltip.6": "每次发射可使功率增加1A MAX", - "gtceu.machine.dyson_sphere.voltage": "最大能量输出:%s EU/t", - "gtceu.machine.efficiency.tooltip": "§7效率:§r%s", - "gtceu.machine.electric_blast_furnace.tooltip.a": "耗时倍数:max(配方温度/炉温,0.5)", - "gtceu.machine.engraving_laser_plant.tooltip.0": "精密激光模式不支持并行", - "gtceu.machine.eut_multiplier.tooltip": "耗能倍数:%s", - "gtceu.machine.eye_of_harmony.tooltip.0": "创造一个微缩宇宙,并获取里面的资源", - "gtceu.machine.eye_of_harmony.tooltip.1": "这台多方块机器需要太多EU,无法用常规手段供能", - "gtceu.machine.eye_of_harmony.tooltip.2": "由无线EU网络直接供给EU,具体数值可在GUI内查看", - "gtceu.machine.eye_of_harmony.tooltip.3": "执行特殊超频模式,每提升16倍功率提升2倍速度,超频由编程电路调节", - "gtceu.machine.eye_of_harmony.tooltip.4": "工作前需设置好电路,1号不执行超频,2-4分别执行1,2,3次超频", - "gtceu.machine.eye_of_harmony.tooltip.5": "启动需1024B的宇宙素,与1024KB的氢和氦", - "gtceu.machine.eye_of_harmony.tooltip.6": "氢和氦存储在机器内部,在机器准备工作之前会持续消耗", - "gtceu.machine.fission_reactor.cooler": "冷却组件数量:%s,相邻数:%s", - "gtceu.machine.fission_reactor.damaged": "损坏:%s", - "gtceu.machine.fission_reactor.fuel": "燃料组件数量:%s,相邻数:%s", - "gtceu.machine.fission_reactor.heat": "堆温:%s K", - "gtceu.machine.fission_reactor.tooltip.0": "反应堆在运行前会获得最大并行数量为燃料组件数量的并行", - "gtceu.machine.fission_reactor.tooltip.1": "反应堆在运行过程中会根据条件升温,每秒升温值(K)=配方产热x(1+相邻燃料棒数量)", - "gtceu.machine.fission_reactor.tooltip.10": "如果供给量是需求量的n倍,则执行特殊超频,超频次数为n", - "gtceu.machine.fission_reactor.tooltip.11": "再次消耗需求量的冷却液,并减少1秒时间,如果无法供给冷却液则中断超频,如果进度到达100%则中断超频并消耗一次需求量的冷却液使温度降低1K", - "gtceu.machine.fission_reactor.tooltip.12": "由蒸馏水作为冷却液时将产生蒸汽,产生量:消耗量xmin(160,160/(1.4^(373-温度)))", - "gtceu.machine.fission_reactor.tooltip.13": "由钠钾合金作为冷却液时产生热钠钾合金,产生量=消耗量,如果温度高于825K则产生同等量的超临界钠钾合金", - "gtceu.machine.fission_reactor.tooltip.14": "无论反应堆是否具有消耗冷却液的条件都能执行配方", - "gtceu.machine.fission_reactor.tooltip.15": "反应堆停止工作后温度将每秒降低1K", - "gtceu.machine.fission_reactor.tooltip.2": "如果温度高于1500K,反应堆将会损坏,损坏达到100%后反应堆爆炸", - "gtceu.machine.fission_reactor.tooltip.3": "在运行过程中提供冷却液并根据不同冷却液的冷却液系数来控制温度", - "gtceu.machine.fission_reactor.tooltip.4": "冷却液系数:蒸馏水:800,钠钾合金:20", - "gtceu.machine.fission_reactor.tooltip.5": "反应堆冷却有如下参数:", - "gtceu.machine.fission_reactor.tooltip.6": "最低冷却液需求量和最高冷却液供给量", - "gtceu.machine.fission_reactor.tooltip.7": "最低需求量=配方产热x实际并行数量x当前温度/1500", - "gtceu.machine.fission_reactor.tooltip.8": "最高供给量=(冷却组件-(相邻数/3))x8", - "gtceu.machine.fission_reactor.tooltip.9": "当供给量>=需求量时达到消耗冷却液条件,消耗提供的冷却液,消耗量为需求量x冷却液系数,并阻止反应堆升温", - "gtceu.machine.flotation_cell_regulator.tooltip.0": "工业级浮游选矿池", - "gtceu.machine.fusion_reactor.tooltip.a": "机器配方等级每高出机器等级1级,最大并行数x4,最高16", - "gtceu.machine.generator_array.tooltip.0": "强大的运行环境会让机器中的小发电机功率x2。", - "gtceu.machine.generator_array.tooltip.1": "可以开启无线电网模式,产生的电能会直接送入无线电网。", - "gtceu.machine.generator_array.wireless": "无线电网模式:", - "gtceu.machine.greenhouse.tooltip.0": "需要阳光才能运行", - "gtceu.machine.greenhouse.tooltip.1": "如太阳光照不足,速度就会减缓", - "gtceu.machine.heat_exchanger.tooltip.0": "每次处理全部输入的热流体", - "gtceu.machine.heat_exchanger.tooltip.1": "需要保证输入的冷却液能将流体全部冷却", - "gtceu.machine.hyper_reactor.tooltip.0": "每次运行前提供额外的1mb等离子体将获得16的并行", - "gtceu.machine.hyper_reactor.tooltip.1": "不同燃料所需的等离子体不同", - "gtceu.machine.hyper_reactor.tooltip.2": "从1-4顺序为:山铜,末影,魔金,亚稳态𬭶", - "gtceu.machine.integrated_ore_processor.tooltip.0": "一步完成矿石处理", - "gtceu.machine.integrated_ore_processor.tooltip.1": "1号电路为研磨-研磨-离心", - "gtceu.machine.integrated_ore_processor.tooltip.2": "2号电路为研磨-洗矿-热离-研磨", - "gtceu.machine.integrated_ore_processor.tooltip.3": "3号电路为研磨-洗矿-研磨-离心", - "gtceu.machine.integrated_ore_processor.tooltip.4": "4号电路为研磨-洗矿-筛选-离心", - "gtceu.machine.integrated_ore_processor.tooltip.5": "5号电路为研磨-浸洗-热离-研磨", - "gtceu.machine.integrated_ore_processor.tooltip.6": "6号电路为研磨-浸洗-研磨-离心", - "gtceu.machine.integrated_ore_processor.tooltip.7": "7号电路为研磨-浸洗-筛选-离心", - "gtceu.machine.isa_mill.tooltip.0": "工业级湿法碾磨", - "gtceu.machine.large_arc_smelter.tooltip.0": "运行闪电处理配方时耗时x4", - "gtceu.machine.large_block_conversion_room.tooltip.1": "电压等级每高出MV1级,每次转换方块数量+64,且不会重复", - "gtceu.machine.large_gas_collector.tooltip.0": "最大并行数:100000", - "gtceu.machine.large_greenhouse.tooltip.0": "无需要阳光就能运行", - "gtceu.machine.large_recycler.tooltip.0": "电压等级每高出EV1级,最大并行数x4", - "gtceu.machine.large_rock_crusher.tooltip.0": "需要在输入仓中放入对应流体", - "gtceu.machine.large_steam_input_hatch.tooltip.0": "将蒸汽多方块配方限制提升到MV,并解锁超频功能", - "gtceu.machine.large_void_miner.tooltip.0": "精准模式消耗精华采集指定矿脉", - "gtceu.machine.large_void_miner.tooltip.1": "随机模式消耗10KB的钻井液和更长的耗时随机采集所有矿石,随机模式注意输出空间要足够", - "gtceu.machine.lightning_rod.tooltip.0": "上方避雷针被雷击后产生大量能量", - "gtceu.machine.lightning_rod.tooltip.1": "每0.5秒只能产生一次能量,且有一定几率破坏上方避雷针", - "gtceu.machine.lightning_rod.tooltip.2": "如果存储能量已满,机器将会爆炸", - "gtceu.machine.module": "已安装的模块数量:%s", - "gtceu.machine.me.me_dual_hatch_stock.data_stick.name":"§oME库存输入总成配置数据", - "gtceu.machine.multiple_recipes.tooltip": "支持跨配方并行", - "gtceu.machine.nano_core.tooltip.0": "能够运行任意等级的纳米锻炉配方", - "gtceu.machine.nano_core.tooltip.1": "处理速度固定为20倍", - "gtceu.machine.nano_forge.tooltip.0": "放入对应的纳米蜂群才能工作,并且按蜂群数量来并行", - "gtceu.machine.nano_forge_1.tooltip.0": "并行数为蜂群数量", - "gtceu.machine.nano_forge_2.tooltip.0": "处理2阶配方时,并行数为蜂群数量", - "gtceu.machine.nano_forge_2.tooltip.1": "处理1阶配方时并行数量翻倍,超频模式改为无损超频", - "gtceu.machine.nano_forge_3.tooltip.0": "处理3阶配方时,并行数为蜂群数量", - "gtceu.machine.nano_forge_3.tooltip.1": "处理2阶配方时并行数量翻倍,超频模式改为无损超频", - "gtceu.machine.nano_forge_3.tooltip.2": "处理1阶配方时并行数翻4倍,超频模式改为每提高4倍功率获得8倍提速", - "gtceu.machine.neutron_accelerator.tooltip.0": "§6最大EU消耗:§r%s", - "gtceu.machine.neutron_accelerator.tooltip.1": "§b每点EU都会转化为§e10~20-eV§b中子动能", - "gtceu.machine.neutron_activator.efficiency": "动能消耗倍速:%s", - "gtceu.machine.neutron_activator.ev": "当前中子动能:%seV", - "gtceu.machine.neutron_activator.height": "高度:%s", - "gtceu.machine.neutron_activator.time": "耗时:%s", - "gtceu.machine.neutron_activator.tooltip.0": "§7超光速运动!", - "gtceu.machine.neutron_activator.tooltip.1": "额外的高速管道方块提供配方时间减免,同时降低中子加速器的效率", - "gtceu.machine.neutron_activator.tooltip.2": "时间减免与加速效率为0.95^额外方块数量", - "gtceu.machine.neutron_activator.tooltip.3": "没有中子加速器运行时,中子动能每秒降低§e72KeV§r中子动能", - "gtceu.machine.neutron_activator.tooltip.4": "输入石墨/铍粉可以立即吸收§e10MeV§r中子动能", - "gtceu.machine.neutron_sensor.tooltip.0": "基于§6中子动能§7输出红石信号,右键以打开GUI进行设置", - "gtceu.machine.off": "关闭", - "gtceu.machine.on": "打开", - "gtceu.machine.parallel_hatch_mk10.tooltip": "允许同时处理至多4096个配方。", - "gtceu.machine.parallel_hatch_mk11.tooltip": "允许同时处理至多16384个配方。", - "gtceu.machine.parallel_hatch_mk12.tooltip": "允许同时处理至多65536个配方。", - "gtceu.machine.parallel_hatch_mk13.tooltip": "允许同时处理至多262144个配方。", - "gtceu.machine.parallel_hatch_mk14.tooltip": "允许同时处理至多1048576个配方。", - "gtceu.machine.parallel_hatch_mk15.tooltip": "允许同时处理至多4194304个配方。", - "gtceu.machine.parallel_hatch_mk16.tooltip": "允许同时处理至多16777216个配方。", - "gtceu.machine.parallel_hatch_mk9.tooltip": "允许同时处理至多1024个配方。", - "gtceu.machine.pcb_factory.tooltip.0": "放入纳米蜂群可获得减免", - "gtceu.machine.pcb_factory.tooltip.1": "时间倍数/每个:金:0.5%,振金:1%", - "gtceu.machine.pcb_factory.tooltip.2": "振金还可使能耗降低4倍", - "gtceu.machine.primitive_magic_energy.tooltip.0": "无尽地吸收机器上方末地水晶的能量", - "gtceu.machine.processing_plant.tooltip.0": "每种模式都需要放入一个对应电压等级的机器才能运行", - "gtceu.machine.processing_plant.tooltip.1": "电压等级每高出LV一级,最大并行数+4", - "gtceu.machine.resource_collection.tooltip.0": "最大并行数:4^(动力模块等级-1)", - "gtceu.machine.slaughterhouse.is_spawn": "实体生成:", - "gtceu.machine.slaughterhouse.tooltip.0": "电动刷怪塔,自动杀怪", - "gtceu.machine.slaughterhouse.tooltip.1": "电压等级每高出MV1级,每次处理次数+8", - "gtceu.machine.slaughterhouse.tooltip.2": "运行前需设置电路,1号电路为非敌对生物,2号为敌对生物", - "gtceu.machine.slaughterhouse.tooltip.3": "打开控制器开关实体模式,实体生成模式为玩家击杀的实际掉落,需要非和平模式", - "gtceu.machine.slaughterhouse.tooltip.4": "实体生成模式为玩家击杀的实际掉落,需要非和平模式", - "gtceu.machine.slaughterhouse.tooltip.5": "非实体生成模式为虚拟掉落,可以和平模式,由玩家击杀的掉落物无法获取", - "gtceu.machine.space_elevator.set_out": "启程", - "gtceu.machine.space_elevator.tooltip.0": "可安装最多8个拓展模块", - "gtceu.machine.space_elevator.tooltip.1": "提升电压等级可为模块提供耗时减免", - "gtceu.machine.space_elevator.tooltip.2": "更高的电压需要更多的算力来维持", - "gtceu.machine.space_probe_surface_reception.tooltip.0": "不要遮挡", - "gtceu.machine.star_ultimate_material_forge_factory.tooltip.0": "最大并行数:1000", - "gtceu.machine.super_computation.tooltip.0": "根据不同的电压等级获得算力输出", - "gtceu.machine.super_computation.tooltip.1": "且每种算力输出需要不同的电路主机8个", - "gtceu.machine.super_computation.tooltip.2": "提供§2UIV§r级电压时,需要放入§b光学处理器主机§r,并提供1024CWU/t", - "gtceu.machine.super_computation.tooltip.3": "提供§eUXV§r级电压时,需要放入§b奇异处理器主机§r,并提供2048CWU/t", - "gtceu.machine.super_computation.tooltip.4": "提供§9§lOpV§r级电压时,需要放入§b寰宇处理器主机§r,并提供4096CWU/t", - "gtceu.machine.super_computation.tooltip.5": "提供§4§lMAX§r级电压时,需要放入§b超因果处理器主机§r,并提供8192CWU/t", - "gtceu.machine.suprachronal_assembly_line.tooltip.0": "§8§l不可视之触§r", - "gtceu.machine.suprachronal_assembly_line.tooltip.1": "可在两侧拓展模块,模块与主机共享并行数", - "gtceu.machine.suprachronal_assembly_line_module.tooltip.0": "安装在超时空装配线两侧", - "gtceu.machine.vacuum_drying_furnace.tooltip.0": "运行真空干燥配方时:", - "gtceu.machine.vacuum_drying_furnace.tooltip.1": "运行脱水配方时:", - "gtceu.machine.weather_control.tooltip.0": "1号电路切换晴天", - "gtceu.machine.weather_control.tooltip.1": "2号电路切换雨天", - "gtceu.machine.weather_control.tooltip.2": "3号电路切换雷暴", - "gtceu.machine.zpm_fluid_drilling_rig.tooltip": "甚至无消耗", - "gtceu.magic_manufacturer": "魔力生成", - "gtceu.mass_fabricator": "质量发生器", - "gtceu.matter_fabricator": "物质生成机", - "gtceu.miner_module": "太空采矿", - "gtceu.multiblock.coil_parallel": "线圈温度每高出900K,并行数x2", - "gtceu.multiblock.dissolving_tank.tooltip.0": "必须保证输入的流体与配方流体比例相同,否则无产物输出", - "gtceu.multiblock.large_combustion_engine.Joint_boosted": "§b联合促燃中", - "gtceu.multiblock.large_combustion_engine.supply_dinitrogen_tetroxide_to_boost": "提供四氧化二氮来联合促燃", - "gtceu.multiblock.laser.tooltip": "允许使用激光仓", - "gtceu.multiblock.mega_fluid_heater": "宿舍限电1500W,你们这是违规电器!", - "gtceu.multiblock.oc_amount": "超频次数:%s", - "gtceu.multiblock.pattern.error.tier": "§c必须使用同种等级方块§r", - "gtceu.multiblock.steam_parallel_machine.modification_oc": "修改超频次数:", - "gtceu.multiblock.steam_parallel_machine.oc": "每次超频将减少2倍耗时和增加3倍蒸汽消耗", - "gtceu.multiblock.uev_fusion_reactor.description": "核聚变反应堆MK-V是台大型多方块结构,用于融合元素形成更重的元素。它仅可使用UEV等级的能源仓。每个能源仓可增加160MEU的能量缓存,最大能量缓存为2560MEU。", - "gtceu.multiblock.uhv_fusion_reactor.description": "核聚变反应堆MK-IV是台大型多方块结构,用于融合元素形成更重的元素。它仅可使用UHV等级的能源仓。每个能源仓可增加80MEU的能量缓存,最大能量缓存为1280MEU。", - "gtceu.multiblock.fusion_reactor_energy": "EU: %dM / %dM", - "gtceu.nano_forge": "纳米蜂群工厂", - "gtceu.naquadah_reactor": "硅岩反应堆", - "gtceu.neutron_activator": "中子活化", - "gtceu.neutron_compressor": "奇点压缩", - "gtceu.pcb_factory": "PCB工厂", - "gtceu.petrochemical_plant": "石化工厂", - "gtceu.plasma_condenser": "等离子冷凝", - "gtceu.precision_assembler": "精密组装", - "gtceu.precision_laser_engraver": "精密激光蚀刻", - "gtceu.primitive_void_ore": "原始虚空采矿", - "gtceu.qft": "量子操纵者", - "gtceu.random_ore": "随机矿石模式", - "gtceu.rare_earth_centrifugal": "稀土离心", - "gtceu.recipe.ca_tier": "外壳等级:%s", - "gtceu.recipe.cleanroom_law.display_name": "绝对超净间", - "gtceu.recipe.ev_max": "最大中子动能:%s MeV", - "gtceu.recipe.ev_min": "最小中子动能:%s MeV", - "gtceu.recipe.evt": "每刻中子动能消耗:%s KeV", - "gtceu.recipe.frheat": "每秒升温:%s K", - "gtceu.recipe.grindball": "研磨球材质:%s", - "gtceu.recipe.nano_forge_tier": "纳米锻炉等级:%s", - "gtceu.recipe.sepm_tier": "需要动力模块:MK%s", - "gtceu.recipe.stellar_containment_tier": "恒星热力容器等级:%s", - "gtceu.rocket_engine": "火箭引擎", - "gtceu.semi_fluid_generator": "半流质发电机", - "gtceu.slaughterhouse": "屠宰场", - "gtceu.small_gas_collector": "小型集气室", - "gtceu.space_elevator": "太空电梯", - "gtceu.space_probe_surface_reception": "宇宙探测", - "gtceu.sps_crafting": "超临界合成", - "gtceu.stellar_forge": "恒星热能熔炼", - "gtceu.super_computation": "超级计算机", - "gtceu.super_particle_collider": "粒子对撞", - "gtceu.supercritical_steam_turbine": "超临界蒸汽涡轮", - "gtceu.suprachronal_assembly_line": "超时空装配线", - "gtceu.tier.advanced": "高级", - "gtceu.tier.base": "基础", - "gtceu.tier.ultimate": "终极", - "gtceu.ultimate_material_forge": "终极物质锻造", - "gtceu.universal.tooltip.ampere_out": "§b输出电流:§r%sA", - "gtceu.vacuum_drying": "真空干燥", - "gtceu.void_fluid_drilling_rig": "虚空流体钻机", - "gtceu.void_miner": "虚空采矿", - "gtceu.weather_control": "天气控制", - "gtceu.wood_distillation": "木化工厂", - "gtceu.world_data_scanner": "世界信息扫描", - "gui.gtceu.neutron_sensor.invert.disabled.0": "红石输出:普通", - "gui.gtceu.neutron_sensor.invert.disabled.1": "点击为以反转红石逻辑", - "gui.gtceu.neutron_sensor.invert.disabled.2": "中子动能介于所设定的最小值和最大值之间时传感器将发出红石信号,小于最小值时则停止发出红石信号", - "gui.gtceu.neutron_sensor.invert.enabled.0": "红石输出:反转", - "gui.gtceu.neutron_sensor.invert.enabled.1": "点击切换为普通红石逻辑", - "gui.gtceu.neutron_sensor.invert.enabled.2": "中子动能介于所设定的最小值和最大值之外时传感器将发出红石信号,小于最小值时则发出红石信号", - "item.gtceu.tool.vajra": "%s金刚杵", - "material.gtceu.1_octene": "1-辛烯", - "material.gtceu.absolute_ethanol": "绝对乙醇", - "material.gtceu.abyssalalloy": "渊狱合金", - "material.gtceu.acetaldehyde": "乙醛", - "material.gtceu.acetonitrile": "乙腈", - "material.gtceu.acetyl_chloride": "乙酰氯", - "material.gtceu.acetylating_reagent": "乙酰化试剂", - "material.gtceu.acetylene": "乙炔", - "material.gtceu.acid_leached_fluoro_carbon_lanthanide_cerium_oxide_powder": "酸浸氟碳镧铈稀土氧化物", - "material.gtceu.acidic_iridium": "酸性铱", - "material.gtceu.acidic_monazite_powder": "酸性独居石", - "material.gtceu.acidic_naquadria_caesiumfluoride": "硫酸二氟超能硅岩酸铯", - "material.gtceu.acrylic_acid": "丙烯酸", - "material.gtceu.acrylonitrile": "丙烯腈", - "material.gtceu.actinium_nitrate": "硝酸锕", - "material.gtceu.actinium_oxalate": "草酸锕", - "material.gtceu.actinium_radium_hydroxide_solution": "氢氧化锕镭溶液", - "material.gtceu.actinium_radium_nitrate_solution": "硝酸锕镭溶液", - "material.gtceu.actinium_superhydride": "超氢化锕", - "material.gtceu.actinium_trinium_hydroxides": "氢氧化锕凯金", - "material.gtceu.actinoids": "锕系元素混合物", - "material.gtceu.actinoids_1": "轻锕系元素混合物", - "material.gtceu.actinoids_2": "重锕系元素混合物", - "material.gtceu.adamantine": "精金", - "material.gtceu.adamantine_compounds": "精金化合物", - "material.gtceu.adamantium": "艾德曼合金", - "material.gtceu.alien_algae": "异星藻类渣", - "material.gtceu.alkaline": "碱金属元素混合物", - "material.gtceu.alkaline_earth": "碱土金属元素混合物", - "material.gtceu.almandine_front": "铁铝榴石矿石泡沫", - "material.gtceu.alumina": "氧化铝", - "material.gtceu.aluminium_bronze": "铝青铜", - "material.gtceu.aluminium_hydride": "氢化铝", - "material.gtceu.aluminium_trifluoride": "氟化铝", - "material.gtceu.aminated_fullerene": "胺化富勒烯", - "material.gtceu.ammonium_bifluoride": "二氟化铵", - "material.gtceu.ammonium_bifluoride_solution": "氟氢化氨", - "material.gtceu.ammonium_fluoride": "氟化铵", - "material.gtceu.ammonium_nitrate_solution": "硝酸铵溶液", - "material.gtceu.ammonium_perrhenate": "高铼酸铵", - "material.gtceu.aniline": "苯胺", - "material.gtceu.anthracene": "蒽", - "material.gtceu.antihydrogen": "反氢", - "material.gtceu.antimatter": "离散反物质", - "material.gtceu.antimony_pentafluoride": "五氟化锑", - "material.gtceu.antimony_trichloride": "三氯化锑", - "material.gtceu.antineutron": "反中子", - "material.gtceu.antiproton": "反质子", - "material.gtceu.arceusalloy2b": "阿尔宙斯合金2B", - "material.gtceu.artherium_sn": "阿瑟锡", - "material.gtceu.ash_leaching_solution": "灰烬浸出液", - "material.gtceu.astatide_solution": "砹化物溶液", - "material.gtceu.astral_silver": "星辰银", - "material.gtceu.astraltitanium": "星体钛", - "material.gtceu.atinium_hydride": "氢化锕", - "material.gtceu.attuned_tengam": "谐镃", - "material.gtceu.azafullerene": "氮杂富勒烯", - "material.gtceu.barium_chloride": "氯化钡", - "material.gtceu.barnarda_air": "巴纳德C空气", - "material.gtceu.bedrock": "基岩", - "material.gtceu.bedrock_gas": "基岩气", - "material.gtceu.bedrock_smoke": "基岩烟", - "material.gtceu.bedrock_soot_solution": "基岩烟溶液", - "material.gtceu.benzaldehyde": "苯甲醛", - "material.gtceu.benzenediazonium_tetrafluoroborate": "四氟硼酸重氮苯", - "material.gtceu.benzophenanthrenylacetonitrile": "苯并菲乙腈", - "material.gtceu.benzyl_chloride": "氯化苄", - "material.gtceu.benzylamine": "苄胺", - "material.gtceu.biohmediumsterilized": "灭菌生物培养基原液", - "material.gtceu.biomediumraw": "生物培养基原液", - "material.gtceu.bisethylenedithiotetraselenafulvalene": "双(乙烯二硫代)四硒富瓦烯", - "material.gtceu.bisethylenedithiotetraselenafulvalene_perrhenate": "高铼酸双(乙烯二硫代)四硒富瓦烯", - "material.gtceu.bismuth_germanate": "锗酸铋", - "material.gtceu.bismuth_nitrate_solution": "硝酸铋溶液", - "material.gtceu.bismuth_tellurite": "亚碲酸铋", - "material.gtceu.black_dwarf_mtter": "黑矮星物质", - "material.gtceu.black_titanium": "黑钛合金", - "material.gtceu.bloodstone": "血石", - "material.gtceu.borane_dimethyl_sulfide": "硼烷二甲基硫醚", - "material.gtceu.boric_acide": "硼酸", - "material.gtceu.borocarbide": "碳化硼混合材料", - "material.gtceu.boron_carbide": "碳化硼", - "material.gtceu.boron_fluoride": "氟化硼", - "material.gtceu.boron_francium_carbide": "硼-钫碳化物", - "material.gtceu.boron_trifluoride_acetate": "三氟化硼乙酸酯", - "material.gtceu.boron_trioxide": "氧化硼", - "material.gtceu.brominated_brine": "含溴盐水", - "material.gtceu.bromine_trifluoride": "三氟化溴", - "material.gtceu.bromo_succinimide": "N-溴代琥珀酰亚胺", - "material.gtceu.bromobutane": "溴丁烷", - "material.gtceu.bromodihydrothiine": "溴二氢硫醚", - "material.gtceu.butane_1_4_diol": "1,4-丁二醇", - "material.gtceu.butyl_lithium": "丁基锂", - "material.gtceu.cadmium_sulfide": "硫化镉", - "material.gtceu.cadmium_tungstate": "钨酸镉", - "material.gtceu.caesium_fluoride": "氟化铯", - "material.gtceu.caesium_hydroxide": "氢氧化铯", - "material.gtceu.caesium_iodide": "碘化铯", - "material.gtceu.caesium_nitrate": "硝酸铯", - "material.gtceu.caesium_xenontrioxide_fluoride": "二氟三氧氙酸铯", - "material.gtceu.calcined_rare_earth_oxide_powder": "焙烧稀土氧化物", - "material.gtceu.calcium_carbide": "电石", - "material.gtceu.calcium_fluoride": "氟化钙", - "material.gtceu.californium_cyclopentadienide": "环戊二烯化锎", - "material.gtceu.californium_trichloride": "三氯化锎", - "material.gtceu.calorite": "耐热金属", - "material.gtceu.carbon_disulfide": "二硫化碳", - "material.gtceu.carbon_nanotubes": "碳纳米管", - "material.gtceu.carbon_tetrachloride": "四氯化碳", - "material.gtceu.celestialtungsten": "天体钨", - "material.gtceu.celestine": "天青石", - "material.gtceu.cerium_chloride_powder": "氯化铈", - "material.gtceu.cerium_oxalate_powder": "草酸铈", - "material.gtceu.cerium_oxide": "氧化铈", - "material.gtceu.cerium_oxide_rare_earth_oxide_powder": "氧化铈稀土氧化物", - "material.gtceu.cerium_rich_mixture_powder": "富铈混合物", - "material.gtceu.cesium_carborane": "碳酸铯", - "material.gtceu.cesium_carborane_precursor": "铯烷预固化剂", - "material.gtceu.chalcopyrite_front": "黄铜矿矿石泡沫", - "material.gtceu.chaos": "混沌物质", - "material.gtceu.charged_caesium_cerium_cobalt_indium": "带电的铯-铈-钴-铟", - "material.gtceu.cinobite": "西诺柏", - "material.gtceu.circuit_compound": "电路化合物", - "material.gtceu.clean_bedrock_solution": "洁净基岩烟溶液", - "material.gtceu.clean_inert_residues": "纯净的惰性残渣", - "material.gtceu.clean_raw_tengam": "洁净生镃", - "material.gtceu.co_ac_ab_catalyst": "Co/AC-AB催化剂", - "material.gtceu.compressed_stone": "压缩石头", - "material.gtceu.concentrated_cerium_chloride_solution": "氯化铈浓缩液", - "material.gtceu.concentrated_monazite_rare_earth_hydroxide_powder": "浓缩独居石稀土氢氧化物", - "material.gtceu.concentrated_nitric_leachate_from_monazite": "浓缩硝酸独居石浸出溶液", - "material.gtceu.concentrated_nitride_monazite_rare_earth_solution": "浓缩氮化独居石稀土溶液", - "material.gtceu.concentrated_rare_earth_chloride_solution": "氯化稀土浓缩液", - "material.gtceu.concentration_mixing_hyper_fuel_1": "浓缩混合超能燃料 I", - "material.gtceu.concentration_mixing_hyper_fuel_2": "浓缩混合超能燃料 II", - "material.gtceu.conductive_alloy": "导电铁", - "material.gtceu.cooling_concentrated_nitric_monazite_rare_earth_powder": "冷却浓缩硝酸独居石稀土", - "material.gtceu.copernicium": "鿔", - "material.gtceu.copper76": "铜-76", - "material.gtceu.cosmic": "宇宙", - "material.gtceu.cosmic_computing_mixture": "寰宇计算混合物", - "material.gtceu.cosmic_element": "宇宙素", - "material.gtceu.cosmic_mesh": "寰宇织网", - "material.gtceu.cosmic_superconductor": "寰宇超导液", - "material.gtceu.cosmicneutronium": "宇宙中子素", - "material.gtceu.crackedradox": "裂化拉多X", - "material.gtceu.crude_hexanitrohexaaxaisowurtzitane": "粗制六硝基六氮杂异伍兹烷", - "material.gtceu.crystalline_nitric_acid": "结晶硝酸", - "material.gtceu.crystalmatrix": "水晶矩阵", - "material.gtceu.cubic_zirconia": "立方氧化锆", - "material.gtceu.cyclooctadiene": "环辛二烯", - "material.gtceu.cycloparaphenylene": "环对苯撑", - "material.gtceu.cyclopentadiene": "环戊二烯", - "material.gtceu.cyclopentadienyl_titanium_trichloride": "环戊二烯基三氯化钛", - "material.gtceu.dalisenite": "大力合金", - "material.gtceu.decaborane": "癸硼烷", - "material.gtceu.degenerate_rhenium": "简并态铼", - "material.gtceu.deglycerated_soap": "脱糖肥皂", - "material.gtceu.dense_hydrazine_fuel_mixture": "浓缩肼混合燃料", - "material.gtceu.dense_hydrazine_mixed_fuel": "浓缩肼混合燃料", - "material.gtceu.dense_neutron": "致密中子素", - "material.gtceu.desh": "戴斯", - "material.gtceu.diamagnetic_residues": "抗磁性残渣", - "material.gtceu.diaminodiphenylmethanmixture": "二氨基二苯甲烷混合物", - "material.gtceu.dibenzyltetraacetylhexaazaisowurtzitane": "二苄基四乙酰六氮杂异纤锌烷", - "material.gtceu.dibismuthhydroborat": "硼氢二铋", - "material.gtceu.diborane": "乙硼烷", - "material.gtceu.dibromoacrolein": "二溴丙烯醛", - "material.gtceu.dibromomethylbenzene": "二溴甲苯", - "material.gtceu.dichlorocyclooctadieneplatinium": "二氯环辛二烯铂", - "material.gtceu.dichlorodicyanobenzoquinone": "二氯二氰苯醌", - "material.gtceu.dichlorodicyanohydroquinone": "二氯二氰氢醌", - "material.gtceu.dichloromethane": "二氯甲烷", - "material.gtceu.diethyl_ether": "二乙醚", - "material.gtceu.diethylthiourea": "二乙基硫脲", - "material.gtceu.difluoroaniline": "二氟苯胺", - "material.gtceu.difluorobenzophenone": "二氟二苯甲酮", - "material.gtceu.dihydroiodotetracene": "二氢碘化四联苯", - "material.gtceu.diiodobiphenyl": "二碘代联苯", - "material.gtceu.dilute_hexafluorosilicic_acid": "稀六氟硅酸", - "material.gtceu.dilute_hydrofluoric_acid": "稀释氢氟酸", - "material.gtceu.diluted_fluoro_carbon_lanthanide_slurry": "稀释氟碳镧铈泥浆", - "material.gtceu.diluted_monazite_slurry": "稀释独居石稀土泥浆", - "material.gtceu.diluted_monazite_sulfate_solution": "稀释硫酸独居石溶液", - "material.gtceu.diluted_rare_earth_chloride_solution": "氯化稀土稀释液", - "material.gtceu.dilutedxenoxene": "钝化异氙", - "material.gtceu.dimensionallytranscendentcrudecatalyst": "粗制超维度催化剂", - "material.gtceu.dimensionallytranscendentexoticcatalyst": "异星超维度催化剂", - "material.gtceu.dimensionallytranscendentprosaiccatalyst": "平凡超维度催化剂", - "material.gtceu.dimensionallytranscendentresidue": "超维度残留", - "material.gtceu.dimensionallytranscendentresplendentcatalyst": "光辉超维度催化剂", - "material.gtceu.dimensionallytranscendentstellarcatalyst": "恒星超维度催化剂", - "material.gtceu.dimethoxyethane": "二甲氧基乙烷", - "material.gtceu.dimethyl_sulfide": "二甲硫醚", - "material.gtceu.dimethylether": "二甲基乙醚", - "material.gtceu.dimethylnaphthalene": "二甲基萘", - "material.gtceu.dimethylterephthalate": "对苯二甲酸二甲酯", - "material.gtceu.dinitrodipropanyloxybenzene": "二硝基二丙氧基苯", - "material.gtceu.dioxygen_difluoride": "二氟化二氧", - "material.gtceu.diphenylmethane_diisocyanate": "4,4'-二苯基甲烷二异氰酸酯", - "material.gtceu.diphenylmethanediisocyanatemixture": "二苯基甲烷二异氰酸酯混合物", - "material.gtceu.dirty_hexafluorosilicic_acid": "污浊的六氟硅酸", - "material.gtceu.ditertbutyl_dicarbonate": "二碳酸二叔丁酯", - "material.gtceu.dmap": "二甲氨基吡啶", - "material.gtceu.draconium": "龙", - "material.gtceu.draconiumawakened": "觉醒龙", - "material.gtceu.dragon_blood": "龙血", - "material.gtceu.dragon_breath": "龙息", - "material.gtceu.dragon_element": "龙素", - "material.gtceu.dried_concentrated_nitric_monazite_rare_earth_powder": "干燥浓缩硝酸独居石稀土", - "material.gtceu.dry_graphene_gel": "干石墨烯凝胶", - "material.gtceu.durene": "杜烯", - "material.gtceu.dusty_liquid_helium_iii": "污浊的液氦-3", - "material.gtceu.dysprosium_oxide": "氧化镝", - "material.gtceu.earth_crystal": "地之魔晶", - "material.gtceu.echoite": "回响合金", - "material.gtceu.eglin_steel": "埃格林钢", - "material.gtceu.enderite": "末影合金", - "material.gtceu.enderium": "末影", - "material.gtceu.energetic_alloy": "充能合金", - "material.gtceu.enriched_dragon_breath": "富集龙息", - "material.gtceu.enriched_naquadah_front": "富集硅岩矿石泡沫", - "material.gtceu.enriched_naquadah_fuel": "富集硅岩燃料", - "material.gtceu.enriched_potassium_iodide_slurry": "富集碘化钾浆液", - "material.gtceu.enriched_rare_earth_chloride_solution": "氯化稀土富集液", - "material.gtceu.enriched_xenoxene": "富集异氙", - "material.gtceu.er_lu_oxides_solution": "铒-镥氧化物溶液", - "material.gtceu.erbium_oxide": "氧化铒", - "material.gtceu.eternity": "永恒", - "material.gtceu.ethanolamine": "乙醇胺", - "material.gtceu.ethyl_acrylate": "丙烯酸乙酯", - "material.gtceu.ethyl_trifluoroacetate": "三氟乙酸乙酯", - "material.gtceu.ethylamine": "乙胺", - "material.gtceu.ethylanthrahydroquinone": "2-乙基蒽氢醌", - "material.gtceu.ethylanthraquinone": "2-乙基蒽醌", - "material.gtceu.ethylene_oxide": "环氧乙烷", - "material.gtceu.ethylene_sulfide": "乙硫酮", - "material.gtceu.ethylenediamine": "乙二胺", - "material.gtceu.ethyleneglycol": "乙二醇", - "material.gtceu.europium_oxide": "氧化铕", - "material.gtceu.euv_photoresist": "EUV光刻胶", - "material.gtceu.exciteddtec": "激发的异星超维度催化剂", - "material.gtceu.exciteddtsc": "激发的恒星超维度催化剂", - "material.gtceu.exotic_heavy_residues": "重奇异残渣", - "material.gtceu.explosivehydrazine": "爆炸性肼燃料混合物", - "material.gtceu.fall_king": "耐摔合金", - "material.gtceu.ferric_ree_chloride": "含稀土氯化铁", - "material.gtceu.ferrocene": "二茂铁", - "material.gtceu.ferromagnetic_residues": "铁磁性残渣", - "material.gtceu.filtered_fluoro_carbon_lanthanide_slurry": "过滤氟碳镧铈泥浆", - "material.gtceu.fissioned_uranium_235": "裂变铀-235", - "material.gtceu.fluorinated_samarium_concentrate_powder": "氟化钐精", - "material.gtceu.fluorine_cracked_aquadah": "加氟裂化硅岩", - "material.gtceu.fluorite": "氟石", - "material.gtceu.fluoro_benzene": "氟苯", - "material.gtceu.fluoro_carbon_lanthanide_cerium_oxide_powder": "氟碳镧铈稀土氧化物", - "material.gtceu.fluoro_carbon_lanthanide_cerium_rare_earth_oxide_powder": "氟碳镧铈罕土氧化物", - "material.gtceu.fluoro_carbon_lanthanide_cerium_rare_earth_suspension": "氟碳镧铈罕土氧化物悬浊液", - "material.gtceu.fluoro_carbon_lanthanide_cerium_solution": "氟碳镧铈稀土浊溶液", - "material.gtceu.fluoroboric_acide": "氟硼酸", - "material.gtceu.fluorocarborane": "碳氟化合物", - "material.gtceu.fluorosilicic_acid": "六氟硅酸", - "material.gtceu.fluorotoluene": "氟甲苯", - "material.gtceu.fluxed_electrum": "通流琥珀金", - "material.gtceu.flyb": "𫓧-镱", - "material.gtceu.force": "力量", - "material.gtceu.francium_caesium_cadmium_bromide": "溴化钫铯镉", - "material.gtceu.francium_carbide": "碳化钫", - "material.gtceu.free_alpha_gas": "高密度自由α粒子气", - "material.gtceu.free_electron_gas": "高密度自由电子气", - "material.gtceu.free_proton_gas": "高密度自由质子气", - "material.gtceu.fullerene": "富勒烯", - "material.gtceu.fullerene_doped_nanotubes": "富勒烯掺杂的纳米管", - "material.gtceu.fullerene_polymer_matrix_pulp": "富勒烯聚合物基体", - "material.gtceu.fuming_nitric_acid": "发烟硝酸", - "material.gtceu.gadolinium_oxide": "氧化钆", - "material.gtceu.gamma_rays_photoresist": "γ射线光刻胶", - "material.gtceu.gammabutyrolactone": "1,4-丁内酯", - "material.gtceu.germanium_ash": "锗灰", - "material.gtceu.germanium_containing_precipitate": "含锗沉淀物", - "material.gtceu.germanium_dioxide": "二氧化锗", - "material.gtceu.germanium_tetrachloride_solution": "四氯化锗", - "material.gtceu.germaniumtungstennitride": "锗-钨氮化物", - "material.gtceu.glacio_spirit": "霜原碎片", - "material.gtceu.glucose": "葡萄糖", - "material.gtceu.glucose_iron_solution": "葡萄糖铁溶液", - "material.gtceu.gluons": "胶子", - "material.gtceu.glyoxal": "乙二醛", - "material.gtceu.gold_chloride": "氯化金", - "material.gtceu.gold_cyanide": "氰化金", - "material.gtceu.gold_depleted_molybdenite": "贫金辉钼矿", - "material.gtceu.gold_trifluoride": "三氟化金", - "material.gtceu.grade_10_purified_water": "10级净化水", - "material.gtceu.grade_11_purified_water": "11级净化水", - "material.gtceu.grade_12_purified_water": "12级净化水", - "material.gtceu.grade_13_purified_water": "13级净化水", - "material.gtceu.grade_14_purified_water": "14级净化水", - "material.gtceu.grade_15_purified_water": "15级净化水", - "material.gtceu.grade_16_purified_water": "16级净化水", - "material.gtceu.grade_1_purified_water": "1级净化水", - "material.gtceu.grade_2_purified_water": "2级净化水", - "material.gtceu.grade_3_purified_water": "3级净化水", - "material.gtceu.grade_4_purified_water": "4级净化水", - "material.gtceu.grade_5_purified_water": "5级净化水", - "material.gtceu.grade_6_purified_water": "6级净化水", - "material.gtceu.grade_7_purified_water": "7级净化水", - "material.gtceu.grade_8_purified_water": "8级净化水", - "material.gtceu.grade_9_purified_water": "9级净化水", - "material.gtceu.graphene_gel_suspension": "石墨烯浆料", - "material.gtceu.graphene_oxide": "氧化石墨烯", - "material.gtceu.grisium": "灰钛合金", - "material.gtceu.grossular_front": "钙铝榴石矿石泡沫", - "material.gtceu.hafnium_chloride": "四氯化铪", - "material.gtceu.hafnium_oxide": "二氧化铪", - "material.gtceu.hassium_chloride": "氯化𬭶", - "material.gtceu.hastelloy_n": "哈斯特洛依合金-N", - "material.gtceu.hastelloy_n_75": "哈斯特洛依合金-N75", - "material.gtceu.hastelloyk_243": "哈斯特洛依合金-K243", - "material.gtceu.hastelloyx_78": "哈斯特洛依合金-X78", - "material.gtceu.heavily_fluorinated_trinium_solution": "重氟化凯金化合物溶液", - "material.gtceu.heavy_diamagnetic_residues": "重磁残渣", - "material.gtceu.heavy_ferromagnetic_residues": "重铁磁性残渣", - "material.gtceu.heavy_lepton_mixture": "重轻子混合物", - "material.gtceu.heavy_metallic_residues": "重金属残渣", - "material.gtceu.heavy_oxidized_residues": "重氧化残渣", - "material.gtceu.heavy_paramagnetic_residues": "重顺磁残渣", - "material.gtceu.heavy_quark_degenerate_matter": "重夸克简并物质", - "material.gtceu.heavy_quark_enriched_mixture": "富集重夸克混合物", - "material.gtceu.heavy_quarks": "重夸克", - "material.gtceu.heavyradox": "重拉多X", - "material.gtceu.helium_iii_hydride": "氢化氦-3", - "material.gtceu.heterogeneous_halide_monazite_rare_earth_mixture_powder": "异质卤化独居石稀土混合物", - "material.gtceu.hexabenzylhexaazaisowurtzitane": "六苄基六氮杂异伍兹烷", - "material.gtceu.hexafluoride_enriched_naquadah_solution": "六氟化富集硅岩溶液", - "material.gtceu.hexafluoride_naquadria_solution": "六氟化超能硅岩溶液", - "material.gtceu.hexafluorophosphoric_acid": "单氟磷酸", - "material.gtceu.hexamethylenetetramine": "环六亚甲基四胺", - "material.gtceu.hexanitrohexaaxaisowurtzitane": "六硝基六氮杂异伍兹烷", - "material.gtceu.high_energy_quark_gluon": "高能夸克-胶子", - "material.gtceu.high_purity_calcium_carbonate": "高纯度碳酸钙", - "material.gtceu.highenergymixture": "高能混合物", - "material.gtceu.highurabilityompoundteel": "高强度复合钢", - "material.gtceu.hikarium": "光素", - "material.gtceu.hmxexplosive": "HMX高能爆炸性化合物", - "material.gtceu.holmium_oxide": "氧化钬", - "material.gtceu.hot_oganesson": "热鿫", - "material.gtceu.hot_sodium_potassium": "热钠钾合金", - "material.gtceu.hydrazine": "肼", - "material.gtceu.hydrobromic_acid": "氢溴酸", - "material.gtceu.hydrogen_peroxide": "过氧化氢", - "material.gtceu.hydroiodic_acid": "氢碘酸", - "material.gtceu.hydroquinone": "对苯二酚", - "material.gtceu.hydroxylamine_hydrochloride": "盐酸羟胺", - "material.gtceu.hydroxylammonium_sulfate": "羟铵硫酸盐", - "material.gtceu.hyper_fuel_1": "超能燃料 I", - "material.gtceu.hyper_fuel_2": "超能燃料 II", - "material.gtceu.hyper_fuel_3": "超能燃料 III", - "material.gtceu.hyper_fuel_4": "超能燃料 IV", - "material.gtceu.hypogen": "海珀珍", - "material.gtceu.ignis_crystal": "火之魔晶", - "material.gtceu.inconel_625": "镍铬基合金-625", - "material.gtceu.inconel_792": "镍铬基合金-792", - "material.gtceu.indalloy_140": "铋铅合金-140", - "material.gtceu.inert_residues": "纯净残渣", - "material.gtceu.infinity": "无尽", - "material.gtceu.infuscolium": "魔金", - "material.gtceu.infused_gold": "注魔金", - "material.gtceu.iodine_containing_slurry": "含碘溶液", - "material.gtceu.iodine_monochloride": "氯化碘", - "material.gtceu.iridium_dioxide": "二氧化铱", - "material.gtceu.iridium_trichloride_solution": "三氯化铱溶液", - "material.gtceu.isochloropropane": "氯丙烷", - "material.gtceu.isophthaloylbis": "间苯二甲酰基二乙基硫脲", - "material.gtceu.isopropyl_alcohol": "异丙醇", - "material.gtceu.jasper": "碧玉", - "material.gtceu.kelp_slurry": "海带浆液", - "material.gtceu.kerosene": "煤油", - "material.gtceu.kevlar": "凯芙拉", - "material.gtceu.krypton_difluoride": "二氟化氪", - "material.gtceu.la_nd_oxides_solution": "镧-钕氧化物溶液", - "material.gtceu.lafium": "路菲恩", - "material.gtceu.lanthanoids_1": "轻镧系元素混合物", - "material.gtceu.lanthanoids_2": "重镧系元素混合物", - "material.gtceu.lanthanum_chloride": "氯化镧", - "material.gtceu.lanthanum_chloride_with_impurities": "含杂氯化镧", - "material.gtceu.lanthanum_embedded_fullerene": "镧-富勒烯包合物", - "material.gtceu.lanthanum_fullerene_mix": "镧-富勒烯混合物", - "material.gtceu.lanthanum_oxide": "氧化镧", - "material.gtceu.leach_residue": "铂浸出渣", - "material.gtceu.leached_turpentine": "松油浸出物", - "material.gtceu.legendarium": "传奇合金", - "material.gtceu.light_quarks": "轻夸克", - "material.gtceu.lightradox": "轻拉多X", - "material.gtceu.liquid_hydrogen": "液态氢", - "material.gtceu.liquid_starlight": "星能液", - "material.gtceu.liquidcrystalkevlar": "液晶凯芙拉", - "material.gtceu.lithium_aluminium_fluoride": "铝-锂氟化物", - "material.gtceu.lithium_aluminium_hydride": "氢化铝锂", - "material.gtceu.lithium_cyclopentadienide": "环戊二烯化锂", - "material.gtceu.lithium_fluoride": "氟化锂", - "material.gtceu.lithium_iodide": "碘化锂", - "material.gtceu.lithium_niobate_nanoparticles": "铌酸锂纳米粒子", - "material.gtceu.lithiumthiinediselenide": "二硒醚合硫锂", - "material.gtceu.lumiium": "流明", - "material.gtceu.luminessence": "流明精华", - "material.gtceu.lutetium_oxide": "氧化镥", - "material.gtceu.magmatter": "磁物质", - "material.gtceu.magnesium_chloride_bromide": "溴氯化镁", - "material.gtceu.magneto_resonatic": "共振紫晶", - "material.gtceu.magnetohydrodynamicallyconstrainedstarmatter": "磁流体约束恒星物质", - "material.gtceu.maleic_anhydride": "顺丁烯二酸酐", - "material.gtceu.mana": "魔力", - "material.gtceu.mar_m_200_steel": "MAR-M200特种钢", - "material.gtceu.metal_residue": "金属残渣", - "material.gtceu.metallic_residues": "金属残渣", - "material.gtceu.metalloid": "类金属元素混合物", - "material.gtceu.metastable_hassium": "亚稳态𬭶", - "material.gtceu.metastable_oganesson": "亚稳态鿫", - "material.gtceu.methylamine": "甲胺", - "material.gtceu.methylbenzophenanthrene": "甲基苯并菲", - "material.gtceu.mithril": "秘银", - "material.gtceu.mixed_astatide_salts": "混合砹化盐", - "material.gtceu.modulated_fluoro_carbon_lanthanide_slurry": "调制氟碳镧铈泥浆", - "material.gtceu.molten_calcium_salts": "熔融钙盐", - "material.gtceu.molybdenum_concentrate": "钼精", - "material.gtceu.molybdenum_flue": "钼烟气", - "material.gtceu.molybdenum_trioxide": "三氧化钼", - "material.gtceu.monazite_front": "独居石矿石泡沫", - "material.gtceu.monazite_rare_earth_filter_residue_powder": "独居石稀土滤渣", - "material.gtceu.monazite_rare_earth_precipitate_powder": "独居石罕土沉淀", - "material.gtceu.monazite_rare_earth_turbid_liquid": "独居石稀土混浊液", - "material.gtceu.monazite_sulfate_powder": "硫酸独居石", - "material.gtceu.monomethylhydrazine": "甲肼", - "material.gtceu.mutated_living_solder": "突变活性焊料", - "material.gtceu.n_difluorophenylpyrrole": "N-二氟苯基吡咯", - "material.gtceu.n_hydroxysuccinimide": "羟基丁二酰亚胺", - "material.gtceu.naquadah_contain_rare_earth": "含硅岩的稀土", - "material.gtceu.naquadah_contain_rare_earth_fluoride": "含硅岩的稀土氟化物", - "material.gtceu.naquadah_fuel": "硅岩燃料", - "material.gtceu.naquadah_solution": "硅岩溶液", - "material.gtceu.naquadria_caesium_xenonnonfluoride": "九氟氙酸超能硅岩铯", - "material.gtceu.naquadria_caesiumfluoride": "二氟超能硅岩酸铯", - "material.gtceu.naquadriatictaranium": "超能硅岩-塔兰金属合金", - "material.gtceu.neodymium_oxide": "氧化钕", - "material.gtceu.neodymium_rare_earth_concentrate_powder": "钕稀土精", - "material.gtceu.neutralised_red_mud": "中和赤泥", - "material.gtceu.neutralized_monazite_rare_earth_filter_residue_powder": "中和独居石稀土滤渣", - "material.gtceu.neutralized_uranium_filter_residue_powder": "中和铀滤渣", - "material.gtceu.neutronium_doped_nanotubes": "掺中子素纳米管", - "material.gtceu.nickel_front": "镍矿石泡沫", - "material.gtceu.nitrated_triniite_compound_solution": "硝化凯金化合物溶液", - "material.gtceu.nitric_leachate_from_monazite": "硝酸独居石浸出混合物", - "material.gtceu.nitrided_fluoro_carbon_lanthanide_cerium_rare_earth_oxide_solution": "氮化氟碳镧铈罕土氧化物", - "material.gtceu.nitrided_samarium_terbium_mixture_powder": "氮化钐-铽混合物", - "material.gtceu.nitrogen_pentoxide": "五氧化二氮", - "material.gtceu.nitronium_tetrafluoroborate": "四氟硝铵", - "material.gtceu.nitrosonium_octafluoroxenate": "八氟氙酸亚硝酰", - "material.gtceu.nitrosonium_tetrafluoroborate": "四氟硼酸亚硝铵", - "material.gtceu.nitrous_acid": "亚硝酸", - "material.gtceu.nitryl_fluoride": "硝酰氟", - "material.gtceu.nmethylpyrolidone": "N-甲基吡咯烷酮", - "material.gtceu.noble_gas": "稀有气体元素混合物", - "material.gtceu.not_found": "非金属元素混合物", - "material.gtceu.oganesson_breeding_base": "鿫增殖基", - "material.gtceu.orichalcum": "山铜", - "material.gtceu.ostrum": "紫金", - "material.gtceu.oxalic_acid": "草酸", - "material.gtceu.oxidized_residual_solution": "氧化残留溶液", - "material.gtceu.oxidized_residues": "氧化残渣", - "material.gtceu.oxydianiline": "二氨基二苯醚", - "material.gtceu.ozone": "臭氧", - "material.gtceu.p_nitroaniline": "对硝基苯胺", - "material.gtceu.p_phenylenediamine": "对苯二胺", - "material.gtceu.paa": "聚酰胺酸(PAA)", - "material.gtceu.palladium_fullerene_matrix": "钯-富勒烯基质", - "material.gtceu.paramagnetic_residues": "顺磁残渣", - "material.gtceu.partially_oxidized_residues": "待分离氧化金属残渣", - "material.gtceu.pcbs": "苯基-C61-丁酸苯乙烯", - "material.gtceu.pentaerythritol": "季戊四醇", - "material.gtceu.pentlandite_front": "镍黄铁矿矿石泡沫", - "material.gtceu.perditio_crystal": "混沌魔晶", - "material.gtceu.perfluorobenzene": "全氟苯", - "material.gtceu.periodicium": "錭錤錶", - "material.gtceu.phenylenedioxydiacetic_acid": "亚苯基二氧二乙酸", - "material.gtceu.phenylpentanoic_acid": "苯基戊酸", - "material.gtceu.phenylsodium": "苯基钠", - "material.gtceu.phosgene": "碳酰氯", - "material.gtceu.phosphorus_free_samarium_concentrate_powder": "脱磷钐精", - "material.gtceu.phosphorus_pentasulfide": "五硫化磷", - "material.gtceu.phosphorus_trichloride": "三氯化磷", - "material.gtceu.photopolymer": "光聚合物溶液", - "material.gtceu.photoresist": "光刻胶", - "material.gtceu.phthalic_anhydride": "邻苯二甲酸酐", - "material.gtceu.pikyonium": "皮卡优合金", - "material.gtceu.piranha_solution": "食人鱼洗液", - "material.gtceu.platinum_front": "铂矿石泡沫", - "material.gtceu.platinum_slag": "铂渣", - "material.gtceu.polycyclic_aromatic_mixture": "多环芳香烃混合物", - "material.gtceu.polyetheretherketone": "聚醚醚酮", - "material.gtceu.polyimide": "聚酰亚胺", - "material.gtceu.polyurethane": "聚氨基甲酸酯", - "material.gtceu.polyurethaneresin": "聚氨酯树脂", - "material.gtceu.poor": "贫金属元素混合物", - "material.gtceu.positive_electron": "反电子", - "material.gtceu.potassium_bisulfite": "亚硫酸氢钾", - "material.gtceu.potassium_bromide": "溴化钾", - "material.gtceu.potassium_ethylate": "乙醇钾", - "material.gtceu.potassium_ethylxanthate": "乙基黄原酸钾", - "material.gtceu.potassium_fluoride": "氟化钾", - "material.gtceu.potassium_hydroxylaminedisulfonate": "羟胺二磺酸钾", - "material.gtceu.potassium_pyrosulfate": "焦硫酸钾", - "material.gtceu.praseodymium_oxide": "氧化镨", - "material.gtceu.prasiolite": "堇云石", - "material.gtceu.pre_zylon": "预处理柴隆纤维", - "material.gtceu.primordialmatter": "流体本源物质", - "material.gtceu.promethium_oxide": "氧化钷", - "material.gtceu.propadiene": "丙二烯", - "material.gtceu.pulsating_alloy": "脉冲铁", - "material.gtceu.purified_tengam": "纯镃", - "material.gtceu.purified_xenoxene": "纯净异氙", - "material.gtceu.pyridine": "吡啶", - "material.gtceu.pyromellitic_dianhydride": "均苯二甲酸酐", - "material.gtceu.pyrope_front": "镁铝榴石矿石泡沫", - "material.gtceu.quantanium": "量子", - "material.gtceu.quantum": "量子合金", - "material.gtceu.quantum_dots": "量子点", - "material.gtceu.quantumchromodynamically_confined_matter": "量子色动力学封闭物质", - "material.gtceu.quark_gluon": "夸克-胶子", - "material.gtceu.quasifissioning": "拟裂变", - "material.gtceu.radium_nitrate": "硝酸镭", - "material.gtceu.radon_cracked_enriched_aquadah": "加氡裂化富集硅岩", - "material.gtceu.radon_difluoride": "二氟化氡", - "material.gtceu.radon_naquadria_octafluoride": "八氟超能硅岩酸氡", - "material.gtceu.radon_trioxide": "三氧化氡", - "material.gtceu.radox": "拉多X聚合物", - "material.gtceu.radox_gas": "拉多X气", - "material.gtceu.rare_earth_chlorides": "稀土氯化物", - "material.gtceu.rare_earth_hydroxides": "稀土氢氧化物", - "material.gtceu.rare_earth_metal": "稀土金属", - "material.gtceu.rare_earth_oxide": "稀土氧化物", - "material.gtceu.rareearth": "稀土元素合金", - "material.gtceu.raw_star_matter": "原始恒星混合物", - "material.gtceu.raw_tengam": "生镃", - "material.gtceu.rawradox": "粗制拉多X", - "material.gtceu.reactor_steel": "反应堆专用钒钢", - "material.gtceu.red_mud": "赤泥", - "material.gtceu.red_slurry": "赤泥浆液", - "material.gtceu.red_zircon_powder": "红锆石", - "material.gtceu.redstone_front": "红石矿石泡沫", - "material.gtceu.reprecipitated_rhodium": "再沉淀铑", - "material.gtceu.residual_triniite_solution": "残留凯金化合物溶液", - "material.gtceu.resorcinol": "间苯二酚", - "material.gtceu.rhenium_chloride": "氯化铼", - "material.gtceu.rhenium_hassium_thallium_isophtaloylbisdiethylthiourea_hexaf": "间苯二甲酰双(二乙基硫脲基)六氟磷酸铼-𬭶-铊", - "material.gtceu.rhenium_sulfuric_solution": "铼硫酸溶液", - "material.gtceu.rhodium_filter_cake": "铑滤饼", - "material.gtceu.rhodium_filter_cake_solution": "铑滤饼溶液", - "material.gtceu.rhodium_nitrate": "硝酸铑", - "material.gtceu.rhodium_rhenium_naquadah_catalyst": "铑-铼-硅岩催化剂", - "material.gtceu.rhodium_salt": "铑盐", - "material.gtceu.rhodium_salt_solution": "铑盐溶液", - "material.gtceu.rhodium_sulfate_gas": "气态硫酸铑", - "material.gtceu.rhugnor": "鲁格诺", - "material.gtceu.rocket_fuel_cn3h7o3": "火箭燃料(硝酸甲肼)", - "material.gtceu.rocket_fuel_h8n4c2o4": "火箭燃料(偏二甲肼-四氧化二氮)", - "material.gtceu.rocket_fuel_rp_1": "火箭燃料 RP-1", - "material.gtceu.roughly_rhodium_metal": "粗制铑金属", - "material.gtceu.rp_1": "RP-1混合燃料", - "material.gtceu.ruthenium_tetroxide_hot": "热四氧化钌", - "material.gtceu.ruthenium_tetroxide_lq": "四氧化钌溶液", - "material.gtceu.samarium_chloride_concentrate_solution": "氯化钐浓缩液", - "material.gtceu.samarium_chloride_sodium_chloride_mixture_powder": "氯化钐-氯化钠混合物", - "material.gtceu.samarium_chloride_with_impurities": "含杂氯化钐", - "material.gtceu.samarium_oxalate_with_impurities": "含杂草酸钐", - "material.gtceu.samarium_oxide": "氧化钐", - "material.gtceu.samarium_precipitate_powder": "钐沉淀", - "material.gtceu.samarium_rare_earth_concentrate_powder": "钐稀土精", - "material.gtceu.samarium_rare_earth_diluted_solution": "钐稀土稀释液", - "material.gtceu.samarium_rare_earth_slurry": "钐稀土泥浆", - "material.gtceu.samarium_refined_powder": "钐精", - "material.gtceu.samarium_rrare_eearth_turbid_liquid": "钐稀土浊液", - "material.gtceu.samarium_terbium_mixture_powder": "钐-铽混合物", - "material.gtceu.sarcosine": "肌氨酸", - "material.gtceu.saturated_monazite_rare_earth_powder": "饱和独居石稀土", - "material.gtceu.scandium_oxide": "氧化钪", - "material.gtceu.scandium_titanium_50_mixture": "钪-钛50混合物", - "material.gtceu.seaborgium_doped_nanotubes": "𬭳掺杂的纳米管", - "material.gtceu.seaweedbroth": "海藻基质", - "material.gtceu.selenium_oxide": "二氧化硒", - "material.gtceu.shirabon": "调律源金", - "material.gtceu.silica_alumina_gel": "硅铝凝胶", - "material.gtceu.silica_gel": "硅胶", - "material.gtceu.silica_gel_base": "硅胶基质", - "material.gtceu.silicon_carbide": "碳化硅", - "material.gtceu.siliconoil": "硅油", - "material.gtceu.silver_chloride": "氯化银", - "material.gtceu.silver_iodide": "碘化银", - "material.gtceu.silver_nitrate": "硝酸银", - "material.gtceu.silver_oxide": "氧化银", - "material.gtceu.silver_perchlorate": "高氯酸银", - "material.gtceu.silver_tetrafluoroborate": "四氟硼酸银", - "material.gtceu.sm_gd_oxides_solution": "钐-钆氧化物溶液", - "material.gtceu.soap": "肥皂", - "material.gtceu.sodium_aluminium_hydride": "氢化铝钠", - "material.gtceu.sodium_azanide": "氨基钠", - "material.gtceu.sodium_azide": "叠氮化钠", - "material.gtceu.sodium_borohydride": "硼氢化钠", - "material.gtceu.sodium_bromide": "溴化钠", - "material.gtceu.sodium_chlorate": "氯酸钠", - "material.gtceu.sodium_cyanide": "氰化钠", - "material.gtceu.sodium_ethylate": "乙醇钠", - "material.gtceu.sodium_ethylxanthate": "乙基黄原酸钠", - "material.gtceu.sodium_fluoride": "氟化钠", - "material.gtceu.sodium_fluorosilicate": "氟硅酸钠", - "material.gtceu.sodium_formate": "甲酸钠", - "material.gtceu.sodium_hexafluoroaluminate": "六氟铝酸钠", - "material.gtceu.sodium_hydride": "氢化钠", - "material.gtceu.sodium_hydroxide_solution": "氢氧化钠溶液", - "material.gtceu.sodium_hypochlorite": "次氯酸钠", - "material.gtceu.sodium_nitrate": "硝酸钠", - "material.gtceu.sodium_nitrate_solution": "硝酸钠溶液", - "material.gtceu.sodium_oxide": "氧化钠", - "material.gtceu.sodium_perchlorate": "高氯酸钠", - "material.gtceu.sodium_rutheniate": "钌酸钠", - "material.gtceu.sodium_seaborgate": "𬭳酸钠", - "material.gtceu.sodium_sulfate": "硫酸钠", - "material.gtceu.sodium_tetrafluoroborate": "四氟硼酸钠", - "material.gtceu.sodium_thiocyanate": "硫氰酸钠", - "material.gtceu.sodium_thiosulfate": "硫代硫酸钠", - "material.gtceu.sodium_toluenesulfonate": "甲苯磺酸钠", - "material.gtceu.spacetime": "时空", - "material.gtceu.spatialfluid": "扩大化空间流体", - "material.gtceu.special_ceramics": "特种陶瓷", - "material.gtceu.spessartine_front": "锰铝榴石矿石泡沫", - "material.gtceu.sphalerite_front": "闪锌矿矿石泡沫", - "material.gtceu.starlight": "星光", - "material.gtceu.starmetal": "星辉", - "material.gtceu.steam_cracked_fluoro_carbon_lanthanide_slurry": "蒸汽裂化氟碳镧铈泥浆", - "material.gtceu.steam_cracked_turpentine": "蒸汽裂化的松油浸出物", - "material.gtceu.stearic_acid": "硬脂酸", - "material.gtceu.stellar_energy_rocket_fuel": "星能火箭燃料", - "material.gtceu.stellite": "铬钴锰钛合金", - "material.gtceu.stone_dust_residue": "石头粉残渣", - "material.gtceu.strontium_europium_aluminate": "锶铕铝酸盐", - "material.gtceu.succinaldehyde": "琥珀醛", - "material.gtceu.succinic_acid": "琥珀酸", - "material.gtceu.succinic_anhydride": "丁二酸酐", - "material.gtceu.succinimide": "琥珀酰亚胺", - "material.gtceu.succinimidyl_acetate": "琥珀酰亚胺醋酸酯", - "material.gtceu.sunnarium": "阳光化合物", - "material.gtceu.super_mutated_living_solder": "超突变活性焊料", - "material.gtceu.supercritical_carbon_dioxide": "超临界二氧化碳", - "material.gtceu.supercritical_sodium_potassium": "超临界钠钾合金", - "material.gtceu.supercritical_steam": "超临界蒸汽", - "material.gtceu.superheavy_h_alloy": "超重元素-重合金", - "material.gtceu.superheavy_l_alloy": "超重元素-轻合金", - "material.gtceu.superheavyradox": "超重拉多X", - "material.gtceu.superlightradox": "超轻拉多X", - "material.gtceu.tairitsu": "对立合金", - "material.gtceu.tanmolyium": "钛钼合金β-C", - "material.gtceu.tannic": "丹宁", - "material.gtceu.tantalloy_61": "钽钨合金-61", - "material.gtceu.taranium": "塔兰", - "material.gtceu.taranium_enriched_liquid_helium_3": "浓缩塔兰金属的液氦-3", - "material.gtceu.taranium_rich_liquid_helium_4": "富塔兰金属的氦-4", - "material.gtceu.tartarite": "溶火之石", - "material.gtceu.tb_ho_oxides_solution": "铽-钬氧化物溶液", - "material.gtceu.tellurium_oxide": "二氧化碲", - "material.gtceu.temporalfluid": "富快子时间流体", - "material.gtceu.terbium_nitrate_powder": "硝酸铽", - "material.gtceu.terbium_oxide": "氧化铽", - "material.gtceu.terephthalaldehyde": "对苯二甲醛", - "material.gtceu.terephthalicacid": "对苯二甲酸", - "material.gtceu.terephthaloyl_chloride": "对苯二甲酰氯", - "material.gtceu.tert_butanol": "叔丁醇", - "material.gtceu.tertbuthylcarbonylazide": "叔丁基羰基叠氮", - "material.gtceu.tetraacetyldinitrosohexaazaisowurtzitane": "四乙酰二硝基六氮杂异戊二烯", - "material.gtceu.tetracene": "并四苯", - "material.gtceu.tetraethylammonium_bromide": "四乙基溴化铵", - "material.gtceu.tetrahydrofuran": "四氢呋喃", - "material.gtceu.thallium_chloride": "氯化铊", - "material.gtceu.thallium_thulium_doped_caesium_iodide": "铊铥掺杂的碘化铯", - "material.gtceu.thaumium": "神秘", - "material.gtceu.thionyl_chloride": "氯化亚砜", - "material.gtceu.thorite_powder": "方钍石", - "material.gtceu.thorium_phosphate_filter_cake_powder": "磷酸钍滤饼", - "material.gtceu.thorium_phosphate_refined_powder": "磷酸钍精", - "material.gtceu.thulium_oxide": "氧化铥", - "material.gtceu.titan_precision_steel": "泰坦精钢", - "material.gtceu.titanium_50": "钛-50", - "material.gtceu.titanium_50_tetrachloride": "四氯化钛-50", - "material.gtceu.titanium_50_tetrafluoride": "四氟化钛-50", - "material.gtceu.titanium_tetrafluoride": "四氟化钛", - "material.gtceu.titansteel": "泰坦钢", - "material.gtceu.titanyl_sulfate": "硫酸钛酯", - "material.gtceu.toluene_diisocyanate": "甲苯二异氰酸脂", - "material.gtceu.transcendentmetal": "超时空金属", - "material.gtceu.transition": "过渡元素合金", - "material.gtceu.transition_1": "前过渡金属元素混合物", - "material.gtceu.transition_2": "中过渡金属元素混合物", - "material.gtceu.transition_3": "后过渡金属元素混合物", - "material.gtceu.trichloroflerane": "三氯𫓧烷", - "material.gtceu.tricotylphosphine": "三辛基膦", - "material.gtceu.trifluoroacetic_phosphate_ester": "三氟乙酸对磷脂", - "material.gtceu.trimethylamine": "三甲胺", - "material.gtceu.trimethylchlorosilane": "三甲基氯硅烷", - "material.gtceu.trimethylsilane": "三甲基硅烷", - "material.gtceu.trimethyltin_chloride": "三甲基氯化锡", - "material.gtceu.trinium_compound": "凯金化合物", - "material.gtceu.trinium_tetrafluoride": "四氟化凯金", - "material.gtceu.trinium_titanium": "凯金钛合金", - "material.gtceu.tritium_hydride": "氢化氚", - "material.gtceu.tungsten_trioxide": "三氧化钨", - "material.gtceu.turbid_dragon_blood": "龙血浊液", - "material.gtceu.turpentine": "松油", - "material.gtceu.ultraacidic_residue_solution": "超酸性残渣溶液", - "material.gtceu.uncommon_residues": "精良残渣", - "material.gtceu.unfolded_fullerene": "未折叠富勒烯", - "material.gtceu.unknownnutrientagar": "未知营养琼脂", - "material.gtceu.unknowwater": "不明液体", - "material.gtceu.uranium_filter_residue_powder": "铀滤渣", - "material.gtceu.uranium_sulfate_waste_solution": "硫酸铀废液", - "material.gtceu.uruium": "乌鲁", - "material.gtceu.uu_amplifier": "UU增幅液", - "material.gtceu.vanadium_pentoxide_powder": "五氧化二钒", - "material.gtceu.vibramantium": "艾德曼振金", - "material.gtceu.vibranium": "振金", - "material.gtceu.vibranium_unstable": "不稳定振金", - "material.gtceu.vibrant_alloy": "脉冲合金", - "material.gtceu.viscoelastic_polyurethane": "粘弹性聚氨酯", - "material.gtceu.viscoelastic_polyurethane_foam": "粘弹性聚氨酯泡沫", - "material.gtceu.water_agar_mix": "琼脂水溶液", - "material.gtceu.wet_rare_earth_oxide_powder": "湿稀土氧化物", - "material.gtceu.wet_zeolite_sieving_pellets": "湿过筛沸石颗粒", - "material.gtceu.white_dwarf_mtter": "白矮星物质", - "material.gtceu.woods_glass": "伍兹玻璃", - "material.gtceu.xenic_acid": "氙酸", - "material.gtceu.xenoauric_fluoroantimonic_acid": "氟锑酸二氙", - "material.gtceu.xenon_hexafluoro_enriched_naquadate": "六氟氙酸富集硅岩", - "material.gtceu.xenon_trioxide": "三氧化氙", - "material.gtceu.xenoxene": "异氙", - "material.gtceu.xenoxene_crystal": "结晶异氙", - "material.gtceu.xenoxene_mixture": "异氙混合物", - "material.gtceu.xpjuice": "液态经验", - "material.gtceu.ytterbium_178": "镱-178", - "material.gtceu.ytterbium_oxide": "氧化镱", - "material.gtceu.yttrium_oxide": "氧化钇", - "material.gtceu.zeolite_sieving_pellets": "过筛沸石颗粒", - "material.gtceu.zinc_sulfate": "硫酸锌", - "material.gtceu.zircon": "锆石", - "material.gtceu.zircon_chlorinating_residue": "锆氯化反应残渣", - "material.gtceu.zirconiu_hafnium_oxychloride": "锆-铪氯氧化物", - "material.gtceu.zirconium_carbide": "碳化锆", - "material.gtceu.zirconium_hafnium_chloride": "锆-铪氯化物", - "material.gtceu.zirconium_oxide": "二氧化锆", - "material.gtceu.znfealcl_catalyst": "锌-铁-铝-氯混合催化剂", - "material.gtceu.zylon": "柴隆纤维", - "tagprefix.ceresstone": "谷神星%s矿石", - "tagprefix.enceladusstone": "土卫二%s矿石", - "tagprefix.ganymedestone": "木卫三%s矿石", - "tagprefix.glacio_stone": "霜原石%s矿石", - "tagprefix.iostone": "木卫一%s矿石", - "tagprefix.mars_stone": "火星石%s矿石", - "tagprefix.mercury_stone": "水星石%s矿石", - "tagprefix.milled": "碾磨%s", - "tagprefix.moon_stone": "月石%s矿石", - "tagprefix.nanoswarm": "%s纳米蜂群", - "tagprefix.plutostone": "冥王星%s矿石", - "tagprefix.titanstone": "土卫六%s矿石", - "tagprefix.venus_stone": "金星石%s矿石", - "block.gtceu.advanced_infinite_driller": "进阶无尽钻机", - "block.gtceu.advanced_neutron_activator": "中子漩涡", - "block.gtceu.advanced_rare_earth_centrifugal": "进阶稀土离心机", - "block.gtceu.advanced_vacuum_drying_furnace": "进阶真空干燥炉", - "block.gtceu.block_bus": "方块总线", - "block.gtceu.tag_filter_me_stock_bus_part_machine": "标签过滤ME库存输入总线", - "block.gtceu.me_dual_hatch_stock_part_machine": "ME库存输入总成", - "block.gtceu.cleaning_gravity_maintenance_hatch": "重力超净维护仓", - "block.gtceu.sterile_cleaning_gravity_maintenance_hatch": "重力无菌维护仓", - "block.gtceu.law_cleaning_gravity_maintenance_hatch": "重力绝对洁净维护仓", - "block.gtceu.gravity_configuration_hatch": "可配置重力控制仓", - "block.gtceu.cleaning_gravity_configuration_maintenance_hatch": "可配置重力超净维护仓", - "block.gtceu.sterile_cleaning_gravity_configuration_maintenance_hatch": "可配置重力无菌维护仓", - "block.gtceu.law_cleaning_gravity_configuration_maintenance_hatch": "可配置重力绝对洁净维护仓", - "block.gtceu.huge_incubator": "微生物支配者", - "block.gtceu.heat_sensor": "温度传感器", - "block.gtceu.space_cosmic_probe_receivers": "天基宇宙探测器接收器", - "block.gtceu.simulation_machine": "多方块模拟机", - "block.gtceu.wireless_data_transmitter_hatch": "无线光学数据源仓", - "block.gtceu.wireless_data_receiver_hatch": "无线光学数据靶仓", - "gtceu.machine.advanced_infinite_driller.not_fluid_head": "无钻头", - "gtceu.machine.advanced_infinite_driller.no_coil": "线圈等级过低", - "gtceu.machine.advanced_infinite_driller.range": "工作范围: %s", - "gtceu.machine.advanced_infinite_driller.heat": "最大温度: %sK / 工作温度: %sK", - "gtceu.machine.advanced_infinite_driller.current_heat": "当前温度: %sK", - "gtceu.machine.advanced_infinite_driller.fast": "高速模式: %s ", - "gtceu.machine.advanced_infinite_driller.process": "损坏: %s", - "gtceu.machine.advanced_infinite_driller.drilled_fluid": "流体: %s 产量: %s", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.0": "更加高级的无尽流体钻井", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.1": "可以更加高效的抽取流体, 与此同时钻机需要一定温度来启动,可以通入液态烈焰来升温", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.2": "可放入中子素钻头(50000)/振金钻头(100000)", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.3": "基础温度: 8000K(泰坦钢线圈), 使用更高级的线圈可以提供更高的额外温度", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.4": "钻机在不同的工作温度下会产生热量,产热公式为(温度/2000)", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.5": "随着温度提升,效率也会提升.当温度超过临界值,钻头将会融毁", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.6": "可以通入不同的流体来降温, 可用冷却液如下.冷却固定消耗为200B/5t", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.7": "蒸馏水 1K ", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.8": "液态氧 2K ", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.9": "液态氦 4K", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.10": "液态烈焰消耗公式为(当前温度^1.3)", - "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.11": "可使用温度传感器", - "gtceu.machine.advanced_neutron_activator.tooltip.1": "工作时会消耗对应电量,自动提供中子动能", - "gtceu.machine.advanced_neutron_activator.tooltip.2": "中子动能自动转换比例为2eu -> 1ev", - "gtceu.machine.block_conversion_room.am": "每次转化数量:%s", - "gtceu.machine.large_block_conversion_room.tooltip.2": "放入转换模拟卡会启用虚拟模式, 放入高速转换模拟卡后单次可以转化整个总线内方块", - "gtceu.machine.heat_sensor.tooltip.0": "基于§6温度§7输出红石信号,右键以打开GUI进行设置", - "gtceu.machine.simulation_machine.tooltip.0": "将主方块放入机器中, 使用终端右键可放置/预览机器内内主方块的结构", - "gtceu.machine.wireless_data_hatch.bind": "无线数据仓绑定完成。", - "gtceu.machine.wireless_data_transmitter_hatch.tooltip.1": "从多方块结构输出研究数据", - "gtceu.machine.wireless_data_transmitter_hatch.tooltip.2": "需要使用闪存右键无线光学数据靶仓和无线数据源仓光学进行绑定。", - "gtceu.machine.wireless_data_transmitter_hatch.to_bind": "源仓数据读取完成,请右键靶仓进行绑定。", - "gtceu.machine.wireless_data_transmitter_hatch.bind": "已绑定无线光学数据靶仓(%s)。", - "gtceu.machine.wireless_data_transmitter_hatch.unbind": "未绑定无线光学数据靶仓。", - "gtceu.machine.wireless_data_receiver_hatch.tooltip.1": "为多方块结构输入研究数据", - "gtceu.machine.wireless_data_receiver_hatch.tooltip.2": "需要使用闪存右键无线光学数据靶仓和无线数光学数据仓进行绑定。", - "gtceu.machine.wireless_data_receiver_hatch.to_bind": "靶仓数据读取完成,请右键源仓进行绑定。", - "gtceu.machine.wireless_data_receiver_hatch.bind": "已绑定无线光学数据源仓(%s)。", - "gtceu.machine.wireless_data_receiver_hatch.unbind": "未绑定无线光学数据源仓。", - "gtceu.space_cosmic_probe_receivers": "天基宇宙探测", - "gtceu.top.electricity": "%sA %s", - "gtceu.top.extra_output": "剩余%s项重复输出已隐藏", - "gtceu.recipe.eu_to_starts": "启动耗能:%sMEU(MK%s)" + "block.gtceu.a_mass_fabricator": "进阶质量发生器", + "block.gtceu.advanced_assembly_line": "进阶装配线", + "block.gtceu.advanced_hyper_reactor": "进阶超能反应堆", + "block.gtceu.advanced_infinite_driller": "进阶无尽钻机", + "block.gtceu.advanced_integrated_ore_processor": "进阶集成矿石处理厂", + "block.gtceu.advanced_multi_smelter": "进阶熔炉", + "block.gtceu.advanced_neutron_activator": "中子漩涡", + "block.gtceu.advanced_rare_earth_centrifugal": "进阶稀土离心机", + "block.gtceu.advanced_sps_crafting": "进阶超临界合成机", + "block.gtceu.advanced_vacuum_drying_furnace": "进阶真空干燥炉", + "block.gtceu.aggregation_device": "聚合装置", + "block.gtceu.annihilate_generator": "人造恒星", + "block.gtceu.assemble_plant": "通用组装厂", + "block.gtceu.assembler_module": "太空电梯组装模块", + "block.gtceu.atomic_energy_excitation_plant": "原子能激发工厂", + "block.gtceu.auto_configuration_maintenance_hatch": "可配置自动维护仓", + "block.gtceu.bedrock_drilling_rig": "基岩钻机", + "block.gtceu.blaze_blast_furnace": "烈焰高炉", + "block.gtceu.block_bus": "方块总线", + "block.gtceu.block_conversion_room": "方块转换室", + "block.gtceu.chemical_distort": "深度化学扭曲仪", + "block.gtceu.chemical_energy_devourer": "化学能吞噬者", + "block.gtceu.chemical_plant": "化工厂", + "block.gtceu.circuit_assembly_line": "电路装配线", + "block.gtceu.cleaning_configuration_maintenance_hatch": "超净可配置维护仓", + "block.gtceu.cleaning_gravity_configuration_maintenance_hatch": "可配置重力超净维护仓", + "block.gtceu.cleaning_gravity_maintenance_hatch": "重力超净维护仓", + "block.gtceu.cold_ice_freezer": "寒冰冷冻机", + "block.gtceu.component_assembly_line": "部件装配线", + "block.gtceu.cooling_tower": "冷却塔", + "block.gtceu.create_aggregation": "创造聚合仪", + "block.gtceu.create_computation": "创造计算机", + "block.gtceu.crystalline_infinity": "无限晶胞", + "block.gtceu.decay_hastener": "衰变加速器", + "block.gtceu.desulfurizer": "脱硫机", + "block.gtceu.digestion_tank": "煮解池", + "block.gtceu.dimensional_focus_engraving_array": "维度聚焦激光蚀刻阵列", + "block.gtceu.dimensionally_transcendent_dirt_forge": "超维度等泥土锻炉", + "block.gtceu.dimensionally_transcendent_mixer": "超维度搅拌机", + "block.gtceu.dimensionally_transcendent_plasma_forge": "超维度等离子锻炉", + "block.gtceu.dimensionally_transcendent_steam_boiler": "超维度等蒸汽锅炉", + "block.gtceu.dimensionally_transcendent_steam_oven": "超维度蒸汽熔炉", + "block.gtceu.disassembly": "拆解机", + "block.gtceu.dissolving_tank": "溶解罐", + "block.gtceu.door_of_create": "创造之门", + "block.gtceu.dragon_egg_copier": "龙蛋复制机", + "block.gtceu.dyson_sphere": "戴森球控制系统", + "block.gtceu.electric_implosion_compressor": "电力聚爆压缩机", + "block.gtceu.element_copying": "元素复制机", + "block.gtceu.engraving_laser_plant": "激光蚀刻工厂", + "block.gtceu.ev_buffer": "§5进阶缓存器 III§r", + "block.gtceu.ev_dehydrator": "§5高级脱水机 III§r", + "block.gtceu.ev_dual_input_hatch": "§5EV§r输入总成", + "block.gtceu.ev_dual_output_hatch": "§5EV§r输出总成", + "block.gtceu.ev_huge_input_hatch": "§5EV§r巨型输入仓", + "block.gtceu.ev_huge_output_hatch": "§5EV§r巨型输出仓", + "block.gtceu.ev_lightning_processor": "§5高级闪电处理机 III§r", + "block.gtceu.ev_lightning_rod": "§5基础避雷针§r", + "block.gtceu.ev_neutron_accelerator": "§5EV 中子加速器§r", + "block.gtceu.ev_rocket_engine": "§5基础火箭引擎§r", + "block.gtceu.ev_world_data_scanner": "§5进阶世界信息扫描仪 III§r", + "block.gtceu.eye_of_harmony": "鸿蒙之眼", + "block.gtceu.field_extruder_factory": "力场压模工厂", + "block.gtceu.fishing_ground": "渔场", + "block.gtceu.fission_reactor": "裂变反应堆", + "block.gtceu.flotation_cell_regulator": "工业浮选机", + "block.gtceu.gas_mega_turbine": "特大燃气涡轮", + "block.gtceu.generator_array": "发电阵列", + "block.gtceu.gravitation_shockburst": "引力震爆器", + "block.gtceu.gravity_configuration_hatch": "可配置重力控制仓", + "block.gtceu.gravity_hatch": "重力控制仓", + "block.gtceu.greenhouse": "温室", + "block.gtceu.heat_exchanger": "热交换机", + "block.gtceu.heat_sensor": "温度传感器", + "block.gtceu.holy_separator": "神圣分离者", + "block.gtceu.huge_incubator": "微生物支配者", + "block.gtceu.hv_dehydrator": "§6高级脱水机 II§r", + "block.gtceu.hv_dual_input_hatch": "§6HV§r输入总成", + "block.gtceu.hv_dual_output_hatch": "§6HV§r输出总成", + "block.gtceu.hv_energy_input_hatch_16a": "16安§4HV§r能源仓", + "block.gtceu.hv_energy_input_hatch_4a": "4安§4HV§r能源仓 ", + "block.gtceu.hv_energy_output_hatch": "§4HV§r动力仓", + "block.gtceu.hv_energy_output_hatch_16a": "16安§4HV§r动力仓", + "block.gtceu.hv_energy_output_hatch_4a": "4安§4HV§r动力仓", + "block.gtceu.hv_huge_input_hatch": "§6HV§r巨型输入仓", + "block.gtceu.hv_huge_output_hatch": "§6HV§r巨型输出仓", + "block.gtceu.hv_lightning_processor": "§6高级闪电处理机 II§r", + "block.gtceu.hv_neutron_accelerator": "§6HV 中子加速器§r", + "block.gtceu.hv_semi_fluid": "§6进阶半流质发电机 II§r", + "block.gtceu.hv_world_data_scanner": "§6进阶世界信息扫描仪 II§r", + "block.gtceu.hyper_reactor": "超能反应堆", + "block.gtceu.incubator": "培养缸", + "block.gtceu.integrated_ore_processor": "集成矿石处理厂", + "block.gtceu.isa_mill": "艾萨研磨机", + "block.gtceu.iv_1048576a_laser_source_hatch": "1048576安§9IV§r激光源仓", + "block.gtceu.iv_1048576a_laser_target_hatch": "1048576安§9IV§r激光靶仓", + "block.gtceu.iv_16384a_laser_source_hatch": "16384安§9IV§r激光源仓", + "block.gtceu.iv_16384a_laser_target_hatch": "16384安§9IV§r激光靶仓", + "block.gtceu.iv_16777216a_laser_source_hatch": "16777216安§9IV§r激光源仓", + "block.gtceu.iv_16777216a_laser_target_hatch": "16777216安§9IV§r激光靶仓", + "block.gtceu.iv_262144a_laser_source_hatch": "262144安§9IV§r激光源仓", + "block.gtceu.iv_262144a_laser_target_hatch": "262144安§9IV§r激光靶仓", + "block.gtceu.iv_4194304a_laser_source_hatch": "4194304安§9IV§r激光源仓", + "block.gtceu.iv_4194304a_laser_target_hatch": "4194304安§9IV§r激光靶仓", + "block.gtceu.iv_65536a_laser_source_hatch": "65536安§9IV§r激光源仓", + "block.gtceu.iv_65536a_laser_target_hatch": "65536安§9IV§r激光靶仓", + "block.gtceu.iv_67108864a_laser_source_hatch": "67108864安§9IV§r激光源仓", + "block.gtceu.iv_67108864a_laser_target_hatch": "67108864安§9IV§r激光靶仓", + "block.gtceu.iv_buffer": "§9精英缓存器§r", + "block.gtceu.iv_dehydrator": "§9精英脱水机§r", + "block.gtceu.iv_dual_input_hatch": "§9IV§r输入总成", + "block.gtceu.iv_dual_output_hatch": "§9IV§r输出总成", + "block.gtceu.iv_huge_input_hatch": "§9IV§r巨型输入仓", + "block.gtceu.iv_huge_output_hatch": "§9IV§r巨型输出仓", + "block.gtceu.iv_lightning_processor": "§9精英闪电处理机§r", + "block.gtceu.iv_lightning_rod": "§9进阶避雷针§r", + "block.gtceu.iv_naquadah_reactor": "§9基础硅岩反应堆§r", + "block.gtceu.iv_neutron_accelerator": "§9IV 中子加速器§r", + "block.gtceu.iv_rocket_engine": "§9进阶火箭引擎§r", + "block.gtceu.iv_world_data_scanner": "§9精英世界信息扫描仪§r", + "block.gtceu.large_block_conversion_room": "大型方块转换室", + "block.gtceu.large_chemical_plant": "大型化工厂", + "block.gtceu.large_cracker": "大型裂化机", + "block.gtceu.large_gas_collector": "大型集气室", + "block.gtceu.large_greenhouse": "大型温室", + "block.gtceu.large_incubator": "大型培养缸", + "block.gtceu.large_naquadah_reactor": "大型硅岩反应堆", + "block.gtceu.large_pyrolyse_oven": "大型热解炉", + "block.gtceu.large_recycler": "回收机", + "block.gtceu.large_rock_crusher": "大型碎岩机", + "block.gtceu.large_semi_fluid_generator": "大型半流质发电机", + "block.gtceu.large_steam_bath": "大型蒸汽浸洗机", + "block.gtceu.large_steam_centrifuge": "大型蒸汽离心机", + "block.gtceu.large_steam_circuit_assembler": "大型蒸汽电路组装机", + "block.gtceu.large_steam_input_hatch": "大型蒸汽输入仓", + "block.gtceu.large_steam_macerator": "大型蒸汽研磨机", + "block.gtceu.large_steam_mixer": "大型蒸汽搅拌机", + "block.gtceu.large_steam_ore_washer": "大型蒸汽洗矿机", + "block.gtceu.large_steam_thermal_centrifuge": "大型蒸汽热力离心机", + "block.gtceu.large_void_miner": "大型虚空采矿厂", + "block.gtceu.lava_furnace": "熔岩炉", + "block.gtceu.law_cleaning_gravity_configuration_maintenance_hatch": "可配置重力绝对洁净维护仓", + "block.gtceu.law_cleaning_gravity_maintenance_hatch": "重力绝对洁净维护仓", + "block.gtceu.law_cleaning_maintenance_hatch": "绝对洁净维护仓", + "block.gtceu.law_configuration_cleaning_maintenance_hatch": "绝对洁净可配置维护仓", + "block.gtceu.luv_1048576a_laser_source_hatch": "1048576安§dLuV§r激光源仓", + "block.gtceu.luv_1048576a_laser_target_hatch": "1048576安§dLuV§r激光靶仓", + "block.gtceu.luv_16384a_laser_source_hatch": "16384安§dLuV§r激光源仓", + "block.gtceu.luv_16384a_laser_target_hatch": "16384安§dLuV§r激光靶仓", + "block.gtceu.luv_16777216a_laser_source_hatch": "16777216安§dLuV§r激光源仓", + "block.gtceu.luv_16777216a_laser_target_hatch": "16777216安§dLuV§r激光靶仓", + "block.gtceu.luv_262144a_laser_source_hatch": "262144安§dLuV§r激光源仓", + "block.gtceu.luv_262144a_laser_target_hatch": "262144安§dLuV§r激光靶仓", + "block.gtceu.luv_4194304a_laser_source_hatch": "4194304安§dLuV§r激光源仓", + "block.gtceu.luv_4194304a_laser_target_hatch": "4194304安§dLuV§r激光靶仓", + "block.gtceu.luv_65536a_laser_source_hatch": "65536安§dLuV§r激光源仓", + "block.gtceu.luv_65536a_laser_target_hatch": "65536安§dLuV§r激光靶仓", + "block.gtceu.luv_67108864a_laser_source_hatch": "67108864安§dLuV§r激光源仓", + "block.gtceu.luv_67108864a_laser_target_hatch": "67108864安§dLuV§r激光靶仓", + "block.gtceu.luv_buffer": "§d精英缓存器 II§r", + "block.gtceu.luv_compressed_fusion_reactor": "压缩核聚变反应堆控制电脑 MK-I", + "block.gtceu.luv_dehydrator": "§d精英脱水机 II§r", + "block.gtceu.luv_huge_input_hatch": "§dLuV§r巨型输入仓", + "block.gtceu.luv_huge_output_hatch": "§dLuV§r巨型输出仓", + "block.gtceu.luv_lightning_processor": "§d精英闪电处理机 II§r", + "block.gtceu.luv_lightning_rod": "§d精英避雷针§r", + "block.gtceu.luv_naquadah_reactor": "§d进阶硅岩反应堆§r", + "block.gtceu.luv_neutron_accelerator": "§dLuV 中子加速器§r", + "block.gtceu.luv_rocket_engine": "§d精英火箭引擎§r", + "block.gtceu.luv_world_data_scanner": "§d精英世界信息扫描仪 II§r", + "block.gtceu.lv_dehydrator": "基础脱水机", + "block.gtceu.lv_dual_input_hatch": "§7LV§r输入总成", + "block.gtceu.lv_dual_output_hatch": "§7LV§r输出总成", + "block.gtceu.lv_energy_input_hatch_16a": "16安§7LV§r能源仓", + "block.gtceu.lv_energy_input_hatch_4a": "4安§7LV§r能源仓 ", + "block.gtceu.lv_energy_output_hatch": "§7LV§r动力仓", + "block.gtceu.lv_energy_output_hatch_16a": "16安§7LV§r动力仓", + "block.gtceu.lv_energy_output_hatch_4a": "4安§7LV§r动力仓", + "block.gtceu.lv_huge_input_hatch": "§7LV§r巨型输入仓", + "block.gtceu.lv_huge_output_hatch": "§7LV§r巨型输出仓", + "block.gtceu.lv_lightning_processor": "基础闪电处理机", + "block.gtceu.lv_neutron_accelerator": "§7LV 中子加速器§r", + "block.gtceu.lv_primitive_magic_energy": "低压原始魔法能源吸收器", + "block.gtceu.lv_semi_fluid": "基础半流质发电机§r", + "block.gtceu.lv_world_data_scanner": "基础世界信息扫描仪", + "block.gtceu.mage_assembler": "巨型组装厂", + "block.gtceu.magic_manufacturer": "魔法制造机", + "block.gtceu.mass_fabricator": "质量发生器", + "block.gtceu.matter_fabricator": "物质生成机", + "block.gtceu.max_1024a_laser_source_hatch": "1024安§c§lMAX§r激光源仓", + "block.gtceu.max_1024a_laser_target_hatch": "1024安§c§lMAX§r激光靶仓", + "block.gtceu.max_1048576a_laser_source_hatch": "1048576安§c§lMAX§r激光源仓", + "block.gtceu.max_1048576a_laser_target_hatch": "1048576安§c§lMAX§r激光靶仓", + "block.gtceu.max_16384a_laser_source_hatch": "16384安§c§lMAX§r激光源仓", + "block.gtceu.max_16384a_laser_target_hatch": "16384安§c§lMAX§r激光靶仓", + "block.gtceu.max_16777216a_laser_source_hatch": "16777216安§c§lMAX§r激光源仓", + "block.gtceu.max_16777216a_laser_target_hatch": "16777216安§c§lMAX§r激光靶仓", + "block.gtceu.max_256a_laser_source_hatch": "256安§c§lMAX§r激光源仓", + "block.gtceu.max_256a_laser_target_hatch": "256安§c§lMAX§r激光靶仓", + "block.gtceu.max_262144a_laser_source_hatch": "262144安§c§lMAX§r激光源仓", + "block.gtceu.max_262144a_laser_target_hatch": "262144安§c§lMAX§r激光靶仓", + "block.gtceu.max_4096a_laser_source_hatch": "4096安§c§lMAX§r激光源仓", + "block.gtceu.max_4096a_laser_target_hatch": "4096安§c§lMAX§r激光靶仓", + "block.gtceu.max_4194304a_laser_source_hatch": "4194304安§c§lMAX§r激光源仓", + "block.gtceu.max_4194304a_laser_target_hatch": "4194304安§c§lMAX§r激光靶仓", + "block.gtceu.max_65536a_laser_source_hatch": "65536安§c§lMAX§r激光源仓", + "block.gtceu.max_65536a_laser_target_hatch": "65536安§c§lMAX§r激光靶仓", + "block.gtceu.max_67108864a_laser_source_hatch": "67108864安§c§lMAX§r激光源仓", + "block.gtceu.max_67108864a_laser_target_hatch": "67108864安§c§lMAX§r激光靶仓", + "block.gtceu.max_buffer": "§c§l终焉缓存器§r", + "block.gtceu.max_energy_input_hatch_16a": "16安§c§lMAX§r能源仓", + "block.gtceu.max_energy_input_hatch_4a": "4安§c§lMAX§r能源仓", + "block.gtceu.max_energy_output_hatch_16a": "16安§c§lMAX§r动力仓", + "block.gtceu.max_energy_output_hatch_4a": "4安§c§lMAX§r动力仓", + "block.gtceu.max_huge_input_hatch": "§c§lMAX§r巨型输入仓", + "block.gtceu.max_huge_output_hatch": "§c§lMAX§r巨型输出仓", + "block.gtceu.max_neutron_accelerator": "§c§lMAX 中子加速器§r", + "block.gtceu.max_neutron_compressor": "中子态素压缩机", + "block.gtceu.max_parallel_hatch": "§c§lMAX§r并行控制仓", + "block.gtceu.max_substation_input_hatch_64a": "64安§c§lMAX§r变电能源仓", + "block.gtceu.max_substation_output_hatch_64a": "64安§c§lMAX§r变电动力仓", + "block.gtceu.me_craft_parallel_core": "§3ME合成核心§r", + "block.gtceu.me_craft_pattern_container": "§3ME样板核心§r", + "block.gtceu.me_craft_speed_core": "§3ME速度核心§r", + "block.gtceu.me_dual_hatch_stock_part_machine": "ME库存输入总成", + "block.gtceu.me_extend_pattern_buffer": "扩展ME样板总成", + "block.gtceu.me_extended_async_export_buffer": "增广ME异步输出总成", + "block.gtceu.me_extended_export_buffer": "增广ME输出总成", + "block.gtceu.me_final_pattern_buffer": "终极ME样板总成", + "block.gtceu.me_mini_pattern_buffer": "小型ME样板总成", + "block.gtceu.me_molecular_assembler_io": "ME分子装配矩阵端口", + "block.gtceu.mega_alloy_blast_smelter": "巨型合金冶炼炉", + "block.gtceu.mega_canner": "特大装罐机", + "block.gtceu.mega_distillery": "超级蒸馏塔", + "block.gtceu.mega_extractor": "特大相变仪", + "block.gtceu.mega_fluid_heater": "超级流体加热器", + "block.gtceu.mega_presser": "特大机床", + "block.gtceu.mega_steam_input_hatch": "特大蒸汽输入仓", + "block.gtceu.mega_steam_output_hatch": "特大蒸汽输出仓", + "block.gtceu.mega_wiremill": "特大线材轧机", + "block.gtceu.mixed_plant": "通用混合厂", + "block.gtceu.molecular_assembler_matrix": "分子操纵者", + "block.gtceu.mv_dehydrator": "§b高级脱水机 §r", + "block.gtceu.mv_dual_input_hatch": "§bMV§r输入总成", + "block.gtceu.mv_dual_output_hatch": "§bMV§r输出总成", + "block.gtceu.mv_energy_input_hatch_16a": "16安§bMV§r能源仓", + "block.gtceu.mv_energy_input_hatch_4a": "4安§bMV§r能源仓 ", + "block.gtceu.mv_energy_output_hatch": "§bMV§r动力仓", + "block.gtceu.mv_energy_output_hatch_16a": "16安§bMV§r动力仓", + "block.gtceu.mv_energy_output_hatch_4a": "4安§bMV§r动力仓", + "block.gtceu.mv_huge_input_hatch": "§bMV§r巨型输入仓", + "block.gtceu.mv_huge_output_hatch": "§bMV§r巨型输出仓", + "block.gtceu.mv_lightning_processor": "§b高级闪电处理机 §r", + "block.gtceu.mv_neutron_accelerator": "§bMV 中子加速器§r", + "block.gtceu.mv_semi_fluid": "§b进阶半流质发电机§r", + "block.gtceu.mv_world_data_scanner": "§b进阶世界信息扫描仪§r", + "block.gtceu.nano_core": "纳米核心", + "block.gtceu.nano_forge_1": "1阶纳米锻炉", + "block.gtceu.nano_forge_2": "2阶纳米锻炉", + "block.gtceu.nano_forge_3": "3阶纳米锻炉", + "block.gtceu.naquadah_alloy_crate": "硅岩合金板条箱", + "block.gtceu.naquadah_alloy_drum": "硅岩合金桶", + "block.gtceu.neutron_activator": "中子活化器", + "block.gtceu.neutron_sensor": "中子传感器", + "block.gtceu.neutronium_crate": "中子素板条箱", + "block.gtceu.neutronium_drum": "中子素桶", + "block.gtceu.opv_1048576a_laser_source_hatch": "1048576安§9§lOpV§r激光源仓", + "block.gtceu.opv_1048576a_laser_target_hatch": "1048576安§9§lOpV§r激光靶仓", + "block.gtceu.opv_16384a_laser_source_hatch": "16384安§9§lOpV§r激光源仓", + "block.gtceu.opv_16384a_laser_target_hatch": "16384安§9§lOpV§r激光靶仓", + "block.gtceu.opv_16777216a_laser_source_hatch": "16777216安§9§lOpV§r激光源仓", + "block.gtceu.opv_16777216a_laser_target_hatch": "16777216安§9§lOpV§r激光靶仓", + "block.gtceu.opv_262144a_laser_source_hatch": "262144安§9§lOpV§r激光源仓", + "block.gtceu.opv_262144a_laser_target_hatch": "262144安§9§lOpV§r激光靶仓", + "block.gtceu.opv_4194304a_laser_source_hatch": "4194304安§9§lOpV§r激光源仓", + "block.gtceu.opv_4194304a_laser_target_hatch": "4194304安§9§lOpV§r激光靶仓", + "block.gtceu.opv_65536a_laser_source_hatch": "65536安§9§lOpV§r激光源仓", + "block.gtceu.opv_65536a_laser_target_hatch": "65536安§9§lOpV§r激光靶仓", + "block.gtceu.opv_67108864a_laser_source_hatch": "67108864安§9§lOpV§r激光源仓", + "block.gtceu.opv_67108864a_laser_target_hatch": "67108864安§9§lOpV§r激光靶仓", + "block.gtceu.opv_buffer": "§9§l传奇缓存器§r", + "block.gtceu.opv_dehydrator": "§9§l传奇脱水机§r", + "block.gtceu.opv_energy_input_hatch_16a": "16安§9§lOpV§r能源仓", + "block.gtceu.opv_energy_input_hatch_4a": "4安§9§lOpV§r能源仓", + "block.gtceu.opv_energy_output_hatch_16a": "16安§9§lOpV§r动力仓", + "block.gtceu.opv_energy_output_hatch_4a": "4安§9§lOpV§r动力仓", + "block.gtceu.opv_huge_input_hatch": "§9§lOpV§r巨型输入仓", + "block.gtceu.opv_huge_output_hatch": "§9§lOpV§r巨型输出仓", + "block.gtceu.opv_lightning_processor": "§9§l传奇闪电处理机§r", + "block.gtceu.opv_neutron_accelerator": "§9§lOpV 中子加速器§r", + "block.gtceu.opv_parallel_hatch": "§9§lOpV§r并行控制仓", + "block.gtceu.opv_substation_input_hatch_64a": "64安§9§lOpV§r变电能源仓", + "block.gtceu.opv_substation_output_hatch_64a": "64安§9§lOpV§r变电动力仓", + "block.gtceu.opv_world_data_scanner": "§9§l传奇世界信息扫描仪§r", + "block.gtceu.pcb_factory": "PCB工厂", + "block.gtceu.petrochemical_plant": "石化工厂", + "block.gtceu.plasma_condenser": "等离子冷凝器", + "block.gtceu.plasma_mega_turbine": "特大等离子涡轮", + "block.gtceu.precision_assembler": "精密组装机", + "block.gtceu.primitive_void_ore": "原始虚空采矿机", + "block.gtceu.processing_plant": "通用加工厂", + "block.gtceu.qft": "量子操纵者", + "block.gtceu.rare_earth_centrifugal": "稀土离心机", + "block.gtceu.resource_collection": "太空电梯资源采集模块", + "block.gtceu.rhodium_plated_palladium_crate": "镀铑钯板条箱", + "block.gtceu.rhodium_plated_palladium_drum": "镀铑钯桶", + "block.gtceu.rocket_large_turbine": "大型火箭引擎涡轮", + "block.gtceu.rocket_mega_turbine": "特大火箭引擎涡轮", + "block.gtceu.rotor_hatch": "转子仓", + "block.gtceu.separated_plant": "通用分离厂", + "block.gtceu.simulation_machine": "多方块模拟机", + "block.gtceu.slaughterhouse": "工业屠宰场", + "block.gtceu.space_cosmic_probe_receivers": "天基宇宙探测器接收器", + "block.gtceu.space_elevator": "太空电梯", + "block.gtceu.space_probe_surface_reception": "宇宙探测器地面接收单元", + "block.gtceu.sps_crafting": "超临界合成机", + "block.gtceu.star_ultimate_material_forge_factory": "恒星终极物质锻造工厂", + "block.gtceu.steam_bath": "蒸汽浸洗机", + "block.gtceu.steam_foundry": "蒸汽铸造炉", + "block.gtceu.steam_mega_turbine": "特大蒸汽涡轮", + "block.gtceu.steam_mixer": "蒸汽搅拌机", + "block.gtceu.steam_ore_washer": "蒸汽洗矿机", + "block.gtceu.steam_piston_hammer": "蒸汽活塞锤", + "block.gtceu.steam_pressor": "蒸汽挤压机", + "block.gtceu.stellar_forge": "恒星炎炀锻炉", + "block.gtceu.sterile_cleaning_gravity_configuration_maintenance_hatch": "可配置重力无菌维护仓", + "block.gtceu.sterile_cleaning_gravity_maintenance_hatch": "重力无菌维护仓", + "block.gtceu.sterile_cleaning_maintenance_hatch": "无菌维护仓", + "block.gtceu.sterile_configuration_cleaning_maintenance_hatch": "无菌可配置维护仓", + "block.gtceu.super_blast_smelter": "超级冶炼炉", + "block.gtceu.super_computation": "超级计算机", + "block.gtceu.super_particle_collider": "超级粒子对撞机", + "block.gtceu.superconducting_electromagnetism": "超导电磁工厂", + "block.gtceu.supercritical_mega_steam_turbine": "特大超临界蒸汽涡轮", + "block.gtceu.supercritical_steam_turbine": "超临界蒸汽涡轮", + "block.gtceu.suprachronal_assembly_line": "超时空装配线", + "block.gtceu.suprachronal_assembly_line_module": "超时空装配线拓展模块", + "block.gtceu.tag_filter_me_stock_bus_part_machine": "标签过滤ME库存输入总线", + "block.gtceu.uev_1048576a_laser_source_hatch": "1048576安§aUEV§r激光源仓", + "block.gtceu.uev_1048576a_laser_target_hatch": "1048576安§aUEV§r激光靶仓", + "block.gtceu.uev_16384a_laser_source_hatch": "16384安§aUEV§r激光源仓", + "block.gtceu.uev_16384a_laser_target_hatch": "16384安§aUEV§r激光靶仓", + "block.gtceu.uev_16777216a_laser_source_hatch": "16777216安§aUEV§r激光源仓", + "block.gtceu.uev_16777216a_laser_target_hatch": "16777216安§aUEV§r激光靶仓", + "block.gtceu.uev_262144a_laser_source_hatch": "262144安§aUEV§r激光源仓", + "block.gtceu.uev_262144a_laser_target_hatch": "262144安§aUEV§r激光靶仓", + "block.gtceu.uev_4194304a_laser_source_hatch": "4194304安§aUEV§r激光源仓", + "block.gtceu.uev_4194304a_laser_target_hatch": "4194304安§aUEV§r激光靶仓", + "block.gtceu.uev_65536a_laser_source_hatch": "65536安§aUEV§r激光源仓", + "block.gtceu.uev_65536a_laser_target_hatch": "65536安§aUEV§r激光靶仓", + "block.gtceu.uev_67108864a_laser_source_hatch": "67108864安§aUEV§r激光源仓", + "block.gtceu.uev_67108864a_laser_target_hatch": "67108864安§aUEV§r激光靶仓", + "block.gtceu.uev_buffer": "§a史诗缓存器 II§r", + "block.gtceu.uev_compressed_fusion_reactor": "压缩核聚变反应堆控制电脑 MK-V", + "block.gtceu.uev_dehydrator": "§a史诗脱水机 II§r", + "block.gtceu.uev_energy_input_hatch_16a": "16安§aUEV§r能源仓", + "block.gtceu.uev_energy_input_hatch_4a": "4安§aUEV§r能源仓", + "block.gtceu.uev_energy_output_hatch_16a": "16安§aUEV§r动力仓", + "block.gtceu.uev_energy_output_hatch_4a": "4安§aUEV§r动力仓", + "block.gtceu.uev_fusion_reactor": "核聚变反应堆控制电脑 MK-V", + "block.gtceu.uev_huge_input_hatch": "§aUEV§r巨型输入仓", + "block.gtceu.uev_huge_output_hatch": "§aUEV§r巨型输出仓", + "block.gtceu.uev_lightning_processor": "§a史诗闪电处理机 II§r", + "block.gtceu.uev_neutron_accelerator": "§aUEV 中子加速器§r", + "block.gtceu.uev_parallel_hatch": "§aUEV§r并行控制仓", + "block.gtceu.uev_rotor_holder": "§aUEV§r转子支架", + "block.gtceu.uev_substation_input_hatch_64a": "64安§aUEV§r变电能源仓", + "block.gtceu.uev_substation_output_hatch_64a": "64安§aUEV§r变电动力仓", + "block.gtceu.uev_world_data_scanner": "§a史诗世界信息扫描仪II§r", + "block.gtceu.uhv_1048576a_laser_source_hatch": "1048576安§4UHV§r激光源仓", + "block.gtceu.uhv_1048576a_laser_target_hatch": "1048576安§4UHV§r激光靶仓", + "block.gtceu.uhv_16384a_laser_source_hatch": "16384安§4UHV§r激光源仓", + "block.gtceu.uhv_16384a_laser_target_hatch": "16384安§4UHV§r激光靶仓", + "block.gtceu.uhv_16777216a_laser_source_hatch": "16777216安§4UHV§r激光源仓", + "block.gtceu.uhv_16777216a_laser_target_hatch": "16777216安§4UHV§r激光靶仓", + "block.gtceu.uhv_262144a_laser_source_hatch": "262144安§4UHV§r激光源仓", + "block.gtceu.uhv_262144a_laser_target_hatch": "262144安§4UHV§r激光靶仓", + "block.gtceu.uhv_4194304a_laser_source_hatch": "4194304安§4UHV§r激光源仓", + "block.gtceu.uhv_4194304a_laser_target_hatch": "4194304安§4UHV§r激光靶仓", + "block.gtceu.uhv_65536a_laser_source_hatch": "65536安§4UHV§r激光源仓", + "block.gtceu.uhv_65536a_laser_target_hatch": "65536安§4UHV§r激光靶仓", + "block.gtceu.uhv_67108864a_laser_source_hatch": "67108864安§4UHV§r激光源仓", + "block.gtceu.uhv_67108864a_laser_target_hatch": "67108864安§4UHV§r激光靶仓", + "block.gtceu.uhv_buffer": "§4史诗缓存器§r", + "block.gtceu.uhv_compressed_fusion_reactor": "压缩核聚变反应堆控制电脑 MK-IV", + "block.gtceu.uhv_dehydrator": "§4史诗脱水机§r", + "block.gtceu.uhv_fusion_reactor": "核聚变反应堆控制电脑 MK-IV", + "block.gtceu.uhv_huge_input_hatch": "§4UHV§r巨型输入仓", + "block.gtceu.uhv_huge_output_hatch": "§4UHV§r巨型输出仓", + "block.gtceu.uhv_lightning_processor": "§4史诗闪电处理机§r", + "block.gtceu.uhv_neutron_accelerator": "§4UHV 中子加速器§r", + "block.gtceu.uhv_parallel_hatch": "§4UHV§r并行控制仓", + "block.gtceu.uhv_rotor_holder": "§4UHV§r转子支架", + "block.gtceu.uhv_world_data_scanner": "§4史诗世界信息扫描仪§r", + "block.gtceu.uiv_1048576a_laser_source_hatch": "1048576安§2UIV§r激光源仓", + "block.gtceu.uiv_1048576a_laser_target_hatch": "1048576安§2UIV§r激光靶仓", + "block.gtceu.uiv_16384a_laser_source_hatch": "16384安§2UIV§r激光源仓", + "block.gtceu.uiv_16384a_laser_target_hatch": "16384安§2UIV§r激光靶仓", + "block.gtceu.uiv_16777216a_laser_source_hatch": "16777216安§2UIV§r激光源仓", + "block.gtceu.uiv_16777216a_laser_target_hatch": "16777216安§2UIV§r激光靶仓", + "block.gtceu.uiv_262144a_laser_source_hatch": "262144安§2UIV§r激光源仓", + "block.gtceu.uiv_262144a_laser_target_hatch": "262144安§2UIV§r激光靶仓", + "block.gtceu.uiv_4194304a_laser_source_hatch": "4194304安§2UIV§r激光源仓", + "block.gtceu.uiv_4194304a_laser_target_hatch": "4194304安§2UIV§r激光靶仓", + "block.gtceu.uiv_65536a_laser_source_hatch": "65536安§2UIV§r激光源仓", + "block.gtceu.uiv_65536a_laser_target_hatch": "65536安§2UIV§r激光靶仓", + "block.gtceu.uiv_67108864a_laser_source_hatch": "67108864安§2UIV§r激光源仓", + "block.gtceu.uiv_67108864a_laser_target_hatch": "67108864安§2UIV§r激光靶仓", + "block.gtceu.uiv_buffer": "§2史诗缓存器 III§r", + "block.gtceu.uiv_dehydrator": "§2史诗脱水机 III§r", + "block.gtceu.uiv_energy_input_hatch_16a": "16安§2UIV§r能源仓", + "block.gtceu.uiv_energy_input_hatch_4a": "4安§2UIV§r能源仓", + "block.gtceu.uiv_energy_output_hatch_16a": "16安§2UIV§r动力仓", + "block.gtceu.uiv_energy_output_hatch_4a": "4安§2UIV§r动力仓", + "block.gtceu.uiv_huge_input_hatch": "§2UIV§r巨型输入仓", + "block.gtceu.uiv_huge_output_hatch": "§2UIV§r巨型输出仓", + "block.gtceu.uiv_lightning_processor": "§2史诗闪电处理机 III§r", + "block.gtceu.uiv_neutron_accelerator": "§2UIV 中子加速器§r", + "block.gtceu.uiv_parallel_hatch": "§2UIV§r并行控制仓", + "block.gtceu.uiv_substation_input_hatch_64a": "64安§2UIV§r变电能源仓", + "block.gtceu.uiv_substation_output_hatch_64a": "64安§2UIV§r变电动力仓", + "block.gtceu.uiv_world_data_scanner": "§2史诗世界信息扫描仪 III§r", + "block.gtceu.ulv_huge_input_hatch": "§8ULV§r巨型输入仓", + "block.gtceu.ulv_huge_output_hatch": "§8ULV§r巨型输出仓", + "block.gtceu.ulv_neutron_accelerator": "§8ULV 中子加速器§r", + "block.gtceu.ulv_primitive_magic_energy": "超低压原始魔法能源吸收器", + "block.gtceu.uv_1048576a_laser_source_hatch": "1048576安§3UV§r激光源仓", + "block.gtceu.uv_1048576a_laser_target_hatch": "1048576安§3UV§r激光靶仓", + "block.gtceu.uv_16384a_laser_source_hatch": "16384安§3UV§r激光源仓", + "block.gtceu.uv_16384a_laser_target_hatch": "16384安§3UV§r激光靶仓", + "block.gtceu.uv_16777216a_laser_source_hatch": "16777216安§3UV§r激光源仓", + "block.gtceu.uv_16777216a_laser_target_hatch": "16777216安§3UV§r激光靶仓", + "block.gtceu.uv_262144a_laser_source_hatch": "262144安§3UV§r激光源仓", + "block.gtceu.uv_262144a_laser_target_hatch": "262144安§3UV§r激光靶仓", + "block.gtceu.uv_4194304a_laser_source_hatch": "4194304安§3UV§r激光源仓", + "block.gtceu.uv_4194304a_laser_target_hatch": "4194304安§3UV§r激光靶仓", + "block.gtceu.uv_65536a_laser_source_hatch": "65536安§3UV§r激光源仓", + "block.gtceu.uv_65536a_laser_target_hatch": "65536安§3UV§r激光靶仓", + "block.gtceu.uv_67108864a_laser_source_hatch": "67108864安§3UV§r激光源仓", + "block.gtceu.uv_67108864a_laser_target_hatch": "67108864安§3UV§r激光靶仓", + "block.gtceu.uv_buffer": "§3终极缓存器§r", + "block.gtceu.uv_compressed_fusion_reactor": "压缩核聚变反应堆控制电脑 MK-III", + "block.gtceu.uv_dehydrator": "§3终极脱水机§r", + "block.gtceu.uv_huge_input_hatch": "§3UV§r巨型输入仓", + "block.gtceu.uv_huge_output_hatch": "§3UV§r巨型输出仓", + "block.gtceu.uv_lightning_processor": "§3终极闪电处理机§r", + "block.gtceu.uv_neutron_accelerator": "§3UV 中子加速器§r", + "block.gtceu.uv_world_data_scanner": "§3终极世界信息扫描仪§r§r", + "block.gtceu.uxv_1048576a_laser_source_hatch": "1048576安§eUXV§r激光源仓", + "block.gtceu.uxv_1048576a_laser_target_hatch": "1048576安§eUXV§r激光靶仓", + "block.gtceu.uxv_16384a_laser_source_hatch": "16384安§eUXV§r激光源仓", + "block.gtceu.uxv_16384a_laser_target_hatch": "16384安§eUXV§r激光靶仓", + "block.gtceu.uxv_16777216a_laser_source_hatch": "16777216安§eUXV§r激光源仓", + "block.gtceu.uxv_16777216a_laser_target_hatch": "16777216安§eUXV§r激光靶仓", + "block.gtceu.uxv_262144a_laser_source_hatch": "262144安§eUXV§r激光源仓", + "block.gtceu.uxv_262144a_laser_target_hatch": "262144安§eUXV§r激光靶仓", + "block.gtceu.uxv_4194304a_laser_source_hatch": "4194304安§eUXV§r激光源仓", + "block.gtceu.uxv_4194304a_laser_target_hatch": "4194304安§eUXV§r激光靶仓", + "block.gtceu.uxv_65536a_laser_source_hatch": "65536安§eUXV§r激光源仓", + "block.gtceu.uxv_65536a_laser_target_hatch": "65536安§eUXV§r激光靶仓", + "block.gtceu.uxv_67108864a_laser_source_hatch": "67108864安§eUXV§r激光源仓", + "block.gtceu.uxv_67108864a_laser_target_hatch": "67108864安§eUXV§r激光靶仓", + "block.gtceu.uxv_buffer": "§e史诗缓存器 IV§r", + "block.gtceu.uxv_dehydrator": "§e史诗脱水机 IV§r", + "block.gtceu.uxv_energy_input_hatch_16a": "16安§eUXV§r能源仓", + "block.gtceu.uxv_energy_input_hatch_4a": "4安§eUXV§r能源仓", + "block.gtceu.uxv_energy_output_hatch_16a": "16安§eUXV§r动力仓", + "block.gtceu.uxv_energy_output_hatch_4a": "4安§eUXV§r动力仓", + "block.gtceu.uxv_huge_input_hatch": "§eUXV§r巨型输入仓", + "block.gtceu.uxv_huge_output_hatch": "§eUXV§r巨型输出仓", + "block.gtceu.uxv_lightning_processor": "§e史诗闪电处理机 IV§r", + "block.gtceu.uxv_neutron_accelerator": "§eUXV 中子加速器§r", + "block.gtceu.uxv_parallel_hatch": "§eUXV§r并行控制仓", + "block.gtceu.uxv_substation_input_hatch_64a": "64安§eUXV§r变电能源仓", + "block.gtceu.uxv_substation_output_hatch_64a": "64安§eUXV§r变电动力仓", + "block.gtceu.uxv_world_data_scanner": "§e史诗世界信息扫描仪 IV§r", + "block.gtceu.vacuum_drying_furnace": "真空干燥炉", + "block.gtceu.void_fluid_drilling_rig": "虚空流体钻机", + "block.gtceu.void_miner": "虚空采矿机", + "block.gtceu.weather_control": "天气控制器", + "block.gtceu.wireless_data_receiver_hatch": "无线光学数据靶仓", + "block.gtceu.wireless_data_transmitter_hatch": "无线光学数据源仓", + "block.gtceu.wood_distillation": "木化工厂", + "block.gtceu.zpm_1048576a_laser_source_hatch": "1048576安§cZPM§r激光源仓", + "block.gtceu.zpm_1048576a_laser_target_hatch": "1048576安§cZPM§r激光靶仓", + "block.gtceu.zpm_16384a_laser_source_hatch": "16384安§cZPM§r激光源仓", + "block.gtceu.zpm_16384a_laser_target_hatch": "16384安§cZPM§r激光靶仓", + "block.gtceu.zpm_16777216a_laser_source_hatch": "16777216安§cZPM§r激光源仓", + "block.gtceu.zpm_16777216a_laser_target_hatch": "16777216安§cZPM§r激光靶仓", + "block.gtceu.zpm_262144a_laser_source_hatch": "262144安§cZPM§r激光源仓", + "block.gtceu.zpm_262144a_laser_target_hatch": "262144安§cZPM§r激光靶仓", + "block.gtceu.zpm_4194304a_laser_source_hatch": "4194304安§cZPM§r激光源仓", + "block.gtceu.zpm_4194304a_laser_target_hatch": "4194304安§cZPM§r激光靶仓", + "block.gtceu.zpm_65536a_laser_source_hatch": "65536安§cZPM§r激光源仓", + "block.gtceu.zpm_65536a_laser_target_hatch": "65536安§cZPM§r激光靶仓", + "block.gtceu.zpm_67108864a_laser_source_hatch": "67108864安§cZPM§r激光源仓", + "block.gtceu.zpm_67108864a_laser_target_hatch": "67108864安§cZPM§r激光靶仓", + "block.gtceu.zpm_buffer": "§c精英缓存器 III§r", + "block.gtceu.zpm_compressed_fusion_reactor": "压缩核聚变反应堆控制电脑 MK-II", + "block.gtceu.zpm_dehydrator": "§c精英脱水机 III§r", + "block.gtceu.zpm_fluid_drilling_rig": "§c无尽流体钻机§r", + "block.gtceu.zpm_huge_input_hatch": "§cZPM§r巨型输入仓", + "block.gtceu.zpm_huge_output_hatch": "§cZPM§r巨型输出仓", + "block.gtceu.zpm_lightning_processor": "§c精英闪电处理机 III§r", + "block.gtceu.zpm_naquadah_reactor": "§c精英硅岩反应堆§r", + "block.gtceu.zpm_neutron_accelerator": "§cZPM 中子加速器§r", + "block.gtceu.zpm_world_data_scanner": "§c精英世界信息扫描仪 III§r", + "config.gtceu.option.hand.output": "手动输出", + "config.jade.plugin_gtceu.me_molecular_assembler_io": "ME分子装配室端口", + "gtceu.advanced_hyper_reactor": "进阶超能反应", + "gtceu.aggregation_device": "聚合装置", + "gtceu.annihilate_generator": "湮灭发电机", + "gtceu.assembler_module": "太空组装", + "gtceu.atomic_energy_excitation": "原子能激发", + "gtceu.bedrock_drilling_rig": "基岩钻机", + "gtceu.block_conversion": "方块转换", + "gtceu.casings.tier": "等级:%s", + "gtceu.circuit_assembly_line": "电路装配线", + "gtceu.circuit_printer": "编程电路复制", + "gtceu.component_assembly_line": "部件装配", + "gtceu.cosmos_simulation": "宇宙模拟", + "gtceu.create_aggregation": "创造聚合", + "gtceu.decay_hastener": "衰变加速", + "gtceu.dehydrator": "脱水机", + "gtceu.desulfurizer": "脱硫", + "gtceu.digestion_treatment": "煮解", + "gtceu.dimensional_focus_engraving_array": "维度聚焦激光蚀刻", + "gtceu.dimensionally_transcendent_mixer": "超维度搅拌", + "gtceu.dimensionally_transcendent_plasma_forge": "超维度熔炼", + "gtceu.disassembly": "拆解", + "gtceu.dissolution_treatment": "溶解", + "gtceu.distort": "深度化学扭曲仪", + "gtceu.door_of_create": "创造之门", + "gtceu.dragon_egg_copier": "龙蛋复制", + "gtceu.drilling_module": "太空钻井", + "gtceu.dyson_sphere": "戴森球", + "gtceu.electric_implosion_compressor": "电力聚爆压缩机", + "gtceu.element_copying": "元素复制机", + "gtceu.fishing_ground": "渔场", + "gtceu.fission_reactor": "裂变反应堆", + "gtceu.flotating_beneficiation": "浮游选矿", + "gtceu.fuel_refining": "燃料精炼", + "gtceu.gravitation_shockburst": "强引力震爆", + "gtceu.greenhouse": "温室", + "gtceu.gui.content.chance_1_in": "消耗概率:%s%%", + "gtceu.gui.content.chance_1_logic_in": "消耗概率:%s%% (%s)", + "gtceu.gui.content.tier_boost_fix": "电压加成:%s%%/每级", + "gtceu.heat_exchanger": "热交换机", + "gtceu.hyper_reactor": "超能反应", + "gtceu.incubator": "培养缸", + "gtceu.integrated_ore_processor": "集成矿石处理", + "gtceu.isa_mill": "湿法碾磨", + "gtceu.jei.bedrock_fluid.benzene_deposit": "苯矿藏", + "gtceu.jei.bedrock_fluid.ceres_krypton_deposit": "氪矿藏", + "gtceu.jei.bedrock_fluid.ceres_neon_deposit": "氖矿藏", + "gtceu.jei.bedrock_fluid.ceres_radon_deposit": "氡矿藏", + "gtceu.jei.bedrock_fluid.ceres_xenon_deposit": "氙矿藏", + "gtceu.jei.bedrock_fluid.charcoal_byproducts": "木炭副产矿藏", + "gtceu.jei.bedrock_fluid.chlorine": "氯矿藏", + "gtceu.jei.bedrock_fluid.coal_gas_deposit": "煤气矿藏", + "gtceu.jei.bedrock_fluid.deuterium_deposit": "氘矿藏", + "gtceu.jei.bedrock_fluid.flat_heavy_oil_deposit": "超平坦重油矿藏", + "gtceu.jei.bedrock_fluid.flat_light_oil_deposit": "超平坦轻油矿藏", + "gtceu.jei.bedrock_fluid.flat_natural_gas_deposit": "超平坦天然气矿藏", + "gtceu.jei.bedrock_fluid.flat_oil_deposit": "超平坦石油矿藏", + "gtceu.jei.bedrock_fluid.flat_raw_oil_deposit": "超平坦原油矿藏", + "gtceu.jei.bedrock_fluid.flat_salt_water_deposit": "超平坦盐水矿藏", + "gtceu.jei.bedrock_fluid.fluorine": "氟矿藏", + "gtceu.jei.bedrock_fluid.helium3_deposit": "氦-3矿藏", + "gtceu.jei.bedrock_fluid.helium_deposit": "氦矿藏", + "gtceu.jei.bedrock_fluid.hydrochloric_acid_deposit": "盐酸矿藏", + "gtceu.jei.bedrock_fluid.methane_deposit": "甲烷矿藏", + "gtceu.jei.bedrock_fluid.nitric_acid_deposit": "硝酸矿藏", + "gtceu.jei.bedrock_fluid.radon_deposit": "氡矿藏", + "gtceu.jei.bedrock_fluid.sulfuric_acid_deposit": "硫酸矿藏", + "gtceu.jei.bedrock_fluid.unknowwater": "不明液体矿藏", + "gtceu.jei.bedrock_fluid.void_heavy_oil_deposit": "虚空重油矿藏", + "gtceu.jei.bedrock_fluid.void_light_oil_deposit": "虚空轻油矿藏", + "gtceu.jei.bedrock_fluid.void_natural_gas_deposit": "虚空天然气矿藏", + "gtceu.jei.bedrock_fluid.void_oil_deposit": "虚空石油矿藏", + "gtceu.jei.bedrock_fluid.void_raw_oil_deposit": "虚空原油矿藏", + "gtceu.jei.bedrock_fluid.void_salt_water_deposit": "虚空盐水矿藏", + "gtceu.jei.ore_vein.apatite_vein_ad": "磷矿脉", + "gtceu.jei.ore_vein.bauxite_vein_ad": "金红石矿脉", + "gtceu.jei.ore_vein.beryllium_vein_aw": "绿宝石矿脉", + "gtceu.jei.ore_vein.calorite_vein_ad": "耐热矿脉", + "gtceu.jei.ore_vein.celestine_vein_ad": "天青石矿脉", + "gtceu.jei.ore_vein.certus_quartz_vein_aw": "AE2矿脉", + "gtceu.jei.ore_vein.desh_vein_ad": "戴斯矿脉", + "gtceu.jei.ore_vein.molybdenum_vein_aw": "钼矿脉", + "gtceu.jei.ore_vein.monazite_vein_ad": "群居石矿脉", + "gtceu.jei.ore_vein.naquadah_vein_ad": "硅岩矿脉", + "gtceu.jei.ore_vein.nickel_vein_ad": "镍矿脉", + "gtceu.jei.ore_vein.olivine_vein_ad": "橄榄石矿脉", + "gtceu.jei.ore_vein.ostrum_vein_ad": "紫金矿脉", + "gtceu.jei.ore_vein.pitchblende_vein_ad": "铀矿脉", + "gtceu.jei.ore_vein.plutonium_vein_ad": "钚矿脉", + "gtceu.jei.ore_vein.quartzite_vein_aw": "石英岩矿脉", + "gtceu.jei.ore_vein.saltpeter_vein_aw": "硝石矿脉", + "gtceu.jei.ore_vein.scheelite_vein_ad": "钨矿脉", + "gtceu.jei.ore_vein.sheldonite_vein_ad": "铂系矿脉", + "gtceu.jei.ore_vein.stibnite_vein_aw": "锑辉矿脉", + "gtceu.jei.ore_vein.sulfur_vein_ad": "硫矿脉", + "gtceu.jei.ore_vein.sulfur_vein_aw": "硫磺矿脉", + "gtceu.jei.ore_vein.topaz_vein_aw": "黄玉矿脉", + "gtceu.jei.ore_vein.zircon_vein_ad": "锆石矿脉", + "gtceu.large_gas_collector": "大型集气室", + "gtceu.large_naquadah_reactor": "大型硅岩反应", + "gtceu.large_recycler": "材料回收", + "gtceu.large_void_miner": "精准矿石模式", + "gtceu.lava_furnace": "熔岩炉", + "gtceu.lightning_processor": "闪电处理", + "gtceu.machine.advanced_assembly_line.tooltip.0": "可拓展至64格", + "gtceu.machine.advanced_assembly_line.tooltip.1": "只能使用数据靶仓", + "gtceu.machine.advanced_hyper_reactor.tooltip.0": "提供不同等离子体获得不同并行", + "gtceu.machine.advanced_hyper_reactor.tooltip.1": "星辉:8,致密中子:16", + "gtceu.machine.advanced_infinite_driller.current_heat": "当前温度: %sK", + "gtceu.machine.advanced_infinite_driller.drilled_fluid": "流体: %s 产量: %s", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.0": "更加高级的无尽流体钻井", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.1": "可以更加高效的抽取流体, 与此同时钻机需要一定温度来启动,可以通入液态烈焰来升温", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.10": "液态烈焰消耗公式为(当前温度^1.3)", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.11": "可使用温度传感器", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.2": "可放入中子素钻头(50000)/振金钻头(100000)", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.3": "基础温度: 8000K(泰坦钢线圈), 使用更高级的线圈可以提供更高的额外温度", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.4": "钻机在不同的工作温度下会产生热量,产热公式为(温度/2000)", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.5": "随着温度提升,效率也会提升.当温度超过临界值,钻头将会融毁", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.6": "可以通入不同的流体来降温, 可用冷却液如下.冷却固定消耗为200B/5t", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.7": "蒸馏水 1K ", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.8": "液态氧 2K ", + "gtceu.machine.advanced_infinite_driller.drilled_fluid.tooltip.9": "液态氦 4K", + "gtceu.machine.advanced_infinite_driller.fast": "高速模式: %s ", + "gtceu.machine.advanced_infinite_driller.heat": "最大温度: %sK / 工作温度: %sK", + "gtceu.machine.advanced_infinite_driller.no_coil": "线圈等级过低", + "gtceu.machine.advanced_infinite_driller.not_fluid_head": "无钻头", + "gtceu.machine.advanced_infinite_driller.process": "损坏: %s", + "gtceu.machine.advanced_infinite_driller.range": "工作范围: %s", + "gtceu.machine.advanced_integrated_ore_processor.tooltip.0": "最大并行数:2147483647", + "gtceu.machine.advanced_neutron_activator.tooltip.1": "工作时会消耗对应电量,自动提供中子动能", + "gtceu.machine.advanced_neutron_activator.tooltip.2": "中子动能自动转换比例为2eu -> 1ev", + "gtceu.machine.aggregation_device.tooltip.0": "电压等级每高出UEV一级最大并行数x2", + "gtceu.machine.assembly_line.tooltip.0": "每个装配线单元可使速度提升1%", + "gtceu.machine.available_recipe_map_10.tooltip": "可用配方类型:%s,%s,%s,%s,%s,%s,%s,%s,%s,%s", + "gtceu.machine.available_recipe_map_11.tooltip": "可用配方类型:%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s", + "gtceu.machine.available_recipe_map_5.tooltip": "可用配方类型:%s,%s,%s,%s,%s", + "gtceu.machine.available_recipe_map_6.tooltip": "可用配方类型:%s,%s,%s,%s,%s,%s", + "gtceu.machine.available_recipe_map_7.tooltip": "可用配方类型:%s,%s,%s,%s,%s,%s,%s", + "gtceu.machine.available_recipe_map_8.tooltip": "可用配方类型:%s,%s,%s,%s,%s,%s,%s,%s", + "gtceu.machine.available_recipe_map_9.tooltip": "可用配方类型:%s,%s,%s,%s,%s,%s,%s,%s,%s", + "gtceu.machine.bedrock_drilling_rig.tooltip.0": "需要基岩在钻头下方", + "gtceu.machine.bedrock_drilling_rig.tooltip.1": "每次运行都有10%的概率破坏基岩", + "gtceu.machine.blaze_blast_furnace.tooltip.0": "需每秒提供§b10x(功率÷120)^1/2§rmb的§e液态烈焰§r", + "gtceu.machine.blaze_blast_furnace.tooltip.1": "最大并行数固定为64", + "gtceu.machine.block_conversion_room.am": "每次转化数量:%s", + "gtceu.machine.block_conversion_room.tooltip.0": "每秒随机选取机器内部一个位置的方块进行转化", + "gtceu.machine.block_conversion_room.tooltip.1": "电压等级每高出MV1级,每次转换方块数量+4,且不会重复", + "gtceu.machine.chemical_distort.tooltip.0": "线圈温度每高出配方温度100K,并行加4", + "gtceu.machine.chemical_distort.tooltip.1": "更高的电压不会提供额外温度", + "gtceu.machine.chemical_plant.tooltip.0": "线圈等级每高出白铜一级能耗与速度x5%", + "gtceu.machine.circuit_assembly_line.tooltip.0": "在主机中放入同配方的机器人可以获得对应数量x2的并行", + "gtceu.machine.cold_ice_freezer.tooltip.0": "需每秒提供§b10x电压等级^2§rmb的§b液态冰§r", + "gtceu.machine.create_computation.tooltip.0": "输入电压:§4§lMAX§r", + "gtceu.machine.dimensionally_transcendent_dirt_forge.tooltip.0": "拥有524288的最大并行,且直接完成配方", + "gtceu.machine.dimensionally_transcendent_mixer.tooltip.0": "运行搅拌机配方时耗时倍数为0.2", + "gtceu.machine.duration_multiplier.tooltip": "耗时倍数:%s", + "gtceu.machine.dyson_sphere.number": "发射次数:%s / 10000", + "gtceu.machine.dyson_sphere.tooltip.0": "发射戴森球模块后开始工作", + "gtceu.machine.dyson_sphere.tooltip.1": "每次运行都有(模块数量/128 + 1)%的概率损坏一次模块", + "gtceu.machine.dyson_sphere.tooltip.2": "当损坏高于60%时,输出效率随损坏值由100%逐渐降低到20%,并输出随损坏值增强的红石信号", + "gtceu.machine.dyson_sphere.tooltip.3": "当损坏达到100%时减少一次模块发射数量,并重制损坏值", + "gtceu.machine.dyson_sphere.tooltip.4": "在损坏值高于60%时发射不会增加发射次数,但会重制损坏值", + "gtceu.machine.dyson_sphere.tooltip.5": "产能功率,和需求算力由发射的模块数量决定", + "gtceu.machine.dyson_sphere.tooltip.6": "每次发射可使功率增加1A MAX", + "gtceu.machine.dyson_sphere.voltage": "最大能量输出:%s EU/t", + "gtceu.machine.efficiency.tooltip": "§7效率:§r%s", + "gtceu.machine.electric_blast_furnace.tooltip.a": "耗时倍数:max(配方温度/炉温,0.5)", + "gtceu.machine.engraving_laser_plant.tooltip.0": "精密激光模式不支持并行", + "gtceu.machine.eut_multiplier.tooltip": "耗能倍数:%s", + "gtceu.machine.eye_of_harmony.tooltip.0": "创造一个微缩宇宙,并获取里面的资源", + "gtceu.machine.eye_of_harmony.tooltip.1": "这台多方块机器需要太多EU,无法用常规手段供能", + "gtceu.machine.eye_of_harmony.tooltip.2": "由无线EU网络直接供给EU,具体数值可在GUI内查看", + "gtceu.machine.eye_of_harmony.tooltip.3": "执行特殊超频模式,每提升16倍功率提升2倍速度,超频由编程电路调节", + "gtceu.machine.eye_of_harmony.tooltip.4": "工作前需设置好电路,1号不执行超频,2-4分别执行1,2,3次超频", + "gtceu.machine.eye_of_harmony.tooltip.5": "启动需1024B的宇宙素,与1024KB的氢和氦", + "gtceu.machine.eye_of_harmony.tooltip.6": "氢和氦存储在机器内部,在机器准备工作之前会持续消耗", + "gtceu.machine.eye_of_harmony.tooltip.7": "使用闪存右键重新绑定无线电网", + "gtceu.machine.fission_reactor.cooler": "冷却组件数量:%s,相邻数:%s", + "gtceu.machine.fission_reactor.damaged": "损坏:%s", + "gtceu.machine.fission_reactor.fuel": "燃料组件数量:%s,相邻数:%s", + "gtceu.machine.fission_reactor.heat": "堆温:%s K", + "gtceu.machine.fission_reactor.tooltip.0": "反应堆在运行前会获得最大并行数量为燃料组件数量的并行", + "gtceu.machine.fission_reactor.tooltip.1": "反应堆在运行过程中会根据条件升温,每秒升温值(K)=配方产热x(1+相邻燃料棒数量)", + "gtceu.machine.fission_reactor.tooltip.10": "如果供给量是需求量的n倍,则执行特殊超频,超频次数为n", + "gtceu.machine.fission_reactor.tooltip.11": "再次消耗需求量的冷却液,并减少1秒时间,如果无法供给冷却液则中断超频,如果进度到达100%则中断超频并消耗一次需求量的冷却液使温度降低1K", + "gtceu.machine.fission_reactor.tooltip.12": "由蒸馏水作为冷却液时将产生蒸汽,产生量:消耗量xmin(160,160/(1.4^(373-温度)))", + "gtceu.machine.fission_reactor.tooltip.13": "由钠钾合金作为冷却液时产生热钠钾合金,产生量=消耗量,如果温度高于825K则产生同等量的超临界钠钾合金", + "gtceu.machine.fission_reactor.tooltip.14": "无论反应堆是否具有消耗冷却液的条件都能执行配方", + "gtceu.machine.fission_reactor.tooltip.15": "反应堆停止工作后温度将每秒降低1K", + "gtceu.machine.fission_reactor.tooltip.2": "如果温度高于1500K,反应堆将会损坏,损坏达到100%后反应堆爆炸", + "gtceu.machine.fission_reactor.tooltip.3": "在运行过程中提供冷却液并根据不同冷却液的冷却液系数来控制温度", + "gtceu.machine.fission_reactor.tooltip.4": "冷却液系数:蒸馏水:800,钠钾合金:20", + "gtceu.machine.fission_reactor.tooltip.5": "反应堆冷却有如下参数:", + "gtceu.machine.fission_reactor.tooltip.6": "最低冷却液需求量和最高冷却液供给量", + "gtceu.machine.fission_reactor.tooltip.7": "最低需求量=配方产热x实际并行数量x当前温度/1500", + "gtceu.machine.fission_reactor.tooltip.8": "最高供给量=(冷却组件-(相邻数/3))x8", + "gtceu.machine.fission_reactor.tooltip.9": "当供给量>=需求量时达到消耗冷却液条件,消耗提供的冷却液,消耗量为需求量x冷却液系数,并阻止反应堆升温", + "gtceu.machine.flotation_cell_regulator.tooltip.0": "工业级浮游选矿池", + "gtceu.machine.fusion_reactor.tooltip.a": "机器配方等级每高出机器等级1级,最大并行数x4,最高16", + "gtceu.machine.generator_array.tooltip.0": "强大的运行环境会让机器中的小发电机功率x2。", + "gtceu.machine.generator_array.tooltip.1": "可以开启无线电网模式,产生的电能会直接送入无线电网。", + "gtceu.machine.generator_array.wireless": "无线电网模式:", + "gtceu.machine.greenhouse.tooltip.0": "需要阳光才能运行", + "gtceu.machine.greenhouse.tooltip.1": "如太阳光照不足,速度就会减缓", + "gtceu.machine.heat_exchanger.tooltip.0": "每次处理全部输入的热流体", + "gtceu.machine.heat_exchanger.tooltip.1": "需要保证输入的冷却液能将流体全部冷却", + "gtceu.machine.heat_sensor.tooltip.0": "基于§6温度§7输出红石信号,右键以打开GUI进行设置", + "gtceu.machine.hyper_reactor.tooltip.0": "每次运行前提供额外的1mb等离子体将获得16的并行", + "gtceu.machine.hyper_reactor.tooltip.1": "不同燃料所需的等离子体不同", + "gtceu.machine.hyper_reactor.tooltip.2": "从1-4顺序为:山铜,末影,魔金,亚稳态\uD872\uDF76", + "gtceu.machine.integrated_ore_processor.tooltip.0": "一步完成矿石处理", + "gtceu.machine.integrated_ore_processor.tooltip.1": "1号电路为研磨-研磨-离心", + "gtceu.machine.integrated_ore_processor.tooltip.2": "2号电路为研磨-洗矿-热离-研磨", + "gtceu.machine.integrated_ore_processor.tooltip.3": "3号电路为研磨-洗矿-研磨-离心", + "gtceu.machine.integrated_ore_processor.tooltip.4": "4号电路为研磨-洗矿-筛选-离心", + "gtceu.machine.integrated_ore_processor.tooltip.5": "5号电路为研磨-浸洗-热离-研磨", + "gtceu.machine.integrated_ore_processor.tooltip.6": "6号电路为研磨-浸洗-研磨-离心", + "gtceu.machine.integrated_ore_processor.tooltip.7": "7号电路为研磨-浸洗-筛选-离心", + "gtceu.machine.isa_mill.tooltip.0": "工业级湿法碾磨", + "gtceu.machine.large_arc_smelter.tooltip.0": "运行闪电处理配方时耗时x4", + "gtceu.machine.large_block_conversion_room.tooltip.1": "电压等级每高出MV1级,每次转换方块数量+64,且不会重复", + "gtceu.machine.large_block_conversion_room.tooltip.2": "放入转换模拟卡会启用虚拟模式, 放入高速转换模拟卡后单次可以转化所有总线内方块", + "gtceu.machine.large_gas_collector.tooltip.0": "最大并行数:100000", + "gtceu.machine.large_greenhouse.tooltip.0": "无需要阳光就能运行", + "gtceu.machine.large_recycler.tooltip.0": "电压等级每高出EV1级,最大并行数x4", + "gtceu.machine.large_rock_crusher.tooltip.0": "需要在输入仓中放入对应流体", + "gtceu.machine.large_steam_input_hatch.tooltip.0": "将蒸汽多方块配方限制提升到MV,并解锁超频功能", + "gtceu.machine.large_void_miner.tooltip.0": "精准模式消耗精华采集指定矿脉", + "gtceu.machine.large_void_miner.tooltip.1": "随机模式消耗10KB的钻井液和更长的耗时随机采集所有矿石,随机模式注意输出空间要足够", + "gtceu.machine.lightning_rod.tooltip.0": "上方避雷针被雷击后产生大量能量", + "gtceu.machine.lightning_rod.tooltip.1": "每0.5秒只能产生一次能量,且有一定几率破坏上方避雷针", + "gtceu.machine.lightning_rod.tooltip.2": "如果存储能量已满,机器将会爆炸", + "gtceu.machine.lockrecipe.line.0": "%s x §e%s§r ", + "gtceu.machine.lockrecipe.line.1": "%s x §e%s§r(出产概率:%s%%) ", + "gtceu.machine.lockrecipe.line.2": "%s x §e%s§r(§c不消耗§r) ", + "gtceu.machine.me.me_dual_hatch_stock.data_stick.name": "§oME库存输入总成配置数据", + "gtceu.machine.me.me_extended_export_buffer.data_stick.name": "§oME增广输出总成配置数据", + "gtceu.machine.me_craft_parallel_core.tooltip.0": "提供§64194304§f并行", + "gtceu.machine.me_craft_pattern_core.tooltip": "提供§6%d§f样板容量", + "gtceu.machine.me_craft_speed_core.tooltip.0": "每颗减少§68§fTick配方耗时", + "gtceu.machine.me_extended_async_export_buffer.tooltip.0": "允许配置§b优先级§f", + "gtceu.machine.me_extended_async_export_buffer.tooltip.1": "使用§6异步输出§f,适合跨配方机器", + "gtceu.machine.me_extended_export_buffer.tooltip.0": "允许配置§6输出过滤§f与§b优先级§f", + "gtceu.machine.me_mini_pattern_buffer.desc.0": "迷你版本§6不提供输出§f。", + "gtceu.machine.me_molecular_assembler_io.tooltip.0": "成型后可进行样板管理", + "gtceu.machine.me_molecular_assembler_io.tooltip.1": "产物自动回流至AE网络", + "gtceu.machine.me_pattern_buffer.desc.0": "§f所有样板槽位都拥有§6完全隔离§f。", + "gtceu.machine.me_pattern_buffer.desc.1": "对槽位§b中键§f打开专用§6催化剂槽§f, 左侧§6电路§f与§6催化剂§f栏位与所有样板共享。", + "gtceu.machine.me_pattern_buffer.desc.2": "在§6样板§f中填写对应电路作为虚拟电路,下单时§6无消耗§f,会§6覆盖§f共享电路设置。", + "gtceu.machine.me_pattern_buffer.desc.3": "自带§6样板倍增§f功能,只需填写基础数量的样板,下单时会一次性发配所需乘数。", + "gtceu.machine.me_pattern_buffer.desc.4": "自带§6配方缓存§f功能,单个槽位默认缓存一种配方,可自定义修改。", + "gtceu.machine.me_pattern_buffer.desc.5": "自带§6ME输出§f功能,无需放置额外的输出舱室。", + "gtceu.machine.me_pattern_buffer_proxy.desc.0": "镜像IO由§6绑定的ME样板总成§f决定。", + "gtceu.machine.module": "已安装的模块数量:%s", + "gtceu.machine.molecular_assembler_matrix.tooltip.0": "工业型§6AE样板§f合成工厂", + "gtceu.machine.molecular_assembler_matrix.tooltip.1": "支持§b合成样板§f,§b锻造台样板§f与§b切石机样板§f", + "gtceu.machine.molecular_assembler_matrix.tooltip.2": "合成样板只允许包含具有§6不毁§f特性的工具,需对应放置在端口催化剂槽内", + "gtceu.machine.molecular_assembler_matrix.tooltip.3": "下单时§6不会消耗§f相应工具", + "gtceu.machine.molecular_assembler_matrix.tooltip.4": "使用ME分子装配矩阵端口或样板管理终端进行样板管理", + "gtceu.machine.molecular_assembler_matrix.tooltip.5": "机器中央可填充§b速度核心§f,§b合成核心§f与§b样板核心§f", + "gtceu.machine.molecular_assembler_matrix.tooltip.6": "样板存储在§6样板核心§f内部,会在挖掘时掉落", + "gtceu.machine.multiple_recipes.tooltip": "支持跨配方并行", + "gtceu.machine.nano_core.tooltip.0": "能够运行任意等级的纳米锻炉配方", + "gtceu.machine.nano_core.tooltip.1": "处理速度固定为20倍", + "gtceu.machine.nano_core.tooltip.2": "最大并行数: 8192", + "gtceu.machine.nano_forge.tooltip.0": "放入对应的纳米蜂群才能工作,并且按蜂群数量来并行", + "gtceu.machine.nano_forge_1.tooltip.0": "并行数为蜂群数量", + "gtceu.machine.nano_forge_2.tooltip.0": "处理2阶配方时,并行数为蜂群数量", + "gtceu.machine.nano_forge_2.tooltip.1": "处理1阶配方时并行数量翻倍,超频模式改为无损超频", + "gtceu.machine.nano_forge_3.tooltip.0": "处理3阶配方时,并行数为蜂群数量", + "gtceu.machine.nano_forge_3.tooltip.1": "处理2阶配方时并行数量翻倍,超频模式改为无损超频", + "gtceu.machine.nano_forge_3.tooltip.2": "处理1阶配方时并行数翻4倍,超频模式改为每提高4倍功率获得8倍提速", + "gtceu.machine.neutron_accelerator.tooltip.0": "§6最大EU消耗:§r%s", + "gtceu.machine.neutron_accelerator.tooltip.1": "§b每点EU都会转化为§e10~20-eV§b中子动能", + "gtceu.machine.neutron_activator.efficiency": "动能消耗倍速:%s", + "gtceu.machine.neutron_activator.ev": "当前中子动能:%seV", + "gtceu.machine.neutron_activator.height": "高度:%s", + "gtceu.machine.neutron_activator.time": "耗时:%s", + "gtceu.machine.neutron_activator.tooltip.0": "§7超光速运动!", + "gtceu.machine.neutron_activator.tooltip.1": "额外的高速管道方块提供配方时间减免,同时降低中子加速器的效率", + "gtceu.machine.neutron_activator.tooltip.2": "时间减免与加速效率为0.95^额外方块数量", + "gtceu.machine.neutron_activator.tooltip.3": "没有中子加速器运行时,中子动能每秒降低§e72KeV§r中子动能", + "gtceu.machine.neutron_activator.tooltip.4": "输入石墨/铍粉可以立即吸收§e10MeV§r中子动能", + "gtceu.machine.neutron_sensor.tooltip.0": "基于§6中子动能§7输出红石信号,右键以打开GUI进行设置", + "gtceu.machine.off": "关闭", + "gtceu.machine.on": "打开", + "gtceu.machine.parallel_hatch_mk10.tooltip": "允许同时处理至多4096个配方。", + "gtceu.machine.parallel_hatch_mk11.tooltip": "允许同时处理至多16384个配方。", + "gtceu.machine.parallel_hatch_mk12.tooltip": "允许同时处理至多65536个配方。", + "gtceu.machine.parallel_hatch_mk13.tooltip": "允许同时处理至多262144个配方。", + "gtceu.machine.parallel_hatch_mk14.tooltip": "允许同时处理至多1048576个配方。", + "gtceu.machine.parallel_hatch_mk15.tooltip": "允许同时处理至多4194304个配方。", + "gtceu.machine.parallel_hatch_mk16.tooltip": "允许同时处理至多16777216个配方。", + "gtceu.machine.parallel_hatch_mk9.tooltip": "允许同时处理至多1024个配方。", + "gtceu.machine.pattern.recipe.cache": "配方已缓存", + "gtceu.machine.pcb_factory.tooltip.0": "放入纳米蜂群可获得减免", + "gtceu.machine.pcb_factory.tooltip.1": "时间倍数/每个:金:0.5%,振金:1%", + "gtceu.machine.pcb_factory.tooltip.2": "振金还可使能耗降低4倍", + "gtceu.machine.primitive_magic_energy.tooltip.0": "无尽地吸收机器上方末地水晶的能量", + "gtceu.machine.processing_plant.tooltip.0": "每种模式都需要放入一个对应电压等级的机器才能运行", + "gtceu.machine.processing_plant.tooltip.1": "电压等级每高出LV一级,最大并行数+4", + "gtceu.machine.resource_collection.tooltip.0": "最大并行数:4^(动力模块等级-1)", + "gtceu.machine.simulation_machine.tooltip.0": "将主方块放入机器中, 使用终端右键可放置/预览机器内内主方块的结构", + "gtceu.machine.slaughterhouse.is_spawn": "实体生成:", + "gtceu.machine.slaughterhouse.tooltip.0": "电动刷怪塔,自动杀怪", + "gtceu.machine.slaughterhouse.tooltip.1": "电压等级每高出MV1级,每次处理次数+8", + "gtceu.machine.slaughterhouse.tooltip.2": "运行前需设置电路,1号电路为非敌对生物,2号为敌对生物", + "gtceu.machine.slaughterhouse.tooltip.3": "打开控制器开关实体模式,实体生成模式为玩家击杀的实际掉落,需要非和平模式", + "gtceu.machine.slaughterhouse.tooltip.4": "实体生成模式为玩家击杀的实际掉落,需要非和平模式", + "gtceu.machine.slaughterhouse.tooltip.5": "非实体生成模式为虚拟掉落,可以和平模式,由玩家击杀的掉落物无法获取", + "gtceu.machine.space_elevator.set_out": "启程", + "gtceu.machine.space_elevator.tooltip.0": "可安装最多8个拓展模块", + "gtceu.machine.space_elevator.tooltip.1": "提升电压等级可为模块提供耗时减免", + "gtceu.machine.space_elevator.tooltip.2": "固定消耗128算力", + "gtceu.machine.space_probe_surface_reception.tooltip.0": "不要遮挡", + "gtceu.machine.star_ultimate_material_forge_factory.tooltip.0": "最大并行数:1000", + "gtceu.machine.structure_check": "更新结构检查", + "gtceu.machine.super_computation.tooltip.0": "根据不同的电压等级获得算力输出", + "gtceu.machine.super_computation.tooltip.1": "且每种算力输出需要不同的电路主机8个", + "gtceu.machine.super_computation.tooltip.2": "提供§2UIV§r级电压时,需要放入§b光学处理器主机§r,并提供1024CWU/t", + "gtceu.machine.super_computation.tooltip.3": "提供§eUXV§r级电压时,需要放入§b奇异处理器主机§r,并提供2048CWU/t", + "gtceu.machine.super_computation.tooltip.4": "提供§9§lOpV§r级电压时,需要放入§b寰宇处理器主机§r,并提供4096CWU/t", + "gtceu.machine.super_computation.tooltip.5": "提供§4§lMAX§r级电压时,需要放入§b超因果处理器主机§r,并提供8192CWU/t", + "gtceu.machine.suprachronal_assembly_line.tooltip.0": "§8§l不可视之触§r", + "gtceu.machine.suprachronal_assembly_line.tooltip.1": "可在两侧拓展模块,模块与主机共享并行数", + "gtceu.machine.suprachronal_assembly_line_module.tooltip.0": "安装在超时空装配线两侧", + "gtceu.machine.vacuum_drying_furnace.tooltip.0": "运行真空干燥配方时:", + "gtceu.machine.vacuum_drying_furnace.tooltip.1": "运行脱水配方时:", + "gtceu.machine.weather_control.tooltip.0": "1号电路切换晴天", + "gtceu.machine.weather_control.tooltip.1": "2号电路切换雨天", + "gtceu.machine.weather_control.tooltip.2": "3号电路切换雷暴", + "gtceu.machine.wireless_data_hatch.bind": "无线数据仓绑定完成。", + "gtceu.machine.wireless_data_receiver_hatch.bind": "已绑定无线光学数据源仓(%s)。", + "gtceu.machine.wireless_data_receiver_hatch.to_bind": "靶仓数据读取完成,请右键源仓进行绑定。", + "gtceu.machine.wireless_data_receiver_hatch.tooltip.1": "为多方块结构输入研究数据", + "gtceu.machine.wireless_data_receiver_hatch.tooltip.2": "需要使用闪存右键无线光学数据靶仓和无线数光学数据仓进行绑定。", + "gtceu.machine.wireless_data_receiver_hatch.unbind": "未绑定无线光学数据源仓。", + "gtceu.machine.wireless_data_transmitter_hatch.bind": "已绑定无线光学数据靶仓(%s)。", + "gtceu.machine.wireless_data_transmitter_hatch.to_bind": "源仓数据读取完成,请右键靶仓进行绑定。", + "gtceu.machine.wireless_data_transmitter_hatch.tooltip.1": "从多方块结构输出研究数据", + "gtceu.machine.wireless_data_transmitter_hatch.tooltip.2": "需要使用闪存右键无线光学数据靶仓和无线数据源仓光学进行绑定。", + "gtceu.machine.wireless_data_transmitter_hatch.unbind": "未绑定无线光学数据靶仓。", + "gtceu.machine.zpm_fluid_drilling_rig.tooltip": "甚至无消耗", + "gtceu.magic_manufacturer": "魔力生成", + "gtceu.mass_fabricator": "质量发生器", + "gtceu.matter_fabricator": "物质生成机", + "gtceu.miner_module": "太空采矿", + "gtceu.molecular_assembler": "分子装配", + "gtceu.multiblock.coil_parallel": "线圈温度每高出900K,并行数x2", + "gtceu.multiblock.dissolving_tank.tooltip.0": "必须保证输入的流体与配方流体比例相同,否则无产物输出", + "gtceu.multiblock.fusion_reactor_energy": "EU: %dM / %dM", + "gtceu.multiblock.large_combustion_engine.Joint_boosted": "§b联合促燃中", + "gtceu.multiblock.large_combustion_engine.supply_dinitrogen_tetroxide_to_boost": "提供四氧化二氮来联合促燃", + "gtceu.multiblock.laser.tooltip": "允许使用激光仓", + "gtceu.multiblock.mega_fluid_heater": "宿舍限电1500W,你们这是违规电器!", + "gtceu.multiblock.oc_amount": "超频次数:%s", + "gtceu.multiblock.only.laser.tooltip": "只能使用激光仓", + "gtceu.multiblock.pattern.error.tier": "§c必须使用同种等级方块§r", + "gtceu.multiblock.steam_parallel_machine.modification_oc": "修改超频次数:", + "gtceu.multiblock.steam_parallel_machine.oc": "每次超频将减少2倍耗时和增加3倍蒸汽消耗", + "gtceu.multiblock.uev_fusion_reactor.description": "核聚变反应堆MK-V是台大型多方块结构,用于融合元素形成更重的元素。它仅可使用UEV等级的能源仓。每个能源仓可增加160MEU的能量缓存,最大能量缓存为2560MEU。", + "gtceu.multiblock.uhv_fusion_reactor.description": "核聚变反应堆MK-IV是台大型多方块结构,用于融合元素形成更重的元素。它仅可使用UHV等级的能源仓。每个能源仓可增加80MEU的能量缓存,最大能量缓存为1280MEU。", + "gtceu.multiblock.universal.distinct.all": "完全隔离:", + "gtceu.multiblock.universal.rotor_obstructed": "转子支架被阻挡!", + "gtceu.nano_forge": "纳米蜂群工厂", + "gtceu.naquadah_reactor": "硅岩反应堆", + "gtceu.neutron_activator": "中子活化", + "gtceu.neutron_compressor": "奇点压缩", + "gtceu.pcb_factory": "PCB工厂", + "gtceu.petrochemical_plant": "石化工厂", + "gtceu.plasma_condenser": "等离子冷凝", + "gtceu.precision_assembler": "精密组装", + "gtceu.precision_laser_engraver": "精密激光蚀刻", + "gtceu.primitive_void_ore": "原始虚空采矿", + "gtceu.qft": "量子操纵者", + "gtceu.random_ore": "随机矿石模式", + "gtceu.rare_earth_centrifugal": "稀土离心", + "gtceu.recipe.ca_tier": "外壳等级:%s", + "gtceu.recipe.cleanroom_law.display_name": "绝对超净间", + "gtceu.recipe.eu_to_starts": "启动耗能:%sMEU(MK%s)", + "gtceu.recipe.ev_max": "最大中子动能:%s MeV", + "gtceu.recipe.ev_min": "最小中子动能:%s MeV", + "gtceu.recipe.evt": "每刻中子动能消耗:%s KeV", + "gtceu.recipe.fail.Input": "输入材料不足", + "gtceu.recipe.fail.Input.Content": "缺少输入: %s", + "gtceu.recipe.fail.Output": "输出空间不足", + "gtceu.recipe.fail.Output.Content": "无法输出: %s", + "gtceu.recipe.fail.block": "上方有方块阻挡", + "gtceu.recipe.fail.cleanroom": "未满足%s条件", + "gtceu.recipe.fail.find": "未找到配方", + "gtceu.recipe.fail.gravity.low": "未满足无重力条件", + "gtceu.recipe.fail.gravity.power": "未满足强重力条件", + "gtceu.recipe.fail.nano_forge.tier": "纳米煅炉等级不足", + "gtceu.recipe.fail.no.enough.cache.energy": "启动耗能不足", + "gtceu.recipe.fail.no.enough.cwu.in": "算力输入不足", + "gtceu.recipe.fail.no.enough.distilledwater": "缺少足够蒸馏水", + "gtceu.recipe.fail.no.enough.eV.consumed": "需求的中子动能过多", + "gtceu.recipe.fail.no.enough.eu.in": "电力输入不足", + "gtceu.recipe.fail.no.enough.eu.out": "电力输出空间不足", + "gtceu.recipe.fail.no.enough.neutron": "中子动能较低", + "gtceu.recipe.fail.no.enough.recipe.tier": "机器等级不足", + "gtceu.recipe.fail.no.enough.skylight": "缺少足够的天空光照", + "gtceu.recipe.fail.no.enough.temperature": "温度不足", + "gtceu.recipe.fail.no.find.researched": "未找到该配方的研究数据", + "gtceu.recipe.fail.no.input.fluid": "缺少输入液体(%s)", + "gtceu.recipe.fail.no.input.item": "缺少输入物品(%s)", + "gtceu.recipe.fail.no.ratio": "配方输入比例不正确", + "gtceu.recipe.fail.no.skylight": "缺少天空光照", + "gtceu.recipe.fail.no.venting": "排气口已堵塞", + "gtceu.recipe.fail.processing.plant.no.input": "未插入机器", + "gtceu.recipe.fail.processing.plant.wrong.input": "插入的机器电压或配方类型不匹配", + "gtceu.recipe.fail.reason": "配方失败原因:%s", + "gtceu.recipe.fail.rotor.different": "转子支架输入异常", + "gtceu.recipe.fail.rotor.isEmpty": "缺少涡轮转子", + "gtceu.recipe.fail.rotor_holder.tier": "转子支架等级太低", + "gtceu.recipe.fail.too.much.neutron": "中子动能过多", + "gtceu.recipe.fail.voltage.tier": "电压等级未达到配方要求", + "gtceu.recipe.frheat": "每秒升温:%s K", + "gtceu.recipe.grindball": "研磨球材质:%s", + "gtceu.recipe.nano_forge_tier": "纳米锻炉等级:%s", + "gtceu.recipe.sepm_tier": "需要动力模块:MK%s", + "gtceu.recipe.stellar_containment_tier": "恒星热力容器等级:%s", + "gtceu.rocket_engine": "火箭引擎", + "gtceu.semi_fluid_generator": "半流质发电机", + "gtceu.slaughterhouse": "屠宰场", + "gtceu.small_gas_collector": "小型集气室", + "gtceu.space_cosmic_probe_receivers": "天基宇宙探测", + "gtceu.space_elevator": "太空电梯", + "gtceu.space_probe_surface_reception": "宇宙探测", + "gtceu.sps_crafting": "超临界合成", + "gtceu.stellar_forge": "恒星热能熔炼", + "gtceu.super_computation": "超级计算机", + "gtceu.super_particle_collider": "粒子对撞", + "gtceu.supercritical_steam_turbine": "超临界蒸汽涡轮", + "gtceu.suprachronal_assembly_line": "超时空装配线", + "gtceu.tier.advanced": "高级", + "gtceu.tier.base": "基础", + "gtceu.tier.ultimate": "终极", + "gtceu.top.buffer_not_bound": "尚未绑定总成", + "gtceu.top.electricity": "%sA %s", + "gtceu.top.extra_output": "剩余%s项重复输出已隐藏", + "gtceu.top.pending_refunds": "待返回列表", + "gtceu.top.recipe_input": "配方输入:", + "gtceu.ultimate_material_forge": "终极物质锻造", + "gtceu.universal.tooltip.ampere_out": "§b输出电流:§r%sA", + "gtceu.vacuum_drying": "真空干燥", + "gtceu.void_fluid_drilling_rig": "虚空流体钻机", + "gtceu.void_miner": "虚空采矿", + "gtceu.weather_control": "天气控制", + "gtceu.wood_distillation": "木化工厂", + "gtceu.world_data_scanner": "世界信息扫描", + "gui.gtceu.neutron_sensor.invert.disabled.0": "红石输出:普通", + "gui.gtceu.neutron_sensor.invert.disabled.1": "点击为以反转红石逻辑", + "gui.gtceu.neutron_sensor.invert.disabled.2": "中子动能介于所设定的最小值和最大值之间时传感器将发出红石信号,小于最小值时则停止发出红石信号", + "gui.gtceu.neutron_sensor.invert.enabled.0": "红石输出:反转", + "gui.gtceu.neutron_sensor.invert.enabled.1": "点击切换为普通红石逻辑", + "gui.gtceu.neutron_sensor.invert.enabled.2": "中子动能介于所设定的最小值和最大值之外时传感器将发出红石信号,小于最小值时则发出红石信号", + "gui.gtlcore.recipe_lock.no_recipe": "未锁定配方", + "gui.gtlcore.recipe_lock.recipe": "已锁定配方", + "item.gtceu.tool.vajra": "%s金刚杵", + "material.gtceu.1_octene": "1-辛烯", + "material.gtceu.absolute_ethanol": "绝对乙醇", + "material.gtceu.abyssalalloy": "渊狱合金", + "material.gtceu.acetaldehyde": "乙醛", + "material.gtceu.acetonitrile": "乙腈", + "material.gtceu.acetyl_chloride": "乙酰氯", + "material.gtceu.acetylating_reagent": "乙酰化试剂", + "material.gtceu.acetylene": "乙炔", + "material.gtceu.acid_leached_fluoro_carbon_lanthanide_cerium_oxide_powder": "酸浸氟碳镧铈稀土氧化物", + "material.gtceu.acidic_iridium": "酸性铱", + "material.gtceu.acidic_monazite_powder": "酸性独居石", + "material.gtceu.acidic_naquadria_caesiumfluoride": "硫酸二氟超能硅岩酸铯", + "material.gtceu.acrylic_acid": "丙烯酸", + "material.gtceu.acrylonitrile": "丙烯腈", + "material.gtceu.actinium_nitrate": "硝酸锕", + "material.gtceu.actinium_oxalate": "草酸锕", + "material.gtceu.actinium_radium_hydroxide_solution": "氢氧化锕镭溶液", + "material.gtceu.actinium_radium_nitrate_solution": "硝酸锕镭溶液", + "material.gtceu.actinium_superhydride": "超氢化锕", + "material.gtceu.actinium_trinium_hydroxides": "氢氧化锕凯金", + "material.gtceu.actinoids": "锕系元素混合物", + "material.gtceu.actinoids_1": "轻锕系元素混合物", + "material.gtceu.actinoids_2": "重锕系元素混合物", + "material.gtceu.adamantine": "精金", + "material.gtceu.adamantine_compounds": "精金化合物", + "material.gtceu.adamantium": "艾德曼合金", + "material.gtceu.alien_algae": "异星藻类渣", + "material.gtceu.alkaline": "碱金属元素混合物", + "material.gtceu.alkaline_earth": "碱土金属元素混合物", + "material.gtceu.almandine_front": "铁铝榴石矿石泡沫", + "material.gtceu.alumina": "氧化铝", + "material.gtceu.aluminium_bronze": "铝青铜", + "material.gtceu.aluminium_hydride": "氢化铝", + "material.gtceu.aluminium_trifluoride": "氟化铝", + "material.gtceu.aminated_fullerene": "胺化富勒烯", + "material.gtceu.ammonium_bifluoride": "二氟化铵", + "material.gtceu.ammonium_bifluoride_solution": "氟氢化氨", + "material.gtceu.ammonium_fluoride": "氟化铵", + "material.gtceu.ammonium_nitrate_solution": "硝酸铵溶液", + "material.gtceu.ammonium_perrhenate": "高铼酸铵", + "material.gtceu.aniline": "苯胺", + "material.gtceu.anthracene": "蒽", + "material.gtceu.antihydrogen": "反氢", + "material.gtceu.antimatter": "离散反物质", + "material.gtceu.antimony_pentafluoride": "五氟化锑", + "material.gtceu.antimony_trichloride": "三氯化锑", + "material.gtceu.antineutron": "反中子", + "material.gtceu.antiproton": "反质子", + "material.gtceu.arceusalloy2b": "阿尔宙斯合金2B", + "material.gtceu.artherium_sn": "阿瑟锡", + "material.gtceu.ash_leaching_solution": "灰烬浸出液", + "material.gtceu.astatide_solution": "砹化物溶液", + "material.gtceu.astral_silver": "星辰银", + "material.gtceu.astraltitanium": "星体钛", + "material.gtceu.atinium_hydride": "氢化锕", + "material.gtceu.attuned_tengam": "谐镃", + "material.gtceu.azafullerene": "氮杂富勒烯", + "material.gtceu.barium_chloride": "氯化钡", + "material.gtceu.barnarda_air": "巴纳德C空气", + "material.gtceu.bedrock": "基岩", + "material.gtceu.bedrock_gas": "基岩气", + "material.gtceu.bedrock_smoke": "基岩烟", + "material.gtceu.bedrock_soot_solution": "基岩烟溶液", + "material.gtceu.benzaldehyde": "苯甲醛", + "material.gtceu.benzenediazonium_tetrafluoroborate": "四氟硼酸重氮苯", + "material.gtceu.benzophenanthrenylacetonitrile": "苯并菲乙腈", + "material.gtceu.benzyl_chloride": "氯化苄", + "material.gtceu.benzylamine": "苄胺", + "material.gtceu.biohmediumsterilized": "灭菌生物培养基原液", + "material.gtceu.biomediumraw": "生物培养基原液", + "material.gtceu.bisethylenedithiotetraselenafulvalene": "双(乙烯二硫代)四硒富瓦烯", + "material.gtceu.bisethylenedithiotetraselenafulvalene_perrhenate": "高铼酸双(乙烯二硫代)四硒富瓦烯", + "material.gtceu.bismuth_germanate": "锗酸铋", + "material.gtceu.bismuth_nitrate_solution": "硝酸铋溶液", + "material.gtceu.bismuth_tellurite": "亚碲酸铋", + "material.gtceu.black_dwarf_mtter": "黑矮星物质", + "material.gtceu.black_titanium": "黑钛合金", + "material.gtceu.bloodstone": "血石", + "material.gtceu.borane_dimethyl_sulfide": "硼烷二甲基硫醚", + "material.gtceu.boric_acide": "硼酸", + "material.gtceu.borocarbide": "碳化硼混合材料", + "material.gtceu.boron_carbide": "碳化硼", + "material.gtceu.boron_fluoride": "氟化硼", + "material.gtceu.boron_francium_carbide": "硼-钫碳化物", + "material.gtceu.boron_trifluoride_acetate": "三氟化硼乙酸酯", + "material.gtceu.boron_trioxide": "氧化硼", + "material.gtceu.brominated_brine": "含溴盐水", + "material.gtceu.bromine_trifluoride": "三氟化溴", + "material.gtceu.bromo_succinimide": "N-溴代琥珀酰亚胺", + "material.gtceu.bromobutane": "溴丁烷", + "material.gtceu.bromodihydrothiine": "溴二氢硫醚", + "material.gtceu.butane_1_4_diol": "1,4-丁二醇", + "material.gtceu.butyl_lithium": "丁基锂", + "material.gtceu.cadmium_sulfide": "硫化镉", + "material.gtceu.cadmium_tungstate": "钨酸镉", + "material.gtceu.caesium_fluoride": "氟化铯", + "material.gtceu.caesium_hydroxide": "氢氧化铯", + "material.gtceu.caesium_iodide": "碘化铯", + "material.gtceu.caesium_nitrate": "硝酸铯", + "material.gtceu.caesium_xenontrioxide_fluoride": "二氟三氧氙酸铯", + "material.gtceu.calcined_rare_earth_oxide_powder": "焙烧稀土氧化物", + "material.gtceu.calcium_carbide": "电石", + "material.gtceu.calcium_fluoride": "氟化钙", + "material.gtceu.californium_cyclopentadienide": "环戊二烯化锎", + "material.gtceu.californium_trichloride": "三氯化锎", + "material.gtceu.calorite": "耐热金属", + "material.gtceu.carbon_disulfide": "二硫化碳", + "material.gtceu.carbon_nanotubes": "碳纳米管", + "material.gtceu.carbon_tetrachloride": "四氯化碳", + "material.gtceu.celestialtungsten": "天体钨", + "material.gtceu.celestine": "天青石", + "material.gtceu.cerium_chloride_powder": "氯化铈", + "material.gtceu.cerium_oxalate_powder": "草酸铈", + "material.gtceu.cerium_oxide": "氧化铈", + "material.gtceu.cerium_oxide_rare_earth_oxide_powder": "氧化铈稀土氧化物", + "material.gtceu.cerium_rich_mixture_powder": "富铈混合物", + "material.gtceu.cesium_carborane": "碳酸铯", + "material.gtceu.cesium_carborane_precursor": "铯烷预固化剂", + "material.gtceu.chalcopyrite_front": "黄铜矿矿石泡沫", + "material.gtceu.chaos": "混沌物质", + "material.gtceu.charged_caesium_cerium_cobalt_indium": "带电的铯-铈-钴-铟", + "material.gtceu.cinobite": "西诺柏", + "material.gtceu.circuit_compound": "电路化合物", + "material.gtceu.clean_bedrock_solution": "洁净基岩烟溶液", + "material.gtceu.clean_inert_residues": "纯净的惰性残渣", + "material.gtceu.clean_raw_tengam": "洁净生镃", + "material.gtceu.co_ac_ab_catalyst": "Co/AC-AB催化剂", + "material.gtceu.compressed_stone": "压缩石头", + "material.gtceu.concentrated_cerium_chloride_solution": "氯化铈浓缩液", + "material.gtceu.concentrated_monazite_rare_earth_hydroxide_powder": "浓缩独居石稀土氢氧化物", + "material.gtceu.concentrated_nitric_leachate_from_monazite": "浓缩硝酸独居石浸出溶液", + "material.gtceu.concentrated_nitride_monazite_rare_earth_solution": "浓缩氮化独居石稀土溶液", + "material.gtceu.concentrated_rare_earth_chloride_solution": "氯化稀土浓缩液", + "material.gtceu.concentration_mixing_hyper_fuel_1": "浓缩混合超能燃料 I", + "material.gtceu.concentration_mixing_hyper_fuel_2": "浓缩混合超能燃料 II", + "material.gtceu.conductive_alloy": "导电铁", + "material.gtceu.cooling_concentrated_nitric_monazite_rare_earth_powder": "冷却浓缩硝酸独居石稀土", + "material.gtceu.copernicium": "鿔", + "material.gtceu.copper76": "铜-76", + "material.gtceu.cosmic": "宇宙", + "material.gtceu.cosmic_computing_mixture": "寰宇计算混合物", + "material.gtceu.cosmic_element": "宇宙素", + "material.gtceu.cosmic_mesh": "寰宇织网", + "material.gtceu.cosmic_superconductor": "寰宇超导液", + "material.gtceu.cosmicneutronium": "宇宙中子素", + "material.gtceu.crackedradox": "裂化拉多X", + "material.gtceu.crude_hexanitrohexaaxaisowurtzitane": "粗制六硝基六氮杂异伍兹烷", + "material.gtceu.crystalline_nitric_acid": "结晶硝酸", + "material.gtceu.crystalmatrix": "水晶矩阵", + "material.gtceu.cubic_zirconia": "立方氧化锆", + "material.gtceu.cyclooctadiene": "环辛二烯", + "material.gtceu.cycloparaphenylene": "环对苯撑", + "material.gtceu.cyclopentadiene": "环戊二烯", + "material.gtceu.cyclopentadienyl_titanium_trichloride": "环戊二烯基三氯化钛", + "material.gtceu.dalisenite": "大力合金", + "material.gtceu.decaborane": "癸硼烷", + "material.gtceu.degenerate_rhenium": "简并态铼", + "material.gtceu.deglycerated_soap": "脱糖肥皂", + "material.gtceu.dense_hydrazine_fuel_mixture": "浓缩肼混合燃料", + "material.gtceu.dense_hydrazine_mixed_fuel": "浓缩肼混合燃料", + "material.gtceu.dense_neutron": "致密中子素", + "material.gtceu.desh": "戴斯", + "material.gtceu.diamagnetic_residues": "抗磁性残渣", + "material.gtceu.diaminodiphenylmethanmixture": "二氨基二苯甲烷混合物", + "material.gtceu.dibenzyltetraacetylhexaazaisowurtzitane": "二苄基四乙酰六氮杂异纤锌烷", + "material.gtceu.dibismuthhydroborat": "硼氢二铋", + "material.gtceu.diborane": "乙硼烷", + "material.gtceu.dibromoacrolein": "二溴丙烯醛", + "material.gtceu.dibromomethylbenzene": "二溴甲苯", + "material.gtceu.dichlorocyclooctadieneplatinium": "二氯环辛二烯铂", + "material.gtceu.dichlorodicyanobenzoquinone": "二氯二氰苯醌", + "material.gtceu.dichlorodicyanohydroquinone": "二氯二氰氢醌", + "material.gtceu.dichloromethane": "二氯甲烷", + "material.gtceu.diethyl_ether": "二乙醚", + "material.gtceu.diethylthiourea": "二乙基硫脲", + "material.gtceu.difluoroaniline": "二氟苯胺", + "material.gtceu.difluorobenzophenone": "二氟二苯甲酮", + "material.gtceu.dihydroiodotetracene": "二氢碘化四联苯", + "material.gtceu.diiodobiphenyl": "二碘代联苯", + "material.gtceu.dilute_hexafluorosilicic_acid": "稀六氟硅酸", + "material.gtceu.dilute_hydrofluoric_acid": "稀释氢氟酸", + "material.gtceu.diluted_fluoro_carbon_lanthanide_slurry": "稀释氟碳镧铈泥浆", + "material.gtceu.diluted_monazite_slurry": "稀释独居石稀土泥浆", + "material.gtceu.diluted_monazite_sulfate_solution": "稀释硫酸独居石溶液", + "material.gtceu.diluted_rare_earth_chloride_solution": "氯化稀土稀释液", + "material.gtceu.dilutedxenoxene": "钝化异氙", + "material.gtceu.dimensionallytranscendentcrudecatalyst": "粗制超维度催化剂", + "material.gtceu.dimensionallytranscendentexoticcatalyst": "异星超维度催化剂", + "material.gtceu.dimensionallytranscendentprosaiccatalyst": "平凡超维度催化剂", + "material.gtceu.dimensionallytranscendentresidue": "超维度残留", + "material.gtceu.dimensionallytranscendentresplendentcatalyst": "光辉超维度催化剂", + "material.gtceu.dimensionallytranscendentstellarcatalyst": "恒星超维度催化剂", + "material.gtceu.dimethoxyethane": "二甲氧基乙烷", + "material.gtceu.dimethyl_sulfide": "二甲硫醚", + "material.gtceu.dimethylether": "二甲基乙醚", + "material.gtceu.dimethylnaphthalene": "二甲基萘", + "material.gtceu.dimethylterephthalate": "对苯二甲酸二甲酯", + "material.gtceu.dinitrodipropanyloxybenzene": "二硝基二丙氧基苯", + "material.gtceu.dioxygen_difluoride": "二氟化二氧", + "material.gtceu.diphenylmethane_diisocyanate": "4,4'-二苯基甲烷二异氰酸酯", + "material.gtceu.diphenylmethanediisocyanatemixture": "二苯基甲烷二异氰酸酯混合物", + "material.gtceu.dirty_hexafluorosilicic_acid": "污浊的六氟硅酸", + "material.gtceu.ditertbutyl_dicarbonate": "二碳酸二叔丁酯", + "material.gtceu.dmap": "二甲氨基吡啶", + "material.gtceu.draconium": "龙", + "material.gtceu.draconiumawakened": "觉醒龙", + "material.gtceu.dragon_blood": "龙血", + "material.gtceu.dragon_breath": "龙息", + "material.gtceu.dragon_element": "龙素", + "material.gtceu.dried_concentrated_nitric_monazite_rare_earth_powder": "干燥浓缩硝酸独居石稀土", + "material.gtceu.dry_graphene_gel": "干石墨烯凝胶", + "material.gtceu.durene": "杜烯", + "material.gtceu.dusty_liquid_helium_iii": "污浊的液氦-3", + "material.gtceu.dysprosium_oxide": "氧化镝", + "material.gtceu.earth_crystal": "地之魔晶", + "material.gtceu.echoite": "回响合金", + "material.gtceu.eglin_steel": "埃格林钢", + "material.gtceu.enderite": "末影合金", + "material.gtceu.enderium": "末影", + "material.gtceu.energetic_alloy": "充能合金", + "material.gtceu.enriched_dragon_breath": "富集龙息", + "material.gtceu.enriched_naquadah_front": "富集硅岩矿石泡沫", + "material.gtceu.enriched_naquadah_fuel": "富集硅岩燃料", + "material.gtceu.enriched_potassium_iodide_slurry": "富集碘化钾浆液", + "material.gtceu.enriched_rare_earth_chloride_solution": "氯化稀土富集液", + "material.gtceu.enriched_xenoxene": "富集异氙", + "material.gtceu.er_lu_oxides_solution": "铒-镥氧化物溶液", + "material.gtceu.erbium_oxide": "氧化铒", + "material.gtceu.eternity": "永恒", + "material.gtceu.ethanolamine": "乙醇胺", + "material.gtceu.ethyl_acrylate": "丙烯酸乙酯", + "material.gtceu.ethyl_trifluoroacetate": "三氟乙酸乙酯", + "material.gtceu.ethylamine": "乙胺", + "material.gtceu.ethylanthrahydroquinone": "2-乙基蒽氢醌", + "material.gtceu.ethylanthraquinone": "2-乙基蒽醌", + "material.gtceu.ethylene_oxide": "环氧乙烷", + "material.gtceu.ethylene_sulfide": "乙硫酮", + "material.gtceu.ethylenediamine": "乙二胺", + "material.gtceu.ethyleneglycol": "乙二醇", + "material.gtceu.europium_oxide": "氧化铕", + "material.gtceu.euv_photoresist": "EUV光刻胶", + "material.gtceu.exciteddtec": "激发的异星超维度催化剂", + "material.gtceu.exciteddtsc": "激发的恒星超维度催化剂", + "material.gtceu.exotic_heavy_residues": "重奇异残渣", + "material.gtceu.explosivehydrazine": "爆炸性肼燃料混合物", + "material.gtceu.fall_king": "耐摔合金", + "material.gtceu.ferric_ree_chloride": "含稀土氯化铁", + "material.gtceu.ferrocene": "二茂铁", + "material.gtceu.ferromagnetic_residues": "铁磁性残渣", + "material.gtceu.filtered_fluoro_carbon_lanthanide_slurry": "过滤氟碳镧铈泥浆", + "material.gtceu.fissioned_uranium_235": "裂变铀-235", + "material.gtceu.fluorinated_samarium_concentrate_powder": "氟化钐精", + "material.gtceu.fluorine_cracked_aquadah": "加氟裂化硅岩", + "material.gtceu.fluorite": "氟石", + "material.gtceu.fluoro_benzene": "氟苯", + "material.gtceu.fluoro_carbon_lanthanide_cerium_oxide_powder": "氟碳镧铈稀土氧化物", + "material.gtceu.fluoro_carbon_lanthanide_cerium_rare_earth_oxide_powder": "氟碳镧铈罕土氧化物", + "material.gtceu.fluoro_carbon_lanthanide_cerium_rare_earth_suspension": "氟碳镧铈罕土氧化物悬浊液", + "material.gtceu.fluoro_carbon_lanthanide_cerium_solution": "氟碳镧铈稀土浊溶液", + "material.gtceu.fluoroboric_acide": "氟硼酸", + "material.gtceu.fluorocarborane": "碳氟化合物", + "material.gtceu.fluorosilicic_acid": "六氟硅酸", + "material.gtceu.fluorotoluene": "氟甲苯", + "material.gtceu.fluxed_electrum": "通流琥珀金", + "material.gtceu.flyb": "\uD86D\uDCE7-镱", + "material.gtceu.force": "力量", + "material.gtceu.francium_caesium_cadmium_bromide": "溴化钫铯镉", + "material.gtceu.francium_carbide": "碳化钫", + "material.gtceu.free_alpha_gas": "高密度自由α粒子气", + "material.gtceu.free_electron_gas": "高密度自由电子气", + "material.gtceu.free_proton_gas": "高密度自由质子气", + "material.gtceu.fullerene": "富勒烯", + "material.gtceu.fullerene_doped_nanotubes": "富勒烯掺杂的纳米管", + "material.gtceu.fullerene_polymer_matrix_pulp": "富勒烯聚合物基体", + "material.gtceu.fuming_nitric_acid": "发烟硝酸", + "material.gtceu.gadolinium_oxide": "氧化钆", + "material.gtceu.gamma_rays_photoresist": "γ射线光刻胶", + "material.gtceu.gammabutyrolactone": "1,4-丁内酯", + "material.gtceu.germanium_ash": "锗灰", + "material.gtceu.germanium_containing_precipitate": "含锗沉淀物", + "material.gtceu.germanium_dioxide": "二氧化锗", + "material.gtceu.germanium_tetrachloride_solution": "四氯化锗", + "material.gtceu.germaniumtungstennitride": "锗-钨氮化物", + "material.gtceu.glacio_spirit": "霜原碎片", + "material.gtceu.glucose": "葡萄糖", + "material.gtceu.glucose_iron_solution": "葡萄糖铁溶液", + "material.gtceu.gluons": "胶子", + "material.gtceu.glyoxal": "乙二醛", + "material.gtceu.gold_chloride": "氯化金", + "material.gtceu.gold_cyanide": "氰化金", + "material.gtceu.gold_depleted_molybdenite": "贫金辉钼矿", + "material.gtceu.gold_trifluoride": "三氟化金", + "material.gtceu.grade_10_purified_water": "10级净化水", + "material.gtceu.grade_11_purified_water": "11级净化水", + "material.gtceu.grade_12_purified_water": "12级净化水", + "material.gtceu.grade_13_purified_water": "13级净化水", + "material.gtceu.grade_14_purified_water": "14级净化水", + "material.gtceu.grade_15_purified_water": "15级净化水", + "material.gtceu.grade_16_purified_water": "16级净化水", + "material.gtceu.grade_1_purified_water": "1级净化水", + "material.gtceu.grade_2_purified_water": "2级净化水", + "material.gtceu.grade_3_purified_water": "3级净化水", + "material.gtceu.grade_4_purified_water": "4级净化水", + "material.gtceu.grade_5_purified_water": "5级净化水", + "material.gtceu.grade_6_purified_water": "6级净化水", + "material.gtceu.grade_7_purified_water": "7级净化水", + "material.gtceu.grade_8_purified_water": "8级净化水", + "material.gtceu.grade_9_purified_water": "9级净化水", + "material.gtceu.graphene_gel_suspension": "石墨烯浆料", + "material.gtceu.graphene_oxide": "氧化石墨烯", + "material.gtceu.grisium": "灰钛合金", + "material.gtceu.grossular_front": "钙铝榴石矿石泡沫", + "material.gtceu.hafnium_chloride": "四氯化铪", + "material.gtceu.hafnium_oxide": "二氧化铪", + "material.gtceu.hassium_chloride": "氯化\uD872\uDF76", + "material.gtceu.hastelloy_n": "哈斯特洛依合金-N", + "material.gtceu.hastelloy_n_75": "哈斯特洛依合金-N75", + "material.gtceu.hastelloyk_243": "哈斯特洛依合金-K243", + "material.gtceu.hastelloyx_78": "哈斯特洛依合金-X78", + "material.gtceu.heavily_fluorinated_trinium_solution": "重氟化凯金化合物溶液", + "material.gtceu.heavy_diamagnetic_residues": "重磁残渣", + "material.gtceu.heavy_ferromagnetic_residues": "重铁磁性残渣", + "material.gtceu.heavy_lepton_mixture": "重轻子混合物", + "material.gtceu.heavy_metallic_residues": "重金属残渣", + "material.gtceu.heavy_oxidized_residues": "重氧化残渣", + "material.gtceu.heavy_paramagnetic_residues": "重顺磁残渣", + "material.gtceu.heavy_quark_degenerate_matter": "重夸克简并物质", + "material.gtceu.heavy_quark_enriched_mixture": "富集重夸克混合物", + "material.gtceu.heavy_quarks": "重夸克", + "material.gtceu.heavyradox": "重拉多X", + "material.gtceu.helium_iii_hydride": "氢化氦-3", + "material.gtceu.heterogeneous_halide_monazite_rare_earth_mixture_powder": "异质卤化独居石稀土混合物", + "material.gtceu.hexabenzylhexaazaisowurtzitane": "六苄基六氮杂异伍兹烷", + "material.gtceu.hexafluoride_enriched_naquadah_solution": "六氟化富集硅岩溶液", + "material.gtceu.hexafluoride_naquadria_solution": "六氟化超能硅岩溶液", + "material.gtceu.hexafluorophosphoric_acid": "单氟磷酸", + "material.gtceu.hexamethylenetetramine": "环六亚甲基四胺", + "material.gtceu.hexanitrohexaaxaisowurtzitane": "六硝基六氮杂异伍兹烷", + "material.gtceu.high_energy_quark_gluon": "高能夸克-胶子", + "material.gtceu.high_purity_calcium_carbonate": "高纯度碳酸钙", + "material.gtceu.highenergymixture": "高能混合物", + "material.gtceu.highurabilityompoundteel": "高强度复合钢", + "material.gtceu.hikarium": "光素", + "material.gtceu.hmxexplosive": "HMX高能爆炸性化合物", + "material.gtceu.holmium_oxide": "氧化钬", + "material.gtceu.hot_oganesson": "热鿫", + "material.gtceu.hot_sodium_potassium": "热钠钾合金", + "material.gtceu.hydrazine": "肼", + "material.gtceu.hydrobromic_acid": "氢溴酸", + "material.gtceu.hydrogen_peroxide": "过氧化氢", + "material.gtceu.hydroiodic_acid": "氢碘酸", + "material.gtceu.hydroquinone": "对苯二酚", + "material.gtceu.hydroxylamine_hydrochloride": "盐酸羟胺", + "material.gtceu.hydroxylammonium_sulfate": "羟铵硫酸盐", + "material.gtceu.hyper_fuel_1": "超能燃料 I", + "material.gtceu.hyper_fuel_2": "超能燃料 II", + "material.gtceu.hyper_fuel_3": "超能燃料 III", + "material.gtceu.hyper_fuel_4": "超能燃料 IV", + "material.gtceu.hypogen": "海珀珍", + "material.gtceu.ignis_crystal": "火之魔晶", + "material.gtceu.inconel_625": "镍铬基合金-625", + "material.gtceu.inconel_792": "镍铬基合金-792", + "material.gtceu.indalloy_140": "铋铅合金-140", + "material.gtceu.inert_residues": "纯净残渣", + "material.gtceu.infinity": "无尽", + "material.gtceu.infuscolium": "魔金", + "material.gtceu.infused_gold": "注魔金", + "material.gtceu.iodine_containing_slurry": "含碘溶液", + "material.gtceu.iodine_monochloride": "氯化碘", + "material.gtceu.iridium_dioxide": "二氧化铱", + "material.gtceu.iridium_trichloride_solution": "三氯化铱溶液", + "material.gtceu.isochloropropane": "氯丙烷", + "material.gtceu.isophthaloylbis": "间苯二甲酰基二乙基硫脲", + "material.gtceu.isopropyl_alcohol": "异丙醇", + "material.gtceu.jasper": "碧玉", + "material.gtceu.kelp_slurry": "海带浆液", + "material.gtceu.kerosene": "煤油", + "material.gtceu.kevlar": "凯芙拉", + "material.gtceu.krypton_difluoride": "二氟化氪", + "material.gtceu.la_nd_oxides_solution": "镧-钕氧化物溶液", + "material.gtceu.lafium": "路菲恩", + "material.gtceu.lanthanoids_1": "轻镧系元素混合物", + "material.gtceu.lanthanoids_2": "重镧系元素混合物", + "material.gtceu.lanthanum_chloride": "氯化镧", + "material.gtceu.lanthanum_chloride_with_impurities": "含杂氯化镧", + "material.gtceu.lanthanum_embedded_fullerene": "镧-富勒烯包合物", + "material.gtceu.lanthanum_fullerene_mix": "镧-富勒烯混合物", + "material.gtceu.lanthanum_oxide": "氧化镧", + "material.gtceu.leach_residue": "铂浸出渣", + "material.gtceu.leached_turpentine": "松油浸出物", + "material.gtceu.legendarium": "传奇合金", + "material.gtceu.light_quarks": "轻夸克", + "material.gtceu.lightradox": "轻拉多X", + "material.gtceu.liquid_hydrogen": "液态氢", + "material.gtceu.liquid_starlight": "星能液", + "material.gtceu.liquidcrystalkevlar": "液晶凯芙拉", + "material.gtceu.lithium_aluminium_fluoride": "铝-锂氟化物", + "material.gtceu.lithium_aluminium_hydride": "氢化铝锂", + "material.gtceu.lithium_cyclopentadienide": "环戊二烯化锂", + "material.gtceu.lithium_fluoride": "氟化锂", + "material.gtceu.lithium_iodide": "碘化锂", + "material.gtceu.lithium_niobate_nanoparticles": "铌酸锂纳米粒子", + "material.gtceu.lithiumthiinediselenide": "二硒醚合硫锂", + "material.gtceu.lumiium": "流明", + "material.gtceu.luminessence": "流明精华", + "material.gtceu.lutetium_oxide": "氧化镥", + "material.gtceu.magmatter": "磁物质", + "material.gtceu.magnesium_chloride_bromide": "溴氯化镁", + "material.gtceu.magneto_resonatic": "共振紫晶", + "material.gtceu.magnetohydrodynamicallyconstrainedstarmatter": "磁流体约束恒星物质", + "material.gtceu.maleic_anhydride": "顺丁烯二酸酐", + "material.gtceu.mana": "魔力", + "material.gtceu.mar_m_200_steel": "MAR-M200特种钢", + "material.gtceu.metal_residue": "金属残渣", + "material.gtceu.metallic_residues": "金属残渣", + "material.gtceu.metalloid": "类金属元素混合物", + "material.gtceu.metastable_hassium": "亚稳态\uD872\uDF76", + "material.gtceu.metastable_oganesson": "亚稳态鿫", + "material.gtceu.methylamine": "甲胺", + "material.gtceu.methylbenzophenanthrene": "甲基苯并菲", + "material.gtceu.mithril": "秘银", + "material.gtceu.mixed_astatide_salts": "混合砹化盐", + "material.gtceu.modulated_fluoro_carbon_lanthanide_slurry": "调制氟碳镧铈泥浆", + "material.gtceu.molten_calcium_salts": "熔融钙盐", + "material.gtceu.molybdenum_concentrate": "钼精", + "material.gtceu.molybdenum_flue": "钼烟气", + "material.gtceu.molybdenum_trioxide": "三氧化钼", + "material.gtceu.monazite_front": "独居石矿石泡沫", + "material.gtceu.monazite_rare_earth_filter_residue_powder": "独居石稀土滤渣", + "material.gtceu.monazite_rare_earth_precipitate_powder": "独居石罕土沉淀", + "material.gtceu.monazite_rare_earth_turbid_liquid": "独居石稀土混浊液", + "material.gtceu.monazite_sulfate_powder": "硫酸独居石", + "material.gtceu.monomethylhydrazine": "甲肼", + "material.gtceu.mutated_living_solder": "突变活性焊料", + "material.gtceu.n_difluorophenylpyrrole": "N-二氟苯基吡咯", + "material.gtceu.n_hydroxysuccinimide": "羟基丁二酰亚胺", + "material.gtceu.naquadah_contain_rare_earth": "含硅岩的稀土", + "material.gtceu.naquadah_contain_rare_earth_fluoride": "含硅岩的稀土氟化物", + "material.gtceu.naquadah_fuel": "硅岩燃料", + "material.gtceu.naquadah_solution": "硅岩溶液", + "material.gtceu.naquadria_caesium_xenonnonfluoride": "九氟氙酸超能硅岩铯", + "material.gtceu.naquadria_caesiumfluoride": "二氟超能硅岩酸铯", + "material.gtceu.naquadriatictaranium": "超能硅岩-塔兰金属合金", + "material.gtceu.neodymium_oxide": "氧化钕", + "material.gtceu.neodymium_rare_earth_concentrate_powder": "钕稀土精", + "material.gtceu.neutralised_red_mud": "中和赤泥", + "material.gtceu.neutralized_monazite_rare_earth_filter_residue_powder": "中和独居石稀土滤渣", + "material.gtceu.neutralized_uranium_filter_residue_powder": "中和铀滤渣", + "material.gtceu.neutronium_doped_nanotubes": "掺中子素纳米管", + "material.gtceu.nickel_front": "镍矿石泡沫", + "material.gtceu.nitrated_triniite_compound_solution": "硝化凯金化合物溶液", + "material.gtceu.nitric_leachate_from_monazite": "硝酸独居石浸出混合物", + "material.gtceu.nitrided_fluoro_carbon_lanthanide_cerium_rare_earth_oxide_solution": "氮化氟碳镧铈罕土氧化物", + "material.gtceu.nitrided_samarium_terbium_mixture_powder": "氮化钐-铽混合物", + "material.gtceu.nitrogen_pentoxide": "五氧化二氮", + "material.gtceu.nitronium_tetrafluoroborate": "四氟硝铵", + "material.gtceu.nitrosonium_octafluoroxenate": "八氟氙酸亚硝酰", + "material.gtceu.nitrosonium_tetrafluoroborate": "四氟硼酸亚硝铵", + "material.gtceu.nitrous_acid": "亚硝酸", + "material.gtceu.nitryl_fluoride": "硝酰氟", + "material.gtceu.nmethylpyrolidone": "N-甲基吡咯烷酮", + "material.gtceu.noble_gas": "稀有气体元素混合物", + "material.gtceu.not_found": "非金属元素混合物", + "material.gtceu.oganesson_breeding_base": "鿫增殖基", + "material.gtceu.orichalcum": "山铜", + "material.gtceu.ostrum": "紫金", + "material.gtceu.oxalic_acid": "草酸", + "material.gtceu.oxidized_residual_solution": "氧化残留溶液", + "material.gtceu.oxidized_residues": "氧化残渣", + "material.gtceu.oxydianiline": "二氨基二苯醚", + "material.gtceu.ozone": "臭氧", + "material.gtceu.p_nitroaniline": "对硝基苯胺", + "material.gtceu.p_phenylenediamine": "对苯二胺", + "material.gtceu.paa": "聚酰胺酸(PAA)", + "material.gtceu.palladium_fullerene_matrix": "钯-富勒烯基质", + "material.gtceu.paramagnetic_residues": "顺磁残渣", + "material.gtceu.partially_oxidized_residues": "待分离氧化金属残渣", + "material.gtceu.pcbs": "苯基-C61-丁酸苯乙烯", + "material.gtceu.pentaerythritol": "季戊四醇", + "material.gtceu.pentlandite_front": "镍黄铁矿矿石泡沫", + "material.gtceu.perditio_crystal": "混沌魔晶", + "material.gtceu.perfluorobenzene": "全氟苯", + "material.gtceu.periodicium": "錭錤錶", + "material.gtceu.phenylenedioxydiacetic_acid": "亚苯基二氧二乙酸", + "material.gtceu.phenylpentanoic_acid": "苯基戊酸", + "material.gtceu.phenylsodium": "苯基钠", + "material.gtceu.phosgene": "碳酰氯", + "material.gtceu.phosphorus_free_samarium_concentrate_powder": "脱磷钐精", + "material.gtceu.phosphorus_pentasulfide": "五硫化磷", + "material.gtceu.phosphorus_trichloride": "三氯化磷", + "material.gtceu.photopolymer": "光聚合物溶液", + "material.gtceu.photoresist": "光刻胶", + "material.gtceu.phthalic_anhydride": "邻苯二甲酸酐", + "material.gtceu.pikyonium": "皮卡优合金", + "material.gtceu.piranha_solution": "食人鱼洗液", + "material.gtceu.platinum_front": "铂矿石泡沫", + "material.gtceu.platinum_slag": "铂渣", + "material.gtceu.polycyclic_aromatic_mixture": "多环芳香烃混合物", + "material.gtceu.polyetheretherketone": "聚醚醚酮", + "material.gtceu.polyimide": "聚酰亚胺", + "material.gtceu.polyurethane": "聚氨基甲酸酯", + "material.gtceu.polyurethaneresin": "聚氨酯树脂", + "material.gtceu.poor": "贫金属元素混合物", + "material.gtceu.positive_electron": "反电子", + "material.gtceu.potassium_bisulfite": "亚硫酸氢钾", + "material.gtceu.potassium_bromide": "溴化钾", + "material.gtceu.potassium_ethylate": "乙醇钾", + "material.gtceu.potassium_ethylxanthate": "乙基黄原酸钾", + "material.gtceu.potassium_fluoride": "氟化钾", + "material.gtceu.potassium_hydroxylaminedisulfonate": "羟胺二磺酸钾", + "material.gtceu.potassium_pyrosulfate": "焦硫酸钾", + "material.gtceu.praseodymium_oxide": "氧化镨", + "material.gtceu.prasiolite": "堇云石", + "material.gtceu.pre_zylon": "预处理柴隆纤维", + "material.gtceu.primordialmatter": "流体本源物质", + "material.gtceu.promethium_oxide": "氧化钷", + "material.gtceu.propadiene": "丙二烯", + "material.gtceu.pulsating_alloy": "脉冲铁", + "material.gtceu.purified_tengam": "纯镃", + "material.gtceu.purified_xenoxene": "纯净异氙", + "material.gtceu.pyridine": "吡啶", + "material.gtceu.pyromellitic_dianhydride": "均苯二甲酸酐", + "material.gtceu.pyrope_front": "镁铝榴石矿石泡沫", + "material.gtceu.quantanium": "量子", + "material.gtceu.quantum": "量子合金", + "material.gtceu.quantum_dots": "量子点", + "material.gtceu.quantumchromodynamically_confined_matter": "量子色动力学封闭物质", + "material.gtceu.quark_gluon": "夸克-胶子", + "material.gtceu.quasifissioning": "拟裂变", + "material.gtceu.radium_nitrate": "硝酸镭", + "material.gtceu.radon_cracked_enriched_aquadah": "加氡裂化富集硅岩", + "material.gtceu.radon_difluoride": "二氟化氡", + "material.gtceu.radon_naquadria_octafluoride": "八氟超能硅岩酸氡", + "material.gtceu.radon_trioxide": "三氧化氡", + "material.gtceu.radox": "拉多X聚合物", + "material.gtceu.radox_gas": "拉多X气", + "material.gtceu.rare_earth_chlorides": "稀土氯化物", + "material.gtceu.rare_earth_hydroxides": "稀土氢氧化物", + "material.gtceu.rare_earth_metal": "稀土金属", + "material.gtceu.rare_earth_oxide": "稀土氧化物", + "material.gtceu.rareearth": "稀土元素合金", + "material.gtceu.raw_star_matter": "原始恒星混合物", + "material.gtceu.raw_tengam": "生镃", + "material.gtceu.rawradox": "粗制拉多X", + "material.gtceu.reactor_steel": "反应堆专用钒钢", + "material.gtceu.red_mud": "赤泥", + "material.gtceu.red_slurry": "赤泥浆液", + "material.gtceu.red_zircon_powder": "红锆石", + "material.gtceu.redstone_front": "红石矿石泡沫", + "material.gtceu.reprecipitated_rhodium": "再沉淀铑", + "material.gtceu.residual_triniite_solution": "残留凯金化合物溶液", + "material.gtceu.resorcinol": "间苯二酚", + "material.gtceu.rhenium_chloride": "氯化铼", + "material.gtceu.rhenium_hassium_thallium_isophtaloylbisdiethylthiourea_hexaf": "间苯二甲酰双(二乙基硫脲基)六氟磷酸铼-\uD872\uDF76-铊", + "material.gtceu.rhenium_sulfuric_solution": "铼硫酸溶液", + "material.gtceu.rhodium_filter_cake": "铑滤饼", + "material.gtceu.rhodium_filter_cake_solution": "铑滤饼溶液", + "material.gtceu.rhodium_nitrate": "硝酸铑", + "material.gtceu.rhodium_rhenium_naquadah_catalyst": "铑-铼-硅岩催化剂", + "material.gtceu.rhodium_salt": "铑盐", + "material.gtceu.rhodium_salt_solution": "铑盐溶液", + "material.gtceu.rhodium_sulfate_gas": "气态硫酸铑", + "material.gtceu.rhugnor": "鲁格诺", + "material.gtceu.rocket_fuel_cn3h7o3": "火箭燃料(硝酸甲肼)", + "material.gtceu.rocket_fuel_h8n4c2o4": "火箭燃料(偏二甲肼-四氧化二氮)", + "material.gtceu.rocket_fuel_rp_1": "火箭燃料 RP-1", + "material.gtceu.roughly_rhodium_metal": "粗制铑金属", + "material.gtceu.rp_1": "RP-1混合燃料", + "material.gtceu.ruthenium_tetroxide_hot": "热四氧化钌", + "material.gtceu.ruthenium_tetroxide_lq": "四氧化钌溶液", + "material.gtceu.samarium_chloride_concentrate_solution": "氯化钐浓缩液", + "material.gtceu.samarium_chloride_sodium_chloride_mixture_powder": "氯化钐-氯化钠混合物", + "material.gtceu.samarium_chloride_with_impurities": "含杂氯化钐", + "material.gtceu.samarium_oxalate_with_impurities": "含杂草酸钐", + "material.gtceu.samarium_oxide": "氧化钐", + "material.gtceu.samarium_precipitate_powder": "钐沉淀", + "material.gtceu.samarium_rare_earth_concentrate_powder": "钐稀土精", + "material.gtceu.samarium_rare_earth_diluted_solution": "钐稀土稀释液", + "material.gtceu.samarium_rare_earth_slurry": "钐稀土泥浆", + "material.gtceu.samarium_refined_powder": "钐精", + "material.gtceu.samarium_rrare_eearth_turbid_liquid": "钐稀土浊液", + "material.gtceu.samarium_terbium_mixture_powder": "钐-铽混合物", + "material.gtceu.sarcosine": "肌氨酸", + "material.gtceu.saturated_monazite_rare_earth_powder": "饱和独居石稀土", + "material.gtceu.scandium_oxide": "氧化钪", + "material.gtceu.scandium_titanium_50_mixture": "钪-钛50混合物", + "material.gtceu.seaborgium_doped_nanotubes": "\uD872\uDF73掺杂的纳米管", + "material.gtceu.seaweedbroth": "海藻基质", + "material.gtceu.selenium_oxide": "二氧化硒", + "material.gtceu.shirabon": "调律源金", + "material.gtceu.silica_alumina_gel": "硅铝凝胶", + "material.gtceu.silica_gel": "硅胶", + "material.gtceu.silica_gel_base": "硅胶基质", + "material.gtceu.silicon_carbide": "碳化硅", + "material.gtceu.siliconoil": "硅油", + "material.gtceu.silver_chloride": "氯化银", + "material.gtceu.silver_iodide": "碘化银", + "material.gtceu.silver_nitrate": "硝酸银", + "material.gtceu.silver_oxide": "氧化银", + "material.gtceu.silver_perchlorate": "高氯酸银", + "material.gtceu.silver_tetrafluoroborate": "四氟硼酸银", + "material.gtceu.sm_gd_oxides_solution": "钐-钆氧化物溶液", + "material.gtceu.soap": "肥皂", + "material.gtceu.sodium_aluminium_hydride": "氢化铝钠", + "material.gtceu.sodium_azanide": "氨基钠", + "material.gtceu.sodium_azide": "叠氮化钠", + "material.gtceu.sodium_borohydride": "硼氢化钠", + "material.gtceu.sodium_bromide": "溴化钠", + "material.gtceu.sodium_chlorate": "氯酸钠", + "material.gtceu.sodium_cyanide": "氰化钠", + "material.gtceu.sodium_ethylate": "乙醇钠", + "material.gtceu.sodium_ethylxanthate": "乙基黄原酸钠", + "material.gtceu.sodium_fluoride": "氟化钠", + "material.gtceu.sodium_fluorosilicate": "氟硅酸钠", + "material.gtceu.sodium_formate": "甲酸钠", + "material.gtceu.sodium_hexafluoroaluminate": "六氟铝酸钠", + "material.gtceu.sodium_hydride": "氢化钠", + "material.gtceu.sodium_hydroxide_solution": "氢氧化钠溶液", + "material.gtceu.sodium_hypochlorite": "次氯酸钠", + "material.gtceu.sodium_nitrate": "硝酸钠", + "material.gtceu.sodium_nitrate_solution": "硝酸钠溶液", + "material.gtceu.sodium_oxide": "氧化钠", + "material.gtceu.sodium_perchlorate": "高氯酸钠", + "material.gtceu.sodium_rutheniate": "钌酸钠", + "material.gtceu.sodium_seaborgate": "\uD872\uDF73酸钠", + "material.gtceu.sodium_sulfate": "硫酸钠", + "material.gtceu.sodium_tetrafluoroborate": "四氟硼酸钠", + "material.gtceu.sodium_thiocyanate": "硫氰酸钠", + "material.gtceu.sodium_thiosulfate": "硫代硫酸钠", + "material.gtceu.sodium_toluenesulfonate": "甲苯磺酸钠", + "material.gtceu.spacetime": "时空", + "material.gtceu.spatialfluid": "扩大化空间流体", + "material.gtceu.special_ceramics": "特种陶瓷", + "material.gtceu.spessartine_front": "锰铝榴石矿石泡沫", + "material.gtceu.sphalerite_front": "闪锌矿矿石泡沫", + "material.gtceu.starlight": "星光", + "material.gtceu.starmetal": "星辉", + "material.gtceu.steam_cracked_fluoro_carbon_lanthanide_slurry": "蒸汽裂化氟碳镧铈泥浆", + "material.gtceu.steam_cracked_turpentine": "蒸汽裂化的松油浸出物", + "material.gtceu.stearic_acid": "硬脂酸", + "material.gtceu.stellar_energy_rocket_fuel": "星能火箭燃料", + "material.gtceu.stellite": "铬钴锰钛合金", + "material.gtceu.stone_dust_residue": "石头粉残渣", + "material.gtceu.strontium_europium_aluminate": "锶铕铝酸盐", + "material.gtceu.succinaldehyde": "琥珀醛", + "material.gtceu.succinic_acid": "琥珀酸", + "material.gtceu.succinic_anhydride": "丁二酸酐", + "material.gtceu.succinimide": "琥珀酰亚胺", + "material.gtceu.succinimidyl_acetate": "琥珀酰亚胺醋酸酯", + "material.gtceu.sunnarium": "阳光化合物", + "material.gtceu.super_mutated_living_solder": "超突变活性焊料", + "material.gtceu.supercritical_carbon_dioxide": "超临界二氧化碳", + "material.gtceu.supercritical_sodium_potassium": "超临界钠钾合金", + "material.gtceu.supercritical_steam": "超临界蒸汽", + "material.gtceu.superheavy_h_alloy": "超重元素-重合金", + "material.gtceu.superheavy_l_alloy": "超重元素-轻合金", + "material.gtceu.superheavyradox": "超重拉多X", + "material.gtceu.superlightradox": "超轻拉多X", + "material.gtceu.tairitsu": "对立合金", + "material.gtceu.tanmolyium": "钛钼合金β-C", + "material.gtceu.tannic": "丹宁", + "material.gtceu.tantalloy_61": "钽钨合金-61", + "material.gtceu.taranium": "塔兰", + "material.gtceu.taranium_enriched_liquid_helium_3": "浓缩塔兰金属的液氦-3", + "material.gtceu.taranium_rich_liquid_helium_4": "富塔兰金属的氦-4", + "material.gtceu.tartarite": "溶火之石", + "material.gtceu.tb_ho_oxides_solution": "铽-钬氧化物溶液", + "material.gtceu.tellurium_oxide": "二氧化碲", + "material.gtceu.temporalfluid": "富快子时间流体", + "material.gtceu.terbium_nitrate_powder": "硝酸铽", + "material.gtceu.terbium_oxide": "氧化铽", + "material.gtceu.terephthalaldehyde": "对苯二甲醛", + "material.gtceu.terephthalicacid": "对苯二甲酸", + "material.gtceu.terephthaloyl_chloride": "对苯二甲酰氯", + "material.gtceu.tert_butanol": "叔丁醇", + "material.gtceu.tertbuthylcarbonylazide": "叔丁基羰基叠氮", + "material.gtceu.tetraacetyldinitrosohexaazaisowurtzitane": "四乙酰二硝基六氮杂异戊二烯", + "material.gtceu.tetracene": "并四苯", + "material.gtceu.tetraethylammonium_bromide": "四乙基溴化铵", + "material.gtceu.tetrahydrofuran": "四氢呋喃", + "material.gtceu.thallium_chloride": "氯化铊", + "material.gtceu.thallium_thulium_doped_caesium_iodide": "铊铥掺杂的碘化铯", + "material.gtceu.thaumium": "神秘", + "material.gtceu.thionyl_chloride": "氯化亚砜", + "material.gtceu.thorite_powder": "方钍石", + "material.gtceu.thorium_phosphate_filter_cake_powder": "磷酸钍滤饼", + "material.gtceu.thorium_phosphate_refined_powder": "磷酸钍精", + "material.gtceu.thulium_oxide": "氧化铥", + "material.gtceu.titan_precision_steel": "泰坦精钢", + "material.gtceu.titanium_50": "钛-50", + "material.gtceu.titanium_50_tetrachloride": "四氯化钛-50", + "material.gtceu.titanium_50_tetrafluoride": "四氟化钛-50", + "material.gtceu.titanium_tetrafluoride": "四氟化钛", + "material.gtceu.titansteel": "泰坦钢", + "material.gtceu.titanyl_sulfate": "硫酸钛酯", + "material.gtceu.toluene_diisocyanate": "甲苯二异氰酸脂", + "material.gtceu.transcendentmetal": "超时空金属", + "material.gtceu.transition": "过渡元素合金", + "material.gtceu.transition_1": "前过渡金属元素混合物", + "material.gtceu.transition_2": "中过渡金属元素混合物", + "material.gtceu.transition_3": "后过渡金属元素混合物", + "material.gtceu.trichloroflerane": "三氯\uD86D\uDCE7烷", + "material.gtceu.tricotylphosphine": "三辛基膦", + "material.gtceu.trifluoroacetic_phosphate_ester": "三氟乙酸对磷脂", + "material.gtceu.trimethylamine": "三甲胺", + "material.gtceu.trimethylchlorosilane": "三甲基氯硅烷", + "material.gtceu.trimethylsilane": "三甲基硅烷", + "material.gtceu.trimethyltin_chloride": "三甲基氯化锡", + "material.gtceu.trinium_compound": "凯金化合物", + "material.gtceu.trinium_tetrafluoride": "四氟化凯金", + "material.gtceu.trinium_titanium": "凯金钛合金", + "material.gtceu.tritium_hydride": "氢化氚", + "material.gtceu.tungsten_trioxide": "三氧化钨", + "material.gtceu.turbid_dragon_blood": "龙血浊液", + "material.gtceu.turpentine": "松油", + "material.gtceu.ultraacidic_residue_solution": "超酸性残渣溶液", + "material.gtceu.uncommon_residues": "精良残渣", + "material.gtceu.unfolded_fullerene": "未折叠富勒烯", + "material.gtceu.unknownnutrientagar": "未知营养琼脂", + "material.gtceu.unknowwater": "不明液体", + "material.gtceu.uranium_filter_residue_powder": "铀滤渣", + "material.gtceu.uranium_sulfate_waste_solution": "硫酸铀废液", + "material.gtceu.uruium": "乌鲁", + "material.gtceu.uu_amplifier": "UU增幅液", + "material.gtceu.vanadium_pentoxide_powder": "五氧化二钒", + "material.gtceu.vibramantium": "艾德曼振金", + "material.gtceu.vibranium": "振金", + "material.gtceu.vibranium_unstable": "不稳定振金", + "material.gtceu.vibrant_alloy": "脉冲合金", + "material.gtceu.viscoelastic_polyurethane": "粘弹性聚氨酯", + "material.gtceu.viscoelastic_polyurethane_foam": "粘弹性聚氨酯泡沫", + "material.gtceu.water_agar_mix": "琼脂水溶液", + "material.gtceu.wet_rare_earth_oxide_powder": "湿稀土氧化物", + "material.gtceu.wet_zeolite_sieving_pellets": "湿过筛沸石颗粒", + "material.gtceu.white_dwarf_mtter": "白矮星物质", + "material.gtceu.woods_glass": "伍兹玻璃", + "material.gtceu.xenic_acid": "氙酸", + "material.gtceu.xenoauric_fluoroantimonic_acid": "氟锑酸二氙", + "material.gtceu.xenon_hexafluoro_enriched_naquadate": "六氟氙酸富集硅岩", + "material.gtceu.xenon_trioxide": "三氧化氙", + "material.gtceu.xenoxene": "异氙", + "material.gtceu.xenoxene_crystal": "结晶异氙", + "material.gtceu.xenoxene_mixture": "异氙混合物", + "material.gtceu.xpjuice": "液态经验", + "material.gtceu.ytterbium_178": "镱-178", + "material.gtceu.ytterbium_oxide": "氧化镱", + "material.gtceu.yttrium_oxide": "氧化钇", + "material.gtceu.zeolite_sieving_pellets": "过筛沸石颗粒", + "material.gtceu.zinc_sulfate": "硫酸锌", + "material.gtceu.zircon": "锆石", + "material.gtceu.zircon_chlorinating_residue": "锆氯化反应残渣", + "material.gtceu.zirconiu_hafnium_oxychloride": "锆-铪氯氧化物", + "material.gtceu.zirconium_carbide": "碳化锆", + "material.gtceu.zirconium_hafnium_chloride": "锆-铪氯化物", + "material.gtceu.zirconium_oxide": "二氧化锆", + "material.gtceu.znfealcl_catalyst": "锌-铁-铝-氯混合催化剂", + "material.gtceu.zylon": "柴隆纤维", + "tagprefix.ceresstone": "谷神星%s矿石", + "tagprefix.enceladusstone": "土卫二%s矿石", + "tagprefix.ganymedestone": "木卫三%s矿石", + "tagprefix.glacio_stone": "霜原石%s矿石", + "tagprefix.iostone": "木卫一%s矿石", + "tagprefix.mars_stone": "火星石%s矿石", + "tagprefix.mercury_stone": "水星石%s矿石", + "tagprefix.milled": "碾磨%s", + "tagprefix.moon_stone": "月石%s矿石", + "tagprefix.nanoswarm": "%s纳米蜂群", + "tagprefix.plutostone": "冥王星%s矿石", + "tagprefix.titanstone": "土卫六%s矿石", + "tagprefix.venus_stone": "金星石%s矿石" } \ No newline at end of file diff --git a/src/main/resources/assets/gtceu/models/block/machine/part/me_extended_export_buffer.json b/src/main/resources/assets/gtceu/models/block/machine/part/me_extended_export_buffer.json new file mode 100644 index 000000000..3f692caf3 --- /dev/null +++ b/src/main/resources/assets/gtceu/models/block/machine/part/me_extended_export_buffer.json @@ -0,0 +1,6 @@ +{ + "parent": "gtceu:block/overlay/front", + "textures": { + "overlay": "gtceu:block/overlay/appeng/me_extended_export_buffer" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/gtceu/models/block/machine/part/me_mini_pattern_buffer.json b/src/main/resources/assets/gtceu/models/block/machine/part/me_mini_pattern_buffer.json new file mode 100644 index 000000000..ec0c1bebd --- /dev/null +++ b/src/main/resources/assets/gtceu/models/block/machine/part/me_mini_pattern_buffer.json @@ -0,0 +1,61 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "particle": "gtceu:block/overlay/machine/overlay_hatch", + "overlay_hatch": "gtceu:block/overlay/machine/overlay_me_mini_pattern_buffer", + "overlay_hatch_emissive": "gtceu:block/overlay/machine/overlay_me_mini_pattern_buffer_emissive" + }, + "elements": [ + { + "from": [ + 0, + 0, + 0 + ], + "to": [ + 16, + 16, + 0 + ], + "faces": { + "north": { + "uv": [ + 0, + 0, + 16, + 16 + ], + "texture": "#overlay_hatch", + "cullface": "north" + } + } + }, + { + "from": [ + 0, + 0, + 0 + ], + "to": [ + 16, + 16, + 0 + ], + "faces": { + "north": { + "uv": [ + 0, + 0, + 16, + 16 + ], + "texture": "#overlay_hatch_emissive", + "cullface": "north", + "tintindex": -101, + "emissivity": 15 + } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/gtceu/sounds/dtpf.ogg b/src/main/resources/assets/gtceu/sounds/dtpf.ogg index e9b7a2bdb..fc685919d 100755 Binary files a/src/main/resources/assets/gtceu/sounds/dtpf.ogg and b/src/main/resources/assets/gtceu/sounds/dtpf.ogg differ diff --git a/src/main/resources/assets/gtceu/sounds/fusionloop.ogg b/src/main/resources/assets/gtceu/sounds/fusionloop.ogg index d532f1a08..914633f69 100755 Binary files a/src/main/resources/assets/gtceu/sounds/fusionloop.ogg and b/src/main/resources/assets/gtceu/sounds/fusionloop.ogg differ diff --git a/src/main/resources/assets/gtceu/textures/block/casings/crafter_core_casing.png b/src/main/resources/assets/gtceu/textures/block/casings/crafter_core_casing.png new file mode 100644 index 000000000..608378354 Binary files /dev/null and b/src/main/resources/assets/gtceu/textures/block/casings/crafter_core_casing.png differ diff --git a/src/main/resources/assets/gtceu/textures/block/casings/pattern_core_casing.png b/src/main/resources/assets/gtceu/textures/block/casings/pattern_core_casing.png new file mode 100644 index 000000000..85791831d Binary files /dev/null and b/src/main/resources/assets/gtceu/textures/block/casings/pattern_core_casing.png differ diff --git a/src/main/resources/assets/gtceu/textures/block/casings/speed_core_casing.png b/src/main/resources/assets/gtceu/textures/block/casings/speed_core_casing.png new file mode 100644 index 000000000..fb17fb9df Binary files /dev/null and b/src/main/resources/assets/gtceu/textures/block/casings/speed_core_casing.png differ diff --git a/src/main/resources/assets/gtceu/textures/block/overlay/appeng/me_extended_export_buffer.png b/src/main/resources/assets/gtceu/textures/block/overlay/appeng/me_extended_export_buffer.png new file mode 100644 index 000000000..f9060f8b8 Binary files /dev/null and b/src/main/resources/assets/gtceu/textures/block/overlay/appeng/me_extended_export_buffer.png differ diff --git a/src/main/resources/assets/gtceu/textures/block/overlay/appeng/me_extended_export_buffer.png.mcmeta b/src/main/resources/assets/gtceu/textures/block/overlay/appeng/me_extended_export_buffer.png.mcmeta new file mode 100644 index 000000000..5b47c3643 --- /dev/null +++ b/src/main/resources/assets/gtceu/textures/block/overlay/appeng/me_extended_export_buffer.png.mcmeta @@ -0,0 +1,6 @@ +{ + "animation":{ + "frametime": 4, + "interpolate": true + } +} \ No newline at end of file diff --git a/src/main/resources/assets/gtceu/textures/block/overlay/machine/overlay_me_mini_pattern_buffer.png b/src/main/resources/assets/gtceu/textures/block/overlay/machine/overlay_me_mini_pattern_buffer.png new file mode 100644 index 000000000..ad6e2159f Binary files /dev/null and b/src/main/resources/assets/gtceu/textures/block/overlay/machine/overlay_me_mini_pattern_buffer.png differ diff --git a/src/main/resources/assets/gtceu/textures/block/overlay/machine/overlay_me_mini_pattern_buffer_emissive.png b/src/main/resources/assets/gtceu/textures/block/overlay/machine/overlay_me_mini_pattern_buffer_emissive.png new file mode 100644 index 000000000..5ce52f02d Binary files /dev/null and b/src/main/resources/assets/gtceu/textures/block/overlay/machine/overlay_me_mini_pattern_buffer_emissive.png differ diff --git a/src/main/resources/assets/gtceu/textures/gui/structure_check.png b/src/main/resources/assets/gtceu/textures/gui/structure_check.png new file mode 100644 index 000000000..9b2c89e20 Binary files /dev/null and b/src/main/resources/assets/gtceu/textures/gui/structure_check.png differ diff --git a/src/main/resources/assets/gtlcore/lang/en_us.json b/src/main/resources/assets/gtlcore/lang/en_us.json new file mode 100644 index 000000000..a13db3a75 --- /dev/null +++ b/src/main/resources/assets/gtlcore/lang/en_us.json @@ -0,0 +1,310 @@ +{ + "block.gtceu.performance_monitor": "Performance Monitor", + "block.gtlcore.16m_storage": "16M Crafting Storage", + "block.gtlcore.1m_storage": "1M Crafting Storage", + "block.gtlcore.256m_storage": "256M Crafting Storage", + "block.gtlcore.4m_storage": "4M Crafting Storage", + "block.gtlcore.64m_storage": "64M Crafting Storage", + "block.gtlcore.advanced_assembly_line_unit": "Advanced Assembly Line Unit Casing", + "block.gtlcore.advanced_compressed_fusion_coil": "Advanced Compressed Fusion Coil Block", + "block.gtlcore.advanced_fusion_coil": "Advanced Fusion Coil Block", + "block.gtlcore.advanced_stellar_containment_casing": "Advanced Stellar Containment Casing", + "block.gtlcore.aluminium_bronze_casing": "Aluminium Bronze Machine Casing", + "block.gtlcore.antifreeze_heatproof_machine_casing": "Antifreeze Heatproof Machine Casing", + "block.gtlcore.blaze_blast_furnace_casing": "Blaze Blast Furnace Casing", + "block.gtlcore.cold_ice_casing": "Cold Ice Machine Casing", + "block.gtlcore.component_assembly_line_casing_ev": "Component Assembly Line Casing (EV)", + "block.gtlcore.component_assembly_line_casing_hv": "Component Assembly Line Casing (HV)", + "block.gtlcore.component_assembly_line_casing_iv": "Component Assembly Line Casing (IV)", + "block.gtlcore.component_assembly_line_casing_luv": "Component Assembly Line Casing (LuV)", + "block.gtlcore.component_assembly_line_casing_lv": "Component Assembly Line Casing (LV)", + "block.gtlcore.component_assembly_line_casing_max": "Component Assembly Line Casing (MAX)", + "block.gtlcore.component_assembly_line_casing_mv": "Component Assembly Line Casing (MV)", + "block.gtlcore.component_assembly_line_casing_opv": "Component Assembly Line Casing (OPV)", + "block.gtlcore.component_assembly_line_casing_uev": "Component Assembly Line Casing (UEV)", + "block.gtlcore.component_assembly_line_casing_uhv": "Component Assembly Line Casing (UHV)", + "block.gtlcore.component_assembly_line_casing_uiv": "Component Assembly Line Casing (UIV)", + "block.gtlcore.component_assembly_line_casing_uv": "Component Assembly Line Casing (UV)", + "block.gtlcore.component_assembly_line_casing_uxv": "Component Assembly Line Casing (UXV)", + "block.gtlcore.component_assembly_line_casing_zpm": "Component Assembly Line Casing (ZPM)", + "block.gtlcore.compressed_fusion_coil": "Compressed Fusion Coil Block", + "block.gtlcore.compressed_fusion_coil_mk2": "Compressed Fusion Coil Block MK-II", + "block.gtlcore.compressed_fusion_coil_mk2_prototype": "Compressed Fusion Coil Block MK-II Prototype", + "block.gtlcore.cooler": "Fission Cooler Component", + "block.gtlcore.create_casing": "Creative Machine Casing", + "block.gtlcore.degenerate_rhenium_constrained_casing": "Degenerate Rhenium Constrained Casing", + "block.gtlcore.dimension_connection_casing": "Dimension Connection Machine Casing", + "block.gtlcore.dimension_injection_casing": "Dimension Injection Machine Casing", + "block.gtlcore.dimensionally_transcendent_casing": "Dimensionally Transcendent Machine Casing", + "block.gtlcore.dragon_strength_tritanium_casing": "Dragon Strength Tritanium Machine Casing", + "block.gtlcore.echo_casing": "Echo Reinforced Machine Casing", + "block.gtlcore.enhance_hyper_mechanical_casing": "Enhanced Hyper Mechanical Casing", + "block.gtlcore.extreme_strength_tritanium_casing": "Extreme Strength Tritanium Machine Casing", + "block.gtlcore.fission_fuel_assembly": "Fission Fuel Assembly", + "block.gtlcore.fission_reactor_casing": "Fission Reactor Casing", + "block.gtlcore.fusion_casing_mk4": "Fusion Machine Casing MK-IV", + "block.gtlcore.fusion_casing_mk5": "Fusion Machine Casing MK-V", + "block.gtlcore.fusion_coil_mk2": "Fusion Coil Block MK-II", + "block.gtlcore.graviton_field_constraint_casing": "Graviton Field Constraint Casing", + "block.gtlcore.hsss_reinforced_borosilicate_glass": "HSS-S Reinforced Borosilicate Glass", + "block.gtlcore.hyper_core": "Hyper Core", + "block.gtlcore.hyper_mechanical_casing": "Hyper Mechanical Casing", + "block.gtlcore.improved_superconductor_coil": "Improved Superconductor Coil Block", + "block.gtlcore.infinity_glass": "Infinity Reinforced Glass", + "block.gtlcore.iridium_casing": "Iridium Reinforced Machine Casing", + "block.gtlcore.lafium_mechanical_casing": "Lafium Mechanical Casing", + "block.gtlcore.law_filter_casing": "Law Filter Machine Casing", + "block.gtlcore.manipulator": "Quantum Manipulator Machine Casing", + "block.gtlcore.max_storage": "MAX Crafting Storage", + "block.gtlcore.molecular_casing": "Molecular Machine Casing", + "block.gtlcore.multi_functional_casing": "Multi-Functional Machine Casing", + "block.gtlcore.naquadah_alloy_casing": "Naquadah Alloy Machine Casing", + "block.gtlcore.opv_hermetic_casing": "Hermetic Machine Casing XIII", + "block.gtlcore.oxidation_resistant_hastelloy_n_mechanical_casing": "Oxidation Resistant Hastelloy-N Mechanical Casing", + "block.gtlcore.performance_monitor.tooltip": "Can monitor the average delay of all machines within 2 seconds and supports highlighting.", + "block.gtlcore.pikyonium_machine_casing": "Pikyonium Machine Casing", + "block.gtlcore.power_core": "Space Elevator Power Core", + "block.gtlcore.power_module": "Space Elevator Power Module MK1", + "block.gtlcore.power_module_2": "Space Elevator Power Module MK2", + "block.gtlcore.power_module_3": "Space Elevator Power Module MK3", + "block.gtlcore.power_module_4": "Space Elevator Power Module MK4", + "block.gtlcore.power_module_5": "Space Elevator Power Module MK5", + "block.gtlcore.process_machine_casing": "Process Machine Casing", + "block.gtlcore.qft_coil": "Quantum Manipulator Coil Block", + "block.gtlcore.rhenium_reinforced_energy_glass": "Rhenium Reinforced Energy Glass", + "block.gtlcore.space_elevator_mechanical_casing": "Space Elevator Mechanical Casing", + "block.gtlcore.space_elevator_support": "Space Elevator Support Structure", + "block.gtlcore.spacetimebendingcore": "Spacetime Bending Core Block", + "block.gtlcore.spacetimecontinuumripper": "Spacetime Continuum Ripper Block", + "block.gtlcore.sps_casing": "Supercritical Casing", + "block.gtlcore.stellar_containment_casing": "Basic Stellar Containment Casing", + "block.gtlcore.super_computation_component": "Super Computation Component", + "block.gtlcore.super_cooler_component": "Super Cooler Component", + "block.gtlcore.supercritical_turbine_casing": "Supercritical Turbine Machine Casing", + "block.gtlcore.uev_hermetic_casing": "Hermetic Machine Casing X", + "block.gtlcore.uiv_hermetic_casing": "Hermetic Machine Casing XI", + "block.gtlcore.ultimate_stellar_containment_casing": "Ultimate Stellar Containment Casing", + "block.gtlcore.uxv_hermetic_casing": "Hermetic Machine Casing XII", + "config.gtlcore.option.MEPatternOutputMax": "ME Pattern Assembly Output Maximum Interval", + "config.gtlcore.option.MEPatternOutputMin": "ME Pattern Assembly Output Minimum Interval", + "config.gtlcore.option.ae2CraftingServiceUpdateInterval": "AE2 Crafting Update Interval", + "config.gtlcore.option.ae2StorageServiceUpdateInterval": "AE2 Storage Update Interval", + "config.gtlcore.option.blackBlockList": "FTBU Chain Mining Blacklist", + "config.gtlcore.option.cellType": "AE Storage Cell Type Multiplier", + "config.gtlcore.option.disableDrift": "Disable Flight Inertia", + "config.gtlcore.option.durationMultiplier": "Recipe Duration Multiplier", + "config.gtlcore.option.ae2CalculationMode": "AE2 Calculation Mode", + "config.gtlcore.option.enablePrimitiveVoidOre": "Enable Primitive Void Ore Miner", + "config.gtlcore.option.enableUltimateMEStocking": "Enable Ultimate ME Stocking Pull Mode", + "config.gtlcore.option.exPatternProvider": "Extended Pattern Provider Pattern Capacity", + "config.gtlcore.option.mobList1": "Slaughterhouse Non-Hostile Mob List", + "config.gtlcore.option.mobList2": "Slaughterhouse Hostile Mob List", + "config.gtlcore.option.oreMultiplier": "Ore Yield Multiplier", + "config.gtlcore.option.recipeMultiMax": "Recipe Int Overflow Extra Output Maximum", + "config.gtlcore.option.recipeMultiOutput": "Enable Recipe Int Overflow Extra Output", + "config.gtlcore.option.spacetimePip": "Spacetime Pipe Flow Rate", + "config.gtlcore.option.travelStaffCD": "Travel Staff Cooldown", + "config.jade.plugin_gtlcore.tick_time_provider": "[GTLCore] Tick Time", + "config.jade.plugin_gtlcore.wireless_data_hatch_provider": "Wireless Data Hatch", + "gtlcore.condition.gravity": "Requires High Gravity Environment", + "gtlcore.condition.zero_gravity": "Requires Zero Gravity Environment", + "gtlcore.gui.filter_config.title": "Filter Configuration", + "gtlcore.machine.me_dual_hatch_stock.turns.0": "Auto Pull: OFF", + "gtlcore.machine.me_dual_hatch_stock.turns.1": "Auto Pull: ALL", + "gtlcore.machine.me_dual_hatch_stock.turns.2": "Auto Pull: ITEM", + "gtlcore.machine.me_dual_hatch_stock.turns.3": "Auto Pull: FLUID", + "gtlcore.me_any": "ME Hatch allows any side connection", + "gtlcore.me_front": "ME Hatch allows front side connection only", + "gtlcore.multiblock.contains_Patttern": "Can contain %d crafting patterns", + "gtlcore.multiblock.tick_Duration": "Fixed Duration %d Tick", + "gtlcore.registry.add": "Added by GregTech Leisure", + "gtlcore.registry.modify": "Modified by GregTech Leisure", + "gui.gtlcore.circuit_format": " Circuit %s", + "gui.gtlcore.coil": "Coil", + "gui.gtlcore.component_assembly_casing": "Component Assembly Casing", + "gui.gtlcore.logic_operators": "Supports logic operators & | ! ( )", + "gui.gtlcore.machine_colon": "Machine:", + "gui.gtlcore.machine_label": "Machine:", + "gui.gtlcore.mirror_mode": "Mirror Mode", + "gui.gtlcore.open.config.map": "Click to open the page for selecting the level block", + "gui.gtlcore.no_hatch_mode": "No Hatch Mode", + "gui.gtlcore.pattern_recipe_divide_2": "Pattern Recipe ÷ 2", + "gui.gtlcore.pattern_recipe_divide_3": "Pattern Recipe ÷ 3", + "gui.gtlcore.pattern_recipe_divide_5": "Pattern Recipe ÷ 5", + "gui.gtlcore.pattern_recipe_multiply_2": "Pattern Recipe x 2", + "gui.gtlcore.pattern_recipe_multiply_3": "Pattern Recipe x 3", + "gui.gtlcore.pattern_recipe_multiply_5": "Pattern Recipe x 5", + "gui.gtlcore.recipe_lock.no_recipe": "No Locked Recipe", + "gui.gtlcore.recipe_lock.recipe": "Locked Recipe", + "gui.gtlcore.repeat_count": "Repeat Count", + "gui.gtlcore.replace_mode": "Replace Mode", + "gui.gtlcore.space_elevator_module": "Space Elevator Module", + "gui.gtlcore.status_off": "[Off]", + "gui.gtlcore.status_on": "[On]", + "gui.gtlcore.stellar_thermal_container": "Stellar Thermal Container", + "gui.gtlcore.tag_blacklist": "Tag Blacklist", + "gui.gtlcore.tag_filter_config": "Tag Filter Configuration", + "gui.gtlcore.tag_whitelist": "Tag Whitelist", + "gui.gtlcore.tier_blocks": "Tier Blocks", + "gui.gtlcore.ultimate_terminal_settings": "Ultimate Terminal Settings", + "gui.gtlcore.voltage_colon": "Voltage: %s (%s)", + "gui.gtlcore.voltage_format": "Voltage: %s (%s)", + "gui.gtlcore.wildcard_info": "Supports wildcards * and ?", + "item.gtlcore.bifidobacteriumm_petri_dish": "Bifidobacterium Petri Dish", + "item.gtlcore.brevibacterium_petri_dish": "Brevibacterium Petri Dish", + "item.gtlcore.cell_component_16m": "16M Storage Component", + "item.gtlcore.cell_component_1m": "1M Storage Component", + "item.gtlcore.cell_component_256m": "256M Storage Component", + "item.gtlcore.cell_component_4m": "4M Storage Component", + "item.gtlcore.cell_component_64m": "64M Storage Component", + "item.gtlcore.cfg_copy": "Configuration Copy Tool", + "item.gtlcore.contaminated_petri_dish": "Contaminated Petri Dish", + "item.gtlcore.conversion_simulate_card": "Conversion Simulation Card", + "item.gtlcore.cupriavidus_petri_dish": "Cupriavidus Petri Dish", + "item.gtlcore.debug_pattern_test": "Debug Pattern Test Tool", + "item.gtlcore.debug_structure_writer": "Debug Structure Writer Tool", + "item.gtlcore.electricaly_wired_petri_dish": "Electrically Wired Petri Dish", + "item.gtlcore.eschericia_petri_dish": "Escherichia Petri Dish", + "item.gtlcore.extremely_max_battery": "Extremely MAX Battery", + "item.gtlcore.fast_conversion_simulate_card": "Fast Conversion Simulation Card", + "item.gtlcore.fast_infinity_cell": "Fast Infinite Cell", + "item.gtlcore.fluid_infinity_cell": "Infinite Fluid Storage Cell", + "item.gtlcore.fluid_storage_cell_16m": "16M Fluid Storage Cell", + "item.gtlcore.fluid_storage_cell_1m": "1M Fluid Storage Cell", + "item.gtlcore.fluid_storage_cell_256m": "256M Fluid Storage Cell", + "item.gtlcore.fluid_storage_cell_4m": "4M Fluid Storage Cell", + "item.gtlcore.fluid_storage_cell_64m": "64M Fluid Storage Cell", + "item.gtlcore.highly_insulating_foil": "Highly Insulating Foil", + "item.gtlcore.infinite_cell_component": "Infinite Storage Component", + "item.gtlcore.insanely_max_battery": "Insanely MAX Battery", + "item.gtlcore.item_infinity_cell": "Infinite Item Storage Cell", + "item.gtlcore.item_storage_cell_16m": "16M Item Storage Cell", + "item.gtlcore.item_storage_cell_1m": "1M Item Storage Cell", + "item.gtlcore.item_storage_cell_256m": "256M Item Storage Cell", + "item.gtlcore.item_storage_cell_4m": "4M Item Storage Cell", + "item.gtlcore.item_storage_cell_64m": "64M Item Storage Cell", + "item.gtlcore.max_conveyor_module": "§4§lMAX§r Conveyor Module", + "item.gtlcore.max_electric_motor": "§4§lMAX§r Electric Motor", + "item.gtlcore.max_electric_piston": "§4§lMAX§r Electric Piston", + "item.gtlcore.max_electric_pump": "§4§lMAX§r Electric Pump", + "item.gtlcore.max_emitter": "§4§lMAX§r Emitter", + "item.gtlcore.max_field_generator": "§4§lMAX§r Field Generator", + "item.gtlcore.max_robot_arm": "§4§lMAX§r Robot Arm", + "item.gtlcore.max_sensor": "§4§lMAX§r Sensor", + "item.gtlcore.me_pattern_buffer_copy": "ME Pattern Buffer Copy Tool", + "item.gtlcore.mega_max_battery": "Mega MAX Battery", + "item.gtlcore.microfocus_x_ray_tube": "Microfocus X-Ray Tube", + "item.gtlcore.pattern_modifier": "Pattern Provider Modifier", + "item.gtlcore.primitive_fluid_regulator": "Primitive Fluid Regulator", + "item.gtlcore.primitive_robot_arm": "Primitive Robot Arm", + "item.gtlcore.protonated_fullerene_sieving_matrix": "Protonated Fullerene Sieving Matrix", + "item.gtlcore.really_max_battery": "Really MAX Battery", + "item.gtlcore.saturated_fullerene_sieving_matrix": "Saturated Fullerene Sieving Matrix", + "item.gtlcore.separation_electromagnet": "Separation Electromagnet", + "item.gtlcore.shewanella_petri_dish": "Shewanella Petri Dish", + "item.gtlcore.sterilized_petri_dish": "Sterilized Petri Dish", + "item.gtlcore.streptococcus_petri_dish": "Streptococcus Petri Dish", + "item.gtlcore.structure_detect": "Structure Detection Tool", + "item.gtlcore.structure_detect.error.0": "Required at %s:\n", + "item.gtlcore.structure_detect.error.1": "Required at %s:", + "item.gtlcore.structure_detect.error.2": "At %s %s", + "item.gtlcore.structure_detect.error.3": "(Mirrored Mode)", + "item.gtlcore.structure_detect.error.4": "(Normal Mode)", + "item.gtlcore.structure_detect.tooltip.0": "Right-click multiblock main block", + "item.gtlcore.structure_detect.tooltip.1": "Shift right-click to switch detection mode", + "item.gtlcore.super_portable_fluid_storage_cell": "Super Portable Fluid Storage Cell", + "item.gtlcore.super_portable_item_storage_cell": "Super Portable Item Storage Cell", + "item.gtlcore.transcendent_max_battery": "Transcendent MAX Battery", + "item.gtlcore.ultimate_terminal": "Ultimate Terminal", + "itemGroup.gtlcore.creative_tab": "GregTech Leisure", + "message.gtlcore.coil_incompatible_recipe_mode": "Cannot use this coil in current recipe mode", + "message.gtlcore.detection_mode_mirrored": "Current detection mode: (Mirrored mode)", + "message.gtlcore.detection_mode_normal": "Current detection mode: (Normal mode)", + "message.gtlcore.equipment_incompatible_dimension": "Your equipment cannot adapt to the target dimension environment", + "message.gtlcore.machine_data_copied": "Machine data copied", + "message.gtlcore.machine_data_not_found": "Machine data not found", + "message.gtlcore.machine_data_pasted": "Machine data pasted", + "message.gtlcore.need_carbon_nano_swarm": "Need to insert Carbon Nano Swarm", + "message.gtlcore.need_carbon_nano_swarm_red": "Need to insert Carbon Nano Swarm", + "message.gtlcore.need_dragon_nano_swarm": "Need to insert Dragon Nano Swarm", + "message.gtlcore.need_dragon_nano_swarm_red": "Need to insert Dragon Nano Swarm", + "message.gtlcore.need_neutronium_nano_swarm": "Need to insert Neutronium Nano Swarm", + "message.gtlcore.need_neutronium_nano_swarm_red": "Need to insert Neutronium Nano Swarm", + "message.gtlcore.only_use_on_pattern_provider": "Can only be used on pattern providers", + "message.gtlcore.pattern_buffer_copied": "Copied internal patterns from pattern buffer", + "message.gtlcore.pattern_buffer_copy_failed": "Failed to copy pattern buffer", + "message.gtlcore.pattern_buffer_not_copied": "Pattern buffer not copied", + "message.gtlcore.pattern_buffer_patterns_copied": "Copied internal patterns from pattern buffer", + "message.gtlcore.pattern_provider_only": "Can only be used on pattern providers", + "message.gtlcore.pattern_updated": "Updated internal patterns, applied %s times", + "message.gtlcore.pattern_updated_count": "Updated internal pattern, applied %s times", + "message.gtlcore.right_click_air_gui": "Right-click air to open GUI", + "message.gtlcore.structure_formed": "Structure formed", + "structure_detect.error": "Required at %s:\n", + "structure_detect.error.2": "At %s ", + "structure_detect.tooltip.0": "Right-click multiblock main block", + "structure_writer.export_order": "Export Order: C:%s S:%s A:%s", + "structure_writer.structural_scale": "Structure Scale: X:%s Y:%s Z:%s", + "tooltip.gtlcore.application_count_limit": "Application count per use (<=16)", + "tooltip.gtlcore.apply_pattern_buffer_right_click": "Right-click to apply copied content to target pattern buffer", + "tooltip.gtlcore.auto_pull_sort_mode": "Auto-pull sorting mode", + "tooltip.gtlcore.bigger_stronger": "Bigger, Stronger(?)", + "tooltip.gtlcore.can_use_transformer_hatch": "Can use transformer power hatches", + "tooltip.gtlcore.change_item_fluid_filter_priority": "Change item/fluid filter settings and priority", + "tooltip.gtlcore.change_priority": "Change priority", + "tooltip.gtlcore.complete_gt_leisure": "§7Fill it up to complete GregTech Leisure", + "tooltip.gtlcore.complete_gtceu_modern": "§7Fill it up to complete GregTechCEu Modern", + "tooltip.gtlcore.copy_pattern_buffer_sneak_right_click": "Sneak right-click to copy patterns and names from target pattern buffer", + "tooltip.gtlcore.current_light_level": "Current Light Level: ", + "tooltip.gtlcore.current_recipe_type": "Current recipe type:", + "tooltip.gtlcore.fill_battery_ascension": "Fill the battery - Mechanical Ascension", + "tooltip.gtlcore.fill_battery_mechanical_ascension": "Fill the battery - Mechanical Ascension", + "tooltip.gtlcore.fill_for_fun": "Filling it is just for fun", + "tooltip.gtlcore.fill_in_lifetime": "§7Fill it up within your lifetime", + "tooltip.gtlcore.helium_storage": "Helium Storage: %smb", + "tooltip.gtlcore.hydrogen_storage": "Hydrogen Storage: %smb", + "tooltip.gtlcore.input_blacklist_keywords": "Input blacklist ID keywords, separated by spaces. Matching one rejects", + "tooltip.gtlcore.input_whitelist_keywords": "Input whitelist ID keywords, separated by spaces. Matching one passes", + "tooltip.gtlcore.installed_module_count": "Installed module count: %s", + "tooltip.gtlcore.mirror_mode": "When enabled, allows mirrored placement of machine blocks", + "tooltip.gtlcore.mirror_placement": "When enabled, allows mirrored placement of machine blocks", + "tooltip.gtlcore.module_installed": "This module is successfully installed", + "tooltip.gtlcore.module_not_installed": "This module is not installed", + "tooltip.gtlcore.no_hatch_mode": "When enabled, will not place various hatches in non-unique situations", + "tooltip.gtlcore.no_hatches_mode": "When enabled, will not place various hatches in non-unique situations", + "tooltip.gtlcore.output_blacklist_keywords": "Output blacklist ID keywords, separated by spaces. Matching one rejects", + "tooltip.gtlcore.output_whitelist_keywords": "Output whitelist ID keywords, separated by spaces. Matching one passes", + "tooltip.gtlcore.pattern_applied_number": "Number of applications per use (<=16)", + "tooltip.gtlcore.pattern_divider_scale": "Set pattern divisor", + "tooltip.gtlcore.pattern_materials_divide_2": "Divide pattern materials and recipe quantities by 2", + "tooltip.gtlcore.pattern_materials_divide_3": "Divide pattern materials and recipe quantities by 3", + "tooltip.gtlcore.pattern_materials_divide_5": "Divide pattern materials and recipe quantities by 5", + "tooltip.gtlcore.pattern_materials_multiply_2": "Multiply pattern materials and recipe quantities by 2", + "tooltip.gtlcore.pattern_materials_multiply_3": "Multiply pattern materials and recipe quantities by 3", + "tooltip.gtlcore.pattern_materials_multiply_5": "Multiply pattern materials and recipe quantities by 5", + "tooltip.gtlcore.pattern_max_fluid_stack": "Set max fluids/bucket after multiplication", + "tooltip.gtlcore.pattern_max_item_stack": "Set max items/piece after multiplication", + "tooltip.gtlcore.pattern_multiplier_scale": "Set pattern multiplier", + "tooltip.gtlcore.primitive_void_ore_random_output": "Randomly generates a group of raw ores per tick based on dimension while running", + "tooltip.gtlcore.random_raw_ore_generation": "Randomly generates a group of raw ores per tick based on dimension while running", + "tooltip.gtlcore.replace_mode": "When enabled, replaces tier blocks with configured tier blocks", + "tooltip.gtlcore.replace_tier_blocks": "When enabled, replaces tier blocks with configured tier blocks", + "tooltip.gtlcore.right_click_apply_copied_content": "Right-click to apply copied content to target pattern buffer", + "tooltip.gtlcore.set_max_fluids_after_multiply": "Set maximum fluid per bucket after multiplication", + "tooltip.gtlcore.set_max_items_after_multiply": "Set maximum items per stack after multiplication", + "tooltip.gtlcore.set_pattern_multiplier": "Set pattern multiplier", + "tooltip.gtlcore.set_programmed_circuit": "Set programmed circuit", + "tooltip.gtlcore.set_recipe_type": "Set recipe type", + "tooltip.gtlcore.set_template_divisor": "Set template divisor", + "tooltip.gtlcore.set_template_multiplier": "Set template multiplier", + "tooltip.gtlcore.sneak_right_click_copy_pattern": "Sneak right-click to copy patterns and names from target pattern buffer", + "tooltip.gtlcore.space_elevator_connected": "Connected to running space elevator", + "tooltip.gtlcore.space_elevator_not_connected": "Space elevator not connected", + "tooltip.gtlcore.startup_energy_cost": "Startup Energy Cost: %sEU", + "tooltip.gtlcore.successfully_installed": "successfully installed", + "tooltip.gtlcore.supported_dimensions": "Supports Overworld, Nether, End", + "tooltip.gtlcore.supports_dimensions": "Supports Overworld, Nether, End", + "tooltip.gtlcore.this_module": "This module", + "tooltip.gtlcore.turbine_rotor_only": "Only turbine rotors can be placed", + "tooltip.gtlcore.structure.source": "§6Structure Source: %s" +} \ No newline at end of file diff --git a/src/main/resources/assets/gtlcore/lang/zh_cn.json b/src/main/resources/assets/gtlcore/lang/zh_cn.json index 40b7e7b49..373beae8e 100644 --- a/src/main/resources/assets/gtlcore/lang/zh_cn.json +++ b/src/main/resources/assets/gtlcore/lang/zh_cn.json @@ -1,170 +1,310 @@ { - "block.gtlcore.16m_storage": "16M 合成存储器", - "block.gtlcore.1m_storage": "1M 合成存储器", - "block.gtlcore.256m_storage": "256M 合成存储器", - "block.gtlcore.4m_storage": "4M 合成存储器", - "block.gtlcore.64m_storage": "64M 合成存储器", - "block.gtlcore.advanced_assembly_line_unit": "进阶装配线控制外壳", - "block.gtlcore.advanced_compressed_fusion_coil": "进阶压缩聚变线圈方块", - "block.gtlcore.advanced_fusion_coil": "进阶聚变线圈方块", - "block.gtlcore.advanced_stellar_containment_casing": "高级恒星热力容器", - "block.gtlcore.aluminium_bronze_casing": "铝青铜机械方块", - "block.gtlcore.antifreeze_heatproof_machine_casing": "防冻隔热机械方块", - "block.gtlcore.blaze_blast_furnace_casing": "烈焰高炉机械方块", - "block.gtlcore.cold_ice_casing": "寒冰机械方块", - "block.gtlcore.component_assembly_line_casing_ev": "部件装配线外壳(EV)", - "block.gtlcore.component_assembly_line_casing_hv": "部件装配线外壳(HV)", - "block.gtlcore.component_assembly_line_casing_iv": "部件装配线外壳(IV)", - "block.gtlcore.component_assembly_line_casing_luv": "部件装配线外壳(LuV)", - "block.gtlcore.component_assembly_line_casing_lv": "部件装配线外壳(LV)", - "block.gtlcore.component_assembly_line_casing_max": "部件装配线外壳(MAX)", - "block.gtlcore.component_assembly_line_casing_mv": "部件装配线外壳(MV)", - "block.gtlcore.component_assembly_line_casing_opv": "部件装配线外壳(OPV)", - "block.gtlcore.component_assembly_line_casing_uev": "部件装配线外壳(UEV)", - "block.gtlcore.component_assembly_line_casing_uhv": "部件装配线外壳(UHV)", - "block.gtlcore.component_assembly_line_casing_uiv": "部件装配线外壳(UIV)", - "block.gtlcore.component_assembly_line_casing_uv": "部件装配线外壳(UV)", - "block.gtlcore.component_assembly_line_casing_uxv": "部件装配线外壳(UXV)", - "block.gtlcore.component_assembly_line_casing_zpm": "部件装配线外壳(ZPM)", - "block.gtlcore.compressed_fusion_coil": "压缩聚变线圈方块", - "block.gtlcore.compressed_fusion_coil_mk2": "压缩聚变线圈方块MK-II", - "block.gtlcore.compressed_fusion_coil_mk2_prototype": "压缩聚变线圈方块MK-II原型", - "block.gtlcore.cooler": "裂变冷却组件", - "block.gtlcore.create_casing": "创造机械方块", - "block.gtlcore.degenerate_rhenium_constrained_casing": "简并态铼约束外壳", - "block.gtlcore.dimension_connection_casing": "维度连接机械方块", - "block.gtlcore.dimension_injection_casing": "维度注入机械方块", - "block.gtlcore.dimensionally_transcendent_casing": "超维度机械方块", - "block.gtlcore.dragon_strength_tritanium_casing": "龙之力量三钛合金机械方块", - "block.gtlcore.echo_casing": "回响强化机械方块", - "block.gtlcore.enhance_hyper_mechanical_casing": "强化超能机械方块", - "block.gtlcore.extreme_strength_tritanium_casing": "极限强度三钛合金机械方块", - "block.gtlcore.fission_fuel_assembly": "裂变燃料组件", - "block.gtlcore.fission_reactor_casing": "裂变反应堆外壳", - "block.gtlcore.fusion_casing_mk4": "聚变机械方块 MK-IV", - "block.gtlcore.fusion_casing_mk5": "聚变机械方块 MK-V", - "block.gtlcore.fusion_coil_mk2": "聚变线圈方块MK-II", - "block.gtlcore.graviton_field_constraint_casing": "引力场约束方块", - "block.gtlcore.hsss_reinforced_borosilicate_glass": "高速钢-S强化硼玻璃", - "block.gtlcore.hyper_core": "超能核心", - "block.gtlcore.hyper_mechanical_casing": "超能机械方块", - "block.gtlcore.improved_superconductor_coil": "改良型超导线圈方块", - "block.gtlcore.infinity_glass": "无尽强化玻璃", - "block.gtlcore.iridium_casing": "铱强化机械方块", - "block.gtlcore.lafium_mechanical_casing": "路菲恩机械方块", - "block.gtlcore.law_filter_casing": "绝对洁净过滤器机械方块", - "block.gtlcore.manipulator": "量子操纵者机械方块", - "block.gtlcore.max_storage": "MAX 合成存储器", - "block.gtlcore.molecular_casing": "分子机械方块", - "block.gtlcore.multi_functional_casing": "多功能机械方块", - "block.gtlcore.naquadah_alloy_casing": "硅岩合金机械外壳", - "block.gtlcore.opv_hermetic_casing": "密封机械方块 XIII", - "block.gtlcore.oxidation_resistant_hastelloy_n_mechanical_casing": "抗氧化哈斯特洛伊合金-N机械方块", - "block.gtlcore.pikyonium_machine_casing": "皮卡优机械方块", - "block.gtlcore.power_core": "太空电梯动力核心", - "block.gtlcore.power_module": "太空电梯动力模块MK1", - "block.gtlcore.power_module_2": "太空电梯动力模块MK2", - "block.gtlcore.power_module_3": "太空电梯动力模块MK3", - "block.gtlcore.power_module_4": "太空电梯动力模块MK4", - "block.gtlcore.power_module_5": "太空电梯动力模块MK5", - "block.gtlcore.process_machine_casing": "处理机械方块", - "block.gtlcore.qft_coil": "量子操纵者线圈方块", - "block.gtlcore.rhenium_reinforced_energy_glass": "铼强化聚能玻璃", - "block.gtlcore.space_elevator_mechanical_casing": "太空电梯机械方块", - "block.gtlcore.space_elevator_support": "太空电梯支撑结构", - "block.gtlcore.spacetimebendingcore": "时空扭曲核心方块", - "block.gtlcore.spacetimecontinuumripper": "时空连续体撕裂方块", - "block.gtlcore.sps_casing": "超临界外壳", - "block.gtlcore.stellar_containment_casing": "基础恒星热力容器", - "block.gtlcore.super_computation_component": "超级计算机组件", - "block.gtlcore.super_cooler_component": "超级冷却组件", - "block.gtlcore.supercritical_turbine_casing": "超临界涡轮机械方块", - "block.gtlcore.uev_hermetic_casing": "密封机械方块 X", - "block.gtlcore.uiv_hermetic_casing": "密封机械方块 XI", - "block.gtlcore.ultimate_stellar_containment_casing": "终极恒星热力容器", - "block.gtlcore.uxv_hermetic_casing": "密封机械方块 XII", - "config.gtlcore.option.cellType": "AE磁盘存储类型倍数", - "config.gtlcore.option.blackBlockList": "FTBU连锁黑名单", - "config.gtlcore.option.recipeMultiOutput": "启用配方超出int额外输出", - "config.gtlcore.option.recipeMultiMax": "配方超出int额外输出最大值", - "config.gtlcore.option.disableDrift": "禁用飞行惯性", - "config.gtlcore.option.durationMultiplier": "配方时间乘数", - "config.gtlcore.option.enablePrimitiveVoidOre": "启用原始虚空矿机", - "config.gtlcore.option.exPatternProvider": "扩展样板供应器样板容量", - "config.gtlcore.option.mobList1": "屠宰场非敌对生物列表", - "config.gtlcore.option.mobList2": "屠宰场敌对生物列表", - "config.gtlcore.option.oreMultiplier": "矿石产量乘数", - "config.gtlcore.option.spacetimePip": "时空管道流量", - "config.gtlcore.option.travelStaffCD": "旅行权杖CD", - "gtlcore.condition.gravity": "需要强重力环境", - "gtlcore.condition.zero_gravity": "需要无重力环境", - "gtlcore.registry.add": "由GregTech Leisure添加", - "gtlcore.registry.modify": "由GregTech Leisure修改", - "item.gtlcore.bifidobacteriumm_petri_dish": "短双歧杆菌培养皿", - "item.gtlcore.brevibacterium_petri_dish": "黄色短杆菌培养皿", - "item.gtlcore.cell_component_16m": "16M 存储组件", - "item.gtlcore.cell_component_1m": "1M 存储组件", - "item.gtlcore.cell_component_256m": "256M 存储组件", - "item.gtlcore.cell_component_4m": "4M 存储组件", - "item.gtlcore.cell_component_64m": "64M 存储组件", - "item.gtlcore.cfg_copy": "配置复制工具", - "item.gtlcore.contaminated_petri_dish": "污染的培养皿", - "item.gtlcore.conversion_simulate_card": "转换模拟卡", - "item.gtlcore.fast_conversion_simulate_card": "高速转换模拟卡", - "item.gtlcore.cupriavidus_petri_dish": "钩虫贪铜菌培养皿", - "item.gtlcore.debug_pattern_test": "配方样板调试工具", - "item.gtlcore.debug_structure_writer": "多方块结构导出工具", - "item.gtlcore.electricaly_wired_petri_dish": "电信号培养皿", - "item.gtlcore.eschericia_petri_dish": "大肠杆菌培养皿", - "item.gtlcore.extremely_max_battery": "极·终极电池", - "item.gtlcore.fluid_infinity_cell": "无限流体磁盘", - "item.gtlcore.fluid_storage_cell_16m": "16M 流体存储元件", - "item.gtlcore.fluid_storage_cell_1m": "1M 流体存储元件", - "item.gtlcore.fluid_storage_cell_256m": "256M 流体存储元件", - "item.gtlcore.fluid_storage_cell_4m": "4M 流体存储元件", - "item.gtlcore.fluid_storage_cell_64m": "64M 流体存储元件", - "item.gtlcore.highly_insulating_foil": "高绝缘性箔", - "item.gtlcore.infinite_cell_component": "无限存储组件", - "item.gtlcore.insanely_max_battery": "狂·终极电池", - "item.gtlcore.item_infinity_cell": "无限物品磁盘", - "item.gtlcore.item_storage_cell_16m": "16M 物品存储元件", - "item.gtlcore.item_storage_cell_1m": "1M 物品存储元件", - "item.gtlcore.item_storage_cell_256m": "256M 物品存储元件", - "item.gtlcore.item_storage_cell_4m": "4M 物品存储元件", - "item.gtlcore.item_storage_cell_64m": "64M 物品存储元件", - "item.gtlcore.max_conveyor_module": "§4§lMAX§r传送带", - "item.gtlcore.max_electric_motor": "§4§lMAX§r电动马达", - "item.gtlcore.max_electric_piston": "§4§lMAX§r电力活塞", - "item.gtlcore.max_electric_pump": "§4§lMAX§r电动泵", - "item.gtlcore.max_emitter": "§4§lMAX§r发射器", - "item.gtlcore.max_field_generator": "§4§lMAX§r力场发生器", - "item.gtlcore.max_robot_arm": "§4§lMAX§r机械臂", - "item.gtlcore.max_sensor": "§4§lMAX§r传感器", - "item.gtlcore.mega_max_battery": "兆·终极电池", - "item.gtlcore.microfocus_x_ray_tube": "微焦点X射线管", - "item.gtlcore.pattern_modifier": "供应器样板修改器", - "item.gtlcore.primitive_fluid_regulator": "原始流体校准器", - "item.gtlcore.primitive_robot_arm": "原始机械臂", - "item.gtlcore.protonated_fullerene_sieving_matrix": "质子化富勒烯筛分基质", - "item.gtlcore.really_max_battery": "真·终极电池", - "item.gtlcore.saturated_fullerene_sieving_matrix": "饱和富勒烯筛分基质", - "item.gtlcore.separation_electromagnet": "分离用电磁铁", - "item.gtlcore.shewanella_petri_dish": "希瓦氏菌培养皿", - "item.gtlcore.sterilized_petri_dish": "无菌培养皿", - "item.gtlcore.streptococcus_petri_dish": "酿脓链球菌培养皿", - "item.gtlcore.structure_detect": "结构检测工具", - "item.gtlcore.super_portable_fluid_storage_cell": "超级便携流体存储元件", - "item.gtlcore.super_portable_item_storage_cell": "超级便携物品存储元件", - "item.gtlcore.transcendent_max_battery": "超·终极电池", - "itemGroup.gtlcore.creative_tab": "格雷科技休闲版", - "structure_writer.export_order": "导出顺序: C:%s S:%s A:%s", - "structure_writer.structural_scale": "结构规模: X:%s Y:%s Z:%s", - "structure_detect.error": "在%s处需要:\n", - "structure_detect.error.2": "在%s处 ", - "structure_detect.tooltip.0": "右键多方快结构主方块即可", - "gtlcore.me_any": "ME仓允许任意面连接", - "gtlcore.me_front": "ME仓只允许正面连接", - "config.jade.plugin_gtlcore.tick_time_provider": "[GTLCore] Tick时间", - "block.gtceu.performance_monitor": "性能监控器", - "block.gtlcore.performance_monitor.tooltip": "能监测全部机器2秒内的平均延迟,并支持高亮显示。" + "block.gtceu.performance_monitor": "性能监控器", + "block.gtlcore.16m_storage": "16M 合成存储器", + "block.gtlcore.1m_storage": "1M 合成存储器", + "block.gtlcore.256m_storage": "256M 合成存储器", + "block.gtlcore.4m_storage": "4M 合成存储器", + "block.gtlcore.64m_storage": "64M 合成存储器", + "block.gtlcore.advanced_assembly_line_unit": "进阶装配线控制外壳", + "block.gtlcore.advanced_compressed_fusion_coil": "进阶压缩聚变线圈方块", + "block.gtlcore.advanced_fusion_coil": "进阶聚变线圈方块", + "block.gtlcore.advanced_stellar_containment_casing": "高级恒星热力容器", + "block.gtlcore.aluminium_bronze_casing": "铝青铜机械方块", + "block.gtlcore.antifreeze_heatproof_machine_casing": "防冻隔热机械方块", + "block.gtlcore.blaze_blast_furnace_casing": "烈焰高炉机械方块", + "block.gtlcore.cold_ice_casing": "寒冰机械方块", + "block.gtlcore.component_assembly_line_casing_ev": "部件装配线外壳(EV)", + "block.gtlcore.component_assembly_line_casing_hv": "部件装配线外壳(HV)", + "block.gtlcore.component_assembly_line_casing_iv": "部件装配线外壳(IV)", + "block.gtlcore.component_assembly_line_casing_luv": "部件装配线外壳(LuV)", + "block.gtlcore.component_assembly_line_casing_lv": "部件装配线外壳(LV)", + "block.gtlcore.component_assembly_line_casing_max": "部件装配线外壳(MAX)", + "block.gtlcore.component_assembly_line_casing_mv": "部件装配线外壳(MV)", + "block.gtlcore.component_assembly_line_casing_opv": "部件装配线外壳(OPV)", + "block.gtlcore.component_assembly_line_casing_uev": "部件装配线外壳(UEV)", + "block.gtlcore.component_assembly_line_casing_uhv": "部件装配线外壳(UHV)", + "block.gtlcore.component_assembly_line_casing_uiv": "部件装配线外壳(UIV)", + "block.gtlcore.component_assembly_line_casing_uv": "部件装配线外壳(UV)", + "block.gtlcore.component_assembly_line_casing_uxv": "部件装配线外壳(UXV)", + "block.gtlcore.component_assembly_line_casing_zpm": "部件装配线外壳(ZPM)", + "block.gtlcore.compressed_fusion_coil": "压缩聚变线圈方块", + "block.gtlcore.compressed_fusion_coil_mk2": "压缩聚变线圈方块MK-II", + "block.gtlcore.compressed_fusion_coil_mk2_prototype": "压缩聚变线圈方块MK-II原型", + "block.gtlcore.cooler": "裂变冷却组件", + "block.gtlcore.create_casing": "创造机械方块", + "block.gtlcore.degenerate_rhenium_constrained_casing": "简并态铼约束外壳", + "block.gtlcore.dimension_connection_casing": "维度连接机械方块", + "block.gtlcore.dimension_injection_casing": "维度注入机械方块", + "block.gtlcore.dimensionally_transcendent_casing": "超维度机械方块", + "block.gtlcore.dragon_strength_tritanium_casing": "龙之力量三钛合金机械方块", + "block.gtlcore.echo_casing": "回响强化机械方块", + "block.gtlcore.enhance_hyper_mechanical_casing": "强化超能机械方块", + "block.gtlcore.extreme_strength_tritanium_casing": "极限强度三钛合金机械方块", + "block.gtlcore.fission_fuel_assembly": "裂变燃料组件", + "block.gtlcore.fission_reactor_casing": "裂变反应堆外壳", + "block.gtlcore.fusion_casing_mk4": "聚变机械方块 MK-IV", + "block.gtlcore.fusion_casing_mk5": "聚变机械方块 MK-V", + "block.gtlcore.fusion_coil_mk2": "聚变线圈方块MK-II", + "block.gtlcore.graviton_field_constraint_casing": "引力场约束方块", + "block.gtlcore.hsss_reinforced_borosilicate_glass": "高速钢-S强化硼玻璃", + "block.gtlcore.hyper_core": "超能核心", + "block.gtlcore.hyper_mechanical_casing": "超能机械方块", + "block.gtlcore.improved_superconductor_coil": "改良型超导线圈方块", + "block.gtlcore.infinity_glass": "无尽强化玻璃", + "block.gtlcore.iridium_casing": "铱强化机械方块", + "block.gtlcore.lafium_mechanical_casing": "路菲恩机械方块", + "block.gtlcore.law_filter_casing": "绝对洁净过滤器机械方块", + "block.gtlcore.manipulator": "量子操纵者机械方块", + "block.gtlcore.max_storage": "MAX 合成存储器", + "block.gtlcore.molecular_casing": "分子机械方块", + "block.gtlcore.multi_functional_casing": "多功能机械方块", + "block.gtlcore.naquadah_alloy_casing": "硅岩合金机械外壳", + "block.gtlcore.opv_hermetic_casing": "密封机械方块 XIII", + "block.gtlcore.oxidation_resistant_hastelloy_n_mechanical_casing": "抗氧化哈斯特洛伊合金-N机械方块", + "block.gtlcore.performance_monitor.tooltip": "能监测全部机器2秒内的平均延迟,并支持高亮显示。", + "block.gtlcore.pikyonium_machine_casing": "皮卡优机械方块", + "block.gtlcore.power_core": "太空电梯动力核心", + "block.gtlcore.power_module": "太空电梯动力模块MK1", + "block.gtlcore.power_module_2": "太空电梯动力模块MK2", + "block.gtlcore.power_module_3": "太空电梯动力模块MK3", + "block.gtlcore.power_module_4": "太空电梯动力模块MK4", + "block.gtlcore.power_module_5": "太空电梯动力模块MK5", + "block.gtlcore.process_machine_casing": "处理机械方块", + "block.gtlcore.qft_coil": "量子操纵者线圈方块", + "block.gtlcore.rhenium_reinforced_energy_glass": "铼强化聚能玻璃", + "block.gtlcore.space_elevator_mechanical_casing": "太空电梯机械方块", + "block.gtlcore.space_elevator_support": "太空电梯支撑结构", + "block.gtlcore.spacetimebendingcore": "时空扭曲核心方块", + "block.gtlcore.spacetimecontinuumripper": "时空连续体撕裂方块", + "block.gtlcore.sps_casing": "超临界外壳", + "block.gtlcore.stellar_containment_casing": "基础恒星热力容器", + "block.gtlcore.super_computation_component": "超级计算机组件", + "block.gtlcore.super_cooler_component": "超级冷却组件", + "block.gtlcore.supercritical_turbine_casing": "超临界涡轮机械方块", + "block.gtlcore.uev_hermetic_casing": "密封机械方块 X", + "block.gtlcore.uiv_hermetic_casing": "密封机械方块 XI", + "block.gtlcore.ultimate_stellar_containment_casing": "终极恒星热力容器", + "block.gtlcore.uxv_hermetic_casing": "密封机械方块 XII", + "config.gtlcore.option.MEPatternOutputMax": "ME样板总成输出最大间隔", + "config.gtlcore.option.MEPatternOutputMin": "ME样板总成输出最小间隔", + "config.gtlcore.option.ae2CraftingServiceUpdateInterval": "AE2合成更新间隔", + "config.gtlcore.option.ae2StorageServiceUpdateInterval": "AE2库存更新间隔", + "config.gtlcore.option.blackBlockList": "FTBU连锁黑名单", + "config.gtlcore.option.cellType": "AE磁盘存储类型倍数", + "config.gtlcore.option.disableDrift": "禁用飞行惯性", + "config.gtlcore.option.durationMultiplier": "配方时间乘数", + "config.gtlcore.option.ae2CalculationMode": "AE2下单计算模式", + "config.gtlcore.option.enablePrimitiveVoidOre": "启用原始虚空矿机", + "config.gtlcore.option.enableUltimateMEStocking": "启用ME库存极限拉取模式", + "config.gtlcore.option.exPatternProvider": "扩展样板供应器样板容量", + "config.gtlcore.option.mobList1": "屠宰场非敌对生物列表", + "config.gtlcore.option.mobList2": "屠宰场敌对生物列表", + "config.gtlcore.option.oreMultiplier": "矿石产量乘数", + "config.gtlcore.option.recipeMultiMax": "配方超出int额外输出最大值", + "config.gtlcore.option.recipeMultiOutput": "启用配方超出int额外输出", + "config.gtlcore.option.spacetimePip": "时空管道流量", + "config.gtlcore.option.travelStaffCD": "旅行权杖CD", + "config.jade.plugin_gtlcore.tick_time_provider": "[GTLCore] Tick时间", + "config.jade.plugin_gtlcore.wireless_data_hatch_provider": "无线数据仓", + "gtlcore.condition.gravity": "需要强重力环境", + "gtlcore.condition.zero_gravity": "需要无重力环境", + "gtlcore.gui.filter_config.title": "过滤设置", + "gtlcore.machine.me_dual_hatch_stock.turns.0": "自动拉取:关闭", + "gtlcore.machine.me_dual_hatch_stock.turns.1": "自动拉取:全部", + "gtlcore.machine.me_dual_hatch_stock.turns.2": "自动拉取:物品", + "gtlcore.machine.me_dual_hatch_stock.turns.3": "自动拉取:流体", + "gtlcore.me_any": "ME仓允许任意面连接", + "gtlcore.me_front": "ME仓只允许正面连接", + "gtlcore.multiblock.contains_Patttern": "可容纳%d个合成样板", + "gtlcore.multiblock.tick_Duration": "固定耗时%dTick", + "gtlcore.registry.add": "由GregTech Leisure添加", + "gtlcore.registry.modify": "由GregTech Leisure修改", + "gui.gtlcore.circuit_format": " 电路%s", + "gui.gtlcore.coil": "线圈", + "gui.gtlcore.component_assembly_casing": "部件装配外壳", + "gui.gtlcore.logic_operators": "支持逻辑运算符 & | ! ( )", + "gui.gtlcore.machine_colon": "机器:", + "gui.gtlcore.machine_label": "机器:", + "gui.gtlcore.mirror_mode": "镜像模式", + "gui.gtlcore.open.config.map": "点击打开选择等级方块页面", + "gui.gtlcore.no_hatch_mode": "无仓室模式", + "gui.gtlcore.pattern_recipe_divide_2": "样板配方 ÷ 2", + "gui.gtlcore.pattern_recipe_divide_3": "样板配方 ÷ 3", + "gui.gtlcore.pattern_recipe_divide_5": "样板配方 ÷ 5", + "gui.gtlcore.pattern_recipe_multiply_2": "样板配方 x 2", + "gui.gtlcore.pattern_recipe_multiply_3": "样板配方 x 3", + "gui.gtlcore.pattern_recipe_multiply_5": "样板配方 x 5", + "gui.gtlcore.recipe_lock.no_recipe": "无锁定配方", + "gui.gtlcore.recipe_lock.recipe": "锁定配方", + "gui.gtlcore.repeat_count": "重复次数", + "gui.gtlcore.replace_mode": "替换模式", + "gui.gtlcore.space_elevator_module": "太空电梯模块", + "gui.gtlcore.status_off": "[关闭]", + "gui.gtlcore.status_on": "[开启]", + "gui.gtlcore.stellar_thermal_container": "恒星热力容器", + "gui.gtlcore.tag_blacklist": "标签黑名单", + "gui.gtlcore.tag_filter_config": "标签过滤配置", + "gui.gtlcore.tag_whitelist": "标签白名单", + "gui.gtlcore.tier_blocks": "等级方块", + "gui.gtlcore.ultimate_terminal_settings": "终极终端设置", + "gui.gtlcore.voltage_colon": "电压:%s(%s)", + "gui.gtlcore.voltage_format": "电压:%s(%s)", + "gui.gtlcore.wildcard_info": "支持通配符 * 和 ?", + "item.gtlcore.bifidobacteriumm_petri_dish": "短双歧杆菌培养皿", + "item.gtlcore.brevibacterium_petri_dish": "黄色短杆菌培养皿", + "item.gtlcore.cell_component_16m": "16M 存储组件", + "item.gtlcore.cell_component_1m": "1M 存储组件", + "item.gtlcore.cell_component_256m": "256M 存储组件", + "item.gtlcore.cell_component_4m": "4M 存储组件", + "item.gtlcore.cell_component_64m": "64M 存储组件", + "item.gtlcore.cfg_copy": "配置复制工具", + "item.gtlcore.contaminated_petri_dish": "污染的培养皿", + "item.gtlcore.conversion_simulate_card": "转换模拟卡", + "item.gtlcore.cupriavidus_petri_dish": "钩虫贪铜菌培养皿", + "item.gtlcore.debug_pattern_test": "配方样板调试工具", + "item.gtlcore.debug_structure_writer": "多方块结构导出工具", + "item.gtlcore.electricaly_wired_petri_dish": "电信号培养皿", + "item.gtlcore.eschericia_petri_dish": "大肠杆菌培养皿", + "item.gtlcore.extremely_max_battery": "极·终极电池", + "item.gtlcore.fast_conversion_simulate_card": "高速转换模拟卡", + "item.gtlcore.fast_infinity_cell": "高速无限磁盘", + "item.gtlcore.fluid_infinity_cell": "无限流体磁盘", + "item.gtlcore.fluid_storage_cell_16m": "16M 流体存储元件", + "item.gtlcore.fluid_storage_cell_1m": "1M 流体存储元件", + "item.gtlcore.fluid_storage_cell_256m": "256M 流体存储元件", + "item.gtlcore.fluid_storage_cell_4m": "4M 流体存储元件", + "item.gtlcore.fluid_storage_cell_64m": "64M 流体存储元件", + "item.gtlcore.highly_insulating_foil": "高绝缘性箔", + "item.gtlcore.infinite_cell_component": "无限存储组件", + "item.gtlcore.insanely_max_battery": "狂·终极电池", + "item.gtlcore.item_infinity_cell": "无限物品磁盘", + "item.gtlcore.item_storage_cell_16m": "16M 物品存储元件", + "item.gtlcore.item_storage_cell_1m": "1M 物品存储元件", + "item.gtlcore.item_storage_cell_256m": "256M 物品存储元件", + "item.gtlcore.item_storage_cell_4m": "4M 物品存储元件", + "item.gtlcore.item_storage_cell_64m": "64M 物品存储元件", + "item.gtlcore.max_conveyor_module": "§4§lMAX§r传送带", + "item.gtlcore.max_electric_motor": "§4§lMAX§r电动马达", + "item.gtlcore.max_electric_piston": "§4§lMAX§r电力活塞", + "item.gtlcore.max_electric_pump": "§4§lMAX§r电动泵", + "item.gtlcore.max_emitter": "§4§lMAX§r发射器", + "item.gtlcore.max_field_generator": "§4§lMAX§r力场发生器", + "item.gtlcore.max_robot_arm": "§4§lMAX§r机械臂", + "item.gtlcore.max_sensor": "§4§lMAX§r传感器", + "item.gtlcore.me_pattern_buffer_copy": "样板总成复制工具", + "item.gtlcore.mega_max_battery": "兆·终极电池", + "item.gtlcore.microfocus_x_ray_tube": "微焦点X射线管", + "item.gtlcore.pattern_modifier": "供应器样板修改器", + "item.gtlcore.primitive_fluid_regulator": "原始流体校准器", + "item.gtlcore.primitive_robot_arm": "原始机械臂", + "item.gtlcore.protonated_fullerene_sieving_matrix": "质子化富勒烯筛分基质", + "item.gtlcore.really_max_battery": "真·终极电池", + "item.gtlcore.saturated_fullerene_sieving_matrix": "饱和富勒烯筛分基质", + "item.gtlcore.separation_electromagnet": "分离用电磁铁", + "item.gtlcore.shewanella_petri_dish": "希瓦氏菌培养皿", + "item.gtlcore.sterilized_petri_dish": "无菌培养皿", + "item.gtlcore.streptococcus_petri_dish": "酿脓链球菌培养皿", + "item.gtlcore.structure_detect": "结构检测工具", + "item.gtlcore.structure_detect.error.0": "在 %s 处需要:", + "item.gtlcore.structure_detect.error.1": "在 %s 处的", + "item.gtlcore.structure_detect.error.2": "[%s, %s, %s]-(%s)", + "item.gtlcore.structure_detect.error.3": "镜像模式", + "item.gtlcore.structure_detect.error.4": "正常模式", + "item.gtlcore.structure_detect.tooltip.0": "右键多方块结构主方块即可", + "item.gtlcore.structure_detect.tooltip.1": "Shift右键切换检测模式", + "item.gtlcore.super_portable_fluid_storage_cell": "超级便携流体存储元件", + "item.gtlcore.super_portable_item_storage_cell": "超级便携物品存储元件", + "item.gtlcore.transcendent_max_battery": "超·终极电池", + "item.gtlcore.ultimate_terminal": "终极终端", + "itemGroup.gtlcore.creative_tab": "格雷科技休闲版", + "message.gtlcore.coil_incompatible_recipe_mode": "当前配方模式无法使用该线圈", + "message.gtlcore.detection_mode_mirrored": "当前检测模式:(镜像模式)", + "message.gtlcore.detection_mode_normal": "当前检测模式:(正常模式)", + "message.gtlcore.equipment_incompatible_dimension": "你的装备无法适应目标维度的环境", + "message.gtlcore.machine_data_copied": "已复制机器数据", + "message.gtlcore.machine_data_not_found": "未找到机器数据", + "message.gtlcore.machine_data_pasted": "已粘贴机器数据", + "message.gtlcore.need_carbon_nano_swarm": "需要放入碳纳米蜂群", + "message.gtlcore.need_carbon_nano_swarm_red": "需要放入碳纳米蜂群", + "message.gtlcore.need_dragon_nano_swarm": "需要放入龙纳米蜂群", + "message.gtlcore.need_dragon_nano_swarm_red": "需要放入龙纳米蜂群", + "message.gtlcore.need_neutronium_nano_swarm": "需要放入中子素纳米蜂群", + "message.gtlcore.need_neutronium_nano_swarm_red": "需要放入中子素纳米蜂群", + "message.gtlcore.only_use_on_pattern_provider": "只能对着样板供应器使用", + "message.gtlcore.pattern_buffer_copied": "已复制样板总成内部样板", + "message.gtlcore.pattern_buffer_copy_failed": "复制样板总成失败", + "message.gtlcore.pattern_buffer_not_copied": "未复制样板总成", + "message.gtlcore.pattern_buffer_patterns_copied": "已复制样板总成内部样板", + "message.gtlcore.pattern_provider_only": "只能对着样板供应器使用", + "message.gtlcore.pattern_updated": "已更新内部的样板,应用了%s次", + "message.gtlcore.pattern_updated_count": "已更新内部的样板,应用了%s次", + "message.gtlcore.right_click_air_gui": "右键空气打开GUI", + "message.gtlcore.structure_formed": "已成型", + "structure_detect.error": "在%s处需要:\n", + "structure_detect.error.2": "在%s处 ", + "structure_detect.tooltip.0": "右键多方快结构主方块即可", + "structure_writer.export_order": "导出顺序: C:%s S:%s A:%s", + "structure_writer.structural_scale": "结构规模: X:%s Y:%s Z:%s", + "tooltip.gtlcore.application_count_limit": "一次使用的应用次数(<=16)", + "tooltip.gtlcore.apply_pattern_buffer_right_click": "右键应用复制内容到目标样板总成", + "tooltip.gtlcore.auto_pull_sort_mode": "自动拉取排序方式", + "tooltip.gtlcore.bigger_stronger": "更大, 更强(?)", + "tooltip.gtlcore.can_use_transformer_hatch": "可使用变电动力仓", + "tooltip.gtlcore.change_item_fluid_filter_priority": "更改物品/流体过滤设置与优先级", + "tooltip.gtlcore.change_priority": "更改优先级", + "tooltip.gtlcore.complete_gt_leisure": "§7填满就能通关GregTech Leisure", + "tooltip.gtlcore.complete_gtceu_modern": "§7填满就能通关GregTechCEu Modern", + "tooltip.gtlcore.copy_pattern_buffer_sneak_right_click": "潜行右键可复制目标样板总成内的样板和命名名称", + "tooltip.gtlcore.current_light_level": "当前光照:", + "tooltip.gtlcore.current_recipe_type": "当前配方类型:", + "tooltip.gtlcore.fill_battery_ascension": "填满电池 机械飞升", + "tooltip.gtlcore.fill_battery_mechanical_ascension": "填满电池 机械飞升", + "tooltip.gtlcore.fill_for_fun": "填满也就图一乐", + "tooltip.gtlcore.fill_in_lifetime": "§7有生之年将它填满", + "tooltip.gtlcore.helium_storage": "氦储量:%smb", + "tooltip.gtlcore.hydrogen_storage": "氢储量:%smb", + "tooltip.gtlcore.input_blacklist_keywords": "输入黑名单ID关键词,以空格分割 匹配1个即否决", + "tooltip.gtlcore.input_whitelist_keywords": "输入白名单ID关键词,以空格分割 匹配1个即通过", + "tooltip.gtlcore.installed_module_count": "已安装的模块数:%s", + "tooltip.gtlcore.mirror_mode": "启用后可以镜像摆放机器方块", + "tooltip.gtlcore.mirror_placement": "启用后可以镜像摆放机器方块", + "tooltip.gtlcore.module_installed": "该模块已成功安装", + "tooltip.gtlcore.module_not_installed": "该模块未成功安装", + "tooltip.gtlcore.no_hatch_mode": "启用后不会在非唯一时放置各种仓室", + "tooltip.gtlcore.no_hatches_mode": "启用后不会在非唯一时放置各种仓室", + "tooltip.gtlcore.output_blacklist_keywords": "输出黑名单ID关键词,以空格分割 匹配1个即否决", + "tooltip.gtlcore.output_whitelist_keywords": "输出白名单ID关键词,以空格分割 匹配1个即通过", + "tooltip.gtlcore.pattern_applied_number": "一次使用的应用次数(<=16)", + "tooltip.gtlcore.pattern_divider_scale": "设置模板除数", + "tooltip.gtlcore.pattern_materials_divide_2": "将样板材料和配方数量 ÷ 2", + "tooltip.gtlcore.pattern_materials_divide_3": "将样板材料和配方数量 ÷ 3", + "tooltip.gtlcore.pattern_materials_divide_5": "将样板材料和配方数量 ÷ 5", + "tooltip.gtlcore.pattern_materials_multiply_2": "将样板材料和配方数量 x 2", + "tooltip.gtlcore.pattern_materials_multiply_3": "将样板材料和配方数量 x 3", + "tooltip.gtlcore.pattern_materials_multiply_5": "将样板材料和配方数量 x 5", + "tooltip.gtlcore.pattern_max_fluid_stack": "设置乘法后最大流体/桶", + "tooltip.gtlcore.pattern_max_item_stack": "设置乘法后最大物品/个", + "tooltip.gtlcore.pattern_multiplier_scale": "设置模板乘数", + "tooltip.gtlcore.primitive_void_ore_random_output": "运行时根据维度每tick随机产出一组任意粗矿", + "tooltip.gtlcore.random_raw_ore_generation": "运行时根据维度每tick随机产出一组任意粗矿", + "tooltip.gtlcore.replace_mode": "启用后替换等级方块为设置的等级方块", + "tooltip.gtlcore.replace_tier_blocks": "启用后替换等级方块为设置的等级方块", + "tooltip.gtlcore.right_click_apply_copied_content": "右键应用复制内容到目标样板总成", + "tooltip.gtlcore.set_max_fluids_after_multiply": "设置乘法后最大流体/桶", + "tooltip.gtlcore.set_max_items_after_multiply": "设置乘法后最大物品/个", + "tooltip.gtlcore.set_pattern_multiplier": "设置模板倍数", + "tooltip.gtlcore.set_programmed_circuit": "设置编程电路", + "tooltip.gtlcore.set_recipe_type": "设置配方类型", + "tooltip.gtlcore.set_template_divisor": "设置模板除数", + "tooltip.gtlcore.set_template_multiplier": "设置模板乘数", + "tooltip.gtlcore.sneak_right_click_copy_pattern": "潜行右键可复制目标样板总成内的样板和命名名称", + "tooltip.gtlcore.space_elevator_connected": "已连接正在运行的太空电梯", + "tooltip.gtlcore.space_elevator_not_connected": "未连接太空电梯", + "tooltip.gtlcore.startup_energy_cost": "启动耗能:%sEU", + "tooltip.gtlcore.successfully_installed": "成功安装", + "tooltip.gtlcore.supported_dimensions": "支持主世界,下界,末地", + "tooltip.gtlcore.supports_dimensions": "支持主世界,下界,末地", + "tooltip.gtlcore.this_module": "该模块", + "tooltip.gtlcore.turbine_rotor_only": "只能放入涡轮转子", + "tooltip.gtlcore.structure.source": "§6结构来源: %s" } \ No newline at end of file diff --git a/src/main/resources/assets/gtlcore/models/item/me_pattern_buffer_copy.json b/src/main/resources/assets/gtlcore/models/item/me_pattern_buffer_copy.json new file mode 100644 index 000000000..9f73f36e9 --- /dev/null +++ b/src/main/resources/assets/gtlcore/models/item/me_pattern_buffer_copy.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "ae2:item/memory_card_pins", + "layer1": "ae2:item/memory_card_base" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/gtlcore/models/item/ultimate_terminal.json b/src/main/resources/assets/gtlcore/models/item/ultimate_terminal.json new file mode 100644 index 000000000..9bb0224d2 --- /dev/null +++ b/src/main/resources/assets/gtlcore/models/item/ultimate_terminal.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "gtlcore:item/ultimate_terminal" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/gtlcore/textures/item/fast_infinity_cell.png b/src/main/resources/assets/gtlcore/textures/item/fast_infinity_cell.png new file mode 100644 index 000000000..b06b40a30 Binary files /dev/null and b/src/main/resources/assets/gtlcore/textures/item/fast_infinity_cell.png differ diff --git a/src/main/resources/assets/gtlcore/textures/item/ultimate_terminal.png b/src/main/resources/assets/gtlcore/textures/item/ultimate_terminal.png new file mode 100644 index 000000000..5b2669698 Binary files /dev/null and b/src/main/resources/assets/gtlcore/textures/item/ultimate_terminal.png differ diff --git a/src/main/resources/assets/gtlcore/textures/item/ultimate_terminal.png.mcmeta b/src/main/resources/assets/gtlcore/textures/item/ultimate_terminal.png.mcmeta new file mode 100644 index 000000000..7840a2c55 --- /dev/null +++ b/src/main/resources/assets/gtlcore/textures/item/ultimate_terminal.png.mcmeta @@ -0,0 +1,5 @@ +{ + "animation":{ + "frametime":6 + } +} \ No newline at end of file diff --git a/src/main/resources/assets/gtmthings/lang/en_us.json b/src/main/resources/assets/gtmthings/lang/en_us.json new file mode 100644 index 000000000..775c845a3 --- /dev/null +++ b/src/main/resources/assets/gtmthings/lang/en_us.json @@ -0,0 +1,64 @@ +{ + "block.gtmthings.iv_1048576a_wireless_laser_source_hatch": "1048576A §9IV§r Wireless Laser Source Hatch", + "block.gtmthings.iv_1048576a_wireless_laser_target_hatch": "1048576A §9IV§r Wireless Laser Target Hatch", + "block.gtmthings.iv_262144a_wireless_laser_source_hatch": "262144A §9IV§r Wireless Laser Source Hatch", + "block.gtmthings.iv_262144a_wireless_laser_target_hatch": "262144A §9IV§r Wireless Laser Target Hatch", + "block.gtmthings.iv_4194304a_wireless_laser_source_hatch": "4194304A §9IV§r Wireless Laser Source Hatch", + "block.gtmthings.iv_4194304a_wireless_laser_target_hatch": "4194304A §9IV§r Wireless Laser Target Hatch", + "block.gtmthings.luv_1048576a_wireless_laser_source_hatch": "1048576A §dLuV§r Wireless Laser Source Hatch", + "block.gtmthings.luv_1048576a_wireless_laser_target_hatch": "1048576A §dLuV§r Wireless Laser Target Hatch", + "block.gtmthings.luv_262144a_wireless_laser_source_hatch": "262144A §dLuV§r Wireless Laser Source Hatch", + "block.gtmthings.luv_262144a_wireless_laser_target_hatch": "262144A §dLuV§r Wireless Laser Target Hatch", + "block.gtmthings.luv_4194304a_wireless_laser_source_hatch": "4194304A §dLuV§r Wireless Laser Source Hatch", + "block.gtmthings.luv_4194304a_wireless_laser_target_hatch": "4194304A §dLuV§r Wireless Laser Target Hatch", + "block.gtmthings.max_1048576a_wireless_laser_source_hatch": "1048576A §c§lMAX§r Wireless Laser Source Hatch", + "block.gtmthings.max_1048576a_wireless_laser_target_hatch": "1048576A §c§lMAX§r Wireless Laser Target Hatch", + "block.gtmthings.max_262144a_wireless_laser_source_hatch": "262144A §c§lMAX§r Wireless Laser Source Hatch", + "block.gtmthings.max_262144a_wireless_laser_target_hatch": "262144A §c§lMAX§r Wireless Laser Target Hatch", + "block.gtmthings.max_4194304a_wireless_laser_source_hatch": "4194304A §c§lMAX§r Wireless Laser Source Hatch", + "block.gtmthings.max_4194304a_wireless_laser_target_hatch": "4194304A §c§lMAX§r Wireless Laser Target Hatch", + "block.gtmthings.opv_1048576a_wireless_laser_source_hatch": "1048576A §9§lOpV§r Wireless Laser Source Hatch", + "block.gtmthings.opv_1048576a_wireless_laser_target_hatch": "1048576A §9§lOpV§r Wireless Laser Target Hatch", + "block.gtmthings.opv_262144a_wireless_laser_source_hatch": "262144A §9§lOpV§r Wireless Laser Source Hatch", + "block.gtmthings.opv_262144a_wireless_laser_target_hatch": "262144A §9§lOpV§r Wireless Laser Target Hatch", + "block.gtmthings.opv_4194304a_wireless_laser_source_hatch": "4194304A §9§lOpV§r Wireless Laser Source Hatch", + "block.gtmthings.opv_4194304a_wireless_laser_target_hatch": "4194304A §9§lOpV§r Wireless Laser Target Hatch", + "block.gtmthings.uev_1048576a_wireless_laser_source_hatch": "1048576A §aUEV§r Wireless Laser Source Hatch", + "block.gtmthings.uev_1048576a_wireless_laser_target_hatch": "1048576A §aUEV§r Wireless Laser Target Hatch", + "block.gtmthings.uev_262144a_wireless_laser_source_hatch": "262144A §aUEV§r Wireless Laser Source Hatch", + "block.gtmthings.uev_262144a_wireless_laser_target_hatch": "262144A §aUEV§r Wireless Laser Target Hatch", + "block.gtmthings.uev_4194304a_wireless_laser_source_hatch": "4194304A §aUEV§r Wireless Laser Source Hatch", + "block.gtmthings.uev_4194304a_wireless_laser_target_hatch": "4194304A §aUEV§r Wireless Laser Target Hatch", + "block.gtmthings.uhv_1048576a_wireless_laser_source_hatch": "1048576A §4UHV§r Wireless Laser Source Hatch", + "block.gtmthings.uhv_1048576a_wireless_laser_target_hatch": "1048576A §4UHV§r Wireless Laser Target Hatch", + "block.gtmthings.uhv_262144a_wireless_laser_source_hatch": "262144A §4UHV§r Wireless Laser Source Hatch", + "block.gtmthings.uhv_262144a_wireless_laser_target_hatch": "262144A §4UHV§r Wireless Laser Target Hatch", + "block.gtmthings.uhv_4194304a_wireless_laser_source_hatch": "4194304A §4UHV§r Wireless Laser Source Hatch", + "block.gtmthings.uhv_4194304a_wireless_laser_target_hatch": "4194304A §4UHV§r Wireless Laser Target Hatch", + "block.gtmthings.uiv_1048576a_wireless_laser_source_hatch": "1048576A §2UIV§r Wireless Laser Source Hatch", + "block.gtmthings.uiv_1048576a_wireless_laser_target_hatch": "1048576A §2UIV§r Wireless Laser Target Hatch", + "block.gtmthings.uiv_262144a_wireless_laser_source_hatch": "262144A §2UIV§r Wireless Laser Source Hatch", + "block.gtmthings.uiv_262144a_wireless_laser_target_hatch": "262144A §2UIV§r Wireless Laser Target Hatch", + "block.gtmthings.uiv_4194304a_wireless_laser_source_hatch": "4194304A §2UIV§r Wireless Laser Source Hatch", + "block.gtmthings.uiv_4194304a_wireless_laser_target_hatch": "4194304A §2UIV§r Wireless Laser Target Hatch", + "block.gtmthings.uv_1048576a_wireless_laser_source_hatch": "1048576A §3UV§r Wireless Laser Source Hatch", + "block.gtmthings.uv_1048576a_wireless_laser_target_hatch": "1048576A §3UV§r Wireless Laser Target Hatch", + "block.gtmthings.uv_262144a_wireless_laser_source_hatch": "262144A §3UV§r Wireless Laser Source Hatch", + "block.gtmthings.uv_262144a_wireless_laser_target_hatch": "262144A §3UV§r Wireless Laser Target Hatch", + "block.gtmthings.uv_4194304a_wireless_laser_source_hatch": "4194304A §3UV§r Wireless Laser Source Hatch", + "block.gtmthings.uv_4194304a_wireless_laser_target_hatch": "4194304A §3UV§r Wireless Laser Target Hatch", + "block.gtmthings.uxv_1048576a_wireless_laser_source_hatch": "1048576A §eUXV§r Wireless Laser Source Hatch", + "block.gtmthings.uxv_1048576a_wireless_laser_target_hatch": "1048576A §eUXV§r Wireless Laser Target Hatch", + "block.gtmthings.uxv_262144a_wireless_laser_source_hatch": "262144A §eUXV§r Wireless Laser Source Hatch", + "block.gtmthings.uxv_262144a_wireless_laser_target_hatch": "262144A §eUXV§r Wireless Laser Target Hatch", + "block.gtmthings.uxv_4194304a_wireless_laser_source_hatch": "4194304A §eUXV§r Wireless Laser Source Hatch", + "block.gtmthings.uxv_4194304a_wireless_laser_target_hatch": "4194304A §eUXV§r Wireless Laser Target Hatch", + "block.gtmthings.zpm_1048576a_wireless_laser_source_hatch": "1048576A §cZPM§r Wireless Laser Source Hatch", + "block.gtmthings.zpm_1048576a_wireless_laser_target_hatch": "1048576A §cZPM§r Wireless Laser Target Hatch", + "block.gtmthings.zpm_262144a_wireless_laser_source_hatch": "262144A §cZPM§r Wireless Laser Source Hatch", + "block.gtmthings.zpm_262144a_wireless_laser_target_hatch": "262144A §cZPM§r Wireless Laser Target Hatch", + "block.gtmthings.zpm_4194304a_wireless_laser_source_hatch": "4194304A §cZPM§r Wireless Laser Source Hatch", + "block.gtmthings.zpm_4194304a_wireless_laser_target_hatch": "4194304A §cZPM§r Wireless Laser Target Hatch", + "item.gtmthings.max_4a_wireless_energy_receive_cover": "4A §c§lMAX§r Wireless Energy Receiver", + "item.gtmthings.max_wireless_energy_receive_cover": "§c§lMAX§r Wireless Energy Receiver" +} \ No newline at end of file diff --git a/src/main/resources/data/gtlcore/recipes/assembler_matrix_frame.json b/src/main/resources/data/gtlcore/recipes/assembler_matrix_frame.json new file mode 100644 index 000000000..0840675d2 --- /dev/null +++ b/src/main/resources/data/gtlcore/recipes/assembler_matrix_frame.json @@ -0,0 +1,24 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "B": { + "item": "gtceu:exquisite_certus_quartz_gem" + }, + "G": { + "item": "gtceu:cleanroom_glass" + }, + "I": { + "item": "gtceu:iridium_plate" + } + }, + "pattern": [ + "BIB", + "IGI", + "BIB" + ], + "result": { + "item": "expatternprovider:assembler_matrix_frame" + }, + "show_notification": true +} \ No newline at end of file diff --git a/src/main/resources/data/gtlcore/recipes/assembler_matrix_glass.json b/src/main/resources/data/gtlcore/recipes/assembler_matrix_glass.json new file mode 100644 index 000000000..8b581c2e4 --- /dev/null +++ b/src/main/resources/data/gtlcore/recipes/assembler_matrix_glass.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "B": { + "item": "gtceu:mercury_barium_calcium_cuprate_double_wire" + }, + "G": { + "tag": "gtceu:circuits/ev" + }, + "I": { + "item": "gtceu:cleanroom_glass" + } + }, + "pattern": [ + "BIB", + "IGI", + "BIB" + ], + "result": { + "count": 2, + "item": "expatternprovider:assembler_matrix_glass" + }, + "show_notification": true +} \ No newline at end of file diff --git a/src/main/resources/data/gtlcore/recipes/assembler_matrix_wall.json b/src/main/resources/data/gtlcore/recipes/assembler_matrix_wall.json new file mode 100644 index 000000000..7fccae976 --- /dev/null +++ b/src/main/resources/data/gtlcore/recipes/assembler_matrix_wall.json @@ -0,0 +1,24 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "B": { + "item": "gtceu:mercury_barium_calcium_cuprate_double_wire" + }, + "G": { + "tag": "gtceu:circuits/ev" + }, + "I": { + "item": "ae2:charged_certus_quartz_crystal" + } + }, + "pattern": [ + "BIB", + "IGI", + "BIB" + ], + "result": { + "item": "expatternprovider:assembler_matrix_wall" + }, + "show_notification": true +} \ No newline at end of file diff --git a/src/main/resources/gtlcore.mixin.json b/src/main/resources/gtlcore.mixin.json index 95b1ee49d..756912ff7 100644 --- a/src/main/resources/gtlcore.mixin.json +++ b/src/main/resources/gtlcore.mixin.json @@ -6,13 +6,18 @@ "client": [ "ae2.client.BuiltInModelHooksAccessor", "ae2.client.ModelBakeryMixin", + "ae2.gui.SetProcessingPatternAmountScreenMixin", + "gtm.MultiblockInfoWrapperMixin", + "gtm.MultiblockInWorldPreviewRendererMixin", "gtm.TagPrefixItemRendererMixin", "gtm.api.recipe.ContentMixin", "gtm.gui.AEFluidConfigSlotWidgetMixin", "gtm.gui.PatternPreviewWidgetMixin", "gtm.gui.ProspectingMapWidgetMixin", "gtm.gui.ProspectingTextureMixin", - "ldlib.CustomBakedModelFixed" + "ldlib.CustomBakedModelFixed", + "mc.GuiGraphicsMixin", + "meRequester.NumberFieldMixin" ], "mixins": [ "adastra.ModUtilsMixin", @@ -20,53 +25,116 @@ "adastra.VehicleItemMixin", "ae2.BasicCellInventoryMixin", "ae2.CPUSelectionListMixin", + "ae2.CreativeCellInventoryMixin", "ae2.CursedInternalSlotMixin", + "ae2.IOPortBlockEntityMixin", "ae2.MixinCowMap", "ae2.MixinGenericSlotCapacities", "ae2.MixinGenericStackInv", "ae2.PortableCellItemMixin", "ae2.TooltipsMixin", + "ae2.crafting.CraftingCalculationMixin", + "ae2.crafting.CraftingCpuHelperMixin", + "ae2.crafting.CraftingTreeNodeMixin", + "ae2.crafting.CraftingTreeProcessMixin", "ae2.gui.PatternEncodingTermMenuMixin", "ae2.gui.ProcessingEncodingPanelMixin", "ae2.gui.StyleManagerMixin", + "ae2.integration.EncodingHelperMixin", + "ae2.integration.ItemIngredientConverterMixin", + "ae2.logic.CraftingCpuLogicMixin", + "ae2.logic.ElapsedTimeTrackerAccessor", + "ae2.logic.ExecutingCraftingJobAccessor", + "ae2.logic.ExecutingCraftingJobTaskProgressAccessor", "ae2.logic.PatternProviderLogicMixin", + "ae2.pattern.AEProcessingPatternMixin", + "ae2.service.CraftingServiceMixin", + "ae2.service.StorageServiceMixin", + "ae2.stacks.AEItemKeyMixin", + "ae2.stacks.AEKey2LongMapMixin", + "ae2.stacks.AEKeyMixin", + "ae2.stacks.GenericStackMixin", + "ae2.ticking.TickHandlerMixin", "extendedae.ContainerPatternModifierMixin", + "extendedae.InfinityCellMixin", "extendedae.PartExPatternProviderMixin", + "extendedae.TileExIOPortMixin", "extendedae.TileExPatternProviderMixin", "ftbu.YouCantBreakAE2Mixin", - "gtm.CWURecipeCapabilityMixin", + "gtm.CommonProxyMixin", "gtm.FluidPipeTypeMixin", - "gtm.MEStockingBusPartMachineMixin", "gtm.MultiblockInfoCategoryMixin", "gtm.OverclockingLogicMixin", "gtm.RecipeLogicProviderMixin", "gtm.RecipeOutputProviderMixin", "gtm.RockBreakerConditionMixin", "gtm.TagPrefixItemMixin", + "gtm.ae.machine.MEBusPartMachineMixin", + "gtm.ae.machine.MEHatchPartMachineAccessor", + "gtm.ae.machine.MEHatchPartMachineMixin", + "gtm.ae.machine.MEInputBusPartMachineMixin", + "gtm.ae.machine.MEInputHatchPartMachineMixin", + "gtm.ae.machine.MEOutputBusPartMachineMixin", + "gtm.ae.machine.MEOutputHatchPartMachineMixin", + "gtm.ae.machine.MEStockingBusPartMachineMixin", + "gtm.ae.machine.MEStockingHatchPartMachineMixin", + "gtm.ae.slot.ExportOnlyAEFluidListMixin", + "gtm.ae.slot.ExportOnlyAEItemListMixin", + "gtm.ae.slot.ExportOnlyAEItemSlotMixin", + "gtm.ae.slot.ExportOnlyAESlotAccessor", + "gtm.ae.slot.ExportOnlyAEStockingFluidListMixin", + "gtm.ae.slot.ExportOnlyAEStockingFluidSlotMixin", + "gtm.ae.slot.ExportOnlyAEStockingItemListMixin", + "gtm.ae.slot.ExportOnlyAEStockingItemSlotMixin", + "gtm.api.capability.CWURecipeCapabilityMixin", + "gtm.api.capability.FluidRecipeCapabilityMixin", + "gtm.api.capability.ItemRecipeCapabilityMixin", + "gtm.api.machine.FluidDrillLogicMixin", "gtm.api.machine.MetaMachineMixin", + "gtm.api.machine.MultiblockControllerMachineMixin", + "gtm.api.machine.MultiblockStateMixin", "gtm.api.machine.RecipeLogicMixin", - "gtm.api.machine.ResearchStationRecipeLogicMixin", "gtm.api.machine.WorkableMultiblockMachineMixin", - "gtm.api.recipe.FluidRecipeCapabilityMixin", + "gtm.api.machine.trait.NotifiableEnergyContainerMixin", + "gtm.api.machine.trait.NotifiableItemStackHandlerMixin", + "gtm.api.misc.EnergyContainerListMixin", + "gtm.api.misc.LaserContainerListMixin", + "gtm.api.pattern.BlockPatternMixin", + "gtm.api.recipe.ChanceLogicOrMixin", + "gtm.api.recipe.ContentModifierMixin", "gtm.api.recipe.GTRecipeLookupMixin", - "gtm.api.recipe.ItemRecipeCapabilityMixin", + "gtm.api.recipe.GTRecipeMixin", + "gtm.api.recipe.OCResultMixin", + "gtm.api.recipe.ParallelLogicMixin", + "gtm.api.recipe.RecipeIteratorAccessor", + "gtm.api.recipe.RecipeIteratorMixin", + "gtm.api.recipe.RecipeLogicAccessor", + "gtm.api.recipe.RecipeModifierListMixin", + "gtm.api.recipe.condition.CleanroomConditionMixin", "gtm.cover.ConveyorCoverMixin", "gtm.cover.InfiniteWaterCoverMixin", "gtm.cover.PumpCoverMixin", "gtm.fix.GTUtilMixin", "gtm.fix.WorkableElectricMultiblockMachineMixin", "gtm.ftb.TeleportFromMapPacketMixin", + "gtm.gui.GTRecipeWidgetMixin", + "gtm.gui.MachineModeFancyConfiguratorMixin", "gtm.gui.ProspectorScannerBehaviorMixin", + "gtm.machine.ActiveTransformerMachineMixin", "gtm.machine.BufferMachineMixin", + "gtm.machine.DataAccessHatchMachineMixin", "gtm.machine.DualHatchPartMachineMixin", "gtm.machine.FluidHatchPartMachineMixin", "gtm.machine.FusionReactorMachineMixin", "gtm.machine.HPCAComputationPartMachineMixin", - "gtm.machine.HugeBusPartMachineMixin", "gtm.machine.ItemBusPartMachineMixin", "gtm.machine.LargeBoilerMachineMixin", + "gtm.machine.LargeTurbineMachineMixin", + "gtm.machine.MufflerPartMachineMixin", "gtm.machine.ParallelHatchPartMachineMixin", + "gtm.machine.RotorHolderPartMachineMixin", "gtm.machine.SimpleGeneratorMachineMixin", + "gtm.machine.SimpleSteamMachineMixin", "gtm.machine.SimpleTieredMachineMixin", "gtm.machine.SteamItemBusPartMachineMixin", "gtm.machine.SteamParallelMultiblockMachineMixin", @@ -74,18 +142,35 @@ "gtm.recipe.CraftingComponentMixin", "gtm.recipe.FuelRecipesMixin", "gtm.recipe.GCyMRecipesMixin", + "gtm.recipe.GTRecipeJSMixin", + "gtm.recipe.MachineRecipeLoaderMixin", "gtm.recipe.MaterialRecipeHandlerMixin", + "gtm.recipe.MetaTileEntityMachineRecipeLoaderMixin", "gtm.recipe.OreRecipeHandlerMixin", "gtm.recipe.WireRecipeHandlerMixin", "gtm.recipe.change.NaquadahRecipesMixin", "gtm.recipe.change.PlatGroupMetalsRecipesMixin", + "gtm.recipe.Ingredient.IntProviderIngredientAccessor", "gtm.registry.GTDimensionMarkersMixin", "gtm.registry.GTMachinesMixin", "gtm.registry.GTRecipeBuilderMixin", + "gtm.registry.GTRecipeJSMixin", + "gtm.registry.GTRecipeModifiersMixin", "gtm.registry.GTRecipeTypeMixin", + "gtmt.CreativeEnergyHatchPartMachineMixin", + "gtmt.CreativeLaserHatchPartMachineMixin", + "gtmt.GTMTRecipeMixin", "gtmt.HugeBusPartMachineMixin", + "gtmt.MEOutputPartMachineMixin", + "gtmt.MEOutputPartMachineMixin$InaccessibleInfiniteHandlerMixin", + "gtmt.MEOutputPartMachineMixin$InaccessibleInfiniteTankMixin", "gtmt.WirelessEnergyReceiveCoverMixin", + "gtmt.trait.CatalystFluidStackHandlerMixin", + "gtmt.trait.CatalystItemStackHandlerMixin", + "ldlib.AsyncThreadDataMixin", + "ldlib.ClickDataMixin", "ldlib.ItemStackTransferMixin", + "mc.LevelChunkMixin", "travelanchors.EventListenerMixin" ], "injectors": {