定义阵法
阵法是"搭出来的东西":玩家按你写的结构摆好方块,用阵盘一点,它就开始每 20 tick 收一次维持费、在半径内做事。结构由方块定义,行为由模块定义,寿命由费用定义——这三件事都在一个 JSON 里。
本篇给示例包加两座阵法:一座聚灵阵(给阵内的人加灵气域与增益),一座禁地阵(守御,把外人的破坏挡在半径之外)。
你要搭建什么
| 文件 | 用途 |
|---|---|
data/example/mxt/formation/spirit_gathering_array.json | 聚灵阵:结构、半径、维持费、增益模块。 |
data/example/mxt/formation/ward_array.json | 禁地阵:守御模块,按方块事件拦人。 |
第 1 步 —— 结构与半径
// data/example/mxt/formation/spirit_gathering_array.json
{
"structure": [
{"offset": [0, 0, 0], "state": "minecraft:gold_block"},
{"offset": [1, 0, 0], "state": "minecraft:iron_block"},
{"offset": [-1, 0, 0], "state": "minecraft:iron_block"},
{"offset": [0, 0, 1], "state": "minecraft:iron_block"},
{"offset": [0, 0, -1], "state": "minecraft:iron_block"}
],
"radius": 12
}| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
structure | List<RequiredBlock> | [] | 内联结构:每项 offset(相对阵心的整数三元组)+ state。 |
structure_template | Identifier | 无 | 用结构模板代替内联结构。与 structure 二选一:两个都给、或都不给,整份定义加载失败。 |
radius | NumberProvider | 必填 | 阵法的作用半径,单位格。半径是球,不是方块。 |
结构就是逐格全等:state 可以直接写方块 id,需要方块状态时写原版那种 {"Name": "…", "Properties": {…}}。offset [0,0,0] 是阵心——它不要求那里有方块,所以留空也是一种写法(很多阵法就是"围着空气搭一圈")。
阵心的容错是 3×3×3:用阵盘点歪一格不会失败,系统在点击位置周围找最近一个满足结构的阵心,点击位置本身有效时永远优先。
第 2 步 —— 添加增益模块
"actions": [
{
"type": "mxt:buff",
"abilities": ["example:body_tempering"],
"target": "allies"
},
{
"type": "mxt:aura",
"aura_zone": "example:misty_valley",
"max_bonus": {"example:qi": 40}
},
{
"type": "mxt:particle",
"shape": "mxt:five"
}
]mxt:buff 是阵法最常用的模块:
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
abilities | HolderOrTag<ability>[] | [] | 授予阵内目标的技能,写法与别处一致。 |
target | all|allies|owner | all | 谁受益。allies 会问敌我识别系统(见下)。 |
mxt:aura 管的是阵地本身:aura_zone 把范围内的灵气解析换成这个域(必填),max_bonus 抬这个域里已有灵气的上限({"example:qi": 40}),没写进域里的灵气加不上。
mxt:particle 只负责让人看得见这座阵法:shape 必填,写的是图形定义的 id(本体自带 mxt:five / mxt:six / mxt:eight,也可以自己写一份环 + 折线的图形,每一笔的颜色、疏密、尘埃大小、起点角度与生长窗口都写在那份图形里)、duration_ticks 默认 0(不画动画;写了就按这个时长把图形从阵心长出来,描线由客户端自己的计时器推)、interval_ticks 默认 10(每 10 tick 发一次心跳,最小也是 10、且必须是 10 的倍数)。
模块的可选类型一共十个:mxt:empty(占位)、mxt:attack(攻击)、mxt:buff(增益)、mxt:aura(灵气域)、mxt:protection(守御)、mxt:particle(阵形绘制)、mxt:send_message(喊话),以及三个通用节拍 mxt:activate / mxt:tick / mxt:inactivate。
target: allies 与敌我识别
allies 走的是 FriendService:任一位阵主认作自己人的实体才吃这份增益(归属是一组 UUID,好友判定会逐个问名单上的每一位),认不出的实体(DEFAULT)不给——把陌生人也算成友军是这里要避免的失败。配置侧还有一层开关:spare_friends 与「阵法 → 敌我识别」。细节见敌我识别系统。
第 3 步 —— 攻击模块与守御模块
// data/example/mxt/formation/ward_array.json
{
"structure_template": "example:ward",
"radius": 16,
"spare_friends": true,
"actions": [
{
"type": "mxt:protection",
"block_break": true,
"block_place": true,
"explosions": true,
"attack_entity": false,
"spare_friends": true
},
{
"type": "mxt:attack",
"damage": 4,
"damage_type": "minecraft:magic",
"attribute_to_owner": true,
"target_condition": {"type": "mxt:not", "condition": {"type": "mxt:formation_ally"}}
}
]
}mxt:attack 只说明"打多重",不说明"打谁"——打谁由逐实体的条件决定:
| 字段 | 默认 | 说明 |
|---|---|---|
damage | 0 | 每次结算的伤害量。 |
damage_type | 无 | 这一击的伤害类型;不写就退回原版的玩家/生物攻击来源。 |
attribute_to_owner | true | 是否把第一位阵主(归属名单首位,即当初立阵的那位)记为加害者(击杀与仇恨归他)。 |
target_condition | 恒真 | 逐实体条件;先过条件,再过伤害。 |
effects | [] | 顺带施加的效果;damage 为 0 时效果照样发。 |
mxt:protection 是按方块/实体事件拦人的守御模块:block_break、block_place、block_interact、explosions、mob_griefing、entity_interact、attack_entity、item_use 全部默认 true(想让它只管破坏就把其余的关掉),delegate_to_claims 默认 false(交给领地插件判)。
顶层还有一个 spare_friends(默认 false):它为真时,运行时把每一位阵主的好友与阵主本人一起放过。注意它和模块里同名的 spare_friends 是两个字段——顶层那个决定"整座阵法要不要跳过好友",模块里那个决定"守御拦不拦好友"。而敌我识别本身还受服务端配置「阵法 → 敌我识别」控制:关掉它,顶层开关就失效(DEFAULT 仍然停火)。
归属是一组 UUID:一座阵法可以有多位阵主,名单上的每一位都算阵主。激活时写入的是立阵的那位;之后用 /mxt formation owners <pos> add|remove <player> 增删(需要 gamemaster 权限),/mxt formation list 会把整组逗号分隔列出来。
第 4 步 —— 费用、存量与激活
"activation_costs": [{"id": "example:qi", "amount": 200}],
"maintenance_costs": [{"id": "example:qi", "amount": 2}],
"storage": {"capacity": {"example:qi": 600}}| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
activation_costs | List<Cost> | [] | 激活时一次性扣除,从激活者自己的账户扣;整份数组全有或全无,任何一项付不出就什么都不扣。写法见共享数据类型 · Cost。 |
maintenance_costs | List<Cost> | [] | 每个周期扣一次,决定阵法能撑多久;阵法内方块供的灵气先抵扣,缺口再由阵主支付。mxt:item 与 mxt:js 在这里永远付不出。 |
storage | 对象 | 无(不启用) | 写了就必须有 capacity:Map<aura, NumberProvider>。 |
费用条目写错会让定义加载失败
这两个列表的条目必须能解码:拼错字段名({"id": "example:qi", "amont": 200})不再是「打一条日志然后丢掉、阵法免费」,而是让整份定义加载失败。写错的消耗条目不会被静默丢弃。
structure 与 actions 同样是严格列表。改完阵法先看日志,别只看"能不能激活"。
激活顺序是:占用检查 → 结构校验 → 领地检查 → 半径 → 事件 → 扣激活费 → 写索引 → 区块灵气标脏 → mxt:activate 的方块行为。之后每个周期(20 tick)按这个顺序找钱:地脉吸收 → 存量 → 阵主。所以一座阵法可以靠脚下的灵脉自己养活;不够时先吃存量,再向阵主收;全都拿不出来就发一个可取消的"维持失败"事件,没人拦着就拆除。
storage 是给"阵主不在也要运转"准备的:容量按灵气分别记,拆除时里面剩下的灵气直接散逸。
激活与拆除都不用命令:
- 拿一块阵盘(
mxt:formation_plate)。 /mxt formation bind example:spirit_gathering_array把阵法写进主手那块阵盘(需要 gamemaster)。- 手持已绑定的阵盘右键阵心 → 激活。没绑定的阵盘会自己认出脚下的阵法(按白名单逐座比对结构,离点击位置最近的一座胜出;默认开启,可用服务端配置「阵法 → 阵盘自动识别」关掉)。
- 对已激活的阵心再用一次阵盘 → 拆除。需要是阵主或管理员;服务端配置「阵法 → 队友可拆除」打开后,阵主的好友也可以。
第 5 步 —— 让阵主受自身阵法影响
想让阵法"自己人也打",靠三个通用节拍模块:mxt:activate(进阵)、mxt:tick(每周期)、mxt:inactivate(出阵),每个都带一个方块行为(阵法自身)和一个实体行为(逐实体)。它们都是普通的方块行为/实体行为,所以阵法的表现力等于行为类型的表现力。
一个实用的开局:进阵提示一声,出阵提示一声。
"actions": [
{
"type": "mxt:activate",
"entity_action": {
"type": "mxt:play_sound",
"sound": "minecraft:block.beacon.activate",
"volume": 0.6,
"pitch": 1.4
}
},
{
"type": "mxt:inactivate",
"entity_action": {
"type": "mxt:play_sound",
"sound": "minecraft:block.beacon.deactivate",
"volume": 0.4,
"pitch": 0.8
}
}
]逐实体那一半只在实体真的在半径内时触发(球半径判定),而且只针对已加载的实体;区块卸载/重载会造成一次"离开再进入",所以进场那一半要写得幂等——重复播放一次提示音没关系,重复发奖励就不行。
在游戏里验证
/mxt registries validate
/mxt formation bind example:spirit_gathering_array
/mxt formation list
/mxt formation info- 摆好结构,
/mxt formation bind …把阵法写进阵盘(提示"已将 %s 绑定到手持阵盘")。 - 右键阵心激活。
/mxt formation list会列出 ID、阵心坐标、半径、阵主名单与已付费的维持次数。 - 站进半径里:
/mxt formation info报告覆盖你的阵法(重叠时全部列出)。 - 观察灵气:如果
aura_zone指向了某个域,站进阵内再查一次灵气,应该看到域切换带来的变化。 - 挖掉结构里的一块方块:不会立刻有提示,但下一个周期校验失败后阵法静默拆除——
list里消失,info也不再报告它。 - 想手动收尾:对已激活的阵心再用一次阵盘。
常见错误
| 现象 | 原因 |
|---|---|
定义加载失败,报 structure_template/structure 相关错误 | 两个都写了或都没写;必须恰好一个。 |
| 右键阵心报"结构不符" | 方块逐格全等比较,差一个方块状态(楼梯朝向、活板门开关)都不算匹配。 |
| 激活报"半径无效" | radius 非有限或 ≤ 0。 |
| 费用看起来"从来没扣过" | 维持费只从地脉、存量与阵主账户出;写 mxt:item 或 mxt:js 的条目在这里永远付不出。字段名写错则直接加载失败,不再表现为静默免费。 |
| 激活后立刻消失 | 结构在激活后被破坏:每 20 tick 校验一次,失败即静默拆除。 |
| 守御不拦某一个外来实体 | 该实体被敌我识别判成 TRUE(好友);顶层 spare_friends + 「阵法 → 敌我识别」一起决定这件事。 |
| 阵内所有人都不受增益 | target 写了 allies,但没人能被判定为友军(DEFAULT 一律不给)。 |
| 灵气域没生效 | aura_zone 写的是域 id,不是"灵气 id";域不存在时该字段解析失败。 |
bind 报"这块阵盘不能激活 %s" | 阵盘的允许列表里没有这座阵法(empty_plate_allows_all 为假时空白名单也不放行)。 |
接下来
- formation(阵法) —— 完整字段表、结构模板、灵气覆写与诊断。
- 敌我识别系统 ——
spare_friends与target: allies背后的判断。 - 灵气计算 —— 灵气域与阵法抽取环境灵气的实现。
- 命令 ——
/formation的全部子命令与阵盘行为。