Skip to content

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

一、概要

(LootJS Wiki 已经非常详尽,本节仅做翻译 + 细微调整)

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

mod链接:Github Curseforge,许可:LGPL-3.0

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

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

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

二、LootJS是如何工作的

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

三、基本格式

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(战利品表类型)为给定战利品表类型[1]添加新的修饰器
getGlobalModifiers()返回包含所有战利品表修饰器的列表
removeGlobalModifier(值)[2]移除给定的战利品表修饰器

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

LootType.UNKNOWN // 未知
LootType.BLOCK // 方块
LootType.ENTITY // 实体
LootType.CHEST // 战利品箱
LootType.FISHING // 钓鱼
LootType.GIFT // 礼物(村民等)

[2]:你可以使用形如@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", [0, 0.1, 0.5, 1]) 
        .thenAdd("minecraft:diamond");
    
    /*
        [0, 0.1, 0.5, 1]:
          没有抢夺附魔时掉落概率为 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(回调)判断实体所骑乘的实体[1]
matchTargetedEntity(回调)判断实体的目标实体[1]
matchSlot(整形 栏位编号, IngredientJS 物品)判断实体给定物品栏栏位的物品

[1]:提供EntityPredicateBuilderJS

例子:

onEvent("lootjs", (event) => {
    event
        .addLootTypeModifier([LootType.ENTITY])
        .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(回调)判断造成伤害的实体源

[2]:提供DamageSourcePredicateBuilderJS

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

[5]:见 本章 常用函数 部分

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

[7]:如果不是则为0

[8]:例如

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

Contributors

Changelog