观前提示

此教程使用游戏版本1.20.1-Forge_47.2.20

kubejs版本2001.6.4-build.138

probejs版本5.9.3-forge

skillslot版本1.20.1-forge-2.1.0

万用卡牌版本1.20.1-1.3.2

其余游戏版本或mod版本请酌情修改代码,代码存放于kubejs\server_scripts之中

距离技能槽模组发布也过去几个月的时间,但迟迟没有教程出现,就由我来开这个头吧

正文的代码均是我为自己整合包准备的创意,各位大佬若是有发现漏洞或有什么建议,也请在评论区提出

在此谢谢各位了

正文

在开始写代码之前,这里先要给大家介绍的是技能槽使用技能的实现方式

把主手的物品替换为使用技能的对应物品,使用(右键)完后再把物品替换回去

听起来是不是很生草?其实我也是在一次游戏卡顿中发现这个秘密的()

基于这个实现方法,还有模组作者给出的例子,我们能很轻易的得出两个事件

物品右击事件和方块右击事件

但其实还存在着第三个事件,那就是实体交互事件,这个事件是会覆盖物品右击事件与方块右击事件的

举个例子,你手里拿着末影珍珠右键箱子,是打开箱子还是使用末影珍珠?

这个例子是不是就能很轻易的理解了?

前期准备

在正式创建技能前,需要给技能物品添加tag,这里使用的是钻石和万用卡牌所有物品

ServerEvents.tags('item', event => {
  //除了直接填写物品id,也可以用@modid的方式,将mod里所有物品添加tag
  event.add('skillslots:skill', ['@omni_card', 'minecraft:diamond'])
})

除此之外,我们还需要加载技能槽的java类,方便我们获取玩家技能槽

const SkillSlotsHandler = player =>
  Java.loadClass('snownee.skillslots.SkillSlotsHandler').of(player)

物品右击事件

虽然我们创建了技能,但还没有给技能设置信息,我们首先写一个给技能槽添加技能的事件

//物品右击事件,限定绿宝石触发
ItemEvents.rightClicked("minecraft:emerald", event => {
  //解构,等同于event.player
  let { player } = event
  //技能对应物品
  let diamond = Item.of('minecraft:diamond')
  //为技能填写信息(nbt)
  diamond.nbt = {}
  diamond.nbt.SkillSlots = {
    //释放所需时间(前摇)
    //CD需要为物品设置cooldown,非nbt填写项
    UseDuration: 20,
    //图标大小缩放
    IconScale: 1.5,
    //设置为被动技能开关,仅限被动
    //CanBeToggled: true,
    //充能完毕音效,cooldown结束播放,留空为无
    ChargeCompleteSound: 'minecraft:entity.player.levelup',
  }
  //给技能槽添加技能(物品),此为给0槽位添加设置技能信息的钻石
  SkillSlotsHandler(player).setItem(0, diamond)
})

之后我们就可以给技能设置触发的能力啦,我在这里先附赠一个mob_effect的创建,代码放在kubejs\startup_scripts之中

有一点对于写mob_effect的注意事项,千万别用mergeNBT修改生物nbt,会导致效果时间不会减少

在这里贴出解释

使用kjs为Skill Slot(技能槽)模组添加技能-第1张图片

StartupEvents.registry('mob_effect', evetn => {
  //加载动物类
  let $Animal = Java.loadClass('net.minecraft.world.entity.animal.Animal')
  evetn.create('love_love_love')
    //设置为有益buff,大概是有益buff吧,毕竟运动有助于健康()
    .beneficial()
    //设置粉色
    .color(Color.PINK_DYE)
    //设置每tick的效果
    .effectTick((entity, lvl) => {
      //判断实体是否属于动物类且不是宝宝
      if (entity instanceof $Animal && !entity.baby) {
        //effect效果除余为0时执行,非0执行else
        //也就是每过一秒才会执行
        if (entity.getEffect('kubejs:love_love_love').duration % 20 == 0) {
          //繁殖CD = 0,发情时间 = 20 * 30,限制生育数量,不然非常恐怖!
          entity.setInLoveTime(600)
          entity.setAge(0)
        } else {
          //繁殖CD = 0,为了jade等高亮显示模组不显示繁殖CD提示
          entity.setAge(0)
        }
      }
    })
})

现在才是我们的技能效果

//物品右击事件,限定钻石触发
ItemEvents.rightClicked("minecraft:diamond", event => {
  //解构,等同于event.player
  let { player } = event
  //物品拥有技能信息时执行
  //?.是可选链操作符,作用的在访问为空或未定义的属性或方法时,不会报错,而是返回undefined
  if (event.item?.nbt?.SkillSlots) {
    //寻找玩家技能槽是否有对应物品,有则返回对应槽位,若无返回-1
    let index = SkillSlotsHandler(player).find(event.item)
    //技能存在时执行
    if (index !== -1) {
      //获取玩家的边界框,扩大5格,11*11
      let aabb = player.getBoundingBox().inflate(5)
      //数值记录,让之后的技能成功释放音效只播放一次
      let num = 0
      //这里的jsdoc实在是写不来,放弃了
      //forEach与之后的回调没有probejs的代码补全,库鲁西
      //对于每个在范围内的非宝宝动物添加效果
      event.level.getEntities(player, aabb).forEach(entity => {
        if (entity.animal && !entity.baby) {
          //给予20*10tick的love效果,效果为每20tick取消繁衍cd,并给予20*30tick的繁衍时间
          entity.potionEffects.add('kubejs:love_love_love', 200)
          //数值记录加1
          num++
          //玩家播放音效(经验拾取),提示技能是否释放
          if (num == 1) {
            //在所有实体列表中选择玩家,然后播放音效(音效名称,音量,音调)
            //不知道为什么player.playSound('entity.experience_orb.pickup', 0.05, 1)不能生效,有点奇怪
            event.server.entities.filterSelector(player).playSound('entity.experience_orb.pickup', 0.05, 1)
          }
        }
      })
    }
  }
})

最后,还记得我们之前所说的问题吗?实体交互事件会覆盖物品右键事件

所以我们还要复制一遍上面的代码,将事件改为以下事件

ItemEvents.entityInteracted("minecraft:diamond", event => {})

这样,我们的第一个技能就做好了,我将其起名为爱之灵药()

测试一下

使用kjs为Skill Slot(技能槽)模组添加技能-第2张图片使用kjs为Skill Slot(技能槽)模组添加技能-第3张图片

使用kjs为Skill Slot(技能槽)模组添加技能-第4张图片

好,完美


接下来看另外一个例子,在写这个事件的时候,出现了一件非常俺寻思的事情

当时我在问extra可不可以传入string数组,然后等了一会儿没人回答我

但俺寻思这代码可以运行,于是去运行测试,发现没有任何问题

结果一看群里,大佬都在说这个不能运行

我:?

迺逸夫大佬傲娇银毛狼耳小萝莉说extra被设计出来就是一对一的映射,不清楚我的kjs为什么能这样work

使用kjs为Skill Slot(技能槽)模组添加技能-第5张图片使用kjs为Skill Slot(技能槽)模组添加技能-第6张图片

//不知道什么时候会失效,总之先用着,以后再改
const omni_card = [
  'omni_card:blank_card',
  'omni_card:flame_card',
  'omni_card:torrent_card',
  'omni_card:thunder_card',
  'omni_card:bramble_card',
  'omni_card:earth_card',
  'omni_card:end_card'
]

//可投掷卡牌
//物品右击事件
ItemEvents.rightClicked(omni_card, event => {
  let { player } = event
  //检测是否为技能
  //WIP
    //寻找玩家技能槽是否有对应物品,有则返回对应槽位,若无返回-1
  let index = SkillSlotsHandler(player).find(event.item)
  //技能存在时执行
  if (index !== -1) {
    //玩家不为创造模式时执行,事件物品的数量加1
    if (!player.creative) {
      event.item.count++
    }
    //事件物品的冷却时间加20tick
    player.addItemCooldown(event.item, 20)
  }
})

//实体交互事件
ItemEvents.entityInteracted(omni_card, event => {
  let { player } = event
  //检测是否为技能
  //WIP
  //检测技能是否存在
  let index = SkillSlotsHandler(player).find(event.item)
  //技能存在时执行
  if (index !== -1) {
    //因为物品右击事件会被实体交互事件覆盖,所以额外写一个物品使用
    event.item.use(player.level, player, event.hand)
    //玩家不为创造模式时执行,事件物品的数量加1
    if (!player.creative) {
      event.item.count++
    }
    //事件物品的冷却时间加20tick
    player.addItemCooldown(event.item, 20)
  }
})

代码目前还是半成品,我目前还没写关于发射出去的飞牌实体会重新变成掉落物的处理,会发生刷取物品的状况,请根据实际情况调整代码

然后仔细观察的读者们可能发现了,我在写实体交互事件的时候比物品右键事件多写了一行代码

为什么要写这一行,我也在注释里解释了,所以我在这里简单讲解一下.use这个method

他是ItemStack类的方法,需要的参数是level(世界),player(玩家),hand(主手|副手)

举个例子,末影之眼

当玩家右键末影之眼的时候,物品末影之眼会变成实体飞出去,指引玩家前往要塞,且有几率物品损坏

所以末影之眼的use就是,将手中的物品数量减1(创造模式除外),并将物品变成实体飞出去,指引玩家前往要塞,且有几率物品损坏

怎么样,能理解了嘛?

万用卡牌的use有点不一样的是,他使用时发射的实体是固定的,不管手中的物品是什么,发射的一定是调用use的物品所对应实体

所以我可以直接写为event.item.use(player.level, player, event.hand),不用做特别的处理

想看.use实现的,可以去b站观看吕不才发布的视频『1.20.1 KubeJS6』物品use方法:飞翔的不死图腾! | 我的世界 Minecraft | 魔改小课堂

结语

呼,终于写完了

这篇教程是我写的最费劲的一篇教程了,注释能写明的全部写明了,应该能够让刚接触魔改的朋友们也能够轻松理解

最后感谢大家看到最后,希望我的教程能够帮助到有需要的人