First day of bioline recipes (#2201)

* material and tag changes

* adds a cleanroom recipe json editor utility script

* most of the initial biochem recipes

* circuit number change

* hoisted up class loaders

* ISP output is so fickle

---------

Co-authored-by: Pyritie <pyritie@gmail.com>
This commit is contained in:
Redeix 2025-11-11 12:35:27 -06:00 committed by GitHub
parent 6b7283539b
commit b063392519
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 445 additions and 23 deletions

View file

@ -1,6 +1,16 @@
// priority: 0
"use strict";
const JsonObject = Java.loadClass('com.google.gson.JsonObject');
const JsonArray = Java.loadClass('com.google.gson.JsonArray');
const JsonParser = Java.loadClass('com.google.gson.JsonParser');
const JsonElement = Java.loadClass('com.google.gson.JsonElement');
// Helper to call `JsonArray.add(JsonElement)` explicitly because "Rhino Moment".
const addJsonElement = (jsonArray, jsonElement) => {
jsonArray.getClass().getMethod("add", JsonElement).invoke(jsonArray, jsonElement);
};
//#region Mixer Recipes
/**
* Function for generating gtceu mixer recipes.
@ -273,16 +283,6 @@ function forEachMaterial(iterator) {
*/
function addCircuitToRecipe(event, recipeId, circuitNumber) {
const JsonObject = Java.loadClass('com.google.gson.JsonObject');
const JsonArray = Java.loadClass('com.google.gson.JsonArray');
const JsonParser = Java.loadClass('com.google.gson.JsonParser');
const JsonElementClass = Java.loadClass('com.google.gson.JsonElement');
// Helper to call JsonArray.add(JsonElement) explicitly because "Rhino Moment".
const addJsonElement = (jsonArray, jsonElement) => {
jsonArray.getClass().getMethod("add", JsonElementClass).invoke(jsonArray, jsonElement);
};
event.findRecipes({ id: recipeId }).forEach(recipe => {
const inputsEl = recipe.json.get("inputs");
let inputsObj;
@ -598,4 +598,55 @@ function sterilizeItem(event, input, output, multiplier, cleanroom) {
autoclave_recipe.cleanroom(cleanroom);
};
};
//#endregion
//#region Cleanroom Tool
/**
* Ensures recipes have a cleanroom recipe condition set to the specified type.
*
* * For each recipe:
* * * If `recipeConditions` is an array, finds an object with `type` === `cleanroom`.
* * * If found, updates its `cleanroom` property to the given `cleanroomType`.
* * * If not found, appends a new condition object `{ type: "cleanroom", cleanroom: cleanroomType }` to the array.
* * * If `recipeConditions` is absent or not an array, creates a new JSON array containing the cleanroom condition.
*
* @throws This function will not work with other recipe conditions present besides `CleanroomType`.
*
* @param {event} event
* @param {string} recipeId - recipe ID.
* @param {'cleanroom'|'sterile_cleanroom'} cleanroomType - Cleanroom type to be assigned.
*/
function addCleanroom(event, recipeId, cleanroomType) {
event.findRecipes({ id: recipeId }).forEach(recipe => {
// Ensure recipe has a cleanroom condition matching the cleanroomType string.
// Replace existing cleanroom condition or add new one if absent.
const desiredCleanroom = cleanroomType;
const conditions = recipe.json.get("recipeConditions");
let conditionArray;
if (conditions && conditions.isJsonArray && conditions.isJsonArray()) {
conditionArray = conditions.getAsJsonArray();
} else {
conditionArray = new JsonArray();
recipe.json.add("recipeConditions", conditionArray);
}
let hasCleanroom = false;
for (let i = 0; i < conditionArray.size(); i++) {
let element = conditionArray.get(i).getAsJsonObject();
if (element.has("type") && element.get("type").getAsString() === "cleanroom") {
element.addProperty("cleanroom", desiredCleanroom);
hasCleanroom = true;
break;
}
}
if (!hasCleanroom) {
let cond = new JsonObject();
cond.addProperty("type", "cleanroom");
cond.addProperty("cleanroom", desiredCleanroom);
addJsonElement(conditionArray, cond);
}
});
};
//#endregion

View file

@ -7,6 +7,7 @@
function registerTFGBiochemRecipes(event) {
const $ISPRecipeLogic = Java.loadClass("su.terrafirmagreg.core.common.data.tfgt.machine.trait.ISPOutputRecipeLogic")
const $SizedIngredient = Java.loadClass("com.gregtechceu.gtceu.api.recipe.ingredient.SizedIngredient")
const Sized = (ing, amount) => $SizedIngredient.create(ing, amount)
/**
* @typedef {Object} BioreactorRecipeData
@ -117,7 +118,6 @@ function registerTFGBiochemRecipes(event) {
/////////////////////////////////////////
//#region Recipes
// EXAMPLE
// growthChamberRecipeText('test/test', 10*60*20, GTValues.VA[GTValues.EV], 'tfg.food_recipe.brining', {
// itemInputs: ['tfc:food/red_apple','tfc:food/red_apple', '1x tfc:silica_glass_bottle'],
@ -132,7 +132,6 @@ function registerTFGBiochemRecipes(event) {
// itemOutputProvider: TFC.isp.of('3x tfc:food/green_apple').copyFood().addTrait('firmalife:smoked')
// })
//#endregion
//#region Multiblock Parts
event.recipes.gtceu.assembler('tfg:uv_led')
@ -188,7 +187,7 @@ function registerTFGBiochemRecipes(event) {
C: '#gtceu:circuits/ev',
D: 'gtceu:aluminium_single_cable',
E: 'gtceu:ev_electric_pump',
F: 'gtceu:ev_electric_motor',
F: 'gtceu:ev_electric_motor'
}).addMaterialInfo().id('tfg:shaped/bioreactor');
event.recipes.gtceu.shaped('tfg:casings/bioculture_rotor_primary', [
@ -354,5 +353,371 @@ function registerTFGBiochemRecipes(event) {
lab_cleaning.forEach(entry => {
sterilizeItem(event, entry.input, entry.output, entry.multiplier, entry.cleanroom);
});
//#endregion
//#region Decellularization
// Hydrogenation of fatty acids to produce lauryl alcohol.
event.recipes.gtceu.chemical_reactor('tfg:lauryl_alcohol')
.inputFluids(
'#firmalife:oils 1000',
Fluid.of('gtceu:hydrogen', 2000)
)
.notConsumable(ChemicalHelper.get(TagPrefix.dust, GTMaterials.Palladium, 1))
.outputFluids(Fluid.of('tfg:lauryl_alcohol', 1000))
.duration(20*20)
.EUt(GTValues.VA[GTValues.EV]);
// Direct synthesis of chlorosulfuric acid.
event.recipes.gtceu.chemical_reactor('tfg:chlorosulfuric_acid')
.inputFluids(
Fluid.of('gtceu:sulfur_trioxide', 1000),
Fluid.of('gtceu:hydrochloric_acid', 1000)
)
.outputFluids(Fluid.of('tfg:chlorosulfuric_acid', 1000))
.duration(10*20)
.EUt(GTValues.VA[GTValues.HV]);
// Synthesis of sodium dodecyl sulfate. Chemistry is not accurate since the organic group in lauryl alcohol is unknown here.
event.recipes.gtceu.chemical_reactor('tfg:sodium_dodecyl_sulfate')
.inputFluids(
Fluid.of('tfg:lauryl_alcohol', 1000),
Fluid.of('tfg:chlorosulfuric_acid', 2000)
)
.outputFluids(
Fluid.of('tfg:sodium_dodecyl_sulfate', 1000),
Fluid.of('gtceu:sulfur_trioxide', 1000)
)
.duration(30*20)
.EUt(GTValues.VA[GTValues.EV]);
// Redox reaction to produce sodium hypochlorite.
event.recipes.gtceu.chemical_reactor('tfg:sodium_hypochlorite')
.itemInputs(
ChemicalHelper.get(TagPrefix.dust, GTMaterials.SodiumHydroxide, 2)
)
.inputFluids(
Fluid.of('gtceu:chlorine', 2000)
)
.outputFluids(
Fluid.of('tfg:sodium_hypochlorite', 1000),
Fluid.of('minecraft:water', 1000)
)
.itemOutputs(Item.of('gtceu:salt'))
.duration(10*20)
.EUt(GTValues.VA[GTValues.HV]);
// Decellularization of organic material to produce cellulose matrix.
/**
* @type {Array<Object>}
* @property {'fluid'|'item'} type - Item or Fluid.
* @property {string} id - Item or fluid ID.
* @property {number} amount - Amount of items or millibuckets of fluid.
*/
const organics = [
{ type: 'item', id: 'gtceu:bio_chaff', amount: 1 },
{ type: 'fluid', id: 'gtceu:biomass', amount: 1000 }
];
/**
* Registers a bioreactor "decellularization" recipes.
*
* @param {event} event
* @param {'fluid'|'item'} organicType - Type of the organic input. Must be either 'fluid' or 'item'.
* @param {string} organicId - Registry ID of the organic input.
* @param {number} organicAmount - Amount of the organic input.
*/
function deccellularizationRecipe(event, organicType, organicId, organicAmount) {
let recipe = event.recipes.gtceu.bioreactor(`tfg:decellularization/${organicId.replace(':', '_')}`)
.inputFluids(
Fluid.of('tfg:sodium_dodecyl_sulfate', 200),
Fluid.of('gtceu:acetone', 1000),
Fluid.of('tfg:sodium_hypochlorite', 1000)
)
.itemInputs(
Ingredient.of('tfg:lab_equipment')
)
.itemOutputs(
Item.of('tfg:cellulose_matrix'),
Item.of('tfg:dirty_lab_equipment')
)
.duration(30*20)
.EUt(GTValues.VA[GTValues.IV])
.cleanroom(CleanroomType.CLEANROOM)
.dimension('ad_astra:venus');
if (organicType === 'fluid') {
recipe.inputFluids(
Fluid.of(organicId, organicAmount)
);
};
if (organicType === 'item') {
recipe.itemInputs(
Ingredient.of(organicId).withCount(organicAmount)
);
};
};
organics.forEach(organic =>
deccellularizationRecipe(event, organic.type, organic.id, organic.amount)
);
//#endregion
//#region Gram Stain
// N,N-Dimethylaniline synthesis.
event.recipes.gtceu.chemical_reactor('tfg:n_n_dimethylaniline')
.inputFluids(
Fluid.of('tfg:aniline', 1000),
Fluid.of('tfg:iodomethane', 2000)
)
.outputFluids(
Fluid.of('tfg:n_n_dimethylaniline', 1000),
Fluid.of('gtceu:hydrogen_iodide', 2000)
)
.duration(20*20)
.EUt(GTValues.VA[GTValues.EV]);
// Crystal violet synthesis.
event.recipes.gtceu.large_chemical_reactor('tfg:crystal_violet')
.inputFluids(
Fluid.of('tfg:n_n_dimethylaniline', 3000),
Fluid.of('gtceu:formaldehyde', 1000),
Fluid.of('gtceu:hydrochloric_acid', 1000),
Fluid.of('gtceu:oxygen', 2000)
)
.outputFluids(
Fluid.of('tfg:crystal_violet', 1000),
Fluid.of('gtceu:water', 3000)
)
.duration(20*20)
.EUt(GTValues.VA[GTValues.IV]);
// Crystal violet to dye.
event.recipes.gtceu.mixer('tfg:crystal_violet_dye')
.inputFluids(
Fluid.of('tfg:crystal_violet', 10),
Fluid.of('minecraft:water', 1000)
)
.outputFluids(Fluid.of('tfc:purple_dye', 1000))
.duration(5*20)
.EUt(GTValues.VA[GTValues.LV]);
//Gram stain solution.
/**
* @type {Array<Object>}
* @property {string} solvent - Solvent fluid ID.
*/
const gramStainSolvents = [
'gtceu:ethanol',
'gtceu:acetone'
];
gramStainSolvents.forEach(solvent => {
event.recipes.gtceu.large_chemical_reactor(`tfg:gram_stain_solvent_${solvent.replace(':', '_')}`)
.inputFluids(
Fluid.of('tfg:crystal_violet', 1000),
Fluid.of('tfc:red_dye', 1000),
Fluid.of(solvent, 1000)
)
.itemInputs(
ChemicalHelper.get(TagPrefix.dust, GTMaterials.Iodine, 1)
)
.outputFluids(Fluid.of('tfg:gram_stain', 4000))
.duration(8*20)
.EUt(GTValues.VA[GTValues.IV])
.cleanroom(CleanroomType.CLEANROOM)
.dimension('ad_astra:venus');
});
//#endregion
//#region Triglcerides
// Butyric acid synthesis.
event.recipes.gtceu.chemical_reactor('tfg:butyric_acid')
.inputFluids(
Fluid.of('gtceu:propene', 2000),
Fluid.of('gtceu:carbon_monoxide', 6000),
Fluid.of('gtceu:hydrogen', 12000)
)
.outputFluids(
Fluid.of('tfg:butyric_acid', 3000)
)
.duration(10*20)
.circuit(4)
.EUt(GTValues.VA[GTValues.EV]);
// Triglycerides from fat.
event.recipes.gtceu.vacuum_freezer('tfg:triglyceride_oil_from_fat')
.inputFluids(
Fluid.of('gtceu:liquid_carbon_dioxide', 1000)
)
.itemInputs(
Ingredient.of('#tfg:solid_fats')
)
.outputFluids(
Fluid.of('tfg:triglyceride_oil', 2000)
)
.itemOutputs(
ChemicalHelper.get(TagPrefix.dust, 'tfg:cholesterol', 1)
)
.duration(20*20)
.dimension('ad_astra:venus')
.EUt(GTValues.VA[GTValues.IV]);
// Triglycerides from cell factory.
bioreactorRecipe('triglyceride_oil_from_smooth_endoplasmic_reticula', 10*20, 1920, {
fluidInputs: [
'gtceu:glycerol 1000',
'tfg:butyric_acid 1000'
],
itemInputs: [
'tfg:smooth_endoplasmic_reticula',
'tfg:lab_equipment'
],
fluidOutputs: [
Fluid.of('tfg:triglyceride_oil', 2000)
],
itemOutputs: [
'tfg:dirty_lab_equipment'
],
cleanroom: CleanroomType.CLEANROOM
});
// Lactose from cell factory.
bioreactorRecipe('lactose_from_rough_endoplasmic_reticula', 10*20, 1920, {
itemInputs: [
'tfg:rough_endoplasmic_reticula',
'tfg:lab_equipment',
'tfg:cholesterol_dust'
],
itemOutputs: [
'4x gtceu:lactose_dust',
'tfg:dirty_lab_equipment'
],
cleanroom: CleanroomType.CLEANROOM,
itemOutputProvider: TFC.isp.of('4x gtceu:lactose_dust')
});
// Alpha keratin from cell factory.
bioreactorRecipe('alpha_keratin_from_rough_endoplasmic_reticula', 10*20, 1920, {
itemInputs: [
'tfg:rough_endoplasmic_reticula',
'tfg:lab_equipment'
],
fluidInputs: [
'tfg:proto_growth_medium 1000'
],
itemOutputs: [
'4x tfg:alpha_keratin',
'tfg:dirty_lab_equipment'
],
cleanroom: CleanroomType.CLEANROOM,
itemOutputProvider: TFC.isp.of('4x tfg:alpha_keratin')
});
//#endregion
//#region Basic Feeder Cells
// Set collagen recipes to require a normal cleanroom instead of sterile.
/**
* @type {Array<Object>}
* @property {'string'} recipeId - Collagen recipe ID's.
*/
const collagenRecipes = [
'gtceu:large_chemical_reactor/collagen_from_bone',
'gtceu:large_chemical_reactor/collagen_from_bone_meal',
'gtceu:chemical_reactor/collagen_from_bone',
'gtceu:chemical_reactor/collagen_from_bone_meal'
];
collagenRecipes.forEach(recipeEntry => {
addCleanroom(event, recipeEntry, 'cleanroom')
});
// Proto growth medium synthesis.
event.recipes.gtceu.bioreactor('tfg:proto_growth_medium')
.inputFluids(
Fluid.of('gtceu:distilled_water', 1000)
)
.itemInputs(
ChemicalHelper.get(TagPrefix.dust, GTMaterials.Calcium, 1),
ChemicalHelper.get(TagPrefix.dust, GTMaterials.SodiumHydroxide, 1),
ChemicalHelper.get(TagPrefix.dust, 'gtceu:lactose', 1)
)
.outputFluids(
Fluid.of('tfg:proto_growth_medium', 1000)
)
.duration(10*20)
.EUt(GTValues.VA[GTValues.EV])
.cleanroom(CleanroomType.CLEANROOM);
// Fibroblast feeder cell synthesis.
event.recipes.gtceu.bioreactor('tfg:fibroblast_feeder_cells')
.inputFluids(
Fluid.of('firmalife:sugar_water', 1000),
Fluid.of('tfg:mutative_yeast', 1000)
)
.itemInputs(
ChemicalHelper.get(TagPrefix.dust, GTMaterials.Collagen, 1),
Ingredient.of('tfg:lab_equipment')
)
.notConsumable(
Ingredient.of('tfg:filled_dna_syringe')
)
.outputFluids(
Fluid.of('tfg:fibroblast_feeder_cells', 1000)
)
.itemOutputs(
Item.of('tfg:dirty_lab_equipment')
)
.duration(1*60*20)
.EUt(GTValues.VA[GTValues.EV])
.circuit(1)
.dimension('ad_astra:venus')
.cleanroom(CleanroomType.CLEANROOM);
// Rough endoplasmic reticula synthesis.
bioreactorRecipe('tfg:rough_endoplasmic_reticula', 1*60*20, GTValues.VA[GTValues.EV], {
itemInputs: [
'gtceu:collagen_dust',
'tfg:lab_equipment'
],
fluidInputs: [
Fluid.of('firmalife:sugar_water', 1000),
Fluid.of('tfg:mutative_yeast', 1000)
],
itemOutputs: [
'tfg:rough_endoplasmic_reticula',
'tfg:dirty_lab_equipment'
],
notConsumable: [
'tfg:filled_dna_syringe'
],
circuit: 2,
cleanroom: CleanroomType.CLEANROOM,
itemOutputProvider: TFC.isp.of('tfg:rough_endoplasmic_reticula').resetFood()
});
// Smooth endoplasmic reticula synthesis.
bioreactorRecipe('tfg:smooth_endoplasmic_reticula', 1*60*20, GTValues.VA[GTValues.EV], {
itemInputs: [
'gtceu:collagen_dust',
'tfg:lab_equipment'
],
fluidInputs: [
Fluid.of('firmalife:sugar_water', 1000),
Fluid.of('tfg:mutative_yeast', 1000)
],
itemOutputs: [
'tfg:smooth_endoplasmic_reticula',
'tfg:dirty_lab_equipment'
],
notConsumable: [
'tfg:filled_dna_syringe'
],
circuit: 3,
cleanroom: CleanroomType.CLEANROOM,
itemOutputProvider: TFC.isp.of('tfg:smooth_endoplasmic_reticula').resetFood()
});
//#endregion
}

View file

@ -318,6 +318,9 @@ const registerTFGItemTags = (event) => {
});
event.add('tfg:foil_packs', 'tfg:foil_pack');
event.add('tfg:foil_packs', 'tfg:clean_foil_pack')
event.add('tfg:solid_fats', 'firmalife:food/butter')
event.add('tfg:solid_fats', 'tfc:blubber')
//#endregion
//#endregion
@ -691,8 +694,6 @@ const registerTFGFluidTags = (event) => {
event.add('tfc:drinkables', 'tfg:semiheavy_ammoniacal_water')
event.add('tfc:any_drinkables', 'tfg:semiheavy_ammoniacal_water')
event.add('tfc:ingredients', 'tfg:semiheavy_ammoniacal_water')
event.add('firmalife:mixable', 'tfg:semiheavy_ammoniacal_water')
event.add('firmalife:usable_in_vat', 'tfg:semiheavy_ammoniacal_water')
event.add('minecraft:water', 'tfg:semiheavy_ammoniacal_water')
event.add('tfc:drinkables', 'tfg:proto_growth_medium')

View file

@ -230,7 +230,7 @@ global.GTCEU_HIDED_ITEMS = /** @type {const} */ ([
"gtceu:iv_damascus_steel_wrench",
"gtceu:lv_damascus_steel_screwdriver",
"gtceu:sus_record",
"gtceu:sus_record"
]);
//#endregion
@ -250,7 +250,7 @@ global.GTCEU_ARMORS = /** @type {const} */ ([
"gtceu:quarktech_leggings",
"gtceu:quarktech_boots",
"gtceu:quarktech_helmet",
"gtceu:advanced_quarktech_chestplate",
"gtceu:advanced_quarktech_chestplate"
]);
//#endregion
@ -313,7 +313,7 @@ global.TFG_CASTING_MOLDS = /** @type {const} */ ([
"tfg:lamp_casting_mold",
"tfg:trapdoor_casting_mold",
"tfg:chain_casting_mold",
"tfg:bell_casting_mold",
"tfg:bell_casting_mold"
]);
global.GTCEU_CASTING_MOLDS = /** @type {const} */ ([
@ -334,7 +334,7 @@ global.GTCEU_CASTING_MOLDS = /** @type {const} */ ([
"gtceu:small_pipe_casting_mold",
"gtceu:normal_pipe_casting_mold",
"gtceu:large_pipe_casting_mold",
"gtceu:huge_pipe_casting_mold",
"gtceu:huge_pipe_casting_mold"
]);
//#endregion
@ -371,7 +371,7 @@ global.ORE_BEARING_STONES = /** @type {const} */ ([
"mars_stone",
"venus_stone",
"mercury_stone",
"glacio_stone",
"glacio_stone"
]);
//#endregion
@ -391,7 +391,10 @@ global.ADD_CIRCUIT = /** @type {const} */ ([
{ recipeId: "gtceu:large_chemical_reactor/sodium_bicarbonate_from_salt", circuitNumber: 2 },
{ recipeId: "gtceu:chemical_reactor/acetic_acid_from_methanol", circuitNumber: 1 },
{ recipeId: "gtceu:large_chemical_reactor/acetic_acid_from_methanol", circuitNumber: 1 }
{ recipeId: "gtceu:large_chemical_reactor/acetic_acid_from_methanol", circuitNumber: 1 },
{ recipeId: "gtceu:chemical_reactor/butraldehyde", circuitNumber: 1 },
{ recipeId: "gtceu:large_chemical_reactor/butraldehyde", circuitNumber: 1 }
]);
//#endregion
@ -406,6 +409,6 @@ global.GTCEU_SUPERCONDUCTORS = /** @type {const} */ ([
{ name: "indium_tin_barium_titanium_cuprate", materialId: "IndiumTinBariumTitaniumCuprate" },
{ name: "uranium_rhodium_dinaquadide", materialId: "UraniumRhodiumDinaquadide" },
{ name: "enriched_naquadah_trinium_europium_duranide", materialId: "EnrichedNaquadahTriniumEuropiumDuranide" },
{ name: "ruthenium_trinium_americium_neutronate", materialId: "RutheniumTriniumAmericiumNeutronate" },
{ name: "ruthenium_trinium_americium_neutronate", materialId: "RutheniumTriniumAmericiumNeutronate" }
]);
//#endregion

View file

@ -26,10 +26,11 @@ const registerTFGBiolineMaterials = (event) => {
event.create('tfg:lauryl_alcohol')
.liquid(new GTFluidBuilder().temperature(293))
.components('12x carbon', '26x hydrogen', '1x oxygen')
.components('12x carbon', '26x hydrogen', '1x oxygen', 'unknown')
.iconSet(GTMaterialIconSet.FINE)
.color(0x9C734E)
.secondaryColor(0xA12727)
.flags(GTMaterialFlags.DISABLE_DECOMPOSITION)
event.create('tfg:chlorosulfuric_acid')
.liquid(new GTFluidBuilder().temperature(293).attribute(GTFluidAttributes.ACID))
@ -44,6 +45,7 @@ const registerTFGBiolineMaterials = (event) => {
.iconSet(GTMaterialIconSet.FINE)
.color(0xCA9851)
.secondaryColor(0xF0D5CE)
.flags(GTMaterialFlags.DISABLE_DECOMPOSITION)
event.create('tfg:sodium_hypochlorite')
.liquid(new GTFluidBuilder().temperature(293))