// priority: 9999 // Java imports const Registry = Java.loadClass("net.minecraft.core.Registry"); // registries, needed for almost everything involving Java classes // const BlockPos = Java.loadClass('net.minecraft.core.BlockPos'); //Block position. For some reason we don't need to import this? const TagKey = Java.loadClass("net.minecraft.tags.TagKey"); const AxisDirection = Java.loadClass("net.minecraft.core.Direction$AxisDirection"); const Random = Java.loadClass("java.util.Random") const InputItem = Java.loadClass("dev.latvian.mods.kubejs.item.InputItem") const OutputItem = Java.loadClass("dev.latvian.mods.kubejs.item.OutputItem") const InputFluid = Java.loadClass("dev.latvian.mods.kubejs.fluid.InputFluid") const OutputFluid = Java.loadClass("dev.latvian.mods.kubejs.fluid.OutputFluid") const FluidStackJS = Java.loadClass("dev.latvian.mods.kubejs.fluid.FluidStackJS") const JsonObject = Java.loadClass("com.google.gson.JsonObject") const Level = Java.loadClass("net.minecraft.world.level.Level") // For some reason, Kubejs requires that you load this class to create explosions that damage blocks const colours = ["white", "orange", "magenta", "light_blue", "lime", "pink", "purple", "light_gray", "gray", "cyan", "brown", "green", "blue", "red", "black", "yellow"] const native_metals = ["iron", "zinc", "lead", "copper", "nickel", "gold"] const wood_types = ["minecraft:oak", "minecraft:spruce", "minecraft:birch", "minecraft:jungle", "minecraft:acacia", "minecraft:dark_oak", "minecraft:mangrove", "minecraft:cherry", "minecraft:crimson", "minecraft:warped"] // None of the modded axes are registered for some reason const unregistered_axes = ["ae2:certus_quartz_axe", "ae2:nether_quartz_axe", "ae2:fluix_axe", "tconstruct:hand_axe", "tconstruct:mattock", "tconstruct:broad_axe", "thermal:flux_saw"] const donutCraft = (event, output, center, ring) => { return event.shaped(output, [ "SSS", "SCS", "SSS" ], { C: center, S: ring }) } /** * Used to make smithing/mechanical crafting recipes which are common in A&B. * If the fourth parameter is excluded, then a stonecutting recipe will be created instead. * * First parmeter is the "base" ingredient, * third parameter is the output, * fourth parameter is the secondary ingredient. * * @param {ItemStackJS|string} machineItem * @param {RecipeEventJS} event * @param {ItemStackJS|string} outputIngredient * @param {ItemStackJS|string} inputIngredient */ // event is the second parameter so that machineItem doesn't look like it's the output item const createMachine = (machineItem, event, outputIngredient, inputIngredient) => { machineItem = Ingredient.of(machineItem) outputIngredient = Item.of(outputIngredient) event.remove({ output: outputIngredient }) if (inputIngredient) { inputIngredient = Ingredient.of(inputIngredient) event.custom({ "type": "create:item_application", "ingredients": [ machineItem.toJson(), inputIngredient.toJson() ], "results": (outputIngredient.isBlock() && outputIngredient.getCount() > 1) ? [ outputIngredient.withCount(1).toJson(), outputIngredient.withCount(outputIngredient.getCount() - 1).toJson() ] : [ outputIngredient.toJson() ] }) } else event.stonecutting(outputIngredient, machineItem) } const andesiteMachine = (event, outputIngredient, inputIngredient) => { return createMachine("kubejs:andesite_machine", event, outputIngredient, inputIngredient) } const copperMachine = (event, outputIngredient, inputIngredient) => { return createMachine("kubejs:copper_machine", event, outputIngredient, inputIngredient) } const goldMachine = (event, outputIngredient, inputIngredient) => { return createMachine("kubejs:gold_machine", event, outputIngredient, inputIngredient) } const brassMachine = (event, outputIngredient, inputIngredient) => { return createMachine("kubejs:brass_machine", event, outputIngredient, inputIngredient) } const zincMachine = (event, outputIngredient, inputIngredient) => { return createMachine("kubejs:zinc_machine", event, outputIngredient, inputIngredient) } const leadMachine = (event, outputIngredient, inputIngredient) => { return createMachine("kubejs:lead_machine", event, outputIngredient, inputIngredient) } const invarMachine = (event, outputIngredient, inputIngredient) => { return createMachine("thermal:machine_frame", event, outputIngredient, inputIngredient) } const enderiumMachine = (event, outputIngredient, inputIngredient) => { return createMachine("kubejs:enderium_machine", event, outputIngredient, inputIngredient) } const fluixMachine = (event, outputIngredient, inputIngredient) => { return createMachine("ae2:controller", event, outputIngredient, inputIngredient) } const toThermalInputJson = (value) => { if (value instanceof InputFluid) { return (FluidStackJS.of(value)).toJson() } value = InputItem.of(value) if (value.count > 1) { let json = new JsonObject() json.add("value", value.ingredient.toJson()) json.addProperty("count", value.count) return json } else { return value.ingredient.toJson(); } } const toThermalOutputJson = (value) => { if (value instanceof OutputFluid) { return (FluidStackJS.of(value)).toJson(); } value = OutputItem.of(value) let json = new JsonObject() json.addProperty("item", value.item.getId()) json.addProperty("count", value.item.getCount()) if (value.getNbt() != null) { json.addProperty("nbt", value.getNbt().toString()) } if (value.hasChance()) { json.addProperty("chance", value.getChance()) } if (value.rolls != null) { json.addProperty("minRolls", value.rolls.getMinValue()) json.addProperty("maxRolls", value.rolls.getMaxValue()) } return json; } const addTreeOutput = (event, trunk, leaf, fluid) => { return event.custom({ type: "thermal:tree_extractor", trunk: { Name: trunk, Properties: { axis: "y" } }, leaves: { Name: leaf, Properties: { persistent: "false" } }, // sapling: "minecraft:jungle_sapling", // min_height: 5, // max_height: 10, // min_leaves: 8, // max_leaves: 12, result: fluid ? toThermalOutputJson(fluid) : { fluid: "thermal:resin", amount: 25 } }) } /** * Creates smeltery casting recipes for nuggets, ingots, blocks, etc * Also creates a chilling recipe for the ingot * This helper function requires all the items and fluids involved to be tagged correctly with the nugget/ingot/block tags * @param {RecipeEventJS} event recipe event * @param {string} metalName the name of the metal to create casting recipes (ex: "forge:ingots/{metalName}") * @param {number} castingTime the time it takes to cast a block in a casting table (nugget and ingot casting times will be calculated based on that) */ const metalCasting = (event, metalName, castingTime) => { let fluidTag = "forge:molten_" + metalName let blockTag = "forge:storage_blocks/" + metalName // block casting if (Ingredient.of(`#forge:storage_blocks/${metalName}`).first != Item.empty) { event.custom({ "type": "tconstruct:casting_basin", "fluid": { "tag": fluidTag, "amount": 810 }, "result": {"tag": blockTag}, "cooling_time": castingTime }).id(`kubejs:smeltery/casting/metal/${metalName}/block`) } let castTypes = [ // {name: 'coin', fluidCost: 30, cooldownMultiplier: 1/(3*Math.sqrt(3))}, {name: "gear", fluidCost: 360, cooldownMultiplier: 2 / 3}, {name: "ingot", fluidCost: 90, cooldownMultiplier: 1 / 3}, {name: "nugget", fluidCost: 10, cooldownMultiplier: 1 / 9}, {name: "plate", fluidCost: 90, cooldownMultiplier: 1 / 3}, {name: "rod", fluidCost: 45, cooldownMultiplier: 1 / (3 * Math.SQRT2)}, {name: "wire", fluidCost: 45, cooldownMultiplier: 1 / (3 * Math.SQRT2)} ] // casting into casts castTypes.forEach(cast=>{ if (Ingredient.of(`#forge:${cast.name}s/${metalName}`).first != Item.empty) { event.custom({ "type": "tconstruct:casting_table", "cast": { "tag": `tconstruct:casts/multi_use/${cast.name}` }, "fluid": { "tag": fluidTag, "amount": cast.fluidCost }, "result": {"tag": `forge:${cast.name}s/${metalName}`}, "cooling_time": Math.round(castingTime * cast.cooldownMultiplier) }).id(`kubejs:smeltery/casting/metal/${metalName}/${cast.name}_gold_cast`) event.custom({ "type": "tconstruct:casting_table", "cast": { "tag": `tconstruct:casts/single_use/${cast.name}` }, "cast_consumed": true, "fluid": { "tag": fluidTag, "amount": cast.fluidCost }, "result": {"tag": `forge:${cast.name}s/${metalName}`}, "cooling_time": Math.round(castingTime * cast.cooldownMultiplier) }).id(`kubejs:smeltery/casting/metal/${metalName}/${cast.name}_sand_cast`) } }) // ingot chilling if (Ingredient.of(`#forge:ingots/${metalName}`).first != Item.empty) { event.custom({ "type": "thermal:chiller", "ingredients": [{ "fluid_tag": fluidTag, "amount": 90 },{ "item": "thermal:chiller_ingot_cast" }], "result": [{ "item": getPreferredItemFromTag("forge:ingots/" + metalName), "count": 1 }], "energy": 5000 }).id(`kubejs:crucible/kubejs/smeltery/casting/metal/${metalName}/ingot_gold_cast`) } } /** * Creates smeltery melting recipes for nuggets, ingots, blocks, etc * Also creates a magma crucible recipe for the ingot * @param {RecipeEventJS} event recipe event * @param {string} metalName the name of the metal to create melting recipes for * @param {number} meltingTime the time it takes to melt a block in the smeltery * @param {number} temperature the temperature required to melt a block in the smeltery */ const metalMelting = (event, metalName, outputFluid, meltingTime, temperature) => { let fluidTag = "forge:molten_" + metalName let blockTag = "forge:storage_blocks/" + metalName // block melting if (Ingredient.of(`#forge:storage_blocks/${metalName}`).first != Item.empty) { event.custom({ "type": "tconstruct:melting", "ingredient": {"tag": `forge:storage_blocks/${metalName}`}, "result": { "fluid": outputFluid, "amount": 810 }, "temperature": temperature, "time": meltingTime }).id(`kubejs:smeltery/melting/metal/${metalName}/block`) } let castTypes = [ {name: "coin", fluidAmount: 30, timeMultiplier: 1 / (3 * Math.sqrt(3))}, {name: "gear", fluidAmount: 360, timeMultiplier: 2 / 3}, {name: "ingot", fluidAmount: 90, timeMultiplier: 1 / 3}, {name: "nugget", fluidAmount: 10, timeMultiplier: 1 / 9}, {name: "plate", fluidAmount: 90, timeMultiplier: 1 / 3}, {name: "rod", fluidAmount: 45, timeMultiplier: 1 / (3 * Math.SQRT2)}, {name: "wire", fluidAmount: 45, timeMultiplier: 1 / (3 * Math.SQRT2)} ] // melting cast shapes castTypes.forEach(cast=>{ if (Ingredient.of(`#forge:${cast.name}s/${metalName}`).first != Item.empty) { event.custom({ "type": "tconstruct:melting", "ingredient": {"tag": `forge:${cast.name}s/${metalName}`}, "result": { "fluid": outputFluid, "amount": cast.fluidAmount }, "temperature": temperature, "time": meltingTime * cast.timeMultiplier }).id(`kubejs:smeltery/melting/metal/${metalName}/${cast.name}`) } }) // ingot crucible melting if (Ingredient.of(`#forge:ingots/${metalName}`).first != Item.empty) { event.custom({ type:"thermal:crucible", ingredient: { "tag": `forge:ingots/${metalName}` }, result:[{ fluid: outputFluid, amount:90 }], energy:Math.round(meltingTime / 3) * 50 }).id(`kubejs:crucible/kubejs/smeltery/melting/metal/${metalName}/ingot`) } } /** Used in datapack events instead of recipe events */ const addChiselingRecipe = (event, id, items, overwrite) => { const json = { type: "rechiseled:chiseling", entries: [], overwrite: !!overwrite } items.forEach(item=>{ json.entries.push({ item: item }) }) event.addJson(id, json) } /** Used in a datapack event to remove a configured feature by its resource location */ const removeFeature = function(event, featureName) { featureName = featureName.split(":") let namespace = featureName[0] let identifier = featureName[1] event.addJson(`${namespace}:worldgen/configured_feature/${identifier}`, { "type": "minecraft:no_op", "config": {} }) } /** Used in a datapack event to add ore generation for an ore to the overworld * This function only works for ores with both a stone and deepslate variant */ const addOregenOverworld = function(event, featureName, blockName, heightType, heightMin, heightMax, veinCount, veinSize, discardChanceOnAirExposure) { featureName = featureName.split(":") let namespace = featureName[0] let identifier = featureName[1] blockName = blockName.split(":") let blockNamespace = blockName[0] let blockIdentifier = blockName[1] event.addJson(`${namespace}:worldgen/configured_feature/${identifier}`, { "type": "minecraft:ore", "config": { "discard_chance_on_air_exposure": discardChanceOnAirExposure, "size": veinSize, "targets": [ { "state": {"Name": `${blockNamespace}:${blockIdentifier}`}, "target": {"predicate_type": "minecraft:tag_match", "tag": "minecraft:stone_ore_replaceables"} }, { "state": {"Name": `${blockNamespace}:deepslate_${blockIdentifier}`}, "target": {"predicate_type": "minecraft:tag_match", "tag": "minecraft:deepslate_ore_replaceables"} } ] } }) let minInclusive = {"absolute": heightMin} let maxInclusive = {"absolute": heightMax} // EMI Oregen will display more useful information if we change to using Above Bottom where it makes sense to if (heightMin < -64) { minInclusive = {"above_bottom": heightMin + 64} maxInclusive = {"above_bottom": heightMax + 64} } else if (heightMax > 320) { minInclusive = {"below_top": -(heightMin - 320)} maxInclusive = {"below_top": -(heightMax - 320)} } event.addJson(`${namespace}:worldgen/placed_feature/${identifier}`, { "feature": `${namespace}:${identifier}`, "placement": [ {"type": "minecraft:count", "count": veinCount}, {"type": "minecraft:in_square"}, { "type": "minecraft:height_range", "height": { "type": heightType, "min_inclusive": minInclusive, "max_inclusive": maxInclusive } }, {"type": "minecraft:biome"} ] }) event.addJson(`${namespace}:forge/biome_modifier/${identifier}`, { "type": "forge:add_features", "biomes": "#minecraft:is_overworld", "features": `${namespace}:${identifier}`, "step": "underground_ores" }) } /** * Gets the prefered item from a tag. Useful for porting Mantle recipes that use tags as outputs. * @param {string} tag Don't include a hashtag in the tag name */ // const ItemOutput = Java.loadClass('slimeknights.mantle.recipe.helper.ItemOutput'); const getPreferredItemFromTag = (tag) => { /* Tried using mantle for this and it didn't work on first launch unfortunately */ // return Item.of(ItemOutput.fromTag(TagKey.create(Registry.ITEM_REGISTRY, tag), 1).get()).getId(); /* Create a copy of the mantle preferred mods list */ const preferredMods = ["minecraft", "create", "alloyed", "createdeco", "createaddition", "createbigcannons", "create_dd", "thermal", "tconstruct", "tmechworks"]; const tagItems = Ingredient.of("#" + tag).itemIds; for (let i = 0;i < preferredMods.length;++i) { let modId = preferredMods[i]; for (let j = 0;j < tagItems.length;++j) { let itemId = tagItems[j]; if (itemId.split(":")[0] === modId) { return itemId; } } } if (tagItems.length > 0) { return tagItems[0]; } return "minecraft:air"; }