配方事件


前言

在开始写代码之前,你自然是最好有一些编程基础,大学的 C 语言课就足够了。当然没有也没关系,照着我写的做就是了,但是如果你有一定的理解能力,那你就可以触类旁通,我也不必那么费心。

然后介绍一下这些:

蓝色的 blockquote 表示信息与提示。
黄色的 blockquote 表示应当注意。
红色的 blockquote 表示警告。

魔改模组绝大部分用途都是改配方,这篇文章教你改配方。

我推荐你使用ProbeJS,它能在 VSCode 里面提供代码提示和补全。

工作台合成

首先你在server_scripts里面新建一个 JS 文件,一个文件只做一类事情,养成好习惯,免得以后忘记代码写在哪了。

然后要知道工作台的配方分有序合成和无序合成两种,来看看怎么写:

关于 JavaScript 的基本数据类型,请看这篇文章
// priority: 0 // 上面一行的作用是设定此 JS 文件加载的优先级,数字大的先加载。 onEvent('recipes', event => { // 指定代码作用的事件,'event'可以改成任何合法的变量名 event.shaped('2x minecraft:golden_apple', // 添加有序合成配方,此配方输出两个金苹果 [ ' G ', 'GAG', ' G ' ], // 物品的摆放样式,使用字符代替不同的物品,空格表示没有物品 { G: '#forge:storage_blocks/gold', A: 'minecraft:apple' } // 定义上面的字符代表的物品(或标签) ).id('kubejs:shaped_example_1') // 指定配方 ID,这不是必须的 event.shapeless('minecraft:copper_ingot', // 添加无序合成配方以及输出的物品 ['9x #forge:nuggets/copper'] // 原料及数量,如果只有一种原料可以不用数组 ) }) // 如果你在使用 1.19,onEvent 要改写成如下方式 // 1.19 的更多变化请看 https://mods.latvian.dev/books/kubejs-legacy/page/migrating-to-kubejs-6 // 本系列的文章基于 1.18,这之后不再演示 1.19 的写法 ServerEvents.recipes(event => { // 和上面一样的代码 }) 写完后保存文件,在游戏里面使用/reload来应用更改。
在未指定配方 ID 的情况下,KubeJS 会随机分配一个 ID。如果你指定的配方 ID 和现有的 ID 相同,则新设定的配方会覆盖旧的。
如果你发现你写的代码没有生效,请在logs/kubejs查看日志!

默认情况下,工作台配方可以被镜像(类似锄头的配方)和缩小(类似木棍)。如果你想写类似于 Mystical Agriculture 精华合成的那种配方,就像这样写:

onEvent('recipes', e => { e.shaped('4x minecraft:stick', [ ' P ', 'P ', ' ' ], {P: '#minecraft:planks'}).noMirror().noShrink() // 这两个方法没有先后顺序 })

这样你就必须把木板放到第一行第二个格子和第二行第一个格子才能合成出木棍了,不能镜像,也不能放到别处(哪怕形状一样)。

移除与修改配方

直接上示例。

onEvent('recipes', e => { e.remove({output: 'minecraft:golden_apple'}) // 移除所有输出金苹果的配方,包括其他模组添加的 e.remove({id: 'minecraft:golden_apple'}) // 移除配方 ID 为 'minecrfat:golden_apple' 的配方,也就是原版的金苹果配方 e.remove({type: 'minecraft:crafting_shaped'}) // 移除所有有序合成配方 e.remove({mod: 'torcherino'}) // 移除所有由 Torcherino 添加的配方,另外它检测的实际上是配方 ID 的命名空间 e.remove({ not: {output: 'minecraft:golden_apple'} }) // 移除所有不输出金苹果的配方,换句话说你只能合成金苹果 e.remove({}) // 移除所有配方,除非你想重新做一个游戏,否则不要使用 })

remove()中需要的是配方过滤器,也适用于修改配方。

// 为了方便写文章,省略 onEvent() // 你还是要写的 e.replaceInput({output: 'minecraft:golden_apple'}, 'minecraft:gold_ingot', 'minecraft:copper_ingot') // 将金苹果配方中的金锭换成铜锭 e.replaceOutput({}, 'minecraft:golden_apple', 'minecraft:golden_carrot') // 将所有输出金苹果的配方换成输出金胡萝卜

使用带 NBT 的物品

假如你想合成一本附魔书:

e.shaped(Item.of('minecraft:enchanted_book', '{StoredEnchantments:[{lvl:3,id:"minecraft:fortune"}]}') // 可以去掉 NBT 外的引号把它变成一个对象 [' D ', 'DBD', ' D '], { D: 'minecraft:diamond', B: 'minecraft:book' } )

Item.of() 接受 2 或 3 个参数,第一个参数为物品 ID 或标签,第二个参数为数量或 NBT,第三个参数为 NBT。

NBT 的数据类型为 Internal.CompoundTag_,表现为 JavaScript 对象。

Item类还有一些其他的方法,我会在之后提到。

其他的原版配方

e.smelting(output, input).id('mod:id') // 熔炉 e.smelting(output, input).cookTime(Number).xp(Number) // 设定熔炼时间和获取的经验,也适用于高炉、烟熏炉和营火烹饪 e.blasting(output, input) // 高炉 e.smoking(output, input) // 烟熏炉 e.campfireCooking(output, input) // 营火烹饪 e.smithing(output, input1, input2) // 锻造台 如果你想修改酿造台配方,请安装 MoreJS // for 1.18 pls use: onEvent("morejs.potion_brewing.register", event => { ... }) MoreJSEvents.registerPotionBrewing(event => { /** * 1. Argument: The top ingredient of the brewing stand * 2. Argument: The bottom ingredient of the brewing stand * 3. Argument: The result of the brewing */ event.addCustomBrewing( "minecraft:gold_ingot", Ingredient.customNBT("minecraft:potion", (nbt) => { return nbt.contains("Potion") && nbt.Potion == "minecraft:water"; }), Item.of("minecraft:potion", { Potion: "kubejs:felix_felicis" }) // This is a custom made potion. It's not vanilla ); event.addCustomBrewing("minecraft:diamond", "minecraft:emerald", "minecraft:gold_ingot"); /** * 1. Argument: The bottom ingredient of the brewing stand * 2. Argument: The input potion of the brewing stand * 3. Argument: The result potion of the brewing */ event.addPotionBrewing("minecraft:emerald", "minecraft:fire_resistance", "minecraft:strength"); /** * Simpler call which automatically use `potion:water` as the input potion. * 1. Argument: The bottom ingredient of the brewing stand * 3. Argument: The result potion of the brewing */ event.addPotionBrewing("minecraft:nether_star", "minecraft:levitation"); /** * Removes all potions where an awkward potion is used as theb base potion ingredient. * 1. Argument: The input potion id to filter. * 2. Argument: The ingredient to filter. * 3. Argument: The result potion id to filter. * Passing `null` counts as a wildcard. */ event.removeByPotion("minecraft:awkward", null, null); });

使用 JSON 配方

// 用法: event.custom(Internal.JsonElement_) // 示例 event.custom({ "type": "create:crushing", "ingredients": [ {"item": "minecraft:oak_sapling"} ], "results": [ {"item": "minecraft:apple"}, {"item": "minecraft:carrot"} ], "processingTime": 100 }) // 你可以像上面那样直接粘贴 JSON,也可以将它改造得更符合 JS 的样子 event.custom({ type: 'create:crushing', ingredients: [ Ingredient.of('minecraft:oak_sapling').toJson() // 大多数情况下,Ingredient 用起来和 Item 没有区别 ], results: [ Item.of('minecraft:apple').toJson(), Item.of('minecraft:carrot').toJson() ], processingTime: 100 })
如果你不知道你想要添加的配方 JSON 长什么样,去 mod 的 jar 文件里找 data/modid/recipes/

使用函数

为了方便,有时候会用函数封装上面那样的 JSON 配方。

// 第一种方法 function createCrushing(results, ingredients, time){ event.custom({ type: 'create:crushing', ingredients: ingredients, results: results, processingTime: time }) } // 第二种方法 let createCrushing = (results, ingredients, time) => { // 和上面相同的代码 } // 用法: createCrushing( [Ingredient.of('minecraft:oak_sapling').toJson()], [Item.of('minecraft:apple').toJson(),Item.of('minecraft:carrot').toJson()], 100 )

上面使用的是普通的函数,这适用于大多数配方,你也可以在函数中添加更多的处理逻辑,这样你就能直接在参数里面用'2x minecraft:apple'了。

下面介绍另一种函数:构造函数。

function DraconicFusionCrafting(result, catalyst){ // 使用了 this 之后,这个函数就成为了一个构造函数,相当于一个类,不过 class 在 KubeJS 中不可用。 this.type = 'draconicevolution:fusion_crafting' this.result = result this.catalyst = catalyst this.total_energy = 100000 this.tier = 'DRACONIUM' this.ingredients = [] } // 通过原型(prototype)为构造函数添加方法 DraconicFusionCrafting.prototype = { // 下面是一个 JSDocument 注释,说明了函数使用的参数的类型,这样你在写代码的时候编辑器就能提示你应该填什么类型的参数了 /** * @param {number} energy */ energy: function(energy){ this.total_energy = energy return this }, /** * @param {string} tier */ tier: function(tier){ this.tier = tier return this }, /** * @param {Array} ingredients */ ingredients: function(ingredients){ this.ingredients = ingredients return this } } // 用法: e.custom( // 不要漏掉 new 关键字 new DraconicFusionCrafting( Item.of('productivebees:spawn_egg_configurable_bee', '{EntityTag:{type:"productivebees:chaos"}}').toJson(), Item.of('productivebees:spawn_egg_configurable_bee', '{EntityTag:{type:"productivebees:awakened"}}').toJson() ) .energy(64000000) .tier('CHAOTIC') .ingredients([ {"item": "draconicevolution:chaotic_core"}, {"item": "draconicevolution:awakened_core"}, {"item": "draconicevolution:chaotic_core"}, {"item": "minecraft:honey_block"}, {"tag": "forge:storage_blocks/honeycombs"}, {"tag": "forge:storage_blocks/honeycombs"}, {"tag": "forge:ingots/draconium_awakened"}, {"item": "draconicevolution:medium_chaos_frag"} ]) ).id('productivebees:chaos_bee')
关于命名,推荐的方法是:
常量使用全大写,单词之间使用下划线连接。(THE_CONSTANT
变量与函数使用小驼峰命名法。(theVariable, theFunction()
类名使用大驼峰命名法。(TheClass
当然命名方法不是唯一的,但是请尽量在你的项目中使用同一套命名规则。
基础知识 KJSEXP 模组兼容