观前提示
此教程使用游戏版本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,会导致效果时间不会减少
在这里贴出解释
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 => {})
这样,我们的第一个技能就做好了,我将其起名为爱之灵药()
测试一下
好,完美
接下来看另外一个例子,在写这个事件的时候,出现了一件非常俺寻思的事情
当时我在问extra可不可以传入string数组,然后等了一会儿没人回答我
但俺寻思这代码可以运行,于是去运行测试,发现没有任何问题
结果一看群里,大佬都在说这个不能运行
我:?
迺逸夫大佬傲娇银毛狼耳小萝莉说extra被设计出来就是一对一的映射,不清楚我的kjs为什么能这样work
//不知道什么时候会失效,总之先用着,以后再改
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 | 魔改小课堂
结语
呼,终于写完了
这篇教程是我写的最费劲的一篇教程了,注释能写明的全部写明了,应该能够让刚接触魔改的朋友们也能够轻松理解
最后感谢大家看到最后,希望我的教程能够帮助到有需要的人