跳到主要内容

炼制丹药

炼丹不是"放下一件方块就开工"。丹炉是一座手搭的固定 3×3×3:核心、左右两个投料仓、顶部产物仓,加 18 块炉壁。丹方也不认物品 ID 清单——玩家在核心点开炉之后,服务端才拿当时格子里的实际药性去判定产物。

这一页给示例包加一条最小产线:两条药性、三株药材(都绑定已有物品)、一份炉型、一种炉壁材料、一条丹方,以及一枚带丹毒的丹药。

你要搭建什么​

文件用途
data/example/mxt/medicinal_property/nourish.json主药药性。
data/example/mxt/medicinal_property/calm.json辅药药性。
data/example/mxt/quality/common.json炉型规格用的那一档品质;示例包里已经有就沿用。
data/example/mxt/spirit_herb/herb_a.json主药:每件 nourish 药力 3,性热。
data/example/mxt/spirit_herb/herb_c.json辅药:每件 calm 药力 3,性寒。
data/example/mxt/spirit_herb/herb_d.json药引:每件调和药力 1,性平。
data/example/mxt/alchemy_furnace/basic.json炉型规格:槽位、容量与冷却。
data/example/mxt/alchemy_wall_material/basic_wall.json炉壁材料的耐温。
data/example/mxt/heat_source/magma_block.json供热方块:岩浆块能提供多高温度、每 tick 升多少。
data/example/recipe/warming.json丹方:药性阈值、寒热容限、目标炉温与容差、时长与产物。
data/example/mxt/pill/warming_pill.json丹药作用:药效、丹毒与过量。
data/example/mxt/pill_binding/warming_pill.json丹药绑定:认领这一族物品,并给出服用次数与冷却。

第 1 步 —— 编写药性​

药性就是一张只有身份的空表,丹方按 ID 引用它。它不是元素:别复用元素、灵气或资源的 ID,也不要在本体里找"活血、聚元"这类名单。

// data/example/mxt/medicinal_property/nourish.json
{}

第二条照抄一份 calm.json。药性定义一个字段都没有(给了也读不到):显示名与描述一律按注册 id 现算,nourish 的名字键是 mxt.medicinal_property.example.nourish、描述键再加 .description,翻译这两个键才有词。

第 2 步 —— 将已有物品声明为药材​

灵植不注册新物品,它给已有物品挂一份元数据:药龄、寒热,以及它在三个角色里各提供多少药力。灵植定义自己没有档位字段——它不携带物品身份,物品堆上没有"我是哪份定义"的组件,所以解析时没有可问的定义;要给这株草一个档,就给它写一条 default_quality 定义,用 items 认领它的果实。

// data/example/mxt/quality/common.json
{}
// data/example/mxt/spirit_herb/herb_a.json
{
"items": ["minecraft:red_mushroom"],
"main_effects": { "example:nourish": "3 + herb_age / 50" },
"thermal_bias": 1.0
}
// data/example/mxt/spirit_herb/herb_c.json
{
"items": ["minecraft:brown_mushroom"],
"auxiliary_effects": { "example:calm": 3 },
"thermal_bias": -1.0
}
// data/example/mxt/spirit_herb/herb_d.json
{
"items": ["minecraft:sugar"],
"catalyst_power": "1 + herb_age / 100"
}

三件事要记住:

  • 角色由放进哪个仓决定,不由定义决定。 主药只看 main_effects,辅药只看 auxiliary_effects,药引只看 catalyst_power。把主药放进辅药仓,等于它在这一炉里没有任何药力。
  • thermal_bias 是寒热,不是元素。 负为寒、正为热,取值在 -1..1;它和火、水元素没有关系。
  • herb_age 是药力公式里的局部变量。 它读的是堆上的 mxt:herb_age 组件,没有组件时用这条灵植的 default_age。年龄抬高的是药力本身,管线不会再乘一次;一年药龄值多少完全由公式决定——这一页的公式把它折成 3 + herb_age / 50(药龄 100 时是 5),写成 3 * (1 + herb_age / 100) 时一件百年 A 才抵得上两件零龄 A。寒热加权也跟着年龄变,所以要求完全配平的丹方对药龄很敏感。

想让药材长在地里,就给定义加 blocks(哪些方块算这株草)与 growth(生长年龄、速率与果实,字段见 spirit_herb)。这一页的三株都只入药,所以省略 growth。

第 3 步 —— 炉型、炉壁材料与供热方块​

炉型是规格,不是世界里的方块:核心物品用组件挂上它,没有规格或规格不在注册表里就不能开炉。

// data/example/mxt/alchemy_furnace/basic.json
{
"quality": "example:common",
"main_slots": 2,
"auxiliary_slots": 2,
"capacity": 64,
"cooling_per_tick": 0.5
}
// data/example/mxt/alchemy_wall_material/basic_wall.json
{
"max_temperature": 200
}
// data/example/mxt/heat_source/magma_block.json
{
"blocks": "minecraft:magma_block",
"max_temperature": 200,
"heating_per_tick": 4
}
  • main_slots 取 1..2,auxiliary_slots 取 0..2。药引固定占辅药仓的第三格,不用声明;规格没用到的格子不能放东西。
  • capacity 是一炉材料的件数上限,还要服从物品自己的堆叠上限。
  • cooling_per_tick 是高于设定值、或停止供热之后每 tick 回落的量。供热格空着时炉温冷却到 0。
  • 炉温上限三者取最低。 这款炉型规格自己可选的 max_temperature、18 块炉壁都有效时它们的最低耐温、以及供热方块给出的最高温,三者里最低的那个就是上限;混用高耐温炉壁不能把薄弱处平均掉。把炉型规格的 max_temperature 写大能抬高上限,但抬不过炉壁与供热。设定温度必须落在 0 到那个上限之间。
  • heat_source 给一类方块定下两个数:能提供的最高温度与每 tick 升温量。它按 blocks 认领方块(单个方块 id、# 前缀的方块标签,或它们的数组),max_temperature 与 heating_per_tick 都必填且是有限正数;几条定义命中同一个方块时比 priority(高的赢,同分回落注册表顺序,默认 0)。文件放在 data/example/mxt/heat_source/<名字>.json 下。这一份让岩浆块当热源;字段与仲裁规则见 heat_source。
  • 品质只决定显示、使用条件,以及丹方可以要求的档位(核心的 furnace_quality 与逐格投料的 input_quality);它不推导槽位、容量、冷却或耐温。

第 4 步 —— 搭建丹炉、放入供热方块、投入药材​

形状是固定的,数据包改不了。把核心朝北放下,其余按图摆:

面向正面。左列 x=2,右列 x=0。中层 y=1,最下面一行最靠近你:

后 z=2
左侧 x=2 壁 17 | 壁 16 | 壁 15 右侧 x=0
主 14 | 壁 13 | 辅 12
前 z=0 壁 11 | 核 10 | 壁 9
  1. 核心放在正面中层 (1,1,0),index = x + 3 * z + 9 * y 等于 10。默认朝北;站在北侧、面朝南时,左侧是本地 x = 2。
  2. 左侧主药仓放 (2,1,1)(index 14),右侧辅药仓放 (0,1,1)(index 12),顶部产物仓放 (1,2,1)(index 22)。两个投料仓是不同的方块 ID,仓的角色不随旋转改变。
  3. 底层 y = 0 只砌四个角(index 0、2、6、8),其余五格(1、3、4、5、7)完全不校验,放什么、放不放、区块有没有加载都不影响成型;其中正中央那一格(index 4)是供热格。
  4. 其余 18 格砌 mxt:alchemy_furnace_casing,每块带上炉壁材料。中层中心 (1,1,1) 也是炉壁。
  5. 供热方块放进底层正中央那一格(index 4),主药放左侧仓,辅药与药引放右侧仓。
give @s mxt:alchemy_furnace[mxt:alchemy_furnace="example:basic"]
give @s mxt:alchemy_main_input
give @s mxt:alchemy_auxiliary_input
give @s mxt:alchemy_output
give @s mxt:alchemy_furnace_casing[mxt:alchemy_wall_material="example:basic_wall"]

炉壁要 18 块(底层四角加上面两层;创造模式里直接取最省事)。壳不齐、炉壁材料无效、或格子被另一座炉占了,都不算成型,界面会用文字列出缺了什么。

本体不提供热源方块

供热方块的数值写在数据包 mxt:heat_source 里,方块也可以自己实现接口。底层正中央那一格没有热源方块、或者放的是没有热量的方块时,可设炉温上限就是 0,开炉会因为温度被拒——先确认那一格放了一个表里有的方块。

第 5 步 —— 丹方​

丹方是原版配方类型 mxt:alchemy,文件放在 data/<命名空间>/recipe/<路径>.json 下,不是数据包注册表;它按药性判定,能写的字段就是下面这些。

// data/example/recipe/warming.json
{
"type": "mxt:alchemy",
"main_requirements": { "example:nourish": 6 },
"auxiliary_requirements": { "example:calm": 6 },
"catalyst_requirement": 1,
"balance_tolerance": 0,
"target_temperature": 100,
"temperature_tolerance": 5,
"duration": 200,
"max_bad_ticks": 2,
"minimum_aura": { "example:qi": 10 },
"success_outputs": [
{
"id": "mxt:pill",
"count": 1,
"components": { "mxt:pill": { "pill": "example:warming_pill" } }
}
],
"failure_outputs": [{ "id": "mxt:alchemy_dregs" }]
}
  • 阈值按角色分开,只比较药性键与数量。同角色里拆堆、合堆不改变结果:两件 herb_a 分放两个主药格,和堆在一格等价。
  • 出现配方没要求的非零主药或辅药药性,这条配方不匹配,也不扣料。三株药材各自只提供一种药性就是为这个。
  • balance_tolerance: 0 要求完全配平,判据是 Σ(数量 × 每件药力 × 寒热) / Σ(数量 × 每件药力) 的绝对值不超过容限。这份丹方的 2 × herb_a + 2 × herb_c + 1 × herb_d(零龄)正好落在 0。
  • 同时匹配上多条丹方时,只保留唯一支配其余每一条的那一条:药性键集合相同、每一项都不小于对方、且至少有一项严格更大。玩家不选丹方,没有唯一结果就是配伍冲突,同样不扣料。
  • target_temperature 与 temperature_tolerance 是炉温窗口。第一次开炉时设定温度必须落在里面,所以上面这份丹方要求你把炉温设到 95..105。
  • minimum_aura 查的是环境灵气,不是消耗,也不是热量。键写成本位世界里确实存在的那条灵气;或者让区域用 alchemy_env_bonus 顶替这道门槛(那个标记只顶门槛,不供热)。
  • duration 会被炼丹修正缩短,开炉时算一次就冻进这一批:原料侧取这批材料里最低的那一档品质的 alchemy_modifier,核心侧取丹炉核心那一档自己的 alchemy_modifier(核心没有档时算 1.0),有效时长 = max(1, round(声明时长 ÷ (原料侧 × 核心侧)))。所以给高炉阶写 alchemy_modifier: 2,这座炉子炼什么丹方都更快。
  • 失败物由丹方声明,服务端不会硬塞药渣;不写 failure_outputs 就什么都不产出。
  • guide 只是可选的示例元数据,丹炉不读也不显示,不想维护就省略。

放材料不会自己开炉。玩家在核心那页看当前配伍是否已匹配,再按「开炉」。

第 6 步 —— 丹药与丹毒​

丹药分成两份写:作用写在 pill 里,哪些物品是它、能服用几次写在 pill_binding 里。上面那条丹方的产物用组件点名了这份作用。

// data/example/mxt/pill/warming_pill.json
{
"color": "#FF9955",
"toxicity_gain": 25,
"toxicity_threshold": 100,
"toxicity_after_overdose": 20,
"on_consume": { "type": "mxt:heal", "amount": 4 }
}
// data/example/mxt/pill_binding/warming_pill.json
{
"items": ["mxt:pill"],
"pill": "example:warming_pill",
"max_uses": 2,
"cooldown": 20
}

手边没有成丹时可以直接发一枚:

give @s mxt:pill[mxt:pill={pill:"example:warming_pill"}]
  • 服用次数按这条绑定计,不按物品 ID,所以换一件别的物品当载体也绕不过上限。次数与冷却记在身体上,死亡、换维度、重新登录都不清。
  • 身份只由绑定的 items 认领产生:只写效果键、或组件指名了定义而这件物品没有被任何绑定认领时,这一口不计次数、也没有冷却。所以上面那条绑定点名了本体丹药载体 mxt:pill。组件的 pill 优先于绑定指名的定义,五个效果键再按字段覆盖。
  • 丹毒也是每个身体一份的账:toxicity_gain 是每次服丹加多少,累计达到 toxicity_threshold 才触发 on_overdose,toxicity_after_overdose 是触发之后剩下的量。阈值是"达到就触发",不是"禁止再吃"。
  • 排毒用实体行为 mxt:modify_pill_toxicity 的负 add,它不清服用次数;读丹毒用实体条件 mxt:pill_toxicity 或公式变量 pill_toxicity。
  • 默认丹毒不会自己退。服务端配置「炼丹 → 每秒丹毒自然消退」设成正数后,已经有丹毒的活跃实体每累计 20 刻退一次,离线不退,也不会给没服过丹的实体建一份空账。
  • 本体载体 mxt:pill 的图标按这份丹药的 color 上色,所以同一份作用挂到几件物品上时,只有本体那颗丹看得出颜色。别的物品照旧用它们自己的贴图,一个字都不改。

在游戏里验证​

(重新打开世界)
/mxt registries validate
/mxt registries list
  1. 按图搭好之后,每一格显示的是整座鼎在该格上的几何。对不上就是壳缺块、炉壁材料无效,或者格子被另一座炉占了。
  2. 右键核心、主药仓、辅药仓、产物仓,各自打开一页:核心那页是炉温读数与「设定 / 开炉 / 终止」,另外三页是各自的格子。四个页面读的是同一座炉的状态,不是方块自己另存了一份库存。
  3. 供热方块放进底层正中央那一格,主药放左侧仓,辅药与药引放右侧仓。放材料不会开炉。
  4. 在核心那页把炉温设到 100 并提交,再按「开炉」:批次开始,状态从待机走到预热、再到炼制,炉温朝设定值靠,进度与剩余刻数跟着走。
  5. 产物出来之后在产物仓里取。产物仓只能取不能投;装不下时批次停在「等待出货」,腾出格子它会自己进去,不用重炼。
这一页不写"炼成的丹药一定正确"

开炉之后那一批的成败判定还没有在游戏里逐条对过,所以本页不承诺某条丹方一定炼出什么。上面第 4、5 条列的是你能亲眼看到的东西——结构成型、四页界面打开、批次开始、炉温移动、产物入仓。判定本身对照核心那一页显示的状态与失败原因。

常见错误​

现象原因
结构不成型壳缺块(底层只认四个角,中层中心也要砌)、炉壁物品没带有效的炉壁材料,或者格子被另一座炉占了。
炉温升不上去底层正中央没有热源方块;或者最薄的那块炉壁、这个方块的 max_temperature、这款炉型规格的 max_temperature 把上限压到了设定值以下。
第一次开炉被拒设定温度必须落在丹方容差内;上限不够时温度根本设不下去。
材料像没生效角色由仓位决定:主药只看 main_effects,辅药只看 auxiliary_effects,药引只看 catalyst_power。放错仓等于没放。
药力够了却报配伍冲突出现了配方没要求的非零主药或辅药药性;或者同时匹配多条丹方而没有唯一支配结果;或者寒热偏差超出 balance_tolerance。
换成自己种的药材就失衡收获的药材带着成熟时的药龄,药力与寒热加权跟着变。要么用零龄药材,要么放宽 balance_tolerance。
产物仓满了批次停在「等待出货」,核心保留已经生成但还没入仓的产物,腾出格子即可。
活动中拆了炉壁或一座仓这一批按失败结算一次,已投入的材料不返还;拆一座仓只掉这座仓自己的物品。
漏斗抽不出产物产物仓只能从下侧面抽,而那一面成型后正对着中层中心那块炉壁。

接下来​