因为太菜了所以在犹豫该不该丢上来,也许会随缘写点既不稳定也不美观的案例?

当然要感谢D指导和YukkuriC大佬的解惑,但我必须声明自己的纯菜与二者绝无关联(铿锵有力)

图案部分主要继承(抄)自此篇:使用KubeJS添加自定义法术(表篇) - [HC]咒法学 (Hex Casting) - MC百科|最大的Minecraft中文MOD百科

作为连计算机都没学过的文科生写的代码能跑就是奇迹,不能要求更高了

1.第一个案例用到了hexParse。该模组可以使你将核心中的内容导出为代码以及将代码写入核心。

当讲台中有一本书时(不论成书或是书与笔,也不论书中有何内容),潜行手持一个写有内容的核心右键讲台,便会给你一本(当然是可编辑的)书与笔(原书消失),内容是核心导出的parse代码,并会自动换行换页。

换行换页的机制是:每一个iota的数据自成一行(内省反思除外),每14行便换页。

而若手持一个未写有内容的核心右键讲台,则会将书中的代码写入核心(原书不变)

因为书与笔是可以编辑的,这就允许你以间接的方式改写核心的内容,不仅可以方便的修改错处,补齐内省,复制粘贴列表,还可以写入数字与常量等,记得用英文逗号隔开。(所以parse本体读取卓越法术的功能相较来讲根本没啥必要限制,都跨次元阿卡夏了都,这就像边言语攻击对方边往对方头上扔tnt完了忏悔自己骂的太凶一样)

//事件:当讲台中有书时,手持有内容之核心潜行右击以将核心内容写入书与笔中,手持无内容之核心右击以从书中读取内容。
const ParserMain = Java.loadClass("io.yukkuric.hexparse.parsers.ParserMain");

BlockEvents.rightClicked('minecraft:lectern', event => {
    const pages = []
    const item = event.item
    const block = event.block
    const player = event.player
    const server = event.server

    // 基础条件
    if (player == null) return
    if (event.hand != 'MAIN_HAND') return
    if (block.entityData == null || !block.entityData.contains("Book")) return
   

    //核心判断
    if (item.id == 'hexcasting:focus' && player.isCrouching()) {
        if (item.nbt != null && item.nbt.contains("data")) {
            let lineCount = 0
            let currentLine = ""
            let data = item.nbt["data"]
            let currentPageContent = []
            let text = ParserMain.ParseIotaNbt(data, player)
           
            //自动换行
            for (const char of text) {
                if (char === ',' || char === ')') {
                    currentLine += char
                    currentPageContent.push(currentLine)
                    currentLine = ""
                    lineCount++

                    //行尽页分
                    if (lineCount >= 14) {
                        pages.push(currentPageContent.join('\n'))
                        currentPageContent = []
                        lineCount = 0
                    }
                } else {
                    currentLine += char
                }
            }

            //分页循环
            if (currentLine.length > 0) {
                currentPageContent.push(currentLine)
                lineCount++
            }
            if (currentPageContent.length > 0) {
                pages.push(currentPageContent.join('\n'))
            }

            //大功告成!(六行后翻面)
            let book = Item.of('minecraft:writable_book', {pages: pages.map(p => p.replace(/\n$/, ""))})
            block.setEntityData({"pages": 114514})
            player.give(book)}
            
//如果在这里脱出的话,讲台的生涯就结束力!有了,那就用池沼到无法生成的page掩盖过去罢!(智将)
       
        //书籍信息
        else{let bookEntityData = block.entityData.getCompound("Book")
             let bookTag = bookEntityData.getCompound("tag")
             if (bookTag != null && bookTag.contains("pages", 9)) {
                let pageList = bookTag.getList("pages", 8)
                    let allItems = [];
                   
                    //遍历删行
                    for (let i = 0; i < pageList.size(); i++) {
                        let pageContent = pageList.getString(i)
                        pageContent.split('\n')
                            .forEach(line => allItems.push(line))
                    }

                        //大功告成(已腐蚀!)
                        let result = '"' + allItems.join('') + '"'
                        player.runCommandSilent(`hexParse ${result}`)}}}});

由于1.20.1结念绳的加入,核心的地位有点尴尬。虽说前者不能覆写,但恐怕做上一组还没一个核心贵,用一个丢一个都行,属实是量变引起质变了。俗话说天道贵弱,削成者以益生者,才不是因为hexParse只有核心写入的原因,嗯!

2.媒质瓶充能

将媒质瓶放在主手,副手放上媒质(四种之一),右键即可将其全部添加进瓶中。

虽然代码很简单(而且因为不想做复杂判断所以写的冗长而丑陋),但还是很好用的。

//当媒质瓶位于主手且副手为媒质(四种之一)时,右键将手中的媒质全部充公
ItemEvents.rightClicked('hexcasting:battery', (event) => {

    //基础定义
    let player = event.getPlayer()
    let mainitem = player.mainHandItem

    //获取媒质瓶的当前容量与最大容量
    if (mainitem != null && mainitem.nbt["hexcasting:media"] != undefined && mainitem.nbt["hexcasting:start_media"] != undefined){
      let media = mainitem.nbt["hexcasting:media"]
      let start = mainitem.nbt["hexcasting:start_media"]
      let offitem = player.offHandItem
     
      //判断完全充能所需的媒质量(最大容量-当前容量)/10000 (以紫水晶粉为例),每种的消耗量和充能量毕竟都不一样
      if (offitem != null && offitem.id == "hexcasting:amethyst_dust"){
        let alpha = offitem.getCount()
        let count = (start - media)/10000
        let remain = (start - media)%10000

        //若不是消耗整数量个紫水晶粉(其实基本也不可能这么巧),则将消耗量向上取整,符合咒法学一贯作风,然后消耗
        if (remain != 0){
          let float = parseInt(count)
          let num = float + 1
          offitem.setCount(offitem.getCount() - num)}

        //若巧
        else{
          offitem.setCount(offitem.getCount() - count)}
       
        //判断:若消耗事件结束后玩家副手不为空则暗示已完全充满,直接返还等同于最大容量的瓶子
        let omega = player.offHandItem
        if (omega != null){
          mainitem.setNbt({ "hexcasting:media": start})}

        //否则,使媒质量增加(副手紫水晶粉数量*10000)点媒质
        else{
          let durability = alpha*10000 + media
          mainitem.setNbt({ "hexcasting:media": durability})}}
          
      //丑陋而无聊的重复,虽然很占空间,但为了顾及不想改参数的人就勉强放上来了
      if (offitem != null && offitem.id == "minecraft:amethyst_shard"){
        let alpha = offitem.getCount()
        let count = (start - media)/50000
        let remain = (start - media)%50000
        if (remain != 0){
          let float = parseInt(count)
          let num = float + 1
          offitem.setCount(offitem.getCount() - num)}
        else{
          offitem.setCount(offitem.getCount() - count)}
        let omega = player.offHandItem
        if (omega != null){
          mainitem.setNbt({ "hexcasting:media": start})}
        else{
          let durability = alpha*50000 + media
          mainitem.setNbt({ "hexcasting:media": durability})
        }}
      if (offitem != null && offitem.id == "hexcasting:charged_amethyst"){
        let alpha = offitem.getCount()
        let count = (start - media)/100000
        let remain = (start - media)%100000
        if (remain != 0){
          let float = parseInt(count)
          let num = float + 1
          offitem.setCount(offitem.getCount() - num)}
        else{
          offitem.setCount(offitem.getCount() - count)}
        let omega = player.offHandItem
        if (omega != null){
          mainitem.setNbt({ "hexcasting:media": start})}
        else{
          let durability = alpha*100000 + media
          mainitem.setNbt({ "hexcasting:media": durability})
        }}
      if (offitem != null && offitem.id == 'hexcasting:quenched_allay_shard'){
        let alpha = offitem.getCount()
        let count = (start - media)/300000
        let remain = (start - media)%300000
        if (remain != 0){
          let float = parseInt(count)
          let num = float + 1
          offitem.setCount(offitem.getCount() - num)}
        else{
          offitem.setCount(offitem.getCount() - count)}
        let omega = player.offHandItem
        if (omega != null){
          mainitem.setNbt({ "hexcasting:media": start})}
        else{
          let durability = alpha*300000 + media
          mainitem.setNbt({ "hexcasting:media": durability})}}}})

3.图案添加

这部分完全是YukkuriC大佬的代码,站内没有1.20.1的,就丢个实例过来了。

(就是依葫芦画瓢,我抄完自己完全没看懂)

将图案注册进帕秋莉手册部分的代码就不放了。我想大部分人大概写不了几个,自己码字得了。

(以下js都请丢进start_up中)

首先将这两篇方法丢进去祭天。他们不关乎具体的注册与事件,所以应当不用修改(实际上是我看不懂)

// priority:10
// 该脚本定义了一个类加载器,用于动态加载 Java 类
{
    let SCOPE = this; // 当前作用域
    let _cls = function (path, children) { // 定义一个类构造函数
        this.path = path; // 类路径
        this.subs = []; // 子类数组
        if (children) this.add(children); // 如果有子类,则添加子类
    };
    _cls.prototype = { // 定义类的原型方法
        add(children) { // 添加子类的方法
            for (let c of children) { // 遍历子类
                if (typeof c === 'string') c = _(c); // 如果子类是字符串,则转换为类对象
                this.subs.push(c); // 将子类添加到子类数组中
            }
            return this; // 返回当前对象,支持链式调用
        },
        build(root) { // 构建类路径的方法
            root = root ? `${root}.${this.path}` : this.path; // 构建完整的类路径
            if (this.subs.length > 0) { // 如果有子类
                for (let c of this.subs) { // 遍历子类
                    c.build(root); // 递归构建子类路径
                }
            } else { // 如果没有子类
                SCOPE[root.substring(root.lastIndexOf('.') + 1)] = Java.tryLoadClass(root); // 加载类并将其添加到作用域中
            }
        },
    };
    let _ = (path, subs) => new _cls(path, subs); // 定义一个辅助函数,用于创建类对象

    let roots = [ // 定义需要加载的类路径数组
        _('net.minecraftforge', [ // Forge 相关类
            'common.util.LazyOptional',
        ]),
        _('net.minecraft', [ // Minecraft 相关类
            _('sounds', ['SoundEvents', 'SoundSource']),
            _('world', [
                _('entity', [
                    'Mob',
                    _('projectile', ['SpectralArrow', 'EyeOfEnder']),
                    'raid.Raider',
                    _('npc', [
                        'AbstractVillager',
                        'Villager',
                        'VillagerTrades',
                    ]),
                ]),
                _('level.block', [
                    'CocoaBlock',
                    'state.properties.IntegerProperty',
                ]),
            ]),
        ]),
        _('java', ['lang.Long', 'util.WeakHashMap']), // Java 核心类
        _('at.petrak.hexcasting', [ // Hexcasting 相关类
            _('forge.cap', ['HexCapabilities', 'adimpl.CapStaticMediaHolder']),
            'xplat.IXplatAbstractions',
            'common.lib.hex.HexEvalSounds',
            _('api', [
                _('casting', [
                    'ActionRegistryEntry',
                    'ParticleSpray',
                    _('eval', ['OperationResult', 'vm.CastingVM', _('sideeffects', ['EvalSound', 'OperatorSideEffect'])]),
                    _('iota', ['IotaType', 'NullIota', 'BooleanIota', 'Vec3Iota', 'ListIota', 'DoubleIota', 'PatternIota']),
                    _('mishaps', ['Mishap', 'MishapNotEnoughArgs', 'MishapInvalidIota', 'MishapAlreadyBrainswept']),
                    _('math', ['HexDir', 'HexPattern']),
                ]),
            ]),
        ]),
    ];
    for (let root of roots) { // 遍历类路径数组
        root.build(); // 构建类路径
    }
}
// priority:10

// 定义一个Args类,用于处理参数栈
function Args(stack, n, keep) {
    // 如果栈的长度小于需要的参数数量n,则抛出错误
    if (stack.length < n) throw MishapNotEnoughArgs(n, stack.length)
    // 根据keep参数决定是对栈进行切片(slice)还是截取(splice),并将结果存储在data属性中
    this.data = stack[keep ? 'slice' : 'splice'](-n)
}

// 定义一个辅助函数,用于生成getter方法
let _buildGetter = (key, keyMishap) => {
    // 如果keyMishap未传入,则使用key作为默认值
    keyMishap = keyMishap || key
    // 构造错误信息的前缀
    keyMishap = 'class.' + keyMishap
    // 返回一个函数,用于获取参数栈中某个位置的值
    return function (i) {
        // 获取参数栈中第i个元素
        let iota = this.data[i]
        // 获取该元素的key属性值
        let res = iota[key]
        // 如果值不存在,则抛出错误
        if (res === undefined) throw MishapInvalidIota.of(iota, this.data.length - i - 1, keyMishap)
        // 返回获取到的值
        return res
    }
}

// 定义Args类的原型方法
Args.prototype = {
    // 获取参数栈中第i个元素
    get(i) {
        return this.data[i]
    },
    // 获取参数栈中第i个元素的brainmerge_target属性
    brainmerge_target(i) {
        // 获取参数栈中第i个元素的实体
        let entity = this.entity(i)
        // 如果实体是AbstractVillager或Raider类型,则返回实体
        if (entity instanceof AbstractVillager || entity instanceof Raider) return entity
        // 否则抛出错误
        throw MishapInvalidIota.of(this.data[i], this.data.length - i - 1, 'class.entity.brainmerge_target')
    },
    // 获取参数栈中第i个元素的villager属性
    villager(i) {
        // 获取参数栈中第i个元素的实体
        let entity = this.entity(i)
        // 如果实体是Villager类型,则返回实体
        if (entity instanceof Villager) return entity
        // 否则抛出错误
        throw MishapInvalidIota.of(this.data[i], this.data.length - i - 1, 'class.entity.villager')
    },
}

// 遍历一系列类型对,为Args类动态添加方法
for (let pair of ['double', 'entity', 'list', 'string', 'pattern', 'vec3/vector', 'bool/boolean']) {
    // 将类型对按'/'分割,得到key和keyMishap
    let [key, keyMishap] = pair.split('/')
    // 使用_buildGetter函数生成getter方法,并将其添加到Args类的原型中
    Args.prototype[key] = _buildGetter(key, keyMishap)
}



// 定义一个ActionJS类,用于表示一个操作
function ActionJS(id, pattern, options) {
    // 获取选项中的sound属性
    const { sound } = options || {}
    // 定义operate方法,用于执行操作
    this.operate = (env, img, cont) => {
        // 获取img的stack属性
        let stack = img.stack
        // 如果stack有toArray方法,则将其转换为数组
        if (stack.toArray) stack = Array.from(stack.toArray())
        // 尝试执行操作
        try {
            // 调用全局的操作映射表中的对应方法,传入参数栈、环境和img,获取副作用列表
            let sideEffects = global.PatternOperateMap[id](stack, env, img) || [] // 用于邪恶目的
            // 创建一个新的img对象,更新栈和其他属性
            let newImg = img.copy(stack, img.parenCount, img.parenthesized, img.escapeNext, img.opsConsumed + 1, img.userData)
            // 返回操作结果,包括新的img、副作用列表、继续执行的函数和声音效果
            return OperationResult(newImg, sideEffects, cont, sound || HexEvalSounds.NORMAL_EXECUTE)
        } catch (e) {
            // 如果捕获到错误
            if (e instanceof Mishap) {
                // 创建一个错误效果,传入错误信息和上下文
                OperatorSideEffect.DoMishap(e, Mishap.Context(pattern, null)).performEffect(CastingVM(img, env))
                // 黑客:无论如何停止操作
                // 创建一个新的img对象,更新栈和其他属性,将opsConsumed设置为maxOpCount-1以显示当前错误
                let newImg = img.copy(stack, img.parenCount, img.parenthesized, img.escapeNext, env.maxOpCount() - 1, img.userData) // -1以显示当前错误
                // 获取错误名称
                let mishapName = Text.translate(`hexcasting.action.yc:${id}`).aqua()
                // 返回操作结果,包括新的img、错误效果、继续执行的函数和错误声音效果
                return OperationResult(
                    newImg,
                    [OperatorSideEffect.DoMishap(e, Mishap.Context(pattern, mishapName))],
                    cont,
                    HexEvalSounds.MISHAP,
                )
            }
            // 如果不是Mishap错误,则重新抛出错误
            throw e
        }
    }
}

尔后就可以开始注册了,先贴两个实例:以下注册了两个图案,分注册图案与图案事件两个js(当然依旧不是我写的!)

  1. 图案time的作用是移除一图案列表和一数,将前一列表延迟该数*tick后运行。

  2. 图案arrow的作用是移除一个向量,在此处创建一只光灵箭。

//注册图案

// 定义一个全局数组,用于存储与世界相关的模式
global.perWorldPatterns = []

// 监听启动事件,用于注册 Hexcasting 模组的动作
StartupEvents.registry('hexcasting:action', e => {
    // 定义一个函数,用于注册动作模式
    function registerPatternWrap(seq, dir, id, isGreat, options) {
        // 将 isGreat 参数转换为布尔值
        isGreat = !!isGreat
       
        // 检查动作标识符是否存在于 PatternOperateMap 中
        if (!id in global.PatternOperateMap) {
            throw new Error('missing operate: ' + id) // 如果不存在,抛出错误
        }
       
        // 生成资源键,格式为 'yc:动作标识符'
        let resourceKey = 'yc:' + id
       
        // 如果是大仪式(Great Ritual),将资源键添加到全局数组中
        if (isGreat) {
            global.perWorldPatterns.push(resourceKey)
        }
       
        // 根据手势序列和方向生成模式对象
        let pattern = HexPattern.fromAngles(seq, dir)
       
        // 使用事件对象注册自定义动作
        e.custom(resourceKey, ActionRegistryEntry(pattern, new ActionJS(id, pattern, options)))}

    // 注册各种动作模式
    // 参数说明:
    // seq: 手势序列(由 a、w、d、q、e 组成的字符串)
    // dir: 手势方向(HexDir 枚举值)
    // id: 动作标识符
    // isGreat: 是否是大仪式(可选参数)
    // options: 额外选项(可选参数)
   
    // 注册射箭动作
    registerPatternWrap('weeeeewq', HexDir.SOUTH_WEST, 'arrow')
   
    // 注册延时动作
    registerPatternWrap('aeeeea', HexDir.EAST, 'time')
})
//图案事件

// 创建一个全局的ScheduleSignals对象,用于存储调度信号
global.ScheduleSignals = new WeakHashMap()
// 创建一个全局的PatternOperateMap对象,用于存储模式操作映射
global.PatternOperateMap = {
    //射箭法术
    "arrow": (stack, ctx) => {
            // Args参数
            let args = new Args(stack, 1)
            // 获取vec3属性
            let pos = args.vec3(0)
            // 断言位置在范围内
            ctx.assertVecInRange(pos)
            /**@type {Internal.SpectralArrow}*/
            // 创建一个光灵箭
            let arrow = new SpectralArrow(ctx.world, ctx.caster)
            // 创建媒质
            let sideEffects = [OperatorSideEffect.ConsumeMedia(Math.ceil(100))]
            // 合并NBT数据
            arrow.mergeNbt({
                life: 1150,
                damage: 1,
                pickup: 0,
                PierceLevel: 5,
            })
            // 设置箭的位置
            arrow.setPos(pos)
            // 生成箭
            arrow.spawn()
            return sideEffects
        },
    //延迟法术
    'time': (stack, ctx) => {
        // 创建一个Args对象,用于处理参数栈
        let args = new Args(stack, 2)
        // 获取参数栈中的代码列表
        let code = args.list(0)
        // 获取参数栈中的超时值
        let timeout = args.double(1)
        // 获取键
        let key = ctx.impetus || ctx.caster
        // 获取旧的信号
        let oldSignal = global.ScheduleSignals.get(key)
        // 如果旧的信号存在,则取消它
        if (oldSignal) oldSignal.cancel = true
        // 创建新的信号
        let mySignal = { cancel: false, code: code }
        // 将新的信号存储到ScheduleSignals中
        global.ScheduleSignals.put(key, mySignal)

        // 调度任务
        ctx.caster.server.scheduleInTicks(timeout, () => {
            // 如果信号被取消,则返回
            if (mySignal.cancel) return
            // 创建一个空的铸造VM
            let harness = CastingVM.empty(ctx)
            // 执行代码
            harness.queueExecuteAndWrapIotas(code, ctx.caster.level)
        })
    }
}

因为消耗媒质为固定值,就直接使用常消耗了,具体参见开篇教程。

(那预付款咋写呢?还没试,下次再说。)

延时的事件相信大家都能看懂,我本想解释。可惜这里空间太小,我写不下。
4.坠星标位

终于,克服重重困阻,我们已然窃取到图案注册的无限神力了!(?)

今天来点生电玩家不想看的东西(

咒法学似乎没有获取生物资源的手段。因此我们可以自然的移除栈顶的四个实体,前三个是僵尸,骷髅和女巫。杀死第四个实体,将他的复制生成于前三个位置:僵尸处复制品没ai,女巫处复制品没战利品,骷髅处复制品生命为1。

因为比较简单,仅提供思路,就不水字数了。

生成矿物的手段似乎也是必要的,血魔法中坠星标位的创意就非常不错。我们自然的入栈一个坐标P(咒法学获得的pos是v3d而非block.pos),检测是否露天,在其y+64处生成下落的岩浆块,32tick后在坐标P位置产生爆炸,P周边落下闪电,P下11格处生成一个矿物球就好了,不要忘记炫酷的特效!(实战确实很帅)

仔细一看其实陨星的落地与否根本不影响爆炸,完全是虚假宣传!

通过global,我们可以在start_up与server之间传递代码。那么在注册完笔顺后,首先在start_up的图案事件中放入如下事件:

//坠星标位
    "star": (stack, ctx) => {
        let args = new Args(stack, 1)
        let pos = args.vec3(0)
        let player = ctx.caster
        let level = player.level
        let server = player.server
        global.lTC(pos, level, server)
    }

然后是server里的坠星标位事件:

global.lTC = (pos, level, server) => {
    var x = Math.floor(pos.x())
    var y = Math.floor(pos.y())
    var z = Math.floor(pos.z())
    var Block = Java.loadClass('net.minecraft.world.level.block.Blocks')
    var BlockPos = Java.loadClass('net.minecraft.core.BlockPos')
    var POS = new BlockPos(x, y, z)
    console.log(POS)

    //*绎演丁真,鉴定为霏误*
    var isOpenSky = true
    for (var Y = y + 1; Y <= 320; Y++) {
        var Pos = POS.offset(0, Y - y, 0)
        var FBI = level.getBlock(Pos)
        //(for block id,Federal Block Inspector!)
        if (!FBI.getId().match(/minecraft:(air|cave_air|void_air)/)) {
            isOpenSky = false
            break}}
    if (!isOpenSky) {
        return}
        
    //*疑岩丁真,鉴定为飞物*
    for (let repeat = 0; repeat < 3; repeat++) {
        server.scheduleInTicks(repeat, () => {
            var SY = POS.getY() + 64
            server.runCommandSilent(
                `summon minecraft:falling_block ${x} ${SY} ${z} ` +
                `{BlockState:{Name:"minecraft:magma_block"}, ` +
                `Motion:[0.0,-2.0,0.0], ` +
                `Time:1 `)
           
            //*亿烟丁真,鉴定为沸雾*
            for (let t = 0; t < 6; t++) {
                server.scheduleInTicks(t * 3, () => {
                    server.runCommandSilent(`particle minecraft:lava ${x} ${SY - t*8} ${z} 1.5 1.5 1.5 0.2 150 force`)
                    server.runCommandSilent(`particle minecraft:smoke ${x} ${SY - t*8} ${z} 1 1 1 0.5 200 force`)
                    for (let i = 0; i < 8; i++) {
                        let angle = (i * 45 + t * 30) % 360
                        let px = x + 1.5 * Math.cos(angle * Math.PI / 180)
                        let pz = z + 1.5 * Math.sin(angle * Math.PI / 180)
                        let py = SY - t * 8;
                        server.runCommandSilent(`particle minecraft:flame ${px} ${py} ${pz} 0.2 0.2 0.2 0.05 300 force`)}})}
           
            //*遗焰丁真,鉴定为绯骛*
            server.scheduleInTicks(32, () => {
                level.createExplosion(x, y - 3, z)
                    .causesFire(true)
                    .strength(16)
                    .explosionMode("tnt")
                    .explode()
                server.runCommandSilent(`particle minecraft:explosion_emitter ${x} ${y} ${z} 2 2 2 5 600 force`)
                server.runCommandSilent(`particle minecraft:flash ${x} ${y + 2} ${z} 8 8 8 3 600 force`)
                let LP = new Set()
                for (let i = 0; i < 12; i++) {
                    let LX, LZ, LPXZ
                    do {LX = x + Math.floor(Math.random() * 32) - 16
                        LZ = z + Math.floor(Math.random() * 32) - 16
                        LPXZ = `${LX},${LZ}`
                    } while (LP.has(LPXZ))
                    LP.add(LPXZ)
                    server.runCommandSilent(`setblock ${LX} ${y} ${LZ} air replace`)
                    server.runCommandSilent(`summon lightning_bolt ${LX} ${y} ${LZ}`)
                    server.runCommandSilent(`particle minecraft:electric_spark ${LX} ${y + 2} ${LZ} 0.8 0.8 0.8 0.3 600 force`)
                    level.createExplosion(LX, y - 3, LZ)
                        .strength(4)
                        .explosionMode("tnt")
                        .explode()}
               
                //*已焉丁真,鉴定为菲芜*
                server.scheduleInTicks(5, () => {
                    let R = 4
                    let ORE = [
                        //深层煤矿石
                        { block: 'deepslate_coal_ore', weight: 15 },
                        //下界石英
                        { block: 'nether_quartz_ore', weight: 15 },
                        //深层青金石
                        { block: 'deepslate_lapis_ore', weight: 15 },
                        //深层铜矿石
                        { block: 'deepslate_copper_ore', weight: 15 },
                        //深层铁矿石
                        { block: 'deepslate_iron_ore', weight: 12 },
                        //深层金矿石
                        { block: 'deepslate_gold_ore', weight: 12 },
                        //深层红石
                        { block: 'deepslate_redstone_ore', weight: 12 },
                        //深层绿宝石
                        { block: 'deepslate_emerald_ore', weight: 2 },
                        //深层钻石
                        { block: 'deepslate_diamond_ore', weight: 1 },
                        //远古残骸
                        { block: 'ancient_debris', weight: 1 }]
                    for (let dx = -R; dx <= R; dx++) {
                        for (let dy = -R; dy <= R; dy++) {
                            for (let dz = -R; dz <= R; dz++) {
                                if (dx * dx + dy * dy + dz * dz > R * R) continue
                                let X = x + dx
                                let Y = y + dy - 11
                                let Z = z + dz
                                let blockPos = new BlockPos(X, Y, Z)
                                let selected = Block.BLACKSTONE
                                if (Math.random() < 0.6) {
                                    for (const ore of ORE) {
                                        if (Math.random() * 100 <= ore.weight) {
                                            selected = Block[ore.block.toUpperCase()]
                                            break}}}
                                level.setBlock(blockPos, selected.defaultBlockState(), 3)}}}})})})}}

这申必的缩进是为了让代码不那么长

后面的案例就更没啥技术含量了(前面也并没有!)

5.探知偏折

仅戴着探知透镜时隐身且将周边生物的跟随半径减小。既没写索敌恢复也没用weakmap,主打跑了就行。

因为不难所以连注释都没写,丁真逃过一劫(建议丢给AI写注释并让他恢复逆天缩进,不荐以人力搏之也)

PlayerEvents.tick(event => {
    let tick = event.getServer().getTickCount()
    if (tick % 40 == 0){
        let {player,level} = event
        let armorCheck = player.getHeadArmorItem().id == 'hexcasting:lens' && player.getChestArmorItem().isEmpty() && player.getLegsArmorItem().isEmpty() && player.getFeetArmorItem().isEmpty()
        if (!armorCheck) return
        player.potionEffects.add("minecraft:invisibility", 80, 0, true, true)
        let processNearby = (R, E) => {
            let box = player.boundingBox.inflate(R)
            for (let entity of level.getEntitiesWithin(box)) {
                if (entity == player || !entity.isLiving()) continue
                E(entity)}}
        processNearby(8, entity => {
            if (entity.tags.contains(114514)) return
                let followRange = entity.getAttribute('generic.follow_range')
                if (!followRange) return
                let original = followRange.getBaseValue()
                followRange.setBaseValue(original * 0.1)
                entity.addTag(114514)})}})

被恶臭蒙蔽了双眼(

6.阿卡夏结构

以下事件是:依照书籍摆出卡巴拉生命树的多方块结构后,手持不死图腾右击即可创建一棵漂亮的大型启迪木,内藏阿卡夏记录。足量的书架与桥接块。

为什么要干这种多此一举的事呢?因为我觉得为启迪树苗单纯创建一个法术很蠢,而且启迪树的枝叶明明很好看,为神马组合成一棵树就有点猥琐……

而且阿卡夏书架合一堆也有点烦的说。干脆结合一下直接搓一个多方块结构好了。

首先手搓个指导界面~

hexcasting魔改(1.20.1萌新版)-第1张图片
其实卡巴拉生命树好像根本就没存数据的功能,算了就当他有吧。而且生命的令符什么的好中二啊啊啊啊

{ "type": "patchouli:multiblock",
      "name": "卡巴拉生命之树",
    "multiblock": {
    "pattern": [
        [ "  D  "],
        [ "C   L"],
        [ "     "],
        [ "R   S"],
        [ "  G  "],
        [ "g 0 t"],
        [ "  B  "],
        [ "     "],
        [ "     "],
        [ "  H  "]
    ],
    "mapping": {
        "D": "minecraft:diamond_block",
        "C": "minecraft:coal_block",
        "L": "minecraft:lapis_block",
        "R": "minecraft:redstone_block",
        "S": "minecraft:sea_lantern",
        "G": "minecraft:gold_block",
        "g": "minecraft:emerald_block",
        "t": "minecraft:gilded_blackstone",
        "B": "minecraft:budding_amethyst",
        "H": "minecraft:honeycomb_block"
    },
    "symmetrical": true
    }
    }

那么创建的结构长啥样呢?就是封面啦
kubejs创建多方块啥的太麻烦了,直接上ruins。下面一大串还是建筑部分压缩版,使用时请在所有endlayer与layer之间换行。

rule1={Name:"hexcasting:edified_wood",Properties:{axis:"x"}}
rule2={Name:"hexcasting:edified_wood",Properties:{axis:"y"}}
rule3={Name:"hexcasting:akashic_bookshelf",Properties:{facing:"west",has_books:"false"},Ruins:{entity:{dummy:0b}}}
rule4={Name:"hexcasting:edified_wood",Properties:{axis:"z"}}
rule5={Name:"hexcasting:akashic_bookshelf",Properties:{facing:"north",has_books:"false"},Ruins:{entity:{dummy:0b}}}
rule6={Name:"hexcasting:akashic_connector"}
rule7={Name:"hexcasting:akashic_bookshelf",Properties:{facing:"south",has_books:"false"},Ruins:{entity:{dummy:0b}}}
rule8={Name:"hexcasting:akashic_bookshelf",Properties:{facing:"east",has_books:"false"},Ruins:{entity:{dummy:0b}}}
rule9={Name:"hexcasting:citrine_edified_leaves",Properties:{distance:"1",persistent:"true",waterlogged:"false"}}
rule10={Name:"hexcasting:aventurine_edified_leaves",Properties:{distance:"1",persistent:"true",waterlogged:"false"}}
rule11={Name:"hexcasting:amethyst_edified_leaves",Properties:{distance:"1",persistent:"true",waterlogged:"false"}}
rule12={Name:"hexcasting:amethyst_edified_leaves",Properties:{distance:"7",persistent:"true",waterlogged:"false"}}
rule13={Name:"hexcasting:akashic_record"}
rule14={Name:"hexcasting:aventurine_edified_leaves",Properties:{distance:"7",persistent:"true",waterlogged:"false"}}
rule15={Name:"hexcasting:edified_log",Properties:{axis:"y"}}
rule16={Name:"hexcasting:citrine_edified_leaves",Properties:{distance:"7",persistent:"true",waterlogged:"false"}}
rule17={Name:"hexcasting:conjured_light",Properties:{waterlogged:"false"},Ruins:{entity:{tag_colorizer:{owner:[I;0,0,0,0],stack:{Count:1b,id:"hexcasting:default_colorizer"}}}}}
rule18={Name:"hexcasting:amethyst_sconce",Properties:{facing:"up",waterlogged:"false"}}
rule19={Name:"hexcasting:citrine_edified_leaves",Properties:{distance:"2",persistent:"true",waterlogged:"false"}}
rule20={Name:"hexcasting:aventurine_edified_leaves",Properties:{distance:"2",persistent:"true",waterlogged:"false"}}
rule21={Name:"hexcasting:aventurine_edified_leaves",Properties:{distance:"3",persistent:"true",waterlogged:"false"}}
rule22={Name:"hexcasting:citrine_edified_leaves",Properties:{distance:"3",persistent:"true",waterlogged:"false"}}
rule23={Name:"hexcasting:amethyst_edified_leaves",Properties:{distance:"4",persistent:"true",waterlogged:"false"}}
rule24={Name:"hexcasting:citrine_edified_leaves",Properties:{distance:"5",persistent:"true",waterlogged:"false"}}
rule25={Name:"hexcasting:aventurine_edified_leaves",Properties:{distance:"6",persistent:"true",waterlogged:"false"}}

layer
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,1,0,0,0,0,0
0,0,0,0,0,2,0,0,0,0,0
0,0,0,0,2,3,2,0,0,0,0
0,0,4,2,7,6,5,2,4,0,0
0,0,0,0,2,8,2,0,0,0,0
0,0,0,0,0,2,0,0,0,0,0
0,0,0,0,0,1,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
endlayer
layer
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,2,0,0,0,0,0
0,0,0,9,2,3,2,9,0,0,0
0,0,0,4,7,6,5,2,0,0,0
0,0,0,11,4,8,2,10,0,0,0
0,0,0,0,0,2,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
endlayer
layer
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,12,0,0,0,0
0,0,0,0,0,2,0,0,0,0,0
0,0,0,0,2,13,2,0,0,0,0
0,0,0,0,0,2,0,0,0,0,0
0,0,0,0,14,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
endlayer
layer
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,14,0,0,0,0,0
0,0,0,12,0,0,0,12,0,0,0
0,0,0,0,0,15,0,0,0,0,0
0,0,0,14,0,0,0,16,0,0,0
0,0,0,0,0,16,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
endlayer
layer
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,16,17,14,0,0,0,0
0,0,0,17,0,0,0,17,0,0,0
0,0,0,0,0,15,0,0,0,0,0
0,0,0,17,0,0,0,17,0,0,0
0,0,0,0,16,17,12,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
endlayer
layer
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,16,0,0,0,0,0
0,0,0,0,12,0,0,0,0,0,0
0,0,0,0,0,15,0,0,0,0,0
0,0,0,0,0,0,14,0,0,0,0
0,0,0,0,0,12,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
endlayer
layer
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,12,18,0,0,0,0,0
0,0,0,14,0,0,0,0,0,0,0
0,0,0,0,0,15,0,0,0,0,0
0,0,0,0,0,0,0,16,0,0,0
0,0,0,0,0,18,14,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
endlayer
layer
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,14,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,14,0,0,0,0,0,0
0,14,0,16,0,15,0,12,0,14,0
0,0,0,0,0,0,16,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,14,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
endlayer
layer
0,0,0,0,0,12,0,0,0,0,0
0,0,0,0,14,8,14,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,14,0,16,0,0,0,14,0,14,0
12,5,0,17,0,15,0,17,0,7,12
0,14,0,12,0,0,0,12,0,14,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,14,3,14,0,0,0,0
0,0,0,0,0,12,0,0,0,0,0
endlayer
layer
0,0,0,0,0,14,0,0,0,0,0
0,0,0,0,16,8,16,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,16,0,0,0,0
0,16,0,0,0,0,0,0,0,16,0
14,5,0,12,0,15,0,14,0,7,14
0,16,0,0,0,0,0,0,0,16,0
0,0,0,0,14,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,16,3,16,0,0,0,0
0,0,0,0,0,14,0,0,0,0,0
endlayer
layer
0,0,0,0,0,16,0,0,0,0,0
0,0,0,0,17,12,17,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,12,0,0,0,0,0
0,17,0,0,0,0,0,16,0,17,0
16,12,0,18,0,15,0,18,0,12,16
0,17,0,14,0,0,0,0,0,17,0
0,0,0,0,0,16,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,17,12,17,0,0,0,0
0,0,0,0,0,16,0,0,0,0,0
endlayer
layer
0,0,0,0,0,17,0,0,0,0,0
0,0,0,0,0,12,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,17,12,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
17,12,0,0,0,15,0,0,0,12,17
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,16,17,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,12,0,0,0,0,0
0,0,0,0,0,17,0,0,0,0,0
endlayer
layer
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,18,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,18,0,0,0,15,0,0,0,18,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,18,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
endlayer
layer
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,19,0,0,0,0,0
0,0,0,0,20,11,20,0,0,0,0
0,0,0,19,11,15,11,19,0,0,0
0,0,0,0,20,11,20,0,0,0,0
0,0,0,0,0,19,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
endlayer
layer
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,21,0,0,0,0,0
0,0,0,0,22,8,22,0,0,0,0
0,0,0,21,5,8,7,21,0,0,0
0,0,0,0,22,3,22,0,0,0,0
0,0,0,0,0,21,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
endlayer
layer
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,23,0,0,0,0,0
0,0,0,0,23,8,23,0,0,0,0
0,0,0,23,5,5,7,23,0,0,0
0,0,0,0,23,3,23,0,0,0,0
0,0,0,0,0,23,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
endlayer
layer
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,24,0,0,0,0,0
0,0,0,0,24,8,24,0,0,0,0
0,0,0,24,5,3,7,24,0,0,0
0,0,0,0,24,3,24,0,0,0,0
0,0,0,0,0,24,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
endlayer
layer
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,18,0,0,0,0,0
0,0,0,0,25,12,25,0,0,0,0
0,0,0,18,12,3,12,18,0,0,0
0,0,0,0,25,12,25,0,0,0,0
0,0,0,0,0,18,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
endlayer
layer
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,17,14,17,0,0,0,0
0,0,0,0,14,12,14,0,0,0,0
0,0,0,0,17,14,17,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
endlayer
layer
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,12,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
endlayer
layer
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,12,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
endlayer
layer
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,12,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
endlayer
layer
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,18,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0
endlayer

最后是检测:

// 生命之树
BlockEvents.rightClicked(event => {
    const player = event.player
    const block = event.block
    const item = event.item

    // 1. 验证触发条件
    if (block.id !== 'minecraft:budding_amethyst') return
    if (item.id !== 'minecraft:stick') return

    // 2. 定义结构配置
    const structure = [
        { dy: -3, dz: 0, block: 'A' },
        { dy: 1, dz: -1, block: 'B' },
        { dy: 1, dz: 1, block: 'C' },
        { dy: 2, dz: 0, block: 'D' },
        { dy: 3, dz: -1, block: 'E' },
        { dy: 3, dz: 1, block: 'F' },
        { dy: 5, dz: -1, block: 'G' },
        { dy: 5, dz: 1, block: 'H' },
        { dy: 6, dz: 0, block: 'I' }
    ]

    const BLOCK_MAPPING = {
        'A': 'minecraft:honeycomb_block',
        'B': 'minecraft:emerald_block',
        'C': 'minecraft:gilded_blackstone',
        'D': 'minecraft:gold_block',
        'E': 'minecraft:redstone_block',
        'F': 'minecraft:sea_lantern',
        'G': 'minecraft:coal_block',
        'H': 'minecraft:lapis_block',
        'I': 'minecraft:diamond_block'  
    }

    // 3. 获取基础坐标
    const baseX = block.x
    const baseY = block.y
    const baseZ = block.z
    const world = block.getLevel()

    // 4. 结构验证
    let isValid = true
    for (let i = 0; i < structure.length; i++) {
        const entry = structure[i]
        const targetBlock = BLOCK_MAPPING[entry.block]
        const checkY = baseY + entry.dy
        const checkZ = baseZ + entry.dz
        const actualBlock = world.getBlock(baseX, checkY, checkZ)
        if (actualBlock.id !== targetBlock) {
            isValid = false
            break
        }
    }

    // 5. 创建遗迹
    if (isValid) {
        player.runCommandSilent(`testruin hexlog ${baseX} ${baseY} ${baseZ}`)}})