Wudji 发表于 2022-5-17 22:26:08

XPlus KubeJS魔改教程(Minecraft 1.16.5-1.18.2)

本帖最后由 Wudji 于 2024-3-1 20:05 编辑

https://m1.miaomc.cn/uploads/20220507_7fc6e1d8f48a4.png

简要介绍

KubeJS是一个于1.16.5版本开始兴起的基于JavaScript的魔改核心模组,同时支持Fabric和Forge环境。在热重载的加持下,您可以很便捷地修改游戏中的绝大多数内容。从新增/修改物品,方块,配方到自定义游戏内逻辑、修改战利品表,自定义世界生成......没有什么是不能借助KubeJS轻松实现的。除此以外,KubeJS还可以用于管理服务器,修改客户端显示内容等。

本文内没有特别注明的原创内容,均根据知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

教程地址(推荐使用GitBook地址浏览教程)


类型地址
GitBookhttps://wudji.gitbook.io/xplus-kubejs-tutorial-v1-zh_cn/
GitHubhttps://github.com/Wudji/XPlus-KubeJS-Tutorial/
MCBBShttps://www.mcbbs.net/thread-1207772-1-1.html

相关链接


类型地址
Wudji的爱发电地址https://afdian.net/a/Wudji_XPlusmodpack
KubeJS MCMODhttps://www.mcmod.cn/class/2450.html
XPlus KubeJS 魔改教程 (Minecraft 1.19+)https://klpbbs.com/thread-131971-1-1.html


[#1] ✅ 1:前言 (点击目录文本即可跳转至对应章节)
*[# 2] ✅ 1-1 基础代码格式和常用指令
[#3 ] ✅ 2: 配方
*[# 4] ✅ 2-1 物品表示与配方的添加
*[#5] ✅ 2-2 配方的修改与删除
*[# 6] ✅ 2-3 非标准配方修改
[# 7] ✅ 3: 标签
[# 8] ✅ 4: 自定义战利品表
[# 9] ✅ 5: 自定义流体
[# 10] ✅ 6: 自定义世界生成
[# 11] ✅ 7: 自定义方块
[# 12] ✅ 8: 聊天事件
[# 13] ✅ 9: 服务端定时任务
*[# 14] ✅ 实例-计划重启
[# 15] ✅ 10: 自定义窗口标题和logo
[# 16] ✅ 11: 针对其他mod的修改
*[#17] ✅ 11-1 JEI信息修改
*[# 18] ✅ 11-2 REI信息修改
*[#19] ✅ 11-3 FTB Quest相关修改
*[#20 ] ✅ 11-4 KubeJS内置游戏阶段 (类似于GameStage)
*[ # 21] ✅ 11-5 KubeJS内置游戏阶段实例:玩家初始物品
[ # 22] ✅ 12: 附属mod
*[#2 3] ✅ 12-1 KubeJS UI
*[#24] ✅ 12-2 KubeJS Blood magic(血魔法拓展)
*[# 25] ✅ 12-3 KubeJS Create(机械动力拓展)
*[#26] 🚧 12-4 KubeJS Mekanism(通用机械拓展)
*[#27] 🚧 12-5 KubeJS Thermal(热力膨胀拓展)
*[#28] 🚧 12-6 KubeJS Immersive engineering(沉浸工程拓展)
*[#29] ✅ 12-7 便捷战利品表修改(LootJS Forge)
[#30] ✅ 13 添加物品和物品tooltip
[#31] ✅ 14 方块和物品属性修改
[#32] ✅ 15 玩家事件,信息获取及操作 & 方块、物品、实体信息获取及操作 & 玩家交互
*[#33] ✅ 15.1 玩家&实体事件,信息获取及操作
*[#34] ✅ 15.2 实体生成 & RayTraceResultJS & ItemStackJS及其他
*[#35] ✅ 15.3 方块信息获取及操作 & ExplosionJS
*[#36] ✅ 15.4 世界,服务器信息获取和操作(WorldJS , ServerJS)
*[#37] ✅ 15.5 本章例子
[#38] ✅ 16 网络包和Painter JS
[#39] ✅ 17 本地化相关
[#40] ✅ 18 杂项示例
*[#41] ✅ 18-1 示例 - 熔炼之镐
*[# 42] ✅ 18-2 示例 - 罐头示例(包含物品注册, 配方添加, 玩家交互和persistentData的使用)

教程版本:v1.0.0 based on KubeJS-forge-1605.3.19-build.299 - klpbbs
https://m1.miaomc.cn/uploads/20220509_e1adf48299446.png

1 前言


简要介绍

KubeJS是一个于1.16.5版本开始兴起的基于JavaScript的魔改核心模组,同时支持Fabric和Forge环境。在热重载的加持下,您可以很便捷地修改游戏中的绝大多数内容。从新增/修改物品,方块,配方到自定义游戏内逻辑、修改战利品表,自定义世界生成......没有什么是不能借助KubeJS轻松实现的。除此以外,KubeJS还可以用于管理服务器,修改客户端显示内容等。

本文相关信息

本文将翻译,讲解部分该mod的常用功能及其附属mod,并提供多个实例以供参考

本文中已完成的部分会标记✅,未完成的部分会标记🚧

本文内没有特别注明的原创内容,均根据知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可

受本人水平,时间因素及KubeJS自身版本更新的影响,本文中难免还存在一些问题/过时内容,还请各位坛友斧正,谢谢!

本教程最初发布于mcbbs: https://www.mcbbs.net/thread-1207772-1-1.html。本人精力有限,此处教程可能不是最新版本。为了更好的阅读体验,推荐您前往mcbbs阅读(有愿意帮忙维护的大佬就更好了[哔哩_脱单] )

实用链接

KubeJS 官方Wiki:https://mods.latvian.dev/books/kubejs

KubeJS 站内搬运贴:https://www.mcbbs.net/thread-1200250-1-1.html

KubeJS 源代码(1.16.5分支):https://github.com/KubeJS-Mods/KubeJS/tree/1.16/main/common/src/main/java/dev/latvian/kubejs

KubeJS 官方Discord交流频道:https://discord.gg/hCVTFHKE

本文部分内容参考/翻译自以上内容,在这里表示感谢

修改内容展示

自定义合成,详见本文 第2章配方修改

https://attachment.mcbbs.net/data/myattachment/forum/202106/22/162619ota7nntemeabba11.png

针对机械动力配方的修改,详见本文 第12章附属mod

https://attachment.mcbbs.net/data/myattachment/forum/202201/12/195245qui3wu3gqrbiqgir.png

自定义世界生成(自定义矿石),详见本文 第6章自定义世界生成

https://attachment.mcbbs.net/data/myattachment/forum/202106/29/093131fr8q0dw8dpv9893d.png

自定义聊天内容前缀(doge),详见本文 第8章聊天信息修改

https://m1.miaomc.cn/uploads/20220407_b9de677546c5d.png

随机传送,详见本文 第15章玩家事件,信息获取及操作 & 方块、物品、实体信息获取及操作 & 玩家交互

https://m1.miaomc.cn/uploads/20220408_c412408992599.gif

本文更新日志

注:章节号可能前后发生变化,请以编辑记录为准!



2022.05.17 1.0.0 port


https://attachment.mcbbs.net/data/myattachment/forum/202111/27/163902ztyfsjqsqssiszss.png

1.1 基础代码格式和常用指令


脚本语言

KubeJS使用 JavaScript 来对游戏进行修改(这也就是为什么 Rhino 会是它的前置)。

文件结构

在正确安装KubeJS并启动过一次游戏后,你可以在版本根目录下找到kubejs这个文件夹,

kubejs
├─assets
│└─kubejs
│      └─textures
│          ├─block
│          └─item
├─client_scripts
├─config
├─data
├─exported
│└─tags
├─server_scripts
└─startup_scripts
以下为各个目录的功能(详见目录下README.txt):


[*]assets 文件夹和资源包的功能基本相同,你可以在这里的对应目录下放自定义方块、物品的纹理、模型等,也可以当做一个全局资源包加载器(类似于OpenLoader)
[*]config 中包括对KubeJS的一些配置选项
[*]data 文件夹和数据包功能基本相同,类似于全局数据包加载器(类似于OpenLoader)
[*]client_scripts 中为客户端资源被加载时加载脚本

[*](如 client.tick 等被标记为Client和Client Startup的事件)(可使用 F3 + T 重载)
[*]server_scripts 中为服务端资源被加载时加载的脚本

[*](如 recipes 等被标记为Server和Server Startup的事件)(可使用游戏内命令 /reload 重载)
[*]startup_scripts 中为启动时就被加载的脚本

[*](如 item.modification 等被标记Startup的事件)(可使用游戏内命令 /kubejs reload_startup_scripts 重载)

代码基础格式

如JavaScript一样,所有的脚本后缀名都应为.js

代码基础格式如下

onEvent('事件名称', event => {

//在此处编写代码 <- 这是一行注释,JavaScript的注释格式详见百度

})
比如,以下代码会在玩家加入世界时将制定内容打印到控制台

onEvent('player.logged_in', event => {
let name = event.player.name
console.log("玩家 " + name + " 加入了游戏")
})
其中 player.logged_in 就是被监听的事件,当该事件发生时,就执行函数内容(console.log语句)

JavaScript语法

在看教程前,你最好先了解以下内容

JavaScript语法

if 和 if else

for循环

while循环

函数

箭头函数

当然,你也可以把 JavaScript教程 看一遍(doge)

KubeJS游戏内指令

使用/kubejs custom_command <command> 可以执行自定义指令

使用/kubejs errors 可以在聊天栏中获取当前脚本的报错

使用/kubejs export 可以将游戏内的配方、tags、所有方块、实体类型、流体类型导出到kubejs\exported\kubejs-server-export.json


[*]注:你可以将该json文档上传至https://export.kubejs.com/来进行分析(截至编辑本文时该功能不可用)

使用/kubejs hand 或 /kjs_hand 可以快速获取手中物品信息,这对于配方自定义等非常有帮助(点击文本即可复制)

https://m1.miaomc.cn/uploads/20220416_cc6b6a0ab79b6.png

使用/kubejs hotbar 可以将快捷栏中所有物品信息打印到聊天栏(同/kubejs hand)

使用/kubejs inventory 可以将玩家物品栏的所有物品信息打印到聊天栏(同/kubejs hand)

使用/kubejs offhand 可以将玩家副手的物品信息打印到聊天栏(同/kubejs hand)

使用/kubejs list_tags <tag> 来将给定标签的内容打印到聊天栏


[*]如/kubejs list_tag minecraft:logs item 会将#minecraft:logs标签下的元素打印出来

使用/kubejs painter <玩家名称> <PainterJS对象> 来调用PainterJS(见第十六章)

使用/kubejs reload 来重载服务器类型脚本、语言文件、纹理资源和启动类型脚本


[*]其中/kubejs reload server_scripts和/reload的效果基本相同
[*]/kubejs reload startup_scripts 并不能重载所有启动脚本事件,如方块注册等

使用/kubejs stage <玩家名称> 来为指定玩家添加、列出、移除或清除游戏阶段


[*]关于Gamestage的详细介绍见11.4和11.5章节

使用/kubejs warnings 来查看当前脚本中的警告信息

使用/kubejs wiki 来打开官方KubeJS Wiki



附表:KubeJS中的事件
事件ID是否可被取消类型
initNoStartup
postinitNoStartup
loadedNoStartup
command.registryNoServer
command.runYesServer
client.initNoClient
client.debug_info.leftNoClient
client.debug_info.rightNoClient
client.logged_inNoClient
client.logged_outNoClient
client.tickNoClient
server.loadNoServer
server.unloadNoServer
server.tickNoServer
server.datapack.firstNoServer
server.datapack.lastNoServer
recipesNoServer
world.loadNoServer
world.unloadNoServer
world.tickNoServer
world.explosion.preYesServer
world.explosion.postNoServer
player.logged_inNoServer
player.logged_outNoServer
player.tickNoServer
player.data_from_server.YesClient
player.data_from_client.YesServer
player.chatYesServer
player.advancementNoServer
player.inventory.openedNoServer
player.inventory.closedNoServer
player.inventory.changedNoServer
player.chest.openedNoServer
player.chest.closedNoServer
entity.deathYesServer
entity.attackYesServer
entity.dropsYesServer
entity.check_spawnYesServer
entity.spawnedYesServer
block.registryNoStartup
block.missing_mappingsNoServer
block.tagsNoServer
block.right_clickYesServer
block.left_clickYesServer
block.placeYesServer
block.breakYesServer
block.dropsNoServer
item.food_eatenYesServer
item.registryNoStartup
item.missing_mappingsNoServer
item.tagsNoServer
item.right_clickYesServer
item.right_click_emptyNoServer
item.left_clickNoServer
item.entity_interactYesServer
item.modificationNoStartup
item.pickupYesServer
item.tooltipNoClient
item.tossYesServer
item.craftedNoServer
item.smeltedNoServer
fluid.registryNoStartup
fluid.tagsNoServer
entity_type.tagsNoServer
worldgen.addNoStartup
worldgen.removeNoStartup



2:配方

1、本段代码文件应放于.minecraft\kubejs\server_scripts下
2、本段内容对应事件:recipes,即你的代码应该是这样的
onEvent('recipes', event => {
      //示例配方修改
      event.shaped('3x minecraft:stone', [
                'SSS',
                'S S',
                'SSS'
          ], {
                S: 'minecraft:sponge'
          })
})

本章为了表述简洁省略了onEvent部分


2.1 物品表示与新建配方


一、物品的表示方法——IngredientJS和ItemstackJS

KubeJS提供了IngredientJS和ItemstackJS用于表示物品。比如,标签Ingredient.of("#minecraft:logs")就是Ingredient,Item.of('minecraft:iron_ingot')就是一个Itemstack

其中Ingredient多用于匹配物品。你可以使用Ingredient.matchAny("条件")来获得一个包含当前筛选条件的物品组,其中条件可以为物品ID(minecraft:diamond),mod注册名(@tinkersconstruct),标签(#minecraft:logs)

Itemstack主要用于表示准确的物品组,ItemstackJS可使用的属性/函数如下:


属性功能返回值
id返回该物品组的id字符串
tags返回该物品组的tagsCollection<ResourceLocation> tags
count设置/返回该物品组中物品数量int
block返回当前物品是否为方块布尔值
nbt返回当前物品组的nbtCompoundTag
nbtString返回字符串形式的nbt字符串
name返回当前物品组的名称Text
enchantments返回当前物品组的附魔MapJS
harvestSpeed返回当前物品组的破坏速度浮点型
itemGroup返回当前物品组在创造物品栏的位置字符串
vanillaPredicate获取当前物品谓词(?)Predicate<ItemStack>
empty返回当前物品组是否为空布尔值


函数功能返回值
hasTag(tag)判断物品是否有指定tag布尔值
withCount(整形 数量)返回一个指定数量的物品组ItemStackJS
hasNBT()判断是否具有NBT布尔值
removeNBT()移除物品的NBTvoid
withNBT(CompoundTag nbt)返回具有指定NBT的物品组ItemStackJS
withName(Text 名称)返回一个具有指定名称的物品组ItemStackJS
strongEquals(any)将当前物品组与给定内容对比(同时比较数量等)布尔值
hasEnchantment(字符串 附魔ID, 整形 等级)判断当前物品组是否有给定的附魔布尔值
enchant(字符串 附魔ID, 整形 等级)返回一个添加了指定附魔的物品组ItemStackJS
enchant(MapJS 附魔内容)返回一个添加了指定附魔的物品组ItemStackJS
ignoreNBT()返回忽略的NBT的IngredientJSIngredientJS
weakNBT()返回部分忽略的NBT的IngredientJSIngredientJS
areItemsEqual(ItemStackJS 对比对象)将当前物品组与给定物品组对比布尔值
isNBTEqual(ItemStackJS 对比对象)将当前物品组与给定物品组的NBT对比布尔值
getHarvestLevel(ToolType 工具类型, nullable PlayerJS 玩家, nullable BlockContainerJS 方块)返回其挖掘等级整形
getHarvestSpeed(nullable BlockContainerJS 方块)返回其挖掘速度浮点型

配方修改需要用到的大都是上表加粗的内容,剩余的内容大都等到第15章判断物品状态时才会用到。

估计你看到上面这些东西得有点麻了,不要慌,让我们看几个例子


例子意义
Item.of("minecraft:iron_ingot").withCount(5)5个铁锭
Item.of("minecraft:iron_ingot").withCount(5).withName("KubeJS魔改教程")5个名字为"KubeJS魔改教程"的铁锭
Item.of("minecraft:diamond_sword").ignoreNBT()忽略了NBT的钻石剑(长用于忽略物品耐久、附魔等属性)
Item.of("minecraft:enchanted_book", {StoredEnchantments:[{lvl:1,id:"minecraft:sweeping"}]})横扫之刃I附魔书(直接添加NBT例子)
Item.of("minecraft:enchanted_book").enchant("minecraft:sweeping", 1)横扫之刃I附魔书(使用函数添加NBT例子)
Item.of(/create:.*/)所有机械动力物品(正则表达式)

显然,上面的方法表示物品非常麻烦。KubeJS提供了一些简写方法:

你可以使用命名空间ID直接表示一个物品,如"minecraft:iron_ingot"

你可以直接使用标签的字符串表示这一标签下的使用物品,如"#minecraft:logs"

你可以在命名空间前加上倍数来表示物品个数,如"5x minecraft:iron_ingot"表示五个铁锭

当然,你也可以直接使用正则表达式

你可以使用指令/kubejs hand来快速获取手持物品信息,详见1.1 基础代码格式和常用指令

二、配方的添加

1、有序配方添加

语句:event.shaped(输出物品 , 输入物品)

例子:用8个海绵合成3个石头

onEvent('recipes', event => { // 监听recipes事件
      // 主体修改内容
      event.shaped('3x minecraft:stone', [
                'SSS',
                'S S',
                'SSS'
          ], {
                S: 'minecraft:sponge'
          })
})
https://attachment.mcbbs.net/data/myattachment/forum/202106/22/162619ota7nntemeabba11.png

可以看到,KubeJS采用了类似原版的格式来修改配方,如果你的配方不需要三行的话,直接留空即可

注:下文为了表述简洁将省略onEvent部分

2、无序配方添加

语句:event.shapeless(输出物品 , 输入物品)

例子:用1个石头和1个带萤石粉标签的物品合成4个圆石

event.shapeless('4x minecraft:cobblestone', ['minecraft:stone', '#forge:dusts/glowstone'])
https://attachment.mcbbs.net/data/myattachment/forum/202106/22/164155jfvtd2fvivvv7eve.png

3、切石机、熔炉、高炉、烟熏炉、锻造台配方的添加

// 添加切石机配方:用1个minecraft:golden_apple合成4个minecraft:apple
event.stonecutting('4x minecraft:apple'/* 输出物品 */, 'minecraft:golden_apple'/* 输入物品 */)

// 添加切石机配方:用1个minecraft:golden_apple合成2个minecraft:carrot
event.stonecutting('2x minecraft:carrot'/* 输出物品 */, 'minecraft:golden_apple'/* 输入物品 */)

// 添加熔炉配方:用1个minecraft:golden_apple合成2个minecraft:carrot
// (event.recipes.minecraft.smelting的缩写)
event.smelting('2x minecraft:carrot'/* 输出物品 */, 'minecraft:golden_apple'/* 输入物品 */)

// 添加高炉,营火和烟熏炉的配方(与上文类似)
event.blasting('3x minecraft:apple', 'minecraft:golden_apple')

// 添加锻造台配方,将后2个物品合并成第一个物品 (将minecraft:gold_ingot和minecraft:apple合成为minecraft:golden_apple)
event.smithing('minecraft:golden_apple', 'minecraft:apple', 'minecraft:gold_ingot')
https://attachment.mcbbs.net/data/myattachment/forum/202106/22/165513fitztt9hjdwaz0at.png

4、NBT和配方ID

带NBT的配方修改

event.shaped('minecraft:book', [
    'CCC',
    'WGL',
    'CCC'
], {
    C: '#forge:cobblestone',
    L: Item.of('minecraft:enchanted_book', {StoredEnchantments:[{lvl:1,id:"minecraft:sweeping"}]}),
    // 尽管格式是相同的,但是对于附魔来说,你还可以将其简写成如下形式:
    W: Item.of('minecraft:enchanted_book').enchant('minecraft:respiration', 2),
    G: '#forge:glass'
})
mc中所有的配方都有一个随机的ID,但以下配方被指定了一个唯一的静态ID。这个功能对于编写Patchouli手册比较有用

event.smelting('minecraft:golden_apple', 'minecraft:carrot').id('wudjimodpack:wudji_first_recipe_id'/* 配方ID */)


2-2 配方的修改与删除
本页面将介绍配方的修改与删除方法
2-2-1 配方的移除

例子 用途解释
event.remove({}) 删除所有配方
event.remove({id: '配方ID'}) 移除合成该物品的所有配方
event.remove({input: '#forge:dusts/redstone'})移除所有以带有#forge:dusts/redstone标签为输入物品的配方
' '内也可以填物品ID
event.remove({output: '#minecraft:wool'})移除所有以带有#minecraft:wool标签为输出物品的配方
' '内也可以填物品ID
event.remove({mod: 'fabricexamplemod'})移除所有id为fabricexamplemod的mod添加的配方
event.remove({type: 'minecraft:campfire_cooking'})移除以营火为合成方式的配方
event.remove({output: 'minecraft:cooked_chicken', type: 'minecraft:campfire_cooking'})(叠加不同修改逻辑的示例)
移除用营火烤鸡肉的配方, 或者叫“移除以营火为合成方式,输出物品为熟鸡肉的配方”[:...:]


2-2-2 配方修改
以下是两个例子
// 在所有无序配方中,将任何木板替换为minecraft:gold_nugget
event.replaceInput({type: 'minecraft:crafting_shapeless'}, '#minecraft:planks', 'minecraft:gold_nugget')
//{}内可以填写配方类别

// 在所有配方中,将输出物品中的minecraft:stick替换为minecraft:oak_sapling
event.replaceOutput({}, 'minecraft:stick', 'minecraft:oak_sapling')

2.3 非标准配方修改


本节将介绍非标准配方的修改,包括非工作台配方、修改输入物品状态等(如原版中蛋糕的合成方式)

例子1:修改机械动力中粉碎轮的合成配方

注意!KubeJS现已有机械动力的拓展mod,无需使用该方法修改!

机械动力的修改教程详见本教程12.3部分

event.custom({
    type: 'create:crushing',//指定合成方式为粉碎轮
    ingredients: [
      Ingredient.of('minecraft:oak_sapling').toJson()//输入内容
    ],
    results: [//这里的 results(包括所有类似的位置的双引号都是可加可不加的)
      Item.of('minecraft:apple').toResultJson(),//100%输出苹果
      Item.of('minecraft:carrot').withChance(0.5).toResultJson()//50%输出苹果
    ],
    processingTime: 100 //所用时间
})
//若上述配方使用Json格式添加(即原版数据包格式)
{
"type": "create:crushing",
"ingredients": [
    {
      "tag": "minecraft:oak_sapling"
    }
],
"results": [
    {
      "item": "minecraft:apple",
      "count": 1
    },
    {
      "item": "minecraft:carrot",
      "chance": 0.5
    }
],
"processingTime": 100
}
如果你使用自定义的配方格式,你必须使用类似于原版Json数据包的格式,也就是必须带有"type": "mod:recipe_id"!(比如上文中提到的"type": "create:crushing"). 通过这种方式,你可以为使用原版配方系统的任何配方处理器添加配方,即使它不兼容KubeJS. KubeJS提供的简写格式为:

{item: '物品注册名', count: 数量} → Item.of('物品注册名', 数量).toResultJson()
{item: '物品注册名'} / {tag: '标签名'} →Ingredient.of('物品注册名').toJson() 和 Ingredient.of('#标签名').toJson()

实际上不就是Json套壳吗

下面我们再看一个例子

例子2:为Extended Crafting添加配方

合成拓展 (Extended Crafting) 介绍(mcmod)

event.custom({
    type: 'extendedcrafting:shaped_table',
    tier: 4,
    pattern: [
      "XXXXXXXXX",
      "X       X",
      "X       X",
      "X       X",
      "X       X",
      "X       X",
      "X       X",
      "X       X",
      "XXXXXXXXX"
],
key: {
      X: ,//标签的使用
}
      result:
})
//上述配方使用数据包修改:
{
"type": "extendedcrafting:shaped_table",
"pattern": [
    "XXXXXXXXX",
    "X       X",
    "X       X",
    "X       X",
    "X       X",
    "X       X",
    "X       X",
    "X       X",
    "XXXXXXXXX"
],
"key": {
    "X": {
      "tag": "forge:ingots/gold"
    }
},
"result": {
    "item": "minecraft:apple"
}
}
例子3:修改输入物品状态

以下是一些内置的函数


功能函数格式
为输入物品减去耐久.damageIngredient(要修改的物品(输入过滤器), 减去的耐久值(整形))
替换输入物品(比如桶).replaceIngredient(要替换的物品(输入过滤器), 替换的物品(物品组))
保持输入物品不变.keepIngredient(要保留的物品(输入过滤器))
自定义事件(Server StartUp脚本注册).customIngredientAction(要操作的物品(输入过滤器), 自定义事件ID(字符串))

其中,所谓的输入过滤器可接受如下类型的内容


内容示例
ItemStackJS'minecraft:dirt', Item.of('minecraft:diamond_sword').ignoreNBT() 等,具体见2.1
合成输入索引整形,如0,1,2......
对象{item: 'something', index: 0}

你可以在整个配方修改脚本的最后加上它们来实现你想要的效果,比如

onEvent('recipes', event => {
      event.shapeless(/*配方脚本*/).damageItem(Item.of('minecraft:diamond_sword').ignoreNBT())
      ......})

下面是几个例子

onEvent('recipes', event => {
    //用钻石剑切西瓜
      event.shapeless('9x minecraft:melon_slice', [ //无序合成,合成输出: 9个西瓜片
      Item.of('minecraft:diamond_sword').ignoreNBT(), //输入一个忽略NBT的钻石剑
      'minecraft:minecraft:melon' // 其他输入内容
    ]).damageItem(Item.of('minecraft:diamond_sword').ignoreNBT()) // 降低钻石剑耐久1点(必须忽略NBT)

    // 使用两个钻石剑合成kubejs:example_block. 合成后索引为1的钻石剑掉一点耐久并保留第二个钻石剑.
    event.shaped('kubejs:example_block', [
      'SD ',
      'D S'
    ], {
      S: Item.of('minecraft:diamond_sword').ignoreNBT(),
      D: 'minecraft:dirt'
    }).damageIngredient(0).keepIngredient('minecraft:diamond_sword')//叠加使用多个函数

    // 使用两个钻石剑合成kubejs:example_block. 合成后钻石剑被替换为石剑
    event.shapeless('kubejs:example_block', [
      Item.of('minecraft:diamond_sword').ignoreNBT(),
      'minecraft:stone',
      Item.of('minecraft:diamond_sword').ignoreNBT(),
      'minecraft:stone'
    ]).replaceIngredient('minecraft:diamond_sword', 'minecraft:stone_sword')

    // 使用沙子,骨粉,土方块和水瓶合成陶土. 合成后,水瓶被玻璃瓶所替代
    event.shapeless('minecraft:clay', [
      'minecraft:sand',
      'minecraft:bone_meal',
      'minecraft:dirt',
      Item.of('minecraft:potion', {Potion: "minecraft:water"})
    ]).replaceIngredient({item: Item.of('minecraft:potion', {Potion: "minecraft:water"})}, 'minecraft:glass_bottle')
})

3:标签

你应将本文件的内容放于.minecraft\kubejs\server_scripts下
onEvent('item.tags', event => {
      // 获取 #forge:cobblestone,然后将minecraft:diamond_ore添加进去
      event.add('forge:cobblestone', 'minecraft:diamond_ore')

      // 获取#forge:cobblestone,然后移除minecraft:mossy_cobblestone
      event.remove('forge:cobblestone', 'minecraft:mossy_cobblestone')

      // 移除#forge:ingots/copper
      event.removeAll('forge:ingots/copper')
})
实体标签等其他类型的标签同理

4 自定义Loot Table


(本章参考了Minecraft 原版模组入门教程 - 战利品表(作者ruhuasiyu))

(Forge推荐安装KubeJS附属LootJS以便捷修改战利品表,教程&相关介绍见本文12-7 便捷战利品表修改(LootJS Forge))

KubeJS目前能修改全局、方块、实体、猫或村民礼物(村庄英雄Buff)、钓鱼、宝箱战利品表

基本的方法名称如下


事件ID覆盖原有战利品表的方法名称修改战利品表的方法名称
generic.loot_tablesaddGenericmodify
block.loot_tablesaddBlockmodifyBlock
entity.loot_tablesaddEntitymodifyEntity
gift.loot_tablesaddGiftmodify
fishing.loot_tablesaddFishingmodify
chest.loot_tablesaddChestmodify

下面就下面5种的战利品表进行讲解。


0、基本格式

onEvent('事件ID', event => {
event.覆盖/修改原有战利品表方法名称('物品/实体注册名', table => {
      //修改内容
}

//table.clearPools()//清空所有随机池
//table.clearConditions()//清空条件
//table.clearFunctions()//清空函数
})
1、方块战利品表(block.loot_tables)

针对方块战利品表,KubeJS提供了很简单的添加单方块掉落物的方法。

onEvent('block.loot_tables', event => {
event.addSimpleBlock('minecraft:dirt', 'minecraft:red_sand')//覆盖泥土的战利品表并使泥土掉落红沙
event.addSimpleBlock('minecraft:oak_leaves')//覆盖橡树树叶的战利品表并使橡树树叶在破坏时掉落自身
})
如果你想要实现更复杂的修改,你需要使用随机池。KubeJS提供了一些简写方法,具体见下方例子。

onEvent('block.loot_tables', event => {
event.addBlock('minecraft:dirt', table => {
    table.addPool(pool => {//新建随机池(覆盖原有的)
      pool.rolls = 1 // 固定值
      // pool.rolls = // 也可写为 {min: 4, max: 6} // 抽奖次数∈
      // pool.rolls = {n: 4, p: 0.3} // 二项分布
      //以上两种值的表达方式为值提供器
      pool.addItem('minecraft:dirt')
      //pool.addItem('minecraft:dirt', 40) // 此处40为权重
      //pool.addItem('minecraft:dirt', 40, ) // 物品个数∈
      // pool.addCondition({json}) //json格式,抽取该物品需要满足的条件,原版数据包格式(具体使用情景详见14.1)
      // pool.addEntry({json})//json格式,项目类别
    })
})
})
注:修改方块战利品表(而不是覆盖)的示例详见14.1

2、实体战利品表(entity.loot_tables)

onEvent('entity.loot_tables', event => {
// 覆盖僵尸的战利品表,掉落5个物品(25%概率为胡萝卜,75%为苹果)
event.addEntity('minecraft:zombie', table => {
    table.addPool(pool => {
      pool.rolls = 5//抽5次奖
      pool.addItem('minecraft:carrot', 1)
      pool.addItem('minecraft:apple', 3)
    })
})

event.modifyEntity('minecraft:pig', table => {
    table.addPool(pool => {
      // 修改猪的战利品表,使在被玩家杀死时多掉落一个泥土
      // 注:只是修改战利品表。并不会覆盖原有的掉落生/熟猪肉的战利品表
      pool.addItem('minecraft:dirt')
      pool.killedByPlayer()
    })
})
})
注:对于gift.loot_tables、fishing.loot_tables、chest.loot_tables,只能使用addJson(物品注册名, json原版格式)来进行修改

3、常用表达方式

      /*
          在新建/修改随机池时,还有以下表达方式可用:
          以下值提供器与上述的表示方法相同
          pool.enchantRandomly(附魔注册名)// 随机附魔
          pool.enchantWithLevels(附魔等级(值提供器), 是否为宝藏(布尔值))// 指定等级和是否为宝藏的随机附魔
          pool.name(name as Text)// 指定名称
          pool.damage(掉落物耐久值(值提供器))// 修改掉落物耐久值
          pool.randomChance(掉落概率(double型))// 修改掉落概率
          pool.randomChanceWithLooting(掉落概率(double型), 倍率(double型))// 使用该表达方式时,抢夺或时运会对掉落物有影响
          pool.furnaceSmelt()// 使当前随机池掉落的物品掉落形式为经过熔炉烧炼(自动烧炼)
          pool.killedByPlayer()// 添加条件:实体被玩家杀死才会掉落
          pool.survivesExplosion()// 添加条件:方块被爆炸破坏时依旧掉落
          //以上方法也可以用于对Loot Table直接修改(例如table.name(name as Text)),具体使用见14.1
      */


5 自定义流体


KubeJS允许你在startup阶段自定义流体。对应事件:fluid.registry

不过到目前为止,Fabric下的KubeJS还不支持自定义流体

不得不说KubeJS还是非常良心的,注册流体还免费送你个桶(doge)


函数功能返回值
create(字符串 流体ID)新建流体FluidBuilder
color(整形 颜色)设置流体颜色FluidBuilder
bucketColor(整形 颜色)设置流体桶内流体的颜色FluidBuilder
textureStill(资源位置 ID)设置液体静止时的贴图FluidBuilder
textureFlowing(资源位置 ID)设置液体流动时的贴图FluidBuilder
textureThick(整形 颜色)设置液体静止时的颜色FluidBuilder
textureThin(整形 颜色)设置液体流动时的颜色FluidBuilder
luminosity(整形 发光度)设置流体亮度(默认值为0)FluidBuilder
density(整形 密度)设置流体密度(默认值为1000)FluidBuilder
temperature(整形 稳定)设置流体温度(默认值为300)FluidBuilder
viscosity(整形 粘稠度)设置流体粘稠度(默认值为1000)FluidBuilder
gaseous()设置流体为气体(???¿¿¿)(默认值为false)FluidBuilder

:使用16进制,形如0x844031

:从本行往下,楼主测试时均无法实现预期效果,功能描述为楼主的"臆断"(游戏版本1.16.5 KubeJS版本1605.3.19-build.299),但是理论上这些是可以用的,可能和楼主电脑有关系吧

例子:

onEvent('fluid.registry', event => {
    event.create("test_mud").displayName("泥").bucketColor(0x844031).textureThick(0x844031).textureThin(0x844031);
})
https://m1.miaomc.cn/uploads/20220503_0795d7c613651.png

https://m1.miaomc.cn/uploads/20220503_b75d0f305a728.png




6:自定义世界生成
添加/移除矿石,湖等 暂不支持fabric

以下为几个例子
onEvent('worldgen.add', event => {
event.addLake(lake => { // 自定义湖
    lake.block = 'minecraft:diamond_block' // 方块ID (使用 [] 来为其添加属性)
    lake.chance = 3 // 约3个区块生成一次
})

event.addOre(ore => { //自定义矿石
    ore.block = 'minecraft:glowstone' // 方块ID (使用 [] 来为其添加属性)
    ore.spawnsIn.blacklist = false // 是否在矿石生成黑名单位置处生成
    ore.spawnsIn.values = [ // 该矿石可以在以下位置生成(支持方块ID、标签)
      '#minecraft:base_stone_overworld' // 默认的生成方式: 用于决定作为地下矿石生成时,该矿石能取代哪些方块。你可以在https://wiki.biligame.com/mc/%E6%A0%87%E7%AD%BE查看更多信息。
    ]
   
    ore.biomes.blacklist = true // 是否在矿石生成黑名单群系中生成
    ore.biomes.values = [ // 矿石可以生成的群系
      'minecraft:plains', // 群系ID
      '#nether' // 或者你可以使用“# + 群系类别”来代表群系, 在文末查看可用的列表
    ]
   
    ore.clusterMinSize = 5 // 每矿簇最少的矿石数量 (现在 ore.clusterMinSize 选项是被忽略的, 该功能将在以后更新, 现在它恒为1)
    ore.clusterMaxSize = 9 // 每矿簇最多的矿石数量
    ore.clusterCount = 30 // 每个区块矿石数量
    ore.minHeight = 0 // 最小Y值
    ore.maxHeight = 64 // 最大Y值
    ore.squared = true // 对X和Z值添加0~16的随机值. 推荐设置为 true
    // ore.chance = 4 // 每大约4个区块生成一次. 对于稀有的矿石来说, 你可以将它和 clusterCount = 1 一同使用
})

event.addSpawn(spawn => { // 自定义实体生成
    spawn.category = 'monster' // 实体类别, 可以设为 'creature', 'monster', 'ambient', 'water_creature' 和 'water_ambient'
    spawn.entity = 'minecraft:magma_cube' // 实体ID
    spawn.weight = 10 // 生成权重
    spawn.minCount = 4 // 每组最小数量
    spawn.maxCount = 4 // 每组最大数量
})
})

/*
      可用的群系类别:
                taiga(针叶林类)
                extreme_hills(高山类)
                jungle(丛林类)
                mesa(恶地类)(太怪了,作者为什么要在这里用基岩版的名称...)
                plains(平原类)
                savanna(热带草原类)
                icy(冰原类)
                the_end(末地)
                beach(沙滩类)
                forest(树林类)
                ocean(海洋类)
                desert(沙漠类)
                river(河流类)
                swamp(沼泽类)
                mushroom(蘑菇岛类)
                nether(下界)

      以下是香草(划掉)世界生成的顺序:
                raw_generation
                lakes
                local_modifications
                underground_structures
                surface_structures
                strongholds
                underground_ores
                underground_decoration
                vegetal_decoration
                top_layer_modification
*/

onEvent('worldgen.remove', event => {
event.removeOres(ores => {//移除矿石
    ores.blocks = [ 'minecraft:coal_ore', 'minecraft:iron_ore' ] // 移除铁矿和煤矿
    ores.biomes.values = [ 'minecraft:plains' ] // 限制该选项仅在平原生效
})

event.removeSpawnsByID(spawns => {//通过实体ID来禁止指定实体生成
    spawns.entities.values = [
      'minecraft:cow',
      'minecraft:chicken',
      'minecraft:pig',
      'minecraft:zombie'
    ]
})

event.removeSpawnsByCategory(spawns => {//移除实体生成
    spawns.biomes.values = [
      'minecraft:plains'//指定为平原群系
    ]
    spawns.categories.values = [//类型为怪物
      'monster'
    ]
})
})
添加类效果展示:https://attachment.mcbbs.net/data/myattachment/forum/202106/29/093111oc44yc88c8rr3nyc.png

https://attachment.mcbbs.net/data/myattachment/forum/202106/29/093102i91r10qn9xiqrdij.jpg
https://attachment.mcbbs.net/data/myattachment/forum/202106/29/093131fr8q0dw8dpv9893d.png
7 添加方块


onEvent('block.registry', event => {
event.create('test_block', block => {
      block.material('glass')//设置方块材质
      block.hardness(0.5)//设置方块硬度
      block.displayName('Test Block')
})
})

// MC1.16版本的KubeJS 3还可以将以上内容简写为:
onEvent('block.registry', event => {
event.create('test_block')
         .material('glass')
       .hardness(0.5)
       .displayName('Test Block')
})

//你还可以把他们在一行中连着写
onEvent('block.registry', event => {
    event.create('example_block').material('wood').hardness(1.0).displayName('Example Block')
})
该方块的纹理必须被放于kubejs/assets/kubejs/textures/block/[方块名],如kubejs/assets/kubejs/textures/block/test_block.png

如果你想要使用自定义的模型,你可以使用Blockbench制作并把它放于 kubejs/assets/kubejs/models/block/[方块名].json , 如 kubejs/assets/kubejs/models/block/test_block.json.

其他支持的方法(需要在它们之前加block.):


[*]material('material')// 设置方块的“质地”,可用的参数见下
[*]type('basic') // 设置方块类型,可用的参数见下
[*]hardness(float) // 设置方块硬度,值需要大于 >= 0.0;若要使方块无法被破坏,详见下方。
[*]resistance(float) // 爆炸抗性,值需要大于 >= 0.0
[*]unbreakable() // 设置方块无法被破坏
[*]lightLevel(等级) // 方块光照等级,值属于(需要为整形)
[*]harvestTool('工具', 工具等级) // 可以破坏该方块的工具。工具参数可以为 pickaxe, axe, hoe, shovel,工具等级需 >= 0
[*]opaque(布尔值) // 方块是否透明
[*]fullBlock(布尔值) // 设置是否为完整方块,与notSolid()共同使用可以实现如下图所示效果(即镂空方块)
https://attachment.mcbbs.net/data/myattachment/forum/202201/29/113256ostx4j7qwsuw4s9k.png
[*]requiresTool(布尔值) // 是否需要对应工具才能掉落
[*]renderType('类型') // 设置方块的渲染类型,可用的选项有solid(实心), cutout(镂刻), translucent(半透明), 其中 cutout 可用于类似于玻璃的方块,translucent 可用于类似于染色玻璃的方块
[*]color(tintindex, color)// 修改方块颜色,具体可参考此处介绍
[*]texture('纹理路径')// 设置方块纹理
[*]texture('方向', '纹理路径')// 设置方块纹理(可以单独设置每个朝向的纹理)
[*]model('模型路径')// 设置方块使用的模型
[*]noItem()// 添加方块而不添加方块对应的物品
[*]box(x0, y0, z0, x1, y1, z1, true) // 设置方块碰撞箱(0~16),默认值为(0,0,0,16,16,16, true)
[*]box(x0, y0, z0, x1, y1, z1, false) // 设置方块碰撞箱(0~1),默认值为(0,0,0,1,1,1, false)

下图即为使用box方法设置的自定义方块

https://m1.miaomc.cn/uploads/20220130_1ec4430bb7573.png
[*]noCollision() // 设置方块是否有碰撞箱
[*]notSolid() // 见上
[*]waterlogged() // 是否可以被充水
[*]noDrops() // 被破坏是否掉落自身
[*]slipperiness(浮点型) // 设置打滑程度
[*]speedFactor(浮点型) // 玩家在上方移动速度倍率
[*]jumpFactor(浮点型)
[*]randomTick(randomTickEvent => {}) // 当方块被随机刻选中时发生的事件

支持以下函数:

BlockContainerJS block

Random random

下面是一个简单的例子

//当test_block_randomTickEvent被随机刻选中时将其换为minecraft:dirt
onEvent('block.registry', event => {
event.create('test_block_randomTickEvent', block => {
      //block.material('glass')
      //block.hardness(1.0)
      block.displayName('Test Block randomTickEvent')
      block.randomTick(randomTickEvent => {
      randomTickEvent.block.set('minecraft:dirt')// BlockContainerJS
      })
})

})
注:BlockContainerJS会在后面讲到(15章,尚未更新)
[*]item(itemBuilder => {}) // 掉落物设置
[*]setLootTableJson(json) // 设置战利品表(原版json格式)
[*]setBlockstateJson(json) // 设置方块状态(原版json格式)
[*]setModelJson(json) // 设置模型
[*]noValidSpawns(布尔值) // 是否可以生成怪物
[*]suffocating(布尔值) // 是否可以使玩家窒息
[*]viewBlocking(布尔值)
[*]redstoneConductor(布尔值) // 是否传导红石信号
[*]transparent(布尔值) // 是否透明
[*]defaultCutout() // 用来制作类似于玻璃的方块的方法的集合
[*]defaultTranslucent() // 与defaultCutout()类似,但是使用透明层
[*]tag('forge:exampletag') // 添加block tag
[*]tagBlockAndItem('forge:exampletag') // 为方块和物品添加tag

可用的方块类型(type() 方法)


[*]basic
[*]slab
[*]stairs
[*]fence
[*]fence_gate
[*]wall
[*]wooden_pressure_plate
[*]stone_pressure_plate
[*]wooden_button
[*]stone_button

可用的材质的值( material('material') ) - 该参数会改变方块被破坏和玩家行走在上面的声音并且预设一些属性(如破坏用的工具等):


材质
air
wood
rock
iron
organic
earth
water
lava
leaves
plants
sponge
wool
sand
glass
tnt
coral
ice
snow
clay
groud
dragon_egg
portal
cake
web
slime
honey
berry_bush
lantern


8 聊天事件


一个基础的例子:当有人在发送‘kubejs教程’时回复'请访问https://www.mcbbs.net/thread-1207772-1-1.html'

onEvent('player.chat', function (event) {
   // 检测如果聊天内容为“kubejs教程” 执行命令, 忽略大小写
if (event.message.trim().equalsIgnoreCase('kubejs教程')) {
   // 将事件推迟1刻,否则服务器信息将会显示在玩家信息之前
    event.server.scheduleInTicks(1, event.server, function (callback) {
      // 对每个人说以下内容,颜色为绿色。聊天信息为
      callback.data.tell(text.green('请访问h ttp s://www.mcbbs.net/thread-1207772-1-1.html'))
      // 下面这个设置了聊天信息为红色的
      callback.data.tell('),text.green('请访问h ttp s://www.mcbbs.net/thread-1207772-1-1.html')])
    })
}
})
另一个例子:监测到聊天信息时执行对应指令

onEvent('player.chat', function (event) {
if (event.message.startsWith('test')) {
    event.server.runCommandSilent('kick '+event.player.name+' test ')
    event.server.runCommandSilent(`say 已踢出玩家${event.player.name}`)
    event.cancel()//取消该事件,也就是说玩家的聊天信息不会显示
}
})
利用取消的这一特性,你甚至还可以做到"伪造"聊天

下面这个例子在检测到发送消息的玩家带有 rankexample 时在其聊天信息前加上rank标识

onEvent('player.chat',function (event){
    let input = event.message.trim();//获取聊天信息
    if(event.player.stages.has("rankexample")){
      event.server.tell(').bold(), `<${event.player.name}> ${input}`]);
      event.cancel();
    }
})https://m1.miaomc.cn/uploads/20220407_b9de677546c5d.png


注:更多关于gamestage的操作详见11-4章节,关于玩家、世界的操作详见第15章


9:服务端定时任务
管理服务器的好帮手
当服务器启动时,你可以为其计划执行各种内容。你可以使用 callback.reschedule(时间) 来在指定时间内重复这个事件(括号内留空即为使用之前设定的时间)

具体见例子吧。。。这里也说不清
9-1 服务端定时重启
前提是你的启动脚本得加上自动启动功能

events.listen('server.load', function (event) {
event.server.schedule(117 * MINUTE, event.server, function (callback) {
    callback.data.tell('服务器将在3分钟后重启!')
    //callback.reschedule(2 * MINUTE) //两分钟以后再通知一次
})

event.server.schedule(120 * MINUTE, event.server, function (callback) {
    callback.data.runCommand('/stop')//这里后边不用加重复事件,因为服务端重启时会自动加载本段代码
})
})
10:自定义窗口标题和logo
逼死其他mod?

你可以通过修改kubejs/config/client.properties中的title一项来修改游戏窗口标题
https://attachment.mcbbs.net/data/myattachment/forum/202106/29/143433so0fzw0vyvfof0vx.png

你可以将你想自定义的窗口logo放于kubejs/config/packicon.png,图片分辨率必须为16*16或其的整数倍
请注意!图片的格式必须为32-bit PNG 而不是Auto-detect/24-bit!
也就是属性内的“位深度”应为32(底下这个图有时间一定换个新的)
https://attachment.mcbbs.net/data/myattachment/forum/202106/29/144453vqkfsshdqm0s4zks.png

效果:https://attachment.mcbbs.net/data/myattachment/forum/202106/29/144535fvvc75v4oc384562.png
11
11.1 JEI信息修改


添加子类型:

onEvent('jei.subtypes', event => {
event.useNBT('example:item')
event.userNBTKey('example:item', 'type')
})
于JEI中隐藏物品/流体请注意,这不等价于删除物品!

onEvent('jei.hide.items', event => {
event.hide('example:ingredient')
})

onEvent('jei.hide.fluids', event => {
event.hide('example:fluid')
})
添加物品/流体

onEvent('jei.add.items', event => {
event.add(Item.of('example:item', {test: 123}))
})

onEvent('jei.add.fluids', event => {
event.add('example:fluid')
})
为物品添加信息

onEvent('jei.information', event => {
event.add('example:ingredient', ['第一行介绍', '第二行介绍'])
})
11.2 REI信息修改


隐藏物品

onEvent('rei.hide.items', event => {
event.hide('example:ingredient')
})
添加物品/流体

onEvent('rei.add.items', event => {
event.add(item.of('example:item').nbt({test: 123}))
})
为物品添加信息

onEvent('rei.information', event => {
event.add('example:ingredient', '标题', ['第一行', '第二行'])
})

11.3 FTB Quest相关修改


FTB Quest任务模组内置对KubeJS的支持,你可以通过KubeJS便捷地修改FTB Quest任务的进度/自定义任务等

FTB Quest自身提供了 ftbquests.completed.[任务ID] (任务完成事件), ftbquests.custom_reward.<奖励ID> (自定义任务奖励)和 ftbquests.custom_task.<任务ID> 三个事件可供监听,你也可以在其他事件中对玩家游戏进度进行修改。

本节将通过任务示例“村民挚友”来讲解如何通过KubeJS修改FTB Quest相关内容(推荐先阅读第15章内容)

https://m1.miaomc.cn/uploads/20220509_ed5c881d5875b.png

1、于其他事件中直接修改进度

例子1:当玩家攻击村民时重置其任务进度

ID为onEvent('entity.attack', event => {
    if(event.source && event.source.actual && event.source.actual.player && event.entity.type == 'minecraft:villager'){// 检测玩家攻击村民,防止为空报错
      event.source.actual.tell("你攻击了村民,村民挚友任务失败!");
      event.source.actual.data.ftbquests.changeProgress("4425171B4650EB54", event => {
            event.reset = true;// 重设ID为4425171B4650EB54的任务的进度
      })
    }
})
// 另一种写法
onEvent('entity.attack', event => {
    if(event.source.actual.player!=null && event.entity.type == 'minecraft:villager'){
      event.source.actual.tell("你攻击了村民,村民挚友任务失败!");
      event.source.actual.data.ftbquests.reset("4425171B4650EB54的");// 重设ID为4425171B4650EB54的任务的进度
    }
})
例子2:玩家攻击掠夺兽时添加进度

onEvent('entity.death', event => {
      if(event.source && event.source.actual && event.source.actual.player && event.entity.type == 'minecraft:ravager'){
            event.source.actual.tell("你击杀了劫掠受,等价于击杀三个唤魔者!");
            event.source.actual.data.ftbquests.addProgress("6B2089D4683E7907", 3);// 为ID为6B2089D4683E7907的任务添加进度值3
      }
})
2、自定义奖励

例子:玩家完成村民挚友任务时给予村庄英雄效果

onEvent('ftbquests.custom_reward.4883F7BD04E2C597', event => {// 设置ID为4883F7BD04E2C597的自定义奖励
    // 给予玩家药水效果奖励(村庄英雄)
    event.player.potionEffects.add('minecraft:hero_of_the_village', 120000, 5, false, false);
})
3、自定义任务

例子:检测玩家是否完成进度minecraft:adventure/trade

onEvent('ftbquests.custom_task.76AA10188A48B51F', event => {// 设置ID为76AA10188A48B51F的自定义任务
    event.checkTimer = 1;// 定义多久检测一次
    event.maxProgress = 1;// 定义最大进度
    event.check = (task, player) => {
      if (player.isAdvancementDone("minecraft:adventure/trade")) {// 检测进度是否完成
            player.tell("done");
            task.progress++;// 添加进度
      }
    }
})
4、监听任务完成事件

onEvent('ftbquests.completed.4425171B4650EB54', event => {// 检测ID为4425171B4650EB54的任务是否完成
    console.log("村民挚友任务已被完成");
})
注:你可以通过右击任务图标并点击复制ID来获取指定任务/奖励的ID

https://m1.miaomc.cn/uploads/20220509_5f8ec1de2bc00.png

附本节用到的FTB Quest配置文件:https://wwu.lanzouw.com/iTone04k5arc



11.4 KubeJs内置游戏阶段 (类似于GameStage)



功能函数返回值
禁用一个阶段状态的更新.addNoUpdate('阶段名')布尔型
启用一个阶段状态的更新.removeNoUpdate('阶段名')布尔型
玩家是否具有阶段.has('阶段名')布尔型
为玩家添加阶段.add('阶段名')布尔型
为玩家移除阶段.remove('阶段名')布尔型
为玩家设置阶段.set('阶段名', 布尔值)布尔型
切换阶段状态.toggle('阶段名')布尔型
同步阶段.sync()void
替换阶段//该功能存疑replace('阶段名')void

通常以上内容都接在event.player.stages后,例如event.player.stages.add('XPlusUser')

下面是一个实例

onEvent('player.logged_in',event=>{
    let username = event.player.name
    if (!event.player.stages.has('0') && username == "Wudji_Notfound") {
      //0代表离线,1代表在线
      event.player.stages.add('0');
      event.player.stages.remove('1');
      event.server.runCommand(`say 无敌鸡加入了游戏!`);
    }
})

onEvent('player.logged_out',event=>{
    let username = event.player.name
    if (!event.player.stages.has('1') && username == "Wudji_Notfound") {
      event.player.stages.add('1');   
      event.player.stages.remove('0');
      event.server.runCommand(`say 无敌鸡离开了游戏!`);
    }
})
你还可以将游戏阶段用于任务设置,详见本教程后半部分

11-5:玩家开始物品
当玩家在第一次加入游戏时给与物品
// 监听玩家登录事件 player.logged_in
onEvent('player.logged_in', event => {
// 检测玩家是否有starting_items阶段
if (!event.player.stages.has('starting_items')) {
    // 没有则添加该阶段
    event.player.stages.add('starting_items')
    // 给予玩家初始物品
    event.player.give('minecraft:stone_sword')
    event.player.give(Item.of('minecraft:stone_pickaxe', "{Damage: 10}"))
    event.player.give('30x minecraft:apple')
}
})

12 KubeJS 附属mod介绍


本部分主要介绍官方针对一些热门mod提供的附属mod

12.1 KubeJS UI

本部分只提供基础的修改教程(因为官方wiki上也没啥,也找不到相关第三方教程。。。
onEvent('ui.main_menu', event => {
event.replace(ui => {
    ui.tilingBackground('kubejsui:textures/example_background.png', 256)//设置背景
    ui.minecraftLogo(30)//设置logo

    ui.button(b => {//新建按钮
      b.name = 'Test'
      b.x = 10
      b.y = 10
      b.action = 'minecraft:singleplayer'
    })

    ui.button(b => {
      b.name = 'Test but in bottom right corner'
      b.x = ui.width - b.width - 10
      b.y = ui.height - b.height - 10
      b.action = 'https://feed-the-beast.com/'
    })

    ui.label(l => {//新建 lable
      l.name = text.yellow('FTB Stranded')
      l.x = 2
      l.y = ui.height - 12
      l.action = 'https://feed-the-beast.com/'
    })

    ui.image(i => {//新建图片
      i.x = (ui.width - 40) / 2
      i.y = (ui.height - 30) / 2
      i.width = 40
      i.height = 30
      i.action = 'https://feed-the-beast.com/'
    })

    ui.label(l => {
      l.name = text.aqua('Large label')
      l.x = 100
      l.y = ui.height - 20
      l.height = 15
      l.shadow = true // 字体是否带阴影
    })
})
})
/*
    常用的值:
    ui.height<=> 页面高度
    ui.width   <=> 页面宽度
    x.action   <=> 点击时执行的操作
    x.name      <=> 该项的显示名称
*/效果:
https://m1.miaomc.cn/uploads/20220112_d97e768a1fe60.png

12.2 KubeJS Blood Magic


支持的格式:
- 祭坛
event.recipes.bloodmagic.altar(output, input)
event.recipes.bloodmagic.altar(output, input).upgradeLevel(int).altarSyphon(int).consumptionRate(int).drainRate(int)
- 炼金矩阵
event.recipes.bloodmagic.array(output, baseInput, addedInput)
event.recipes.bloodmagic.array(output, baseInput, addedInput).texture(string)
- 狱火熔炉
event.recipes.bloodmagic.soulforge(output, )
event.recipes.bloodmagic.soulforge(output, ).minimumDrain(double).drain(double)
- 奥术粉灰
event.recipes.bloodmagic.arc(output, input, tool)
event.recipes.bloodmagic.arc(output, input, tool, )
event.recipes.bloodmagic.arc(output, input, tool, ).consumeIngredient(boolean).outputFluid(fluid)
- 炼金术桌
event.recipes.bloodmagic.alchemytable(output, )
event.recipes.bloodmagic.alchemytable(output, ).syphon(int).ticks(int).upgradeLevel(int)


例:
onEvent('recipes', event => {
const { altar, array, soulforge, arc, alchemytable } = event.recipes.bloodmagic
altar('minecraft:carrot', 'minecraft:apple')
array('minecraft:spruce_planks', 'minecraft:oak_planks', 'minecraft:birch_planks')
soulforge('minecraft:stone', ['minecraft:gold_ore', 'minecraft:diamond_ore', 'minecraft:iron_ore']).drain(1.0)
arc('minecraft:netherite_ingot', 'minecraft:iron_ingot', 'minecraft:iron_pickaxe', )
alchemytable('minecraft:gold_ingot', ['minecraft:iron_ingot', 'minecraft:iron_ingot', 'minecraft:iron_ingot', 'minecraft:iron_ingot']).upgradeLevel(2)
})
12.3 KubeJS Create



支持的配方   格式说明
转化event.recipes.createConversion(output[], input[])如将竖直十字齿轮箱变为十字齿轮箱
粉碎轮event.recipes.createCrushing(output[], input[])输入和输出不一定为数组,输入可以为ingredients或流体①,输出可以为物品或流体
动力锯event.recipes.createCutting(output[], input[])-
石磨event.recipes.createMilling(output[], input[])-
工作盆event.recipes.createBasin(output[], input[])-
动力搅拌器event.recipes.createMixing(output[], input[])在其后可加 .heated() 和.superheated() (是否加热及其程度(加热和高温))
装配event.recipes.createCompacting(output[], input[])在其后可加 .heated() 和.superheated() (是否加热及其程度(加热和高温))
动力辊压机event.recipes.createPressing(output[], input[])-
砂纸打磨event.recipes.createSandpaperPolishing(output[], input[])-
洗涤event.recipes.createSplashing(output[], input[])-
机械手event.recipes.createDeploying(output[], input[])-
注液event.recipes.createFilling(output[], input[])-
倒出液体event.recipes.createEmptying(output[], input[])-
动力合成event.recipes.createMechanicalCrafting(output, pattern[], {合成键值})详见下方示例
装配event.recipes.createSequencedAssembly(output[], input, sequence[]).transitionalItem(item).loops(int)详见下方示例及说明

①:以水为例,流体可以为以下格式:Fluid.of('minecraft:water', 1000) 和 {fluidTag: 'some:fluid_tag', amount: 1000}

②:带有合成时间的项目可以在配方后加.processingTime(时间(整形))来修改其合成时间,在物品后加.withChance(2.0)可以修改其产出概率


示例:

例子1:红石矿石 -> 2个圆石 + 一个红石粉 + 50%概率一个红石粉
https://attachment.mcbbs.net/data/myattachment/forum/202201/12/195245qui3wu3gqrbiqgir.png
event.recipes.createCrushing([
'2x minecraft:cobblestone',
'minecraft:redstone',
Item.of('minecraft:redstone').withChance(0.5)//概率的表示方法
], 'minecraft:redstone_ore')
例子2:3个萤石粉 + 3个黑曜石粉末 + 磨制玫瑰石英 --高温--> 异彩化合物
https://attachment.mcbbs.net/data/myattachment/forum/202201/12/195300u5088hmd00bzliwz.png
event.recipes.createMixing('create:chromatic_compound', [
'#forge:dusts/glowstone',
'#forge:dusts/glowstone',
'#forge:dusts/glowstone',
'create:powdered_obsidian',
'create:powdered_obsidian',
'create:powdered_obsidian',
'create:polished_rose_quartz'
]).superheated()
例子3:使用注液器制作烈焰蛋糕
https://attachment.mcbbs.net/data/myattachment/forum/202201/12/195315pzl9imdqg3kkzppc.png
event.recipes.createFilling('create:blaze_cake', [
'create:blaze_cake_base',
Fluid.of('minecraft:lava', 250)
])
例子4:分液:蜂蜜瓶 -> 玻璃瓶 + 250单位蜂蜜
https://attachment.mcbbs.net/data/myattachment/forum/202201/12/195448nz63uiiu4b9j9ii7.png
event.recipes.createEmptying([
'minecraft:glass_bottle',
Fluid.of('create:honey', 250)
], 'minecraft:honey_bottle')
例子5:动力合成,基本和标准配方格式一样
https://attachment.mcbbs.net/data/myattachment/forum/202201/12/195416s5tis3tdz8zczt3t.png
event.recipes.createMechanicalCrafting('minecraft:piston', [
'CCCCC',
'CPIPC',
'CPRPC'
], {
C: '#forge:cobblestone',
P: '#minecraft:planks',
R: '#forge:dusts/redstone',
I: '#forge:ingots/iron'
})
例子6:序列组装
https://attachment.mcbbs.net/data/myattachment/forum/202201/12/195515zq7fof7blqyq7ryy.png
event.recipes.createSequencedAssembly([
Item.of('6x create:large_cogwheel').withChance(32.0),
Item.of('create:brass_ingot').withChance(2.0),
'minecraft:andesite',
'create:cogwheel',
'minecraft:stick',
'minecraft:iron_nugget'
], 'create:brass_ingot', [
event.recipes.createDeploying('create:incomplete_large_cogwheel', ['create:incomplete_large_cogwheel', '#minecraft:planks']),
event.recipes.createDeploying('create:incomplete_large_cogwheel', ['create:incomplete_large_cogwheel', '#minecraft:wooden_buttons']),
event.recipes.createCutting('create:incomplete_large_cogwheel', 'create:incomplete_large_cogwheel').processingTime(50)
]).transitionalItem('create:incomplete_large_cogwheel').loops(6)
对这部分代码的解释

event.recipes.createSequencedAssembly([
   //成品:
   Item.of('6x create:large_cogwheel').withChance(32.0),
   //随机废料:
   Item.of('create:brass_ingot').withChance(2.0),
   'minecraft:andesite',
   'create:cogwheel',
   'minecraft:stick',
   'minecraft:iron_nugget'
    ],
    //输入物品:
    'create:brass_ingot',
    [
    //每步的配方
    /*
    其中create:incomplete_large_cogwheel作为合成的半成品(即.transitionalItem('半成品注册名'))
    .loops(6)代表合成循环次数
    .transitionalItem和.loops是可选的
    */
      event.recipes.createDeploying('create:incomplete_large_cogwheel', ['create:incomplete_large_cogwheel', '#minecraft:planks']),//第一步
   event.recipes.createDeploying('create:incomplete_large_cogwheel', ['create:incomplete_large_cogwheel', '#minecraft:wooden_buttons']),//第二步
   event.recipes.createCutting('create:incomplete_large_cogwheel', 'create:incomplete_large_cogwheel').processingTime(50)//第三步
]).transitionalItem('create:incomplete_large_cogwheel').loops(6)
即一般格式为:

event.recipes.createSequencedAssembly(输出物品组(数组)[], 输入物品, 序列合成步骤(数组)[]).transitionalItem(半成品注册名).loops(循环次数(整形))

另外,如果你想要使用你自己注册的半成品,你需要在Startup 脚本中这样填写:

onEvent('item.registry', event => {
// 该物品的纹理应位于 kubejs/assets/kubejs/textures/item/my_part.png
event.create('my_part').type('create:sequenced_assembly').displayName('My Part')
})
12-4 placeholder
12-5 placeholder
12-6 placeholder

12.7 便捷战利品表修改(LootJS Forge)

一、概要

(本节部分内容参考自 LootJS Wiki)

LootJS是一个KubeJS的附属mod,能够让你更加便捷地修改战利品表,掉落战利品时执行事件等

mod链接:GithubCurseforge,许可:LGPL-3.0

由于战利品表是在服务端侧被处理的,所以显然你应该把它放到kubejs\server_scripts文件夹下

你可以通过/reload命令来重载LootJS的修改内容

推荐先阅读15章来了解物品的效果内容和实体,玩家修改内容

二、LootJS是如何工作的

下面这张图展示了游戏掉落物品的过程

https://m1.miaomc.cn/uploads/20220424_31c09db2a2078.png

三、基本格式

LootJS添加了一个KubeJS事件lootjs来修改战利品表,也就是这样:

onEvent("lootjs", (event) => {
    // 在此处编写代码
});
下面是一个基础的例子,使苦力怕有30%的概率掉落烈焰棒

onEvent("lootjs", (event) => {
    // 你把底下这个写成一行也行,除了可能会有点难看以外(
    event
      .addEntityLootModifier("minecraft:creeper")// 获取LootActionsBuilder
      .randomChance(0.3) // 战利品表条件:添加掉落概率
      .thenAdd("minecraft:blaze_rod");// 战利品表事件:掉落物品
});
每一个战利品表修改都应至少有一个战利品表事件

1、常用函数

函数enableLogging()会将修改过的战利品表打印到控制台便于排查问题

函数.disableLootModification(战利品表)会锁定指定战利品表以防修改

onEvent("lootjs", (event) => {
    // 启用战利品表输出
    event.enableLogging();

    // 禁用树叶的战利品表(通过正则表达式)
    event.disableLootModification(/.*:blocks\/.*_leaves/);

    // 禁用单个战利品表
    event.disableLootModification("minecraft:entities/bat");
});
LootJS提供了一些返回LootActionsBuilder对象的函数,这使修改掉落条件和掉落物成为可能。


函数功能
addBlockLootModifier(命名空间)为方块添加新的战利品表修饰器
addEntityLootModifier(命名空间)为实体添加新的战利品表修饰器
addLootTableModifier(命名空间ID)为给定战利品表添加新的修饰器
addLootTypeModifier(战利品表类型)为给定战利品表类型添加新的修饰器
getGlobalModifiers()返回包含所有战利品表修饰器的列表
removeGlobalModifier(值)移除给定的战利品表修饰器

以下是可用的战利品表类型(LootJS)的值

LootType.UNKNOWN // 未知
LootType.BLOCK // 方块
LootType.ENTITY // 实体
LootType.CHEST // 战利品箱
LootType.FISHING // 钓鱼
LootType.GIFT // 礼物(村民等)
:你可以使用形如@modid来移除指定mod的全局战利品表修改,使用形如examplemod:example_loot_change的战利品表ID来移除指定战利品表,此外,该函数仅对全局战利品表生效,不能阻止mod直接将其物品添加到战利品表中

2、掉落物修改(战利品表事件)

战利品表事件用于修改战利品表的掉落物或为其添加不同效果。每一个战利品表修饰器都至少包含一个战利品表事件

thenAdd(物品) 可以向当前战利品表修饰器中添加物品

onEvent("lootjs", (event) => {
    event
      .addBlockLootModifier("minecraft:gravel")
      .thenAdd("minecraft:flint");// 返回方块的战利品表修饰器并添加单个物品
});
thenRemove(物品) 将移除战利品表在所有符合给定条件的物品

onEvent("lootjs", (event) => {
    event
      .addEntityLootModifier("minecraft:creeper")
      .thenRemove('#forge:gunpowder')// 从苦力怕的战利品表中移除所有#forge:gunpowder
});
thenReplace(被替换物品, 替换物品) 将战利品表中符号条件的物品全部替换为给定物品

onEvent("lootjs", (event) => {
    event
      .addEntityLootModifier("minecraft:creeper")
      .thenReplace('#forge:gunpowder', 'minecraft:diamond')// 现在这只苦力怕会掉钻石了
});
thenModify(物品, 回调) 对于当前随机池的所有符合条件的物品,一个回调将被调用。LootJS会把该物品传给回调来修改它并返回该物品。记住一定要有返回语句。

onEvent("lootjs", (event) => {
    event
      .addLootTypeModifier(LootType.ENTITY)
      .weatherCheck({
            raining: true,
      })
      .thenModify(Ingredient.getAll(), (itemStack) => {
            return itemStack.withCount(itemStack.getCount() * 2);// 返回语句
      });
});
在这个例子中,下雨时实体的掉落物品数量将翻倍

thenExplode(范围, 是否损坏方块, 是否引火) 可以在掉落战利品时生成一个爆炸,而掉落物不会被炸毁。范围可以是任何数字而后两个参数(是否损坏方块, 是否引火)应为布尔值

onEvent("lootjs", (event) => {
    event
      .addBlockLootModifier("minecraft:gravel")
      .thenExplode(1, false, false);// ExplosionJS详见15.3
});
thenLightningStrike(是否破坏方块) 可以在掉落战利品时生成一个闪电。同样,物品不会被破坏。

onEvent("lootjs", (event) => {
    event
      .addBlockLootModifier("minecraft:gravel")
      .thenLightningStrike(false);
});
thenApply(回调) 可以在掉落战利品时进行回调,非常有意思的一个功能。

onEvent("lootjs", (event) => {
    event
      .addBlockLootModifier("minecraft:gravel")
      .thenApply((context) => {
            // 有关玩家(Player),实体(EntityJS),方块(BlockContainerJS)等还请查看15章。
            // 回调函数
            // if(context.getPlayer()!=null){context.getPlayer().tell("测试")}
      });
});
可用函数(推荐先阅读15章)见文末LootContextJS

3、战利品表掉落条件

除了战利品表事件,你还可以为战利品表掉落添加条件限制

值得注意的是,这些条件仅对新增的战利品修饰器生效

matchLoot(物品, 可选 是否全部比对) 可以在符合条件的物品掉落时执行修改。是否全部比对的默认值为false,当为true时,当前战利品池中所有的战利品必须符合条件才能执行修改

onEvent("lootjs", (event) => {
    event
      .addEntityLootModifier("minecraft:cow")
      .matchLoot("minecraft:leather")
      .thenAdd("minecraft:carrot");// 在牛掉落皮革时再多掉落一个胡萝卜
});
matchMainHand(IngredientJS 物品) 当玩家主手手持指定物品时才执行修改。

onEvent("lootjs", (event) => {
    event
      .addBlockLootModifier("#forge:ores")
      .matchMainHand(Item.of("minecraft:netherite_pickaxe").ignoreNBT())
      .thenAdd("minecraft:gravel");// 手持下界合金斧(忽略nbt)时添加掉落物砂砾
});
matchOffHand(IngredientJS 物品) 当玩家副手手持指定物品时才执行修改。

onEvent("lootjs", (event) => {
    event
      .addBlockLootModifier("#forge:ores")
      .matchOffHand(Item.of("minecraft:netherite_pickaxe").ignoreNBT())
      .thenAdd("minecraft:gravel");
});
survivesExplosion() 可以防止修改的掉落物被爆炸破坏

onEvent("lootjs", (event) => {
    event
      .addBlockLootModifier("minecraft:gravel")
      .survivesExplosion()
      .thenAdd("minecraft:gravel");
});
timeCheck(period, min, max)*和 timeCheck(min, max) 使修改内容在符合指定游戏刻条件下才能生效

*当前游戏时间除以period值所得的余数来与value匹配。(若period被设置为100,value被设置为1,则时间为1/101/201……时通过),min和max则为数值范围(开区间)

onEvent("lootjs", (event) => {
    event
      .addBlockLootModifier("minecraft:gravel")
      .timeCheck(24000, 0, 9000) // 只有白天才会掉落
      .thenAdd("minecraft:diamond");
});
weatherCheck(值) 可以为战利品表添加天气条件。其中值的格式:

{
    raining: true, // 雨天天气
    thundering: true // 雷雨天气
}
如果你只使用一个值,则另一个会被忽略

onEvent("lootjs", (event) => {
    event
      .addBlockLootModifier("minecraft:gravel")
      .weatherCheck({
            raining: true, // 只使用一个值
      })
      .thenAdd("minecraft:diamond");
});
randomChance(浮点型 随机值) 可以概率执行掉落物品修改

onEvent("lootjs", (event) => {
    event
      .addBlockLootModifier("minecraft:gravel")
      .randomChance(0.3) // 30%
      .thenAdd("minecraft:diamond");
});
randomChanceWithLooting(浮点型 概率, 整形 附魔等级) 可以根据抢夺等级概率调整掉落概率

onEvent("lootjs", (event) => {
    event
      .addBlockLootModifier("minecraft:gravel")
      .randomChanceWithLooting(0.3, 2) // 30%
      .thenAdd("minecraft:diamond");
});
randomChanceWithEnchantment(字符串 附魔ID, [概率]) 可以根据给定附魔的等级调整概率

onEvent("lootjs", (event) => {
    event
      .addBlockLootModifier("minecraft:gravel")
      .randomChanceWithEnchantment("minecraft:looting", )
      .thenAdd("minecraft:diamond");

    /*
      :
          没有抢夺附魔时掉落概率为 0%
          抢夺 I 时掉落概率为    10%
          抢夺 II 时掉落概率为   50%
          抢夺 III 时掉落概率为100%

    */
});
biome(...字符串 群系ID) 当所有群系均符合时执行修改。你可以传入群系标签(形如#cold)来限定群系必须为寒冷群系(见6 自定义世界生成)

onEvent("lootjs", (event) => {
    event
      .addBlockLootModifier("minecraft:gravel")
      .biome("minecraft:jungle")
      .thenAdd("minecraft:diamond");// 丛林群系才会掉落物品
});
anyBiome(...字符串 群系ID) 当任意群系符合时即执行修改。你可以传入群系标签(形如#cold)来限定群系必须为寒冷群系

onEvent("lootjs", (event) => {
    event
      .addBlockLootModifier("minecraft:gravel")
      .anyBiome("minecraft:jungle", "minecraft:ocean")
      .thenAdd("minecraft:diamond");// 在丛林或海洋时掉落
});
anyDimension(...字符串 维度ID) 当任意维度符合时即执行修改。

onEvent("lootjs", (event) => {
    event
      .addBlockLootModifier("minecraft:gravel")
      .anyDimension("minecraft:nether")
      .thenAdd("minecraft:diamond");
});
anyStructure([字符串 结构ID], 布尔值 是否精确匹配) 可以判断战利品掉落于指定结构中。当精确匹配的值为true时,玩家在结构的建筑内才生效;为false时则在结构范围内即可

onEvent("lootjs", (event) => {
    event
      .addBlockLootModifier("minecraft:gravel")
      .anyStructure(["minecraft:stronghold", "minecraft:village"], false)
      .thenAdd("minecraft:diamond");// 在给定结构范围内即掉落
});
lightLevel(min, max) 在给定光照强度范围内才执行修改(开区间)

onEvent("lootjs", (event) => {
    event
      .addBlockLootModifier("minecraft:gravel")
      .lightLevel(0, 15)
      .thenAdd("minecraft:diamond");
});
killedByPlayer() 只有被玩家杀死时才执行修改

onEvent("lootjs", (event) => {
    event
      .addEntityLootModifier("minecraft:iron_golem")
      .thenRemove("#forge:ingots/iron");// 先移除再添加
    event
      .addEntityLootModifier("minecraft:iron_golem")
      .killedByPlayer()
      .thenAdd(Item.of("minecraft:iron_ingot").withCount(2));// 只有玩家杀死铁傀儡时才掉落铁锭,干掉刷铁机(
});
matchEntity(callback) 可以匹配触发战利品表的实体(如死亡的 / 打开箱子 / 破坏方块的),当条件符合时执行修改

onEvent("lootjs", (event) => {
    event
      .addEntityLootModifier("minecraft:creeper")
      .matchEntity((entity) => {
            // EntityPredicateBuilderJS(见文末)
      })
      .thenAdd("minecraft:diamond");
});
matchDirectKiller(callback) 可以匹配直接击杀目标的实体(例如弓箭实体而不是发射的实体),当条件符合时执行修改

onEvent("lootjs", (event) => {
    event
      .addEntityLootModifier("minecraft:creeper")
      .matchKiller((entity) => {
            // EntityPredicateBuilderJS(见文末)
      })
      .thenAdd("minecraft:diamond");
});
matchPlayer(callback) 可以匹配玩家,当条件符合时执行修改。当玩家杀死了另一个玩家时,将以击杀者作为匹配对象

onEvent("lootjs", (event) => {
    event
      .addEntityLootModifier("minecraft:creeper")
      .matchPlayer((player) => {
            // EntityPredicateBuilderJS(见文末)
      })
      .thenAdd("minecraft:diamond");
});
matchDamageSource(callback) 可以匹配伤害类型,当条件符合时执行修改

onEvent("lootjs", (event) => {
    event
      .addEntityLootModifier("minecraft:creeper")
      .matchDamageSource((source) => {
            // DamageSourcePredicateBuilderJS(见文末)
      })
      .thenAdd("minecraft:diamond");
});
distanceToKiller(IntervalJS 距离) 可以检测实体掉落与击杀者之间的距离

onEvent("lootjs", (event) => {
    event
      .addEntityLootModifier("minecraft:creeper")
      .distanceToKiller(Interval.min(25))/*IntervalJS (见文末)*/
      .thenAdd("minecraft:diamond");
});
hasAnyStage(...字符串 Gamestage) 可以检测玩家是否具有指定Gamestage,当具有任意一个时执行修改

onEvent("lootjs", (event) => {
    event
      .addEntityLootModifier("minecraft:pig")
      .hasAnyStage("stoneage")
      .thenAdd("minecraft:coal");// 玩家具有stoneage时掉落一个煤炭
});
playerPredicate(callback),entityPredicate(callback),killerPredicate(callback),directKillerPredicate(callback) 可以自定义回调函数来匹配玩家,实体,击杀者,直接击杀者,当条件符合时执行修改。注意:该回调必须返回true或false

onEvent("lootjs", (event) => {
    event
      .addEntityLootModifier("minecraft:pig")
      .playerPredicate((player) => player.stages.has("stoneage"))
      .thenAdd("minecraft:emerald");
});

onEvent("lootjs", (event) => {
    event
      .addEntityLootModifier("minecraft:pig")
      .entityPredicate((entity) => entity.type == "minecraft:pig")
      .thenAdd("minecraft:diamond");
});

onEvent("lootjs", (event) => {
    event
      .addEntityLootModifier("minecraft:pig")
      .killerPredicate((entity) => entity.type == "minecraft:pig")
      .thenAdd("minecraft:feather");
});

onEvent("lootjs", (event) => {
    event
      .addEntityLootModifier("minecraft:pig")
      .directKillerPredicate((entity) => entity.type == "minecraft:pig")
      .thenAdd("minecraft:stone");
});
not(callback) 可以反转战利品表条件

onEvent("lootjs", (event) => {
    event
      .addEntityLootModifier("minecraft:creeper")
      .not((n) => {
            n.biome("minecraft:jungle")
      })
      .thenAdd("minecraft:diamond");// 当群系不是丛林时掉落钻石
});
or(callback) 可以同时添加多个条件,当一个条件为真时即通过

onEvent("lootjs", (event) => {
    event
      .addEntityLootModifier("minecraft:creeper")
      .or((or) => {
            or.biome("minecraft:jungle").anyDimension("minecraft:nether");
      })
      .thenAdd("minecraft:diamond");// 群系为丛林**或**维度为下界时掉落钻石
});
and(callback) 可以同时添加多个条件,当所有条件为真时即通过

onEvent("lootjs", (event) => {
    event
      .addEntityLootModifier("minecraft:creeper")
      .and((and) => {
            and.biome("minecraft:jungle").distanceToKiller(Interval.min(25));
      })
      .thenAdd("minecraft:diamond");// 群系为丛林**且**击杀者距离大于25时掉落钻石
});
4、IntervalJS,EntityPredicateBuilderJS,DamageSourcePredicateBuilderJS和LootContextJS

IntervalJS可以用来表示两个数字之间的范围。注意:以下所有范围均为开区间


格式描述返回值
Interval.between(min, max)返回最小最大值之间的范围 (min, max)IntervalJS
Interval.min(min)返回只限制了最小值的范围 (min,+∞)IntervalJS
Interval.max(max)返回只限制了最大值的范围 (-∞,max)IntervalJS
函数描述返回值
matches(值)检测值是否在范围内布尔值
matchesSqr(值)检测值是否在范围内布尔值

EntityPredicateBuilderJS 可以用条件限制实体以判断是否执行修改,以下返回值均为EntityPredicateBuilderJS


函数描述
anyType(...类型)判断实体是否有给定标签(输入示例:#skeletons)
isOnFire(布尔值 条件真假)判断实体是否着火
isCrouching(布尔值 条件真假)判断实体是否正在潜行
isSprinting(布尔值 条件真假)判断实体是否正在疾跑
isSwimming(布尔值 条件真假)判断实体是否正在游泳
isBaby(布尔值 条件真假)判断实体是否为幼年状态
isInWater(布尔值 条件真假)判断实体是否在水里
isMonster(布尔值 条件真假)判断实体是否为敌对生物
isCreature(布尔值 条件真假)判断实体是否为友好生物
isOnGround(布尔值 条件真假)判断实体是否在地上
isUndeadMob(布尔值 条件真假)判断实体是否为亡灵生物
isArthropodMob(布尔值 条件真假)判断实体是否为节肢动物
isWaterMob(布尔值 条件真假)判断实体是否为水生动物
isIllegarMob(布尔值 条件真假)判断实体是否为掠夺生物
hasEffect(字符串 效果ID, 整形 等级) 和 hasEffect(字符串 效果ID)判断实体是否具有给定效果
nbt(json)判断实体是否具有指定nbt
matchMount(回调)判断实体所骑乘的实体
matchTargetedEntity(回调)判断实体的目标实体
matchSlot(整形 栏位编号, IngredientJS 物品)判断实体给定物品栏栏位的物品

:提供EntityPredicateBuilderJS

例子:

onEvent("lootjs", (event) => {
    event
      .addLootTypeModifier()
      .matchEntity((entity) => {
            entity.anyType("#skeletons");
            entity.matchMount((mount) => {
                mount.anyType("minecraft:spider");
            });
      })
      .thenAdd("minecraft:magma_cream");// 当骷髅类实体骑乘蜘蛛时掉落一个演讲稿
});
DamageSourcePredicateBuilderJS 用条件来限制伤害类型以判断是否执行修改


函数描述
anyType(...类型)判断伤害是否符合类型
isProjectile(布尔值 条件真假)判断伤害是否为投掷物
isExplosion(布尔值 条件真假)判断伤害是否为爆炸
doesBypassArmor(布尔值 条件真假)判断伤害是否无视盔甲
doesBypassInvulnerability(布尔值 条件真假)判断伤害是否无视隐身效果
doesBypassMagic(布尔值 条件真假)判断伤害是否为饥饿引起
isFire(布尔值 条件真假)判断伤害是否为火
isMagic(布尔值 条件真假)判断伤害是否为饥饿
isLightning(布尔值 条件真假)判断伤害是否为闪电
matchDirectEntity(回调)判断直接造成伤害的实体
matchSourceEntity(回调)判断造成伤害的实体源

:提供DamageSourcePredicateBuilderJS


函数描述返回值
getType()返回战利品类型LootTypes
getPosition()返回掉落位置Vector3d
getEntity()返回具有当前战利品表的实体(可能为null)EntityJS
getKillerEntity()返回杀死对象的实体(可能为null)EntityJS
getPlayer()返回杀死对象的玩家(可能为null)PlayerJS
getDamageSource()返回伤害类型(可能为null)DamageSourceJS
getTool()返回触发战利品表使用的工具(可能为null)ItemStackJS
getDestroyedBlock()返回被破坏的方块(可能为null)BlockContainerJS
isExploded()返回战利品表是否被爆炸触发布尔值
getExplosionRadius()返回触发的爆炸的半径整形
getLevel()返回WorldJSWorldJS
getServer()返回ServerJSServerJS
getLuck()返回玩家幸运值整形
getLooting()返回LootingModifier整形
getRandom()返回范围(?)Random
addLoot(ItemStackJS 物品)添加掉落物void
removeLoot(IngredientJS 物品)移除掉落物void
findLoot(IngredientJS 物品)返回掉落物列表List<ItemStackJS>
hasLoot(IngredientJS 物品)返回是否掉落指定物品布尔值
forEachLoot(Consumer<ItemStackJS> action)遍历每个物品并给予回调void

:见 本章 常用函数 部分

:为null的例子:骷髅射杀苦力怕

:如果不是则为0

:例如

const callback = (item) => {
    console.log(item);
};

5、例子

1、雨天实体掉落物翻倍

onEvent("lootjs", (event) => {
    event
      .addLootTypeModifier(LootType.ENTITY) // 修改实体战利品表
      .weatherCheck({
            raining: true,// 检测当前天气为雨天
      })
      .thenModify(Ingredient.getAll(), (itemStack) => {
            // 掉落物数量翻倍
            return itemStack.withCount(itemStack.getCount() * 2);
      });
});
2、检测玩家手持物品,当手持下界合金斧破坏带#forge:ores标签的方块时时额外掉落砂砾

onEvent("lootjs", (event) => {
    event
      .addBlockLootModifier("#forge:ores") // 使用方块tag匹配战利品表
      .matchEquip(EquipmentSlot.MAINHAND, Item.of("minecraft:netherite_pickaxe").ignoreNBT())
      .thenAdd("minecraft:gravel");
});
3、修改宝箱战利品表,概率掉落下界合金剑

onEvent("lootjs", (event) => {
    event
      .addLootTypeModifier(LootType.CHEST)
      .randomChance(0.05)
      .thenAdd("minecraft:netherite_sword");
});
4、修改宝箱战利品表,将所有钻石替换为下界合金

onEvent("lootjs", (event) => {
    event
      .addLootTypeModifier(LootType.CHEST)
      .thenReplace("minecraft:diamond","minecraft:netherite");
});
5、修改铁傀儡战利品表,使其只在村庄中被杀死时掉落铁锭

onEvent("lootjs", (event) => {
    event
      .addEntityLootModifier("minecraft:iron_golem")
      .not((callback)=>{
            callback.anyStructure(["minecraft:village"], false)
      })
      .thenRemove("minecraft:iron_ingot");// 反转条件修改掉落物
});

13:物品修改(添加物品和物品tooltip)
基础的物品修改

1、物品的添加
基本格式:

// 监听 item registry event
onEvent('item.registry', event => {
// 物品的材质应被放于 kubejs/assets/kubejs/textures/item/test_item.png (文件名:物品ID.png)
// 如果你想使用自定义的物品模型,你可以使用 Blockbench 制作然后把它放于 kubejs/assets/kubejs/models/item/test_item.json (文件名:物品ID.json)
event.create('test_item').displayName('Test Item')//必须的新建物品格式
})

下面是一个例子
onEvent('item.registry', event => {
event.create('another_apple').displayName('嘿化的苹果')
event.create('another_helmet').displayName('嘿化的头盔')
})

但是现在这两个物品除了能看看没有任何功能[:doge:]
你可以通过在.displayName(物品名称)后面加以下后缀来使其具有一些基本的功能
type('itemType')//物品类型
tier('itemTier')//物品等级
maxStackSize(size)//物品最大堆叠数
unstackable()//不可堆叠
maxDamage(damage)//最大耐久(应该是)
burnTime(ticks)//可以燃烧的时间,单位为刻
containerItem(item_id)//不知道是啥的东西
tool(type, level)//定义工具(类型, 等级)
miningSpeed(speed)//挖掘速度
attackDamage(damage)//攻击伤害
attackSpeed(speed)//攻击速度
rarity(rarity)//稀有度
glow(true/false)//是否发光
tooltip(text...)//物品tooltip 详见下文
group(group_id)//所属物品组的ID
color(index, colorHex)//颜色(索引, 颜色编码)
texture(customTexturePath)//自定义材质(后加材质目录)
parentModel(customParentModel)//继承模型
food((food) => {内容})//新建食物内容
其中,物品类型一项可以填写以下内容
basic //默认类型
sword
pickaxe
axe
shovel
hoe
helmet
chestplate
leggings
boots
物品等级一项可以填写以下内容
剑和工具:
      wood
      stone
      iron
      gold
      diamond
      netherite
护甲:
      leather
      chainmail
      iron
      gold
      diamond
      turtle
      netherite
以下是两个实例(含新建食物):


onEvent('item.registry', event => {
event.create('another_apple').displayName('嘿化的苹果').maxStackSize(8).food((food) => {food.hunger(8).saturation(0.5).effect('minecraft:nausea', 100, 5, 1.0).effect('minecraft:speed', 100, 5, 1.0)})
event.create('another_helmet').displayName('嘿化的头盔').type('helmet').tier('netherite')
})

效果展示:https://attachment.mcbbs.net/data/myattachment/forum/202106/29/093020hulc6odwk1q9ckpk.png
2、物品tooltip

以下为几个栗子,你可以根据以下代码进行修改来达到你想要的效果~
onEvent('item.tooltip', tooltip => {
// 批量添加tooltip
tooltip.add(['minecraft:diorite', 'minecraft:granite', 'minecraft:andesite'], '三 大 废 岩')
// 你可以使用除#tag以外的其他任何表述方法
tooltip.add(/refinedstorage:red_/, 'Can be any color')
// 你可以使用数组来新建多行tooltip. 你可以使用其他转义符来避开'这个符号
tooltip.add('thermal:latex_bucket', ["Not equivalent to Industrial Foregoing's Latex", 'Line 2'])

tooltip.addAdvanced('thermal:latex_bucket', (item, advanced, text) => {
    text.add(0/*index*/, Text.of('Hello')) // 当index为0时,这种表示方法 会替换掉物品名称. 如果你想要在第二行添加tooltip,index必须为1,以此类推.
})
})
14 方块和物品属性修改


KubeJs允许你修改一些已经存在的物品/方块的属性

1、物品属性修改

onEvent('item.modification', event => {
event.modify('minecraft:ender_pearl', item => {
    item.maxStackSize = 64
    item.fireResistant = true
})
})
该段示例脚本将末影珍珠每组最大数量调整为了64,并且不会被烧毁

以下是可以使用的属性(a = b):


[*]maxStackSize // 每组数量
[*]maxDamage // 最大耐久值
[*]burnTime // 燃烧时间(燃料)
[*]craftingReminder // 合成后剩余物品(字符串)
[*]fireResistant // 是否抗火
[*]rarity // 稀有度(如Rarity.COMMON)
[*]tier = tierOptions => { // 物品等级

[*]uses // 耐久
[*]speed // 挖掘速度
[*]attackDamageBonus // 攻击伤害
[*]level // 工具等级
[*]enchantmentValue // 附魔值
[*]repairIngredient // 铁砧修复需要使用的物品,格式如Ingredient.of('物品注册名')
[*]}
[*]foodProperties = food => { // 食物设置。注: 使用函数而不是 a = b

[*]hunger(int) // 饱食度
[*]saturation(float) // 饱和度
[*]meat(boolean) // 是否为肉食
[*]alwaysEdible(boolean) // 是否总是可食用(无论饱食度)
[*]fastToEat(boolean) // 是否能被快速吃掉
[*]effect(效果ID(字符串), 时间(整形), 效果等级(整形), 获得倍率(浮点型)) // 吃掉后给予的效果
[*]removeEffect(String effectId) // 吃掉后移除的效果
[*]}

2、方块属性修改

onEvent('block.modification', event => {
event.modify('minecraft:stone', block => {
    block.destroySpeed = 0.1
    block.hasCollision = false
})
})
该段示例脚本将石头的破坏速度调整为0.1, 并且使其无碰撞箱

以下是可使用的方法(a = b):


[*]material // 材质
[*]boolean hasCollision // 是否有碰撞箱(布尔型)
[*]destroySpeed // 破坏速度(浮点型)
[*]explosionResistance // 爆炸抗性(浮点型)
[*]randomlyTicking // 是否接受随机刻控制
[*]soundType // 声音类型
[*]friction // 摩擦(浮点型)
[*]speedFactor // 速度倍率
[*]jumpFactor // 跳跃倍率
[*]lightEmission // 发光强度
[*]requiredTool // 是否需要工具掉落



15 玩家事件,信息获取及操作 & 方块、物品、实体信息获取及操作 & 玩家交互


本章将可能用到的事件


事件类型
player.logged_inServer
player.logged_outServer
player.tickServer
player.data_from_server.Client
player.data_from_client.Server
player.chatServer
player.advancementServer
player.inventory.openedServer
player.inventory.closedServer
player.inventory.changedServer
player.chest.openedServer
player.chest.closedServer
entity.deathServer
entity.attackServer
entity.dropsServer
entity.check_spawnServer
entity.spawnedServer
block.right_clickServer
block.left_clickServer
block.placeServer
block.breakServer
block.dropsServer
item.right_clickServer
item.right_click_emptyServer
item.left_clickServer
item.entity_interactServer
item.pickupServer
item.tossServer
item.craftedServer
item.smeltedServer



15.1 玩家&实体事件,信息获取及操作


一、信息获取

以下内容会返回玩家/实体的信息


格式功能返回值类型
inventory返回玩家的库存(仅适用于玩家)InventoryJS
block返回涉及到的方块BlockContainerJS
selectedSlot返回当前玩家选中的格子(仅适用于玩家)整形
mouseItem返回当前玩家鼠标选取的物品(仅适用于玩家)ItemStackJS
creativeMode返回当前玩家是否为创造模式(仅适用于玩家)布尔值
spectator返回当前玩家是否为观察者模式(仅适用于玩家)布尔值
stats返回玩家统计数据(仅适用于玩家)PlayerStatsJS
foodLevel返回玩家饱食度等级(仅适用于玩家)整形
xp返回玩家经验值(仅适用于玩家)整形
xplevel返回玩家经验等级(仅适用于玩家)整形
miningBlock返回玩家是否正在破坏方块(仅适用于玩家)布尔值
airSupply返回玩家当前氧气供应值(仅适用于玩家)整形
maxAirSupply返回玩家最大氧气供应值(仅适用于玩家)整形
stages返回玩家游戏阶段(仅适用于玩家)Stages
name返回玩家名称Text
displayName返回玩家显示名称Text
alive返回游戏中玩家是否存活布尔值
living返回是否处于活跃状态布尔值
crouching返回玩家/实体是否正在潜行布尔值
sprinting返回玩家/实体是否正在疾跑布尔值
swimming返回玩家/实体是否正在游泳布尔值
glowing返回玩家/实体是否正在发光布尔值
invisible返回玩家/实体是否隐形布尔值
invulnerable返回玩家/实体是否不受伤害布尔值
onGround返回玩家/实体是否站在地上布尔值
fallDistance返回玩家/实体目前掉落距离(落地后该值归0)浮点型
noClip返回玩家/实体是否没有碰撞箱(观察者模式下该值为true)布尔值
x y z返回玩家/实体的x/y/z坐标浮点型
yaw pitch返回玩家/实体的偏航角/俯仰角浮点型
motionX motionY motionZ返回玩家/实体动量浮点型
ticksExisted返回玩家/实体存在的游戏刻数整形
world返回WorldJSWorldJS
ridingEntity返回玩家/实体正在骑的实体EntityJS
teamId返回玩家/实体当前所在队伍名称字符串
customName返回玩家/实体自定义名称Text
hasCustomName返回玩家/实体是否有自定义名称布尔值
customNameAlwaysVisible返回玩家/实体自定义名称是否总是可见布尔值
eyeHeight返回玩家/实体视线高度浮点型
fullNBT返回玩家/实体NBTCompoundTag
inWater返回玩家/实体是否在水里布尔值
underWater返回玩家/实体是否在水下布尔值
child返回玩家/实体是否为幼年个体布尔值
potionEffects返回玩家/实体具有的效果EntityPotionEffectsJS
lastDamageSource返回玩家/实体最后收到的伤害来源DamageSourceJS
health返回玩家/实体的血量浮点型
maxHealth返回玩家/实体的最大血量浮点型
lastAttackedEntity返回最后攻击的实体LivingEntityJS
idleTime返回玩家/实体无动作时间整形
attackingEntity返回玩家/实体攻击对象LivingEntityJS
mainHandItem返回玩家/实体主手物品ItemStackJS
offHandItem返回玩家/实体副手物品ItemStackJS
headArmorItem返回玩家/实体头盔物品ItemStackJS
chestArmorItem返回玩家/实体胸甲物品ItemStackJS
legsArmorItem返回玩家/实体腿甲物品ItemStackJS
feetArmorItem返回玩家/实体鞋子物品ItemStackJS
movementSpeed返回玩家/实体移动速度浮点型
reachDistance返回玩家/实体攻击距离浮点型
onFire返回玩家/实体着火状态(支持通过a = b方式修改)整形
statusMessage返回玩家/实体状态栏消息(支持通过a = b方式修改)Text
horizontalFacing返回玩家水平方向Direction
facing返回玩家方向Direction

、、、、、见后文

见11.4 KubeJs内置游戏阶段 (类似于GameStage)

见6 自定义世界生成

返回值可为DOWN, UP, NORTH, SOUTH, WEST, EAST

二、函数


格式功能返回值
sendInventoryUpdate()(仅适用于玩家)向客户端(?)发送背包数据更新void
give(ItemStackJS 物品) (仅适用于玩家)给予玩家物品void
giveInHand(ItemStackJS 物品)(仅适用于玩家)给予玩家物品(快捷栏而不是背包)void
sendData(字符串 频道, 数据)(仅适用于玩家)发送网络包(详见后文)void
addFood(整形 食物水平, 浮点型 饱和度)(仅适用于玩家)为玩家添加饥饿值void
addExhaustion(浮点型 饥饿等级)(仅适用于玩家)为玩家添加饥饿等级void
addXP(整形 经验值)(仅适用于玩家)为玩家添加经验值void
addXPLevels(整形 经验等级)(仅适用于玩家)为玩家添加经验等级void
paint(CompoundTag 渲染器)(仅适用于玩家)调用Painter API(详见后文)void
boostElytraFlight()(仅适用于玩家)加速玩家鞘翅飞行(类似于烟花)void
closeInventory()(仅适用于玩家)关闭玩家背包void
addItemCooldown(Item 物品, 整形 冷却tick)(仅适用于玩家)添加物品冷却voi
runCommand(字符串 命令内容)执行命令int
runCommandSilent(字符串 命令内容)静默执行命令int
setMotion(浮点型 x, 浮点型 y, 浮点型 z)设置实体动量void
setRotation(浮点型 偏航角,浮点型 俯仰角)设置玩家旋转角度void
setPosition(浮点型 x, 浮点型 y, 浮点型 z)设置玩家坐标void
setPositionAndRotation(浮点型 x, 浮点型 y, 浮点型 z, 浮点型 偏航角,浮点型 俯仰角)上边两个函数的合并void
addMotion(浮点型 x, 浮点型 y, 浮点型 z)添加实体动量void
kill()我给你毙了void
startRiding(EntityJS 实体, 布尔值 是否强制)使玩家骑某个实体boolean
removePassengers()移除当前实体乘客void
dismountRidingEntity()取消当前实体骑乘状态void
isPassenger(EntityJS 实体)返回实体是否为骑乘状态布尔值
isOnSameTeam(EntityJS 实体)返回是否与某实体为同一队布尔值
isOnScoreboardTeam(字符串 队伍名称)返回是否在计分板队伍上布尔值
extinguish()灭火void
playSound(SoundEvent 音乐名, 浮点型 音量, 浮点型 角度)播放声音void
playSound(SoundEvent 音乐名)同上void
spawn()立即重生void
attack(字符串 来源, 浮点型 血量)使对象受伤void
attack(浮点型 血量)同上void
rayTrace(浮点型 距离)rayTraceEvent(详见后文)RayTraceResultJS
getDistanceSq(浮点型 x, 浮点型 y, 浮点型 z)返回与坐标之间距离的平方浮点型
getDistanceSq(BlockPos 坐标)返回与坐标之间距离的平方浮点型
getDistance(浮点型 x, 浮点型 y, 浮点型 z)返回与坐标之间距离浮点型
getDistance(BlockPos 坐标)返回与坐标之间距离浮点型
heal(浮点型 血量)为对象添加血量void
swingArm(InteractionHand 手)晃动手臂void
getEquipment(EquipmentSlot 栏位)返回指定栏位的装备ItemStackJS
setEquipment(EquipmentSlot 栏位, ItemStackJS 物品)设置指定栏位的装备void
getHeldItem(InteractionHand 手)返回指定手的物品ItemStackJS
setHeldItem(InteractionHand 手, ItemStackJS 手)设置指定手的物品void
damageEquipment(EquipmentSlot 栏位, 整形 扣除耐久值)损坏物品void
damageEquipment(EquipmentSlot 栏位, 整形 扣除耐久值, Consumer<ItemStackJS> onBroken)损坏物品void
damageEquipment(EquipmentSlot 栏位)损坏物品void
damageHeldItem(InteractionHand 手, 整形 扣除耐久值)损坏物品void
damageHeldItem(InteractionHand 手, 整形 扣除耐久值, Consumer<ItemStackJS> onBroken)损坏物品void
damageHeldItem()损坏物品void
isHoldingInAnyHand(内容)指定内容是否拿在手中boolean
canEntityBeSeen(EntityJS 实体)是否能看到指定实体boolean
rayTrace(整形 距离)rayTraceEvent(详见后文)RayTraceResultJS
mergeFullNBT(字符串 NBT)将给定的NBT与原实体的合并void

:该函数与ServerJS下的同名方法有两点不同 ① 此处执行命令需要加斜杠( / ),而后者不用 ② 此处执行命令会以玩家的权限执行,而前者为服务器控制台权限。另外成功执行命令时该函数返回1,失败返回0

:getDistance与getDistanceSq的区别见下:

https://m1.miaomc.cn/uploads/20220326_6988c88d4bdc4.png

:InteractionHand 接受值为 MAIN_HAND或OFF_HAND

:EquipmentSlot 接受值为 MAINHAND、OFFHAND、HEAD、CHEST、LEGS、FEET

:相同的会被新值覆盖

注1:实体与玩家的操作方法大体相同(也就是适用于玩家的很多函数同时适用于实体)

注2:实际上PlayerJS继承于LivingEntityJS,而后者又继承于EntityJS。


15.2 实体生成 & RayTraceResultJS & ItemStackJS及其他


本节内容:实体生成,RayTraceResultJS ,ItemStackJS,InventoryJS,PlayerStatsJS

一、实体生成

实体生成较为简单,你需要先新建一个实体,然后设置属性,再在合适的时间生成它即可

以下代码会在玩家在聊天栏发送内容时在玩家位置生成一个火球

onEvent("player.chat", event => {
    let p = event.player;
    // 新建实体
    var entity = event.world.createEntity('minecraft:fireball');
    // 设置实体NBT
    entity.mergeFullNBT('{ExplosionPower:2}');
    // 设置坐标
    entity.setPosition(p.x, p.y, p.z);
    // 生成实体,没有这一行实体是不会生成的
    entity.spawn();
})
createEntity的返回值为EntityJS,所以绝大多数15.1中的东西都是可以应用于实体生成的,这里就不再赘述了。

二、RayTraceResultJS

RayTraceResultJS 会返回玩家所看的方向、方块、实体等内容。


属性功能返回值
fromEntity返回"视线"的来源EntityJS
type返回玩家看到的内容类型字符串
distance返回目标距离玩家距离浮点型
hitX返回"视线"目标的X坐标浮点型
hitY返回"视线"目标的Y坐标浮点型
hitZ返回"视线"目标的Z坐标浮点型
block返回视线目标方块BlockContainerJS
facing返回视线的方向Direction
entity返回视线目标实体EntityJS

:该值可以为"block"、"entity"和"miss"

:因为KubeJS的特性,有关实体的内容仅在客户端侧可用且距离不得大于玩家的攻击距离。

值得注意的是,绝大多数的属性在玩家没有面向对象时均为null,也就是说在使用RayTraceResultJS时,必须先判断其值是否为null或者利用type来判断类型(否则你得到一堆报错)

以下例子为指南针添加了一个实用功能:返回其右键点击的方块id和属性并生成对应/setblock指令供玩家复制

onEvent('item.right_click', event => {
    let player = event.player;
    let target = player.rayTrace(1000);
    if(target.type == "block" && player.mainHandItem.id == "minecraft:compass"){
      let prop = target.block.properties.toString();
      // 把信息告诉玩家
      player.tell(`你刚才看的方块是:${target.block.id},距离当前位置${player.getDistance(target.hitX, target.hitY, target.hitZ).toFixed(1)}格,方块属性:${prop}`);
      // 复制指令(TextJS)
      player.tell(Text.of("[点此复制/setblock指令]").bold().click(`copy:/setblock ~ ~ ~ ${target.block}`));
      // 为物品添加冷却
      // player.addItemCooldown("minecraft:compass", 1000);
    }
})
三、ItemStackJS

1、属性


属性功能返回值
id返回该物品组的id字符串
tags返回该物品组的tagsCollection<ResourceLocation> tags
count设置/返回该物品组中物品数量int
block返回当前物品是否为方块布尔值
nbt返回当前物品组的nbtCompoundTag
nbtString返回字符串形式的nbt字符串
name返回当前物品组的名称Text
enchantments返回当前物品组的附魔MapJS
harvestSpeed返回当前物品组的破坏速度浮点型
itemGroup返回当前物品组在创造物品栏的位置字符串
vanillaPredicate获取当前物品谓词(?)Predicate<ItemStack>
empty返回当前物品组是否为空布尔值

2、函数


函数功能返回值
hasTag(tag)判断物品是否有指定tag布尔值
withCount(整形 数量)返回一个指定数量的物品组ItemStackJS
hasNBT()判断是否具有NBT布尔值
removeNBT()移除物品的NBTvoid
withNBT(CompoundTag nbt)返回具有指定NBT的物品组ItemStackJS
withName(Text 名称)返回一个具有指定名称的物品组ItemStackJS
strongEquals(any)将当前物品组与给定内容对比(同时比较数量等)布尔值
hasEnchantment(字符串 附魔ID, 整形 等级)判断当前物品组是否有给定的附魔布尔值
enchant(字符串 附魔ID, 整形 等级)返回一个添加了指定附魔的物品组ItemStackJS
enchant(MapJS 附魔内容)返回一个添加了指定附魔的物品组ItemStackJS
ignoreNBT()返回忽略的NBT的原料(配方相关)IngredientJS
weakNBT()返回部分忽略的NBT的原料(配方相关)IngredientJS
areItemsEqual(ItemStackJS 对比对象)将当前物品组与给定物品组对比布尔值
isNBTEqual(ItemStackJS 对比对象)将当前物品组与给定物品组的NBT对比布尔值
getHarvestLevel(ToolType 工具类型, nullable PlayerJS 玩家, nullable BlockContainerJS 方块)返回其挖掘等级整形
getHarvestSpeed(nullable BlockContainerJS 方块)返回其挖掘速度浮点型

四、InventoryJS

你可以使用InventoryJS来快捷地操作玩家物品栏


函数功能返回值
get(int 格子编号)返回指定格子的物品ItemStackJS
set(int 格子编号,ItemStackJS 物品)设置指定格子的物品void
clear()清空玩家背包void
clear(ItemStackJS 物品)清空玩家背包中指定物品void
find()返回玩家背包中有物品的格子编号整形
find(ItemStackJS 物品)返回玩家背包中有指定物品的格子编号整形
count()返回物品背包中物品总数整形
count(ItemStackJS 物品)返回玩家背包中指定物品数整形

当无该物品时会返回 -1,有则为最小编号

当无物品时会返回 0

下图是玩家物品栏格子编号(图源:Minecraft Wiki)

https://patchwiki.biligame.com/images/mc/c/c6/g1edcd1iybb1x4tw6ntsvmvo5auixhl.png

五、PlayerStatsJS

PlayerStatsJS 允许你操作玩家的统计信息中的数据


函数功能返回值
get(命名空间)返回玩家指定项的统计信息整形
set(命名空间, 整形 值)设置玩家指定项的统计信息void
add(命名空间, 整形 值)为玩家指定项的统计信息添加值void
getBlocksMined(命名空间)返回玩家破坏指定方块的统计信息的值整形
getItemsCrafted(命名空间)返回玩家合成指定物品的统计信息的值整形
getItemsUsed(命名空间)返回玩家使用指定物品的统计信息的值整形
getItemsBroken(命名空间)返回玩家用坏指定物品的统计信息的值整形
getItemsPickedUp(命名空间)返回玩家捡起指定物品的统计信息的值整形
getItemsDropped(命名空间)返回玩家丢弃指定物品的统计信息的值整形
getKilled(命名空间)返回玩家杀死指定生物的统计信息的值整形
getKilledBy(命名空间)返回玩家被指定生物杀死的统计信息的值整形


15.3 方块信息获取及操作


一、BlockContainerJS

1、属性


属性功能返回值类型
world返回WorldJSWorldJS
pos返回BlockPosBlockPos
x返回方块x坐标整形
y返回方块y坐标整形
z返回方块z坐标整形
down返回该方块下方的方块BlockContainerJS
up返回该方块上方的方块BlockContainerJS
north返回该方块北方的方块BlockContainerJS
south返回该方块南方的方块BlockContainerJS
west返回该方块西方的方块BlockContainerJS
east返回该方块东方的方块BlockContainerJS
blockState返回包含该方块状态的对象BlockState
id返回方块ID字符串
light返回光照等级整形
canSeeSky是否露天布尔值
item返回方块对应的物品ItemStackJS
playersInRadius返回范围内的玩家EntityArrayList
biomeId返回所处群系的ID字符串
properties返回带有当前方块所有属性的对象对象

如block.properties.facing会返回当前方块朝向的字符串

2、方法


方法功能返回值类型
offset(Direction 方向, int 格数)返回在给定方向上指定格数以外的方块BlockContainerJS
offset(Direction 方向)返回在给定方向上一格以外的方块BlockContainerJS
offset(整形 x, 整形 y, 整形 z)返回以当前方块为坐标系原点,指定X,Y,Z以外的方块BlockContainerJS
setBlockState(BlockState 状态值, int )设置方块状态值void
hasTag(tag)返回是否具有指定标签布尔值
set(命名空间 方块ID, Map<字符串, 字符串> 方块属性, 整形 flags)在当前位置设置方块void
set(命名空间 方块ID, Map<字符串, 字符串> 方块属性)在当前位置设置方块void
set(命名空间 方块ID)在当前位置设置方块void
createExplosion()新建爆炸事件(见下文)ExplosionJS
createEntity(命名空间ID)新建实体(见15.2)EntityJS
spawnLightning(布尔值 是否只有效果, nullable EntityJS 玩家)生成闪电void
spawnLightning(布尔值 是否只有效果)生成闪电void
spawnFireworks(FireworksJS 烟花)生成烟花(见下文)void
getInventory(Direction 方向)获取方块的库存nullable InventoryJS
getPlayersInRadius(浮点型 范围)返回指定范围内的玩家列表EntityArrayList

二、ExplosionJS

新建爆炸和新建实体类似,你需要先新建一个爆炸,然后设置属性,再在合适的时间生成它即可


函数功能返回值类型
exploder(EntityJS 实体)设定是谁引起的爆炸ExplosionJS
strength(浮点型 爆炸强度)设定爆炸的强度ExplosionJS
causesFire(布尔值 是否引火)设定爆炸是否会生成火ExplosionJS
damagesTerrain(布尔值 是否破坏环境)设定爆炸是否破坏地形ExplosionJS
destroysTerrain(布尔值 是否破坏环境)设定爆炸是否破坏地形ExplosionJS
explode()炸!void

damagesTerrain会破坏方块但是掉落方块,destroysTerrain会破坏方块且不掉落方块。若不设置默认为破坏且掉落方块。

一个简单的例子,在玩家右键方块时生成一个小爆炸

onEvent('block.right_click', event => {
    // 新建爆炸
    let explosion = event.block.createExplosion();
    // 设置爆炸强度,默认值为3
    explosion.strength(100.0);
    // 设置是否生成火,默认值为false
    explosion.causesFire(true);
    explosion.explode();
})
(应该很小吧)

https://m1.miaomc.cn/uploads/20220416_f84d7444ffa57.jpg

15.4 WorldJS , ServerJS


一、WorldJS

WorldJS允许你获取、修改当前世界的一些信息,而ServerJS允许你操作当前服务器中的一些内容

1、属性


属性描述返回值
side返回当前运行环境ScriptType
gameRules返回游戏规则效果内容GameRulesJS
server返回ServerJSServerJS
time获取游戏总时间长整型
localTime获取游戏日时间长整型
dimension返回游戏维度名字符串
overworld返回当前世界是否为主世界布尔型
daytime返回当前世界是否为白天布尔型
raining返回当前世界天气是否为雨天布尔型
thundering返回当前世界天气是否为雷暴布尔型
rainStrength返回当前世界下雨强度浮点型
players返回当前世界玩家列表EntityArrayList
entities返回当前世界实体列表EntityArrayList

:值可以为client/server

:见本节末GameRulesJS

2、函数


函数描述返回值
getBlock(整形 x, 整形 y, 整形 z)返回指定坐标方块BlockContainerJS
getBlock(BlockPos pos)返回指定坐标方块BlockContainerJS
getBlock(BlockEntity blockEntity)返回指定坐标方块BlockContainerJS
createExplosion(浮点型 x, 浮点型 y, 浮点型 z)新建爆炸ExplosionJS
createEntity(命名空间id)新建实体EntityJS
spawnLightning(浮点型 x, 浮点型 y, 浮点型 z, 布尔值 是否只有效果, nullable EntityJS 玩家对象)生成闪电void
spawnLightning(浮点型 x, 浮点型 y, 浮点型 z, 布尔值 是否只有效果)生成闪电void
spawnFireworks(浮点型 x, 浮点型 y, 浮点型 z, FireworksJS 烟花)生成烟花void

:见15.3 ExplosionJS 部分

:见15.2 EntityJS部分

二、ServerJS

1、属性


属性描述返回值
overworld返回ServerWorldJSServerWorldJS
worlds返回ServerWorldJS列表List<ServerWorldJS>
running返回服务器是否正在运行布尔值
hardcore返回服务器是否为极限模式布尔值
singlePlayer返回是否为单人模式布尔值
dedicated返回是否为"纯"服务器端(?)布尔值
motd返回服务器的motd字符串
players返回当前服务器玩家列表EntityArrayList
entities返回当前服务器实体列表EntityArrayList

2、函数


函数描述返回值
stop()停止当前服务器void
getLevel(字符串 维度名称)返回给定维度WorldJS
getPlayer(UUID uuid)返回给定UUID的玩家nullable ServerPlayerJS
getPlayer(字符串 玩家名称)返回给定名称的玩家nullable ServerPlayerJS
getEntities(字符串 过滤器)返回符合给定过滤器的实体列表EntityArrayList
getAdvancement(命名空间id)返回给定成就AdvancementJS
sendDataToAll(字符串 通道, nullable any 数据)向所有玩家发送数据void
setMotd(Component motd)设置服务器motdvoid
getName()返回服务器名称Text
getDisplayName()返回服务器显示名称Text
tell(Component 消息)向服务器所有玩家发送消息void
setStatusMessage(Component 消息)设置服务器所有玩家的状态栏消息void

:见本节末ServerWorldJS

:形如"@e"等

:见本节末AdvancementJS

:见16 网络包

:这里指从服务器后台执行命令时括号中显示的“名称”

三、GameRulesJS


函数描述返回值
getString(字符串 游戏规则)返回指定游戏规则的值字符串
getBoolean(字符串 游戏规则)返回指定游戏规则的值布尔型
getInt(字符串 游戏规则)返回指定游戏规则的值整形
set(字符串 游戏规则, Object 值)设置指定游戏规则的值void

四、AdvancementJS


属性描述返回值
displayText返回进度文本Text
title返回进度标题Text
description返回进度描述Text


函数描述返回值
addChild(AdvancementJS a)添加子进度void
hasDisplay()该进度是否会显示布尔值



15.5 本章例子



一、防止玩家开创造

onEvent("player.tick", event => {// 监听player.tick事件
    if(event.player.creativeMode || event.player.creativeMode.spectator){// 判断玩家游戏模式(创造或观察者)
      // 上述判断通过即通过服务器静默执行指令将玩家调为生存模式
      event.server.runCommandSilent(`gamemode survival ${event.player.name}`);
    }
})
二、玩家佩戴下界合金头盔时给予缓慢效果

onEvent("player.tick", event => {// 监听player.tick事件
    if(event.player.headArmorItem.id == "minecraft:netherite_helmet"){// 判断玩家头盔是不是下界合金头盔
      // 上述判断通过即给予玩家1 tick的缓慢I效果
      event.player.add("minecraft:slowness", 1, 1, false, false);
    }
})
三、禁止非创造玩家捡起基岩

onEvent("item.pickup", event => {// 监听item.pickup事件
    if(event.item.id == "minecraft:bedrock" && !event.player.creativeMode){// 判断物品是不是基岩
      // 判断通过则取消事件防止被捡起
      event.cancel();
    }
})
四、防止烟花多次触发

onEvent("item.right_click", event => {// 监听item.right_click事件
    if(event.item.id == "minecraft:firework_rocket" && event.player.onGround){// 判断右键点击的物品是不是烟花且玩家在地面上
      event.player.addItemCooldown("minecraft:firework_rocket", 200);
    }
})
五、消耗铁加速玩家鞘翅飞行

onEvent("item.right_click", event => {// 监听item.right_click事件
    if(event.item.id == "minecraft:iron" && !event.player.onGround && event.player.chestArmorItem.id == "minecraft:elytra"){// 判断物品是不是钻石且玩家不在地面上且玩家穿着鞘翅
      // 消耗一个铁加速玩家鞘翅飞行
      event.player.boostElytraFlight();
      event.item.count--;
      event.player.addItemCooldown("minecraft:iron", 60);
    }
})

六、使玩家在潜行状态下无法使用指定物品

第一块是对着空气使用物品,第二块是对着方块使用物品

onEvent('item.right_click', event => {
    if(event.player.crouching == true && event.item.id == "minecraft:diamond"){
      event.cancel();// 取消该事件
    }
})

onEvent('block.right_click', event => {
    if(event.player.crouching == true && event.item.id == "minecraft:diamond"){
      event.cancel();// 取消该事件
    }
})
七、随机传送

// 最小及最大坐标
const minX = 1000;
const minZ = 1000;
const maxX = 10000;
const maxZ = 10000;
const minY = 100;
const maxY = 200;
// 是否随机取负值
const reverseNumber = true;

// 返回随机数
function getNumber(min,max){
    let randNumber = Math.floor(Math.random() * (max - min + 1) ) + min;
    if(Math.random() > 0.5 && reverseNumber){
      randNumber = -randNumber;
    }
    return randNumber;
}

// 监听聊天事件,如果内容为 "!rtp" 就随机传送并给予缓降效果
onEvent('player.chat',function (event){
    let input = event.message.trim();
    if(input == "!rtp"){
      event.player.setPosition(getNumber(minX,maxX), Math.abs(getNumber(minY,maxY)), getNumber(minZ,maxZ));// 设置坐标
      event.player.statusMessage = `已将你传送到 x: ${event.player.x} y: ${event.player.y} z: ${event.player.z}`;// 玩家状态栏显示文字
      event.player.potionEffects.add('minecraft:slow_falling', 600, 10, false, false);// 给予缓降效果
      /**
         * EntityPotionEffectsJS支持以下函数
         * .clear() 清除药水效果
         * .getActive() 返回具有的效果
         * .isActive(MobEffect 效果) 返回是否具有给定效果
         * .getDuration(MobEffect 效果) 返回给定效果剩余时间
         * .add(MobEffect 效果, 整形 持续时间(tick), 整形 等级, 布尔值 是否为信标产生, 布尔值 是否显示粒子)
         *   其中后三项,后两项,后一项均可省略不写,默认值对应为0,false, true.
         * .isApplicable(MobEffect 效果) 返回给定效果是否能应用于当前实体上
         */
      event.cancel();// 取消该事件,也就是说玩家发送的消息不会显示在其他玩家的公屏上
    }
})
/**
* 另一种写法:监听物品右键事件,如果物品id为钻石则消耗一个钻石随机传送并冷却100秒
* 此处钻石可以替换为你想使用的物品
* 注:重进世界会刷新冷却时间
*/
onEvent('item.right_click',function (event){
    if(event.player.mainHandItem.id == "minecraft:diamond"){
      event.player.setPosition(getNumber(minX,maxX), Math.abs(getNumber(minY,maxY)), getNumber(minZ,maxZ));
      event.player.statusMessage = `已将你传送到 x: ${event.player.x} y: ${event.player.y} z: ${event.player.z}`;
      event.player.potionEffects.add('minecraft:slow_falling', 600, 10, false, false);
      // 钻石数量减一
      event.item.count--;
      event.player.addItemCooldown('minecraft:diamond', 2000);
    }
})
https://m1.miaomc.cn/uploads/20220408_c412408992599.gif

八、爆炸“法杖”

这里的烈焰棒只是一个"占位符",你可以换成你想使用的任何物品

// 绘制粒子函数
function drawParticle(event, intensity, particle) {
    let start = event.getPlayer()
    let target = event.player.rayTrace(1000)
    if(target == null){
      return false;
    }
    let rayx = target.block.x;
    let rayy = target.block.y
    let rayz = target.block.z;

    let d = getDistance(start.x, start.y + 1, start.z, rayx, rayy, rayz)
    for (let i = -1; i < d*intensity; i++) {
      let delta = i / d
      let x = (1 - delta) * start.x + delta * (rayx + 0.5)
      let y = (1 - delta) * start.y + delta * (rayy + 0.5)
      let z = (1 - delta) * start.z + delta * (rayz + 0.5)
      event.server.runCommandSilent(`/particle ${particle} ${x} ${y} ${z} 0.1 0.1 0.1 0.01 5 normal`)
    }
    return true;
}
// 生成爆炸(使用火球实体来实现爆炸)
onEvent("item.right_click", event => {
    if(event.player.rayTrace(1000).block != null && event.item.id == "minecraft:blaze_rod"){
      let entity = event.world.createEntity('minecraft:fireball');
      // 合并NBT设置爆炸等级
      entity.mergeFullNBT('{ExplosionPower:2}')
      entity.setPosition(event.player.rayTrace(1000).block.x, event.player.rayTrace(1000).block.y + 1, event.player.rayTrace(1000).block.z);
      entity.setMotion(0, -20, 0);
      entity.spawn();
      drawParticle(event,0.5,"minecraft:spit");
      // 添加物品冷却
         event.player.addItemCooldown(event.item.id, 1000);
    }
})
// 生成爆炸(使用ExplosionJS)
onEvent("item.right_click", event => {
    if(event.player.rayTrace(1000).block != null && event.item.id == "minecraft:blaze_rod"){
      let explosion = event.player.rayTrace(1000).block.createExplosion();
      // 设置引爆者
      explosion.exploder(event.player);
      // 设置其他属性
      explosion.strength(2.0);
      explosion.causesFire(true);
      // 引爆
      explosion.explode();
      // 绘制粒子
      drawParticle(event,0.5,"minecraft:spit");
      // 添加物品冷却
      event.player.addItemCooldown(event.item.id, 1000);
    }
})
https://m1.miaomc.cn/uploads/20220416_2f6b7441fc14d.gif

九、扫地机器人

// 物品白名单
const whitelist = Ingredient.matchAny([
    'minecraft:diamond', //单个物品
    'minecraft:gold_ingot',//单个物品
    '@tinkersconstruct', //mod物品示例
    'minecraft:emerald'
])
// 执行一次间隔(注:该值必须大于1)
const minutes = 30;

// ======================================

var lastResult = [];
var totalResult = [];
var lastItemCount = 0;
var totalItemCount = 0;


function clearLag(server){
    lastResult = [];
    lastItemCount = 0;
    server.getEntities("@e").forEach(entity => {
      if (!whitelist.test(entity.item.id)){
            lastResult.push();
            totalResult.push();
            lastItemCount += entity.item.count;
            entity.kill();
      }
    });
    lastResult.sort();
    totalResult.sort();
    server.tell('), `本次共清除 ${lastItemCount} 个物品`]);
    server.tell('), "在聊天框中输入 !clearLag last来获取本次详细信息"]);
    server.tell('), "在聊天框中输入 !clearLag total 来获取全部详细信息"]);
}

function countResult(result,event){
    if(result != []){
      result.forEach((singleResult, index) => {
      event.server.tell(} , 个数为 ${singleResult}`])
      })
    }
}

onEvent('server.load', function (event) {
    event.server.scheduleInTicks(100, event.server, function (callback0) {
      callback0.data.tell('), `加载成功,使用 !clearlag help 查看帮助`]);
    })
    event.server.schedule((minutes - 1) * MINUTE, event.server, function (callback1) {
      callback1.data.tell('), "1分钟后将清理地面掉落物"]);
      callback1.data.schedule(MINUTE, callback1.data, function(callback2) {
            clearLag(callback2.data);
      })
      callback1.reschedule();
    })
})

// 聊天事件,只有有OP权限的文件才能执行查询/扫地
onEvent('player.chat',function (event){
    let input = event.message.trim();
    switch (input) {
      case "!clearlag last":
            if (event.player.op){// 判断玩家权限
                countResult(lastResult,event);
            }else{
                event.player.tell('), "你没有权限这样做"]);
            }
            break;
      case "!clearlag total":
            if (event.player.op){
                countResult(totalResult,event);
            }else{
                event.player.tell('), "你没有权限这样做"]);
            }
            break;
      case "!clearlag help":
            event.player.tell('), "扫地机器人 by Wudji@mcbbbs.net. Powered by KubeJS"]);
            event.player.tell('), "在聊天框中输入 !clearLag last来获取本次详细信息"]);
            event.player.tell('), "在聊天框中输入 !clearLag total 来获取全部详细信息"]);
            event.player.tell('), "在聊天框中输入 !clearLag 立即清除掉落物"]);
            break;
      case "!clearlag":
            if (event.player.op){
                clearLag(event.server);
            }else{
                event.player.tell('), "你没有权限这样做"]);
            }
            break;
    }
})


16 网络包和Painter API


一、网络包

事件:

客户端从服务端侧接收脚本player.data_from_server.<信道ID>

服务端从客户端侧接收脚本player.data_from_server.<信道ID>

// 监听玩家物品右键事件
// 你可以把下面这段脚本放到服务端或客户端脚本中,取决于你想从哪一侧发包
onEvent('item.right_click', event => {
// 监听物品右键事件
if (event.server) {
    // 发送 {test: 123} 到信道 "test_channel_1". 信道ID可以为任何字符串, 但还是推荐使用蛇形命名方法
    // 接收端为客户端 (发送端为服务端).
    event.player.sendData('test_channel_1', { test: 123 });// 发包
} else {
    // 推荐使用不同信道 尽管这不是必须的
    // 收包 (发送端为客户端).
    event.player.sendData('test_channel_2', { test: 456 })// 发包
}
})

// 监听从服务端收到网络包时的事件
// 这段是客户端脚本
onEvent('player.data_from_server.test_channel_1', event => {
log.info(event.data.test) // 打印网络包信息到日志(123)
event.player.tell(`来自客户端的网络包信息:${event.data.test}`)
})

// 监听从客户端收到网络包时的事件
// 这段是服务端脚本
onEvent('player.data_from_client.test_channel_2', event => {
log.info(event.data.test) // 打印网络包信息到日志(456)
event.server.runCommand(`say 来自服务端的网络包信息:${event.data.test}`)
})
二、Painter API

Painter API允许你在客户端屏幕上绘制内容,其可以从服务端控制也可以从客户端直接触发

目前(2022年5月10日)它不支持玩家输入/交互,但以后可能会支持游戏内菜单甚至是渲染引擎

Painter对象是以NBT/Json对象建立的,并且每一个都有自己的ID. 如果你没有提供一个的话,KubeJS就会随机生成一个. x 和 z 是对象在屏幕上的的绝对坐标, 但你能将元素对齐到屏幕的给定位置。你可以在一个json对象中添加多个对象. 所有的属性都是可选的,但是显然你至少应该填写坐标和大小等信息。

paint({...})遵守更新插入法则,如果对象不存在则创建,若存在则更新其属性:

event.player.paint({example: {type: 'rectangle', x: 10, y: 10, w: 20, h: 20}}); // 新建矩形
event.player.paint({example: {x: 50}}); // 更新先前ID为example的Painter对象的x属性
你可以批量新建/修改Painter对象:

event.player.paint({a: {x: 10}, b: {x: 30}, c: {type: 'rectangle', x: 10}});
你可以使用remove来移除Painter对象:

event.player.paint({a: {remove: true}}); // 移除ID为a的Painter对象
event.player.paint({a: {remove: true}, b: {remove: true}}); // 批量移除多个对象
event.player.paint({'*': {remove: true}}); //移除全部
还记得在1.1中提到的/kubejs painter <玩家名称> <PainterJS对象>指令吗?它和上述的函数功能是一样的

/kubejs painter @p {example: {type: 'rectangle', x: 10, y: 10, w: 20, h: 20}}
如果创建对象时多次重复出现,推荐在玩家登录时创建对象并为其设置属性visible: false,之后在合适时间将该属性改为true以取消隐藏。值得注意的是,Painter对象在玩家离开服务器时会被清除,所以你需要在每次玩家登录时重新为其添加Painter对象。

可用对象

Underlined objects are not something you can create



(可用于所有对象)


[*]Boolean visible

屏幕对象

(可用于所有屏幕/2D内容)


[*]Unit x // x坐标
[*]Unit y // y坐标
[*]Unit z // z坐标
[*]Unit w // 宽度
[*]Unit h // 高度
[*]Enum alignX (支持 'left', 'center', 'right') // x方向对齐参数
[*]Enum alignY (支持 'top', 'center', 'bottom') // y方向对齐参数
[*]Enum draw (支持 'ingame', 'gui', 'always') // 绘制场景
[*]Unit moveX
[*]Unit moveY
[*]Unit expandW
[*]Unit expandH

正方形


[*]Color color
[*]String texture
[*]Float u0
[*]Float v0
[*]Float u1
[*]Float v1

渐变(gradient)


[*]Color color
[*]Color colorT
[*]Color colorB
[*]Color colorL
[*]Color colorR
[*]Color colorTL
[*]Color colorTR
[*]Color colorBL
[*]Color colorBR
[*]String texture
[*]Float u0
[*]Float v0
[*]Float u1
[*]Float v1

文本


[*]Text text
[*]Boolean shadow
[*]Float scale
[*]Color color
[*]Boolean centered

官方示例

onEvent('player.logged_in', event => {
    // 初始化Painter对象
    event.player.paint({
      example_rectangle: {
            type: 'rectangle',
            x: 10,
            y: 10,
            w: 50,
            h: 20,
            color: '#00FF00',
            draw: 'always'
      },
      last_message: {
            type: 'text',
            text: 'No last message',
            scale: 1.5,
            x: -4,
            y: -4,
            alignX: 'right',
            alignY: 'bottom',
            draw: 'always'
      }
    })
})

onEvent('player.chat', event => {
    // 更新 example_rectangle 的 x 值 和 last_message 文本的值为上次聊天文本
    event.player.paint({example_rectangle: {x: '(sin((time() * 1.1)) * (($screenW - 32) / 2))', w: 32, h: 32, alignX: 'center', texture: 'kubejs:textures/item/diamond_ore.png'}})
    event.player.paint({last_message: {text: `Last message: ${event.message}`}})
    // 一次更新两个对象,这两种方式没有区别,你想用哪个都行
    // event.player.paint({example_rectangle: {x: 120}, last_message: {text: `Last message: ${event.message}`}})
    event.player.paint({lava: {type: 'atlas_texture', texture: 'minecraft:block/lava_flow'}})
})
https://m1.miaomc.cn/uploads/20220510_5389abd49fab4.gif

// TODO: 18.3 Painter API示例:服务器挖掘排行榜(显示挖掘方块最多的人)
// ↑ 又给自己挖坑是吧


17 本地化相关


KubeJS的本地化相关内容非常人性化,你可以直接使用现有的本地化键名来翻译新建的物品,方块和流体而不用手动新建。

对于方块,你需要使用本地化键名block.kubejs.<自定义方块ID>

对于物品,你需要使用本地化键名item.kubejs.<自定义物品ID>

对于流体,你需要使用本地化键名fluid.kubejs.<自定义流体ID>

下面我们来看一个例子。

假设你的脚本文件是这样的:

kubejs\startup_scripts\registry.js

onEvent('block.registry', event => {
    event.create('super_tnt').material('wood').hardness(1.0).displayName('超级TNT');
})

onEvent('item.registry', event => {
    event.create('can').displayName('罐头').maxStackSize(8);
})

onEvent('fluid.registry', event => {
    event.create("test_mud").displayName("泥").bucketColor(0x844031).textureThick(0x844031).textureThin(0x844031);
})
那么你的语言文件应该按以下格式书写:

kubejs\assets\kubejs\lang\en_us.json

{
    "block.kubejs.super_tnt":"Super TNT",
    "item.kubejs.can":"Can",
    "fluid.kubejs.test_mud":"Mud"
}
语言文件内翻译项目的顺序无所谓,只要都有就行

18 placeholder
本章节包含一些杂项例子
18.1 示例 - 熔炼之镐


本示例是对添加配方和Loot Table修改的综合运用

效果:使用下界合金镐挖掘方块时,矿石和石头类的方块会被自动熔炼

//代码块 1
onEvent('recipes', event => {
event.shapeless(Item.of('minecraft:netherite_pickaxe', {furnaceSmelt:"true"}), ).replaceIngredient('minecraft:lava_bucket', 'minecraft:bucket')
})
//代码块 2
onEvent('block.loot_tables', event => {
event.modifyBlock(/minecraft:.*_ore/, table => {
    table.furnaceSmelt()
    table.addCondition({"condition": "minecraft:alternative","terms": [{"condition": "minecraft:match_tool","predicate": {"nbt": "{id:'furnaceSmelt'}"}}]})
})
event.modifyBlock(/minecraft:.*stone/, table => {
    table.furnaceSmelt()
    table.addCondition({"condition": "minecraft:alternative","terms": [{"condition": "minecraft:match_tool","predicate": {"nbt": "{id:'furnaceSmelt'}"}}]})
})
})
代码块1中使用了 2.1配方的添加中的nbt合成 和 11.3非标准配方修改中的替换输入物品

代码块2中使用了 4自定义loot table和其addCondition的写法(即原版json格式)


18.2 罐头示例(包含物品注册, 配方添加, 玩家交互和persistentData的使用)


在这个例子中,我们将:


[*]添加3个物品:密封的牛肉罐头,打开的牛肉罐头,罐头盒
[*]添加配方:8个玻璃板合成3个罐头盒,罐头盒+两个牛肉无序合成密封的牛肉罐头
[*]添加事件:当玩家手持密封的牛肉罐头长按右键时打开罐头
[*]添加事件:当玩家吃完罐头后给予玩家一个空罐头盒

1、物品注册

kubejs\startup_scripts\item_registry.js

onEvent('item.registry', event => { // 监听物品注册事件
    event.create('can_empty').displayName('空罐头盒').maxStackSize(32);// 最大堆叠数:32
    event.create('can').displayName('密封的牛肉罐头').maxStackSize(8);// 最大堆叠数:8
    event.create('can_open').displayName('打开的牛肉罐头').maxStackSize(1).food(food => {
      food.hunger(18);// 设置食物信息
      food.saturation(18);
    });
})
2、配方修改

kubejs\server_scripts\recipe.js

onEvent('recipes', event => { // 监听配方事件

    // 空罐头盒合成
    event.shaped('8x kubejs:can_empty', [
            'GGG',
            'G G',
            'GGG'
      ], {
            G: 'minecraft:glass_pane'
    })

    // 罐头合成
    event.shapeless('kubejs:can', ['kubejs:can_empty', '2x minecraft:cooked_beef'])
})
3、罐头事件系统

kubejs\server_scripts\can_events.js

onEvent("item.right_click", event => {// 监听物品右键事件
    if(event.player.mainHandItem.id == "kubejs:can"){ // 判断物品是否为给定物品
      if(event.player.persistentData.canholdtime / 10 >= 1){
            // 当时间达到一定值时就执行物品交换

            // 给予玩家空罐头盒
            event.player.give("kubejs:can_open");

            // 重置persistentData
            event.player.persistentData.canholdtime = 0;

            // 密封罐头数-1
            event.item.count--;
      }
      // 若时间未达到一定值则自增1
      event.player.persistentData.canholdtime++;

      // 刷新罐头打开进度条
      event.player.statusMessage = `罐头打开进度:${event.player.persistentData.canholdtime * 10}%`;
    }
})

onEvent("player.logged_in", event =>{// 监听玩家登录事件

    // 玩家加入世界时初始化 persistentData 否则其值将为NAN
    event.player.persistentData.canholdtime = 0;
})

onEvent("item.food_eaten", event =>{// 监听吃食物事件

    // 如果物品id为打开的罐头就给予玩家一个罐头盒
    if(event.item.id == "kubejs:can_open"){
      event.player.give("kubejs:can_empty")
    }
})
4、修改展示

(图片较大,请耐心等待)

https://m1.miaomc.cn/uploads/20220509_9be568769fad2.png

https://m1.miaomc.cn/uploads/20220509_de72468c663a2.png

https://m1.miaomc.cn/uploads/20220509_765843f2162a8.png

https://m1.miaomc.cn/uploads/20220509_17accde033a92.gif

https://m1.miaomc.cn/uploads/20220509_13b127ea703dd.gif



发表于 2022-5-18 08:42:46

@璀璨星河丶
是你自觉加精还是怎么
https://attachment.mcbbs.net/data/myattachment/forum/202205/15/001136lx1mmb9tnjgbmm7z.jpg

UvealZoo42661 发表于 2022-6-22 10:18:03

我学费了

蓝大官人 发表于 2024-3-2 11:58:41

学费了!多谢多谢!
页: [1]
查看完整版本: XPlus KubeJS魔改教程(Minecraft 1.16.5-1.18.2)