前言

[1.18.2]如何用实体伤害事件做出有特殊加成的装备?-第1张图片

本实例为Forge1.18.2,此类魔改脚本都应当放在kubejs/server_scripts里。

本人最近才接触JS,会的不多,本实例也是在学习与摸索过程中一点一点搓出来的,特意分享给大家。


正文


起初,只是因为觉得TAC的枪械在加了神化的情况下显得特别弱,于是我想着让枪械伤害也能造成神化的流血效果,在这样的想法驱动下,我给BA光环写了一套效果。

  onEvent('entity.hurt', event => {
    if (event.source.actual && event.entity.isLiving() && event.source.actual.isPlayer() && event.source.type === 'bullet' && event.source.actual.headArmorItem === 'yuushya:wriggle_nightbug' && event.source.actual.headArmorItem.getNbt()) {
      let {entity,  source} = event;
      let actual = source.actual;
      let health = Math.floor(entity.getHealth());
      let maxHealth = Math.floor(entity.getMaxHealth());
      let cmData = actual.headArmorItem.getNbt().CustomModelData;
     
      if (cmData === 11821901) { // 破甲
        let sundering = entity.potionEffects.getActive('apotheosis:sundering');
        if (sundering == null) {
          event.server.runCommandSilent(`effect give ${entity.id} apotheosis:sundering 15`);
        } else {
            let sua = sundering.amplifier + 1;
            let sud = sundering.duration / 20;
            let suTime = Math.round(sud + 1);
            let suLevel = Math.min(sua, 250);

            event.server.runCommandSilent(`effect give ${entity.id} apotheosis:sundering ${suTime} ${suLevel}`);
        }
        let strength = actual.potionEffects.getActive('minecraft:strength');
        if (strength != null) {

          let dx = entity.x - actual.x;
          let dy = (entity.y+entity.eyeHeight) - (actual.y+actual.eyeHeight);
          let dz = entity.z - actual.z;

          let yaw = Math.atan2(dx, dz);
          let yawDegrees = -(yaw * (180 / 3.14159)).toFixed(2);

          let distance = Math.sqrt(dx * dx + dz * dz);
          let pitch = Math.atan2(dy, distance);
          let pitchDegrees = -(pitch * (180 / 3.14159)).toFixed(2);

          actual.setRotation(yawDegrees,pitchDegrees);
        }
      }
      if (cmData === 11821902) { // 闪电
        if (health % 5 === 0 || health % 7 === 0){
          event.server.scheduleInTicks(1, () => {
            event.server.runCommandSilent(`execute in ${event.level.dimension} run summon minecraft:lightning_bolt ${entity.x} ${entity.y} ${entity.z}`);
            entity.attack(event.source, event.damage*1.5);
          })
        }
      }
      if (cmData === 11821903) { // 爆炸
        event.server.runCommandSilent(`execute in ${event.level.dimension} run summon minecraft:tnt ${entity.x} ${entity.y+1} ${entity.z}`);
      }
      if (cmData === 11821904) { // 起飞
        event.server.runCommandSilent(`effect give ${entity.id} minecraft:levitation 1 100`);
      }
      if (cmData === 11821905) { // 护盾
        let absorption = actual.potionEffects.getActive('minecraft:absorption');
        if (absorption == null) {
          event.server.runCommandSilent(`effect give ${actual} minecraft:absorption 5`);
          event.server.runCommandSilent(`effect give ${actual} minecraft:resistance 5 1`);
        } else {
          let aba = absorption.amplifier + 1;
          let abTime = aba * 5 - 5;
          let abLevel = Math.min(aba, 4);
          event.server.runCommandSilent(`effect give ${actual} minecraft:absorption ${5 + abTime} ${0 + abLevel}`);
          event.server.runCommandSilent(`effect give ${actual} minecraft:resistance ${5 + abTime} 1`);
        }
      }
      if (cmData === 11821906) { // 治疗
        if (actual.crouching) {
          event.server.runCommandSilent(`execute at ${actual} run effect give @e[type=!item,distance=..5] minecraft:instant_health`);
          event.server.scheduleInTicks(1, () => {
            for (let i = 0; i < 180; i++) {
              let x = actual.x + 5 * Math.cos(i);
              let z = actual.z + 5 * Math.sin(i);
              let y = actual.y;
              event.server.runCommandSilent(`execute in ${event.level.dimension} run particle dust 0 255 0 1 ${x} ${y + 0.1} ${z} 0 0 0 0.01 1`);
            }
          })
        } else {
          actual.heal(event.damage/2);
        }
      }
      if (cmData === 11821907) { // 互换
        let x1 = actual.x;
        let y1 = actual.y;
        let z1 = actual.z;
        let yaw1 = actual.getYaw();
        let pitch1 = actual.getPitch();
        let x2 = entity.x;
        let y2 = entity.y;
        let z2 = entity.z;
        let yaw2 = entity.getYaw();
        let pitch2 = entity.getPitch();
        event.server.runCommandSilent(`effect give ${actual} minecraft:resistance 1 4`)
        event.server.runCommandSilent(`effect give ${entity.id} minecraft:resistance 1 4`)
        event.server.scheduleInTicks(1, () => {
          entity.playSound('minecraft:entity.enderman.teleport')
          entity.setPositionAndRotation(x1,y1,z1,yaw1,pitch1)
        })
        event.server.scheduleInTicks(2, () => {
          actual.playSound('minecraft:entity.enderman.teleport')
          actual.setPositionAndRotation(x2,y2,z2,yaw2,pitch2)
        })
      }
      if (cmData === 11821908) { // 破伤
        let damage = event.damage;
        let minDamage = Math.min(damage * 5, health - damage - 1);
        if (health >= maxHealth * 0.9) {
          event.server.scheduleInTicks(1, () => {
            entity.attack(event.source, minDamage);
          })
        }
      }
      if (cmData === 11821909) { // 斩杀
        if (health <= maxHealth / 3) {
          event.server.scheduleInTicks(1, () => {
            entity.kill();
          })
        }
      }
      if (cmData === 11821910) { // 范围
        let entityList = event.level.getEntitiesWithin(AABB.of(
          entity.x-3,
          entity.y-1,
          entity.z-3,
          entity.x+3,
          entity.y+3,
          entity.z+3
        ))
        for (let entities of entityList) {
          entities.attack(event.source, event.damage/2);
        }
      }
      if (cmData === 11821911) { // 流血
        let bleeding = entity.potionEffects.getActive('apotheosis:bleeding');
        if (bleeding == null) {
          event.server.runCommandSilent(`effect give ${entity.id} apotheosis:bleeding 15`);
        } else {
            let bla = bleeding.amplifier + 1;
            let bld = bleeding.duration / 20;
            let blTime = Math.round(bld + 1);
            let blLevel = Math.min(bla, 250);
            event.server.runCommandSilent(`effect give ${entity.id} apotheosis:bleeding ${blTime} ${0 + blLevel}`);
        }
      }
      if (cmData === 11821912) { // 赌徒
        let random = () => Utils.random.nextInt(6); // 0-5
        let randomList = [random(), random()]
        event.server.scheduleInTicks(1, () => {
          if (randomList[0] === 0) {
            if (randomList[1] === 0 || randomList[1] === 2 || randomList[1] === 4) {
              entity.attack(event.source, event.damage*19);
            } else {
              actual.attack(event.source, event.damage*19);
            }
          }
        })
      }
      if (cmData === 11821913) { // 虚弱
        let weakness = entity.potionEffects.getActive('minecraft:weakness');
        if (weakness == null) {
          event.server.runCommandSilent(`effect give ${entity.id} minecraft:weakness 15`);
        } else {
            let wea = weakness.amplifier + 1;
            let wed = weakness.duration / 20;
            let weTime = Math.round(wed + 15);
            let weLevel = Math.min(wea, 4);
            event.server.runCommandSilent(`effect give ${entity.id} minecraft:weakness ${weTime} ${0 + weLevel}`);
        }
      }
    }
  })

[1.18.2]如何用实体伤害事件做出有特殊加成的装备?-第2张图片

讲解


很显然,一下子全发出来肯定有很多人看不懂,所以我接下来会逐一解释,首先如果你想直接跑一遍这串脚本,你至少需要安装KJSTAC神化悠然一派方块小镇热力系列

其中,神化和悠然一派用到了药水效果,方块小镇用到了帽子模型,热力系列用到了核弹,你也许想问,既然是给BA光环写的效果那为啥没有BA光环?那是因为BA光环的获取方法都是硬编码,没法魔改,如果我想修改光环配方只能另想办法,所以!我用到了原版的自定义模型Data,也就是数据包,通过魔改同样可以戴头上的物品模型(也就是方块小镇的帽子模型)实现了光环的复刻 (缺点是动画效果没了,geo模型实在没办法)。

判断伤害来源

在entity.hurt事件中,使用if判断玩家是否具有特殊条件。

 if (event.source.actual && event.entity.isLiving() && event.source.actual.isPlayer() &&  event.source.type === 'bullet' && event.source.actual.headArmorItem === 'yuushya:wriggle_nightbug' && event.source.actual.headArmorItem.getNbt())

event.source.actual   判断伤害源是否有直接伤害源(这条必须加,不然会报错),比如骷髅射箭击中玩家,伤害源为箭,直接伤害源为骷髅。

event.entity.isLiving()   判断受伤实体是否为活物。

event.source.actual.isPlayer()   判断直接伤害源是否是玩家。

event.source.type === 'bullet' 判断伤害源的类型是否未'bullet'(也就是子弹伤害)。

event.source.actual.headArmorItem === 'yuushya:wriggle_nightbug'   判断直接伤害源的头盔是否绝对等于'yuushya:wriggle_nightbug' 。

event.source.actual.headArmorItem.getNbt()   判断直接伤害源的头盔是否有NBT(这条也是必须加,不然就报错)。

定义

后面会重复使用的一些函数,可以提前定义常用函数以简化写法。

      let { entity,  source } = event;
      let actual = source.actual;
      let health = Math.floor(entity.getHealth());
      let maxHealth = Math.floor(entity.getMaxHealth());
      let cmData = actual.headArmorItem.getNbt().CustomModelData;

这些都比较通俗易懂,其中CustomModelData是一个自定义的模型ID,具体如何使用请看CustomModelData

在这里我一共做了15个自定义模型,ID分别是11821901 - 11821915,不同ID对应了不同的物品模型。

if (cmData === 11821901)

以第一个举例,如果检测到物品NBT里的CustomModelData一栏绝对等于这个数,那么则为true。

获取实体药水效果

let sundering = entity.potionEffects.getActive('apotheosis:sundering'); // 判断受伤实体身上的药水效果是否为'apotheosis:sundering'(破甲)。
        if (sundering == null) { // 如果受伤实体身上没有破甲效果。
          event.server.runCommandSilent(`effect give ${entity.id} apotheosis:sundering 15`); // 给受伤实体一个流血15秒的效果。
        } else { // 如果已经有破甲效果了。
            let sua = sundering.amplifier + 1; // 获取效果等级+1。
            let sud = sundering.duration / 20; // 获取效果时长(药水时长为tick值,游戏tick为每秒20,所以这里要除20)。
            let suTime = Math.round(sud + 1); // 延长1秒的效果时长。
            let suLevel = Math.min(sua, 250); // 设定一个最大值,不能超过250级。

            event.server.runCommandSilent(`effect give ${entity.id} apotheosis:sundering ${suTime} ${suLevel}`); // 受伤实体的流血效果延长1秒并且增加1级。
        }

锁头效果

let strength = actual.potionEffects.getActive('minecraft:strength'); // 检测直接伤害源身上的药水效果是否为'minecraft:strength'(力量)。
        if (strength != null) { // 如果直接伤害源拥有力量效果。

          let dx = entity.x - actual.x; // 获取受伤实体与直接伤害源的X差值。
          let dy = (entity.y+entity.eyeHeight) - (actual.y+actual.eyeHeight); // 获取受伤实体与直接伤害源的视线高度差值。
          let dz = entity.z - actual.z; // 获取受伤实体与直接伤害源的Z差值。

          // 计算yaw值
          let yaw = Math.atan2(dx, dz);
          let yawDegrees = -(yaw * (180 / 3.14159)).toFixed(2);

          // 计算pitch值
          let distance = Math.sqrt(dx * dx + dz * dz);
          let pitch = Math.atan2(dy, distance);
          let pitchDegrees = -(pitch * (180 / 3.14159)).toFixed(2);

          actual.setRotation(yawDegrees,pitchDegrees); // 设置直接伤害源的视线值(yaw值和pitch值)。
        }

假随机效果

if (health % 5 === 0 || health % 7 === 0){ // 检测受伤实体的血量处于5或7是否没有余数,如果没有余数则为true。
          event.server.scheduleInTicks(1, () => { // 延迟1tick执行。
            event.server.runCommandSilent(`execute in ${event.level.dimension} run summon minecraft:lightning_bolt ${entity.x} ${entity.y} ${entity.z}`); // 在实体坐标中心生成闪电。
            entity.attack(event.source, event.damage*1.5); // 给予受伤实体额外的1.5倍伤害,并且伤害的类型为伤害源。
          })
        }

画圆

if (actual.crouching) { // 检测直接伤害源是否为蹲下状态
          event.server.runCommandSilent(`execute at ${actual} run effect give @e[type=!item,distance=..5] minecraft:instant_health`); // 半径5格內的所有除物品以外的实体获得瞬间治疗效果。
          event.server.scheduleInTicks(1, () => { // 延迟1tick执行。
            for (let i = 0; i < 180; i++) {
              // 用180个粒子效果绘制一个半径为5格的圆形。
              let x = actual.x + 5 * Math.cos(i);
              let z = actual.z + 5 * Math.sin(i);
              let y = actual.y;
              event.server.runCommandSilent(`execute in ${event.level.dimension} run particle dust 0 255 0 1 ${x} ${y + 0.1} ${z} 0 0 0 0.01 1`);
            }
          })
        } else { // 如果不是蹲下的。
          actual.heal(event.damage/2); // 获取伤害的一半并用于治疗直接伤害源。
        }

位置互换

        //定义直接伤害源的x,y,z值和yaw,pitch值。
        let x1 = actual.x;
        let y1 = actual.y;
        let z1 = actual.z;
        let yaw1 = actual.getYaw();
        let pitch1 = actual.getPitch();
        //定义受伤实体的x,y,z值和yaw,pitch值。
        let x2 = entity.x;
        let y2 = entity.y;
        let z2 = entity.z;
        let yaw2 = entity.getYaw();
        let pitch2 = entity.getPitch();
        //给予直接伤害源和受伤实体1秒的抗性提升5(100%免伤)。
        event.server.runCommandSilent(`effect give ${actual} minecraft:resistance 1 4`)
        event.server.runCommandSilent(`effect give ${entity.id} minecraft:resistance 1 4`)
        // 受伤实体延迟1tick传送到直接伤害源的位置,并且发出末影人传送的声音。
        event.server.scheduleInTicks(1, () => {
          entity.playSound('minecraft:entity.enderman.teleport')
          entity.setPositionAndRotation(x1,y1,z1,yaw1,pitch1)
        })
         // 直接伤害源延迟2tick传送到受伤实体的位置,并且发出末影人传送的声音。
        event.server.scheduleInTicks(2, () => {
          actual.playSound('minecraft:entity.enderman.teleport')
          actual.setPositionAndRotation(x2,y2,z2,yaw2,pitch2)
        })

范围伤害

        let entityList = event.level.getEntitiesWithin(AABB.of( // 检测一个范围內的所有实体。
          entity.x-3,
          entity.y-1,
          entity.z-3,
          entity.x+3,
          entity.y+3,
          entity.z+3
        ))
        for (let entities of entityList) { // 遍历所有实体。
          entities.attack(event.source, event.damage/2); // 额外造成伤害的一半,并且伤害类型为伤害源。 
        }

俄罗斯转盘

        let random = () => Utils.random.nextInt(6); // 定义一个0-5之间的随机数(就是随机6个数)。
        let randomList = [random(), random()] // 随机两次
        event.server.scheduleInTicks(1, () => {
          if (randomList[0] === 0) { // 如果第一个随机数绝对等于0(理论上六分之一概率)。
            if (randomList[1] === 0 || randomList[1] === 2 || randomList[1] === 4) { // 如果第二个随机数绝对等于0,2或4(理论上二分之一概率)。
              entity.attack(event.source, event.damage*19); // 给受伤实体额外造成19倍伤害,并且伤害类型为伤害源。
            } else { // 如果第二个随机数不是0,2或4。
              actual.attack(event.source, event.damage*19); // 给直接伤害源额外造成19倍伤害,并且伤害类型为伤害源。
            }
          }
        })


DLC


这里就不是entity.hurt事件了,而且其他一些有意思的小功能。

吐籽

onEvent('item.food_eaten', event => { // 食物食用事件
  // 检测玩家是否头戴有特殊NBT的物品。
  if (event.player && event.player.headArmorItem === 'yuushya:wriggle_nightbug' && event.player.headArmorItem.getNbt() && event.entity.headArmorItem.getNbt().CustomModelData === 11821914) {
    let spitting = event.player.potionEffects.getActive('atmospheric:spitting'); // 检测玩家是否有吐籽效果。
    if (spitting == null) { // 如果没有吐籽效果。
      event.server.runCommandSilent(`effect give ${event.player} atmospheric:spitting 5`); // 给予玩家5秒吐籽效果。
    } else { // 如果已经有吐籽效果了。
        let spa = spitting.amplifier + 1; // 获取效果等级+1。
        let spd = spitting.duration / 20; // 获取效果时长除以20。
        let spTime = Math.round(spd + 2); // 效果时长+2秒。
        let spLevel = Math.min(spa, 250); // 效果等级最大不超过250级。
        event.server.runCommandSilent(`effect give ${event.player} atmospheric:spitting ${spTime} ${0 + spLevel}`); // 玩家的吐籽效果延长2秒并且增加1级。
    }
  }
})

虚空湮灭

onEvent('entity.death', event => { // 实体死亡事件
  // 检测实体是否为玩家,并且玩家是否头戴有特殊NBT的物品。
  if (event.entity.isPlayer() && event.entity.headArmorItem === 'yuushya:wriggle_nightbug' && event.entity.headArmorItem.getNbt() && event.entity.headArmorItem.getNbt().CustomModelData === 11821915) {
    event.entity.headArmorItem.count-=1; // 移除当前头戴的物品。
    event.server.runCommandSilent(`execute at ${event.entity} run kill @e[type=!item,distance=..18]`); // kill掉半径18格內所有除物品以外的实体。
    event.server.scheduleInTicks(1, () => {
      event.server.runCommandSilent(`execute in ${event.level.dimension} run summon thermal:nuke_tnt ${event.entity.x} ${event.entity.y} ${event.entity.z}`); // 延迟1tick生成一颗核弹。
    })
  }
})