跳到主要内容

宠物行为模块

宠物行为模块是一个告诉宠物该做什么的 Lua 文件:跟随主人、漂移、绕着营火打转、蜇刺。每个宠物物种文件会列出其宠物运行的模块,并为它们提供设置。本页面向想要修改某个模块或编写新模块的生物作者。若要使用和调整自带模块,参见宠物。

模块使用与 EliteMobs 行为程序相同的 Lua 方言:ai.module、ai.behavior、ai.sensor、ai.controls、带类型的记忆,以及 c.memory、c.perception、c.actuator 和 c.entity 上下文。那一页涵盖共享的规则。本页介绍宠物增加的内容:模块文件、c.pet、c.settings,以及宠物运行时受到的限制。

文件​

  • 模块位于 plugins/FreeMinecraftModels/pet_behaviors/,可在该文件夹或其任意子文件夹中。那里的每个 .lua 文件都是一个模块。
  • 模块的名称是其去掉 .lua 的文件名,使用小写。只能使用字母、数字、.、_ 和 -。宠物文件通过这个名称列出模块。
  • 模块的 id 必须是 fmm:pet/ 加上它的名称,所以 pet_behaviors/mine/slow_drift.lua 声明 id = 'fmm:pet/slow_drift'。id 与文件名不匹配的模块会被跳过并给出警告。
  • 当两个文件同名时,路径排序靠前的那个会加载,控制台会针对另一个给出 Duplicate content filename 警告。字节完全相同的副本会被静默跳过。
  • 链接到 pet_behaviors/ 之外的文件或文件夹会被跳过。
  • 对于自带的 15 个模块,只要文件夹中任何位置都没有同名文件,FreeMinecraftModels 就会写入该模块。被删除的模块会在下次加载时以当前自带版本回来。已经存在的文件永远不会被覆盖,无论是否编辑过,即使你把它移到子文件夹中也是如此,因此它保留你的更改,同时也错过自带模块之后的更新。
  • /fmm reload 会重新读取每个模块和宠物文件,把宠物收起,再让它们用新模块回来。它们的记忆从空开始。编辑模块后无需提高其 revision。

从一个副本开始​

自带模块是最好的示例:drift.lua 移动飞行者,wander.lua 行走,night_glow.lua 是单个传感器,sting.lua 有两个。请从最接近你想要的那个开始。

  1. 用新名称复制它,例如从 drift.lua 复制出 pet_behaviors/mine/slow_drift.lua。
  2. 把它的 id 改为 fmm:pet/slow_drift。
  3. 重命名它的记忆、行为和传感器,使它们以新名称开头:drift_to 变成 slow_drift_to,在 memories 中和每一个 c.memory 调用中都要改,行为的 id = 'fmm:pet/drift' 变成 fmm:pet/slow_drift。同一个生物上的两个模块不能共用记忆名称、行为 id 或传感器 id;列表中靠后的那个会被排除并给出警告。重命名可以让副本与原件并排运行。
  4. 在宠物文件中用它替换原件:- slow_drift,或带设置:- slow_drift: { speed: 0.03 }。
  5. 运行 /fmm reload 并检查控制台。

直接就地编辑自带模块也可以,但这会改变列出它的每个物种。

模块文件​

这个模块很小,但很完整。夜间,宠物不再漂移,而是停在其锚点附近的一个位置上,并且从选定该位置的那一刻起,每 10 秒播放一次动画。

-- At night: settles on one spot near its anchor (its owner, or home when wild) and plays an animation now and then.
return ai.module {
id = 'fmm:pet/night_rest', revision = 1,
-- radius: blocks around the anchor. speed: blocks per tick for a flyer, at species speed 1.
settings = { radius = 3, speed = 0.04, animation = 'interact' },
memories = { night_rest_to = 'position', night_rest_retry = 'integer' },
behaviors = {
ai.behavior {
id = 'fmm:pet/night_rest', priority = 12, probe_interval = 10,
controls = { ai.controls.move, ai.controls.look },
can_start = function(c)
return c.pet.night and not c.pet.staying and c.pet.anchor ~= nil
and c.tick >= (c.memory:get('night_rest_retry') or 0)
end,
can_continue = function(c)
return c.pet.night and not c.pet.staying and c.memory:contains('night_rest_to')
end,
start = function(c)
local anchor = c.pet.anchor
local to = anchor and c.pet:rest_point(anchor, c.settings.radius)
if to ~= nil then
c.memory:set('night_rest_to', to)
else
-- Nothing fits here: look again in 5 seconds instead of every tick.
c.memory:set('night_rest_retry', c.tick + 100)
end
end,
tick = function(c)
local to = c.memory:get('night_rest_to')
if to ~= nil then c.pet:steer_to(to, c.settings.speed * c.pet.species.speed) end
end,
stop = function(c)
c.memory:forget('night_rest_to')
c.pet:stop()
end
}
},
sensors = {
ai.sensor {
id = 'fmm:pet/night_rest_animation', interval = 200,
sense = function(c)
if c.memory:contains('night_rest_to') then c.pet:play_animation(c.settings.animation) end
end
}
}
}

随后宠物文件把它列为 - night_rest,或 - night_rest: { radius: 5, animation: idle }。

字段说明
id必填。fmm:pet/ 加文件名
revision必填。任何正整数
settings可选。宠物文件可以更改的默认值。每个默认值的类型就是宠物文件必须给出的类型:数字、true/false、文本,或文本列表。参见设置值
memories可选。模块为每只宠物记住的内容,按名称和类型声明:integer、number、boolean、string、uuid 或 position。请让每个名称都以模块的名称开头。参见记忆
dependencies可选。此模块所需的 pet_behaviors/ 中其他模块的 id。它们会在每个列出此模块的生物上运行,使用默认设置,除非宠物文件也列出了它们
behaviors可选。ai.behavior { ... } 条目,用于移动或转动宠物
sensors可选。ai.sensor { ... } 条目,从不移动宠物

允许在 return 之上编写代码,例如局部函数和常量;wander.lua 和 idle_look.lua 中就有一些。

回调能得到什么​

名称含义
c.pet宠物,只读。参见 c.pet
c.settings此模块的设置:它的默认值,加上宠物文件和变体的值叠加其上,如 c.settings.radius。每只宠物有自己的副本,因此写入只会改变那只宠物
c.memory针对 memories 中名称的 get(name)、set(name, value)、forget(name) 和 contains(name)。每只宠物有自己的记忆
c.tick当前游戏 tick,用于计时器
c.perception、c.actuator、c.entityMagmaCore 的 Mind 上下文,与 EliteMobs 行为程序中相同。c.entity 是隐藏的身体,而不是模型。参见 Mind 程序上下文。idle_look.lua 使用 c.perception:nearest_player(range) 和 c.actuator:look_at(point)

c.pet​

点是一个带有 x、y 和 z,并可选带有 world 的表,如 c.pet.position。没有 world 的点位于宠物所在的世界。位于另一个世界的点视为无:方法返回 nil、false 或 0,而 distance_to 返回 math.huge。c.pet 返回的点总是在宠物所在的世界中,本应在别处的点会读作 nil。

  • 方法用冒号调用:c.pet:distance_to(point)。
  • 数字参数必须是有限数。半径、深度、高度、等级、持续时间和其他范围会按下文所列设置上限。没有任何方法会加载区块。
  • c.pet 和 c.pet.species 是只读的。对它们赋值会抛出错误;请把每只宠物的值保存在 c.memory 中。
  • 属性和 route_target() 是快照:每个宠物 tick 读取一次,并在该 tick 的所有回调中保持不变。c.pet:teleport_near_owner 和 c.pet:stop 会刷新它们。以其他方式进行的移动(例如 c.entity:teleport)会在下一个 tick 显示出来。speed 是例外,始终是新读取的。
  • 每次读取位置都会得到一个新的表,模块可以修改它。

属性​

名称类型值
wildbool野生宠物为 true,玩家的宠物为 false
stayingbool被命令停留
settledbool没有停留,并且是野生的或距主人在 follow.distance 以内
babybool是幼崽
migratingbool正在飞行夜间迁徙路线
modelstring其变体的模型。穿着 babyModel 的幼崽仍读取其变体
positionpoint其身体所在位置
homepoint/nil野生宠物居住的地方;玩家的宠物为 nil
anchorpoint/nil它围绕游荡的对象:它的主人,或者对野生宠物而言是它的家,没有家时则是它所在的位置。主人离线或在另一个世界时为 nil
stay_anchorpoint/nil它被命令停留的位置;不处于停留时为 nil
owner_locationpoint/nil野生宠物,或主人离线、死亡或在另一个世界时为 nil
owner_distancenumber距主人的方块数;本世界没有主人时为 math.huge
night、dayboolday 仅在主世界类型的世界白天为真;黄昏、黎明、下界和末地都算作 night
rainingbool雨淋在宠物身上,最多每 10 个 tick 检查一次
speednumber其身体当前的速度,单位为方块每 tick
route_cruisenumber迁徙时其迁徙群的巡航速度,否则为 0
speciestable宠物文件中的数值,见下文

c.pet.species 包含:

字段宠物文件键
idspecies
speedfollow.speed
hover_heightfloat.hoverHeight
hover_variancefloat.hoverVariance
roam_radiusfollow.roamRadius
follow_distancefollow.distance
leash_distancefollow.leashDistance
teleport_distancefollow.teleportDistance
scalescale
baby_scalebabyScale

移动​

只能在行为的 start、tick 或 stop 中调用这些方法,并且该行为的 controls 要列出它们所需的控制权。在 can_start 或传感器中调用时,steer_to 和 stop 会抛出一个说明它们应该在哪里调用的错误。

方法返回值作用
steer_to(point, cruise)bool朝该点前进一个 tick,所以每个 tick 都要调用。需要 move 和 look。飞行者以每 tick cruise 个方块(0 到 4)飞行,临近时减速。行走者或游泳者以其身体类型移动速度的 4 × cruise 倍原生寻路前往那里,且绝不低于该移动速度的四分之一;cruise 为 0 时它会停下并返回 false。无法前往那里时返回 false
stop()nil停止移动。需要 move
teleport_near_owner(radius)bool把身体放到主人身边 radius 个方块以内(最多 64),在其自己的介质中;游泳者寻找水的范围至少为 6 个方块。行走者或游泳者会落在距主人至少一个方块处。当主人不在、在另一个世界、没有合适的位置,或该位置没有比宠物现在的位置至少近一个方块地靠近主人时,返回 false。它不需要任何控制权,但请从持有 move 的行为中调用,就像 follow_owner 那样,这样它就不会把宠物从另一个行为中拉走

位置与距离​

这些方法在任何回调中都可使用。返回点的方法,在没有任何位置合适或区块未加载时返回 nil。半径最大为 64。

方法返回值作用
hover_point(center, radius)point/nilradius 范围内其悬停带中的一个点
rest_point(center, radius)point/nil在其自身介质中休息的位置:飞行者是悬停带,行走者或固定身体是可站立的地面,游泳者是水(center 在水中时位于 center 的高度,否则位于水面下 0.5 到 2 个方块),两栖身体则是地面,没有地面时为水
ground_point(center, radius)point/nil一个可站立的位置,范围从 center 下方约 5 个方块到上方 4 个方块。位于液体或长距离落差之上时为 nil
water_point(center, radius, min_depth, max_depth)point/nil水面下两个深度之间的游泳位置(0 到 16)
ground_below(point, depth)number/nil向下 depth 个方块(1 到 64)范围内地面顶部的高度。水面或岩浆表面算作地面,除非物种是 AQUATIC。范围内没有地面时,为该点的高度减去 depth
trail_point(back, height)point/nil主人所面朝方向身后 back 个方块处;FLYING 物种高出主人脚部 height,其他物种与主人脚部同高。主人不在或在另一个世界时为 nil
distance_to(point)number距其身体的方块数
route_target()point/nil其迁徙路线在这个 tick 想让它去的位置。不在迁徙,或路线已结束时为 nil
find_perch(height, radius)point/nil位于它下方最近的树叶之上 height 个方块(0 到 8)处,向侧面最多 radius 个方块(0 到 4),或位于紧邻其下方的地面之上。玩家的宠物只会得到距主人 follow.distance 以内的栖息处
nearest_campfire(radius, keep_ticks, blocks, soul_variants, height)point/nilradius(1 到 32)范围内它可以使用的最近的点燃的火堆方块。blocks 列出它接受的火堆种类,CAMPFIRE、SOUL_CAMPFIRE 或两者,两者都没提到的列表则两者都接受;soul_variants 列出只接受灵魂营火的模型,此时其他所有模型只接受普通营火。玩家的宠物只会选择这样的火堆:其上方 height 处的位置距主人在 follow.distance 减 1 以内。每个区块的搜索结果保留 keep_ticks(20 到 1200),并由所有宠物共享
campfire_usable(fire, blocks, soul_variants, height)bool之前找到的火堆是否仍然点燃并且仍然合适

效果​

这些方法在任何回调中都可使用;传感器是常见的使用位置。

方法返回值作用
set_glow(light, lantern_bone, animation)nil以 light(0 到 15)点亮模型的显示骨骼,仅限 Java 版玩家。带有名为 lantern_bone 的骨骼的模型还会循环播放 animation。后两个参数是可选的
clear_glow()nil恢复为世界光照,并结束该动画
scale_toward(target, span, ticks, elapsed)nil让身体大小平滑地趋向其基础大小的 target 倍。从 1 到 span 的完整变化需要 ticks(至少 2);elapsed 是自上次调用以来的 tick 数。倍数 0.1 到 16
sting_contacts(on, effect, duration, amplifier, damage, cooldown)number蜇在最近一秒内触碰到野生宠物模型的玩家,从不蜇创造或旁观模式的玩家,并返回被蜇的数量。即使 on 为 false,它也会清除这些触碰记录。玩家的宠物不记录触碰
sting_enemies(reach, effect, duration, amplifier, damage, cooldown)number蜇模型碰撞箱 reach 个方块(0 到 4)范围内的敌对生物,从不蜇玩家或宠物,并返回被蜇的数量
drizzle(depth, chance)bool当宠物静止悬停在 depth 个方块(1 到 8)范围内的作物上方时,产生水粒子,并以 chance(0 到 1)的几率让该作物生长一个阶段。位于作物上方时返回 true
owner_effect(effect, duration, amplifier, range)bool当主人处于 range 个方块以内时,给予主人持续 duration 个 tick(1 到 6000)的药水效果,且不带粒子。主人已经拥有的、强度不低于它且持续时间不短于它的效果会被保留不动。主人不在、在另一个世界或超出范围,或者该效果不存在时返回 false
play_animation(name)bool播放模型的某个动画一次。模型没有它时返回 false

对于这两种蜇,effect 是药水效果名称或 NONE,duration 以 tick 为单位(1 到 6000),amplifier 为 0 是 I 级(最高 255),damage 以半颗心为单位,cooldown 是这只宠物能再次蜇同一个受害者之前的 tick 数(至少 1)。蜇刺会显示火花并播放蜜蜂蜇刺的声音,成年宠物会播放它的 attack 动画。不存在的药水效果名称,会对每个物种和名称各警告一次,并且不施加任何效果。

行为与传感器​

  • 行为会移动或转动宠物。它列出自己所需的控制权,c.pet 的移动调用需要 ai.controls.move 和 ai.controls.look,并且同一时间只有一个行为持有某项控制权。行为处于停止状态时,MagmaCore 会询问它的 can_start。当它返回 true 且控制权空闲时,它就会取得控制权并运行 start,然后在 can_continue 返回 true 期间每个 tick 运行 tick(没有 can_continue 时由 can_start 决定),最后运行 stop。参见生命周期。
  • priority 数字较小的先行。行为会从数字较大的、正在运行的行为手中夺取控制权,后者的 stop 随后运行。示例中的 12 使它排在跟随和停留之后,位于漂移和悬停之前,但也在 campfire 之前,所以同时列出两者的宠物在夜间永远不会去看营火。大于 15 的数字会让营火胜出。
  • 行为只争夺它列出的控制权。idle_look 只取 look,所以它在没有行为持有视线时运行;每个自带的移动行为也都取 look,所以移动总是优先于它。
  • 宠物文件无法更改优先级。若要重新排序,请复制该模块并更改数字。
  • probe_interval 是可选的,以 tick 为单位:行为处于停止状态时,只每隔那么多个 tick 询问一次 can_start,且每只宠物有各自的相位。当场上宠物很多时,它能节省服务器时间,代价是行为最多晚那么多个 tick 启动。它默认为 1,大于 200 时按 200 计。正在运行的行为仍然每个 tick 运行。
  • 传感器从不移动宠物。无论行为在做什么,它都会在宠物的身体第一次被 tick 的那个 tick 运行 sense,然后在宠物自己的相位上每隔 interval 个 tick 运行一次。发光、膨胀、蜇刺、细雨和主人效果都是传感器。

自带模块使用:

模块优先级probe_interval传感器 interval
migrate110
stay54
follow_owner104
campfire1510
day_perch1810
rain_bloom19104(膨胀)
drift、wander、swim2010
hover304
idle_look3010
night_glow40
sting4(玩家),10(生物)
crop_drizzle20
owner_effect20

规则​

  • 只能在行为的 start、tick 或 stop 中调用 steer_to 和 stop,并且该行为要列出它们所需的控制权。
  • 把每只宠物的状态保存在 c.memory 中,在 memories 下声明,名称以模块的名称开头。
  • 不要写入 c.pet;对它赋值会抛出错误。
  • 保持回调简短。每只宠物都在这些固定限制下运行,模块和宠物文件都无法更改:
限制值
单个回调1.5 毫秒
一只宠物在一个 tick 内的回调3 毫秒,最多 32 个回调
所有宠物在一个 tick 内的回调10 毫秒
单个回调的 CPU 时间,硬性限制20 毫秒
单个回调的 Lua 指令数,硬性限制20,000

当宠物达到前三个限制之一时,它剩余的回调会等到下一个 tick。超过硬性限制的回调会被停止,并像错误一样失败。参见预算。

出问题时​

这里的任何问题都不会使服务器或其他生物停止。在控制台中搜索 pet behavior、pet_behaviors 和 .pet.yml,可以找到这些警告。

  • 模块无法加载。 无法编译、id 与文件名不匹配,或文件名含有其他字符的模块,会被跳过并给出指明文件的警告,对于 Lua 错误还会给出行号。其他所有模块仍会加载。
  • 宠物文件指定了一个不存在的模块。 警告会指出宠物文件,并且在某个模块只差一两个字母时建议最接近的模块名称。该生物仍会加载,只是没有那个模块。当模块存在但加载失败时,警告会指向有关其文件的那条警告。
  • 某个设置有误。 模块没有的设置,或类型错误的值,会连同宠物文件的名称一起报告,并且保留默认值。参见设置值。
  • 两个模块冲突。 当一个生物上的两个模块声明了相同的记忆名称、行为 id 或传感器 id 时,该生物保留列表中靠前的那个,并在没有另一个的情况下运行,同时给出警告。
  • 宠物运行期间回调失败。 在下次重载之前,每个程序、回调和失败类型只记录一次。程序被命名为 fmm:pet/creature/<species>/<variant>,回调以其行为或传感器 id 命名,例如 fmm:pet/drift.tick。失败的行为会被停止或不启动,并从下一个 tick 起重试;在它持续失败期间,优先级数字更大的、移动或转动宠物的行为可能不会运行。失败的传感器会在下一个间隔再次运行。
  • c.pet 调用在 FreeMinecraftModels 内部失败。 原因会连同堆栈跟踪,对每个物种和调用只记录一次,并且回调以 c.pet:<method> failed; the server log has the cause 失败。
  • 药水效果名称不存在。 对每个物种和名称只警告一次,并且该调用不施加任何效果。

修复之后,运行 /fmm reload。