Skip to content

内联战利品表(谓词,物品修饰器)简要介绍

by CR_019
本文亦发布于 虚灵论坛B站专栏

1.20.5及以后,战利品表、谓词和物品修饰器可以内联在函数中调用,例如:

mcfunction
#内联战利品表
loot give @s loot {"pools": [{"rolls": 1,"entries": [{"type": "item","name": "minecraft:stone"}]}]}
#内联物品修饰器
item modify entity @s weapon.mainhand [{"function": "minecraft:set_components","components":{"custom_data":{"dc_random":1}}}]
#内联谓词
execute if predicate {"condition":"entity_properties",entity:"this",predicate:{type:"player"}}

对于简单的,无复用需求的战利品表,谓词等,可以直接内联在函数内,不需要专门新建一个文件去引用。
这种用法本身并没有什么好讲的,但是看起来很多人并不了解,在wiki上也仅在1.20.5的更新页面上有两行的篇幅,确实容易令人忽略,我觉得还是有提一下的需要。

内联战利品表(及其组件)与宏的综合运用

在数据包开发中,战利品表及其组件有一些很好的特性,例如战利品表的带权重随机抽取,谓词能提供很多的条件判定,物品修饰器可以直接修改玩家手中的物品而不需要专门将物品取出获取数据等等,可以较大的方便的数据包作者。
然而,他们与1.20.2引入的函数宏的相性并不好。作为函数外的额外json文件,战利品表组分无法加入动态的参数内容。而内联战利品表能解决这一问题。内联战利品表将json文件中的内容转移到了函数中,从而使得其中的组分可以使用宏参数动态控制。

简单实例:带权重的随机任务抽取

在我的dc前置包中,有这样一个需求:列表中有若干事件项,其中有一个weight参数,执行时按照权重从其中随机抽取一个事件执行。
使用函数实现需要处理很多额外的问题,比如权重的模拟,事件总数的获取等;但这种抽取和战利品表的抽取逻辑很相似。因此我们可以使用组建一个内联战利品表参数,存储在storage中,再使用函数宏调用它,生成物品,从其中抓取我们需要的事件参数。
这样就实现了动态的战利品表的效果。

具体的函数文件:

dc/function/events/random/execute.mcfuction:

mcfunction
#args required:events(带有weight属性作为权重,缺省默认为1)

execute if data storage dc events.temp.target.args.events run function dc:events/random/loot_table

execute if data storage dc events.temp.target.args.events run function dc:events/random/loot with storage dc:temp random

前一条指令用于生成战利品表,后一条指令用于应用内联战利品表生成物品并提取信息;

dc/function/events/random/loot_table.mcfuction:

mcfunction
data modify storage dc:temp random.loot_table set value {\
    "pools": [\
        {\
            "rolls": 1,\
            "entries": []\
        }\
    ]\
}
function lay:macro/list/init {list:"storage dc events.temp.target.args.events",func:"dc:events/random/events"}

首先在storage中初始化一个战利品表的外壳,然后从指定路径取出列表中的每一项加入战利品表的entries中;
lay:macro/list/init是一个列表操作模块,功能是将list中指定的列表中的每一项抽出,并执行func中的指令操作该列表项。

dc/function/events/random/events.mcfuction:

mcfunction
$data modify storage dc:temp random.event set value $(value)
data modify storage dc:temp random.entry set value {\
                    "type": "item",\
                    "name": "minecraft:stone",\
                    "functions": [\
                        {\
                            "function": "minecraft:set_components",\
                            "components":{\
                                "custom_data":{\
                                    "dc_random":1\
                                }\
                            }\
                        }\
                    ]\
                }
data modify storage dc:temp random.entry.functions[0].components.custom_data.event set from storage dc:temp random.event
execute if data storage dc:temp random.event.weight run data modify storage dc:temp random.entry.weight set from storage dc:temp random.event.weight

data modify storage dc:temp random.loot_table.pools[0].entries append from storage dc:temp random.entry

设定战利品表项。在前一个函数中我们将事件列表中的一项取出,现在我们建立一个战利品表物品抽取项的模板,然后将抽取出的事件中的event字段放在custom_data物品组件下供后续提取,将weight字段放在表项的weight位置,使战利品表根据此权重抽取该物品。
最后将这个物品项放进前一个函数构建的战利品表框架中。

dc/function/events/random/loot.mcfuction:

mcfunction
$loot spawn ~ ~ ~ loot $(loot_table)
execute as @e[type=item,distance=..5,limit=1,nbt={Item:{components:{"minecraft:custom_data":{dc_random:1}}}}] run tag @s add dc_random_temp
execute as @e[tag=dc_random_temp,limit=1] run data modify storage dc events.temp.target set from entity @s Item.components."minecraft:custom_data".event
kill @e[tag=dc_random_temp,limit=1]

function dc:events/_detect/event_execute with storage dc events.temp.target

使用刚才的战利品表路径作为宏参数,生成物品,并通过dc_random字段找到刚刚生成的物品,提取出抽出的事件项,放入事件处理模块执行。
最后,删除生成的临时物品即可。

小结

内联战利品表确实是很简单的特性,这也是为什么很少有人去讲它。但是它给我们提供了一个使用动态战利品表特性的可能性,基于这种特性,能够在特定情况下使数据包开发更方便。因此,还是有提一提的必要的。

附录:相关链接

wiki - 1.20.5更新内容wiki - 命令/itemwiki - 命令/lootwiki - 命令/execute#(if|unless)_predicate