Cataclysm-DDA 地形变换系统完全指南:深入解析 ter_furn_transform JSON 对象
【免费下载链接】Cataclysm-DDACataclysm - Dark Days Ahead. A turn-based survival game set in a post-apocalyptic world.项目地址: https://gitcode.com/GitHub_Trending/ca/Cataclysm-DDA
ter_furn_transform 是《大灾变:黑暗降临》(Cataclysm-DDA)中用于将地图上的"格"(tile)从一种类型变换为另一种类型的 JSON 对象,可作用于 field(场)、furniture(家具)、terrain(地形)与 trap(陷阱)四类地图元素。本文将以 doc/JSON/TER_FURN_TRANSFORM.md 为骨架,结合src/magic_ter_fur_transform.cpp、src/magic_ter_furn_transform.h的源码实现与data/mods/Magiclysm/ter_fur_transform/下的真实数据,系统讲解该对象的完整语法、加权随机、flag 匹配、消息提示机制,以及如何在 effect on conditions(EoC)、mapgen 与法术中调用它。读完本文,你将能独立编写、调试并复用地形变换定义,为魔法、陷阱、事件触发等模组功能提供可靠的地图变换能力。
一、什么是 ter_furn_transform
ter_furn_transform是一种 JSON 数据对象类型,它允许你指定一个"格"从一种类型变换为另一种类型。这里"格"(tile)指代的是地图上的四类元素之一:field(场)、furniture(家具)、terrain(地形)或trap(陷阱)。
从源码角度看,该对象由src/magic_ter_fur_transform.cpp中的generic_factory<ter_furn_transform>工厂管理(magic_ter_fur_transform.cpp),它继承自模板类ter_furn_data<T>,其中T可以是ter_str_id、furn_str_id、trap_str_id或field_type_id之一(见 magic_ter_furn_transform.h)。这意味着同一套变换语法被统一抽象,四类元素共用一套"结果 + 权重 + 消息"的数据模型。
一个最基础的示例:
[ { "type": "ter_furn_transform", "id": "example", "terrain": [ { "result": "t_dirt", "valid_terrain": [ "t_sand" ], "message": "sandy!", "message_good": true } ] } ]上面的例子将地形t_sand(沙子)变换为t_dirt(泥土)。它是通过直接比较地形 ID 实现的——当变换被触发时,系统读取目标格的当前地形 ID,与valid_terrain列表中的 ID 逐一匹配。
二、核心语法:result 与 valid_* 列表
2.1 单一结果
每个变换条目由两个核心字段组成:
result:变换的目标类型 ID(或 ID 列表/加权列表),支持terrain、furniture、field、trap四类对应的 ID 前缀(t_、f_、fd_、tr_)。valid_*:触发条件的源类型 ID 列表,分别为valid_terrain、valid_furniture、valid_field、valid_trap。
源码中的加载逻辑证实了这一点:在 magic_ter_fur_transform.cpp 中,terrain、furniture、field、trap四个成员分别被遍历,并把valid_terrain、valid_furniture、valid_field、valid_trap数组中的每个 ID 映射到同一个ter_furn_data结果对象(使用std::map::emplace存入对应的变换表)。
2.2 多个结果与随机选择
如果希望沙子变换为"泥土或草地",可以把result写成 ID 数组:
"terrain": [ { "result": [ "t_dirt", "t_grass" ], "valid_terrain": [ "t_sand" ], "message": "sandy!" } ]message_good是可选的,默认值为true(源码中通过jo.get_bool( "message_good", true )读取,见 magic_ter_fur_transform.cpp)。上面的例子在泥土与草地之间以1:1 比例随机选择,即两者各有 50% 概率。
2.3 加权随机结果
1:1 的随机比例可以通过加权调整为任意比例,例如 4:1:
"terrain": [ { "result": [ [ "t_dirt", 4 ], "t_grass" ], "valid_terrain": [ "t_sand" ], "message": "sandy!" } ]语法要点如下:
- 数组中每一项可以是单个字符串(隐式权重为 1),也可以是
[ "id", 权重 ]形式的嵌套数组; - 两种写法可以混合使用;
- 实际抽取时,系统会依据权重做加权随机。实现上,
weighted_int_list<T>是权重列表的核心容器,其pick()方法负责完成加权抽样,最终由ter_furn_data<T>::pick()返回被选中的 ID(见 magic_ter_fur_transform.cpp); - 当
result是单个字符串时,源码load_transform_results会直接以权重 1 将其加入列表(magic_ter_fur_transform.cpp),这与文档中"每个单字符串权重为 1"的描述完全一致。
2.4 四类元素通用
以上所有语法同样适用于fields、furniture和traps。例如:
"field": [ { "result": "fd_null", "valid_field": [ "fd_fire" ], "message": "The fires suddenly vanishes!", "message_good": true } ], "furniture": [ { "result": [ [ "f_null", 4 ], "f_chair" ], "valid_furniture": [ "f_hay", "f_woodchips" ], "message": "I need a chair" } ]值得注意的是,result可以是空类型 ID(如fd_null、f_null、t_null、tr_null),这意味着该变换可以用于移除对应的场、家具、地形或陷阱。
2.5 使用 flag 而非 ID 匹配
除了精确 ID 匹配,furniture、terrain和trap三类还支持用**flag(标志位)**进行匹配:
"terrain": [ { "result": "t_dirt", "valid_flags": [ "DIGGABLE" ], "message": "digdug" } ]valid_flags列表中的每个 flag 会与目标地形/家具/陷阱实际拥有的 flag 进行比对。从源码结构看,flag 匹配与 ID 匹配被存储在不同的表中:ter_flag_transform、furn_flag_transform、trap_flag_transform分别对应三类元素的 flag 变换表(magic_ter_furn_transform.h)。需要注意的是,field 不支持 flag 匹配——源码中 field 只有field_transform一张按field_type_id精确匹配的表。
匹配的优先级逻辑在transform()中体现:系统先尝试精确 ID 匹配,只有当 ID 匹配失败时,才遍历 flag 变换表,调用has_flag()检查目标是否携带该 flag(magic_ter_fur_transform.cpp)。例如对于地形:先查ter_transform中是否有t_sand这样的精确条目;若无命中,再遍历ter_flag_transform,检查当前地形是否带有DIGGABLE等 flag,命中即停止。
三、字段独立性:四类变换互不干扰
一个ter_furn_transform可以同时包含field、furniture、terrain和trap四个字段。但它们彼此独立处理——不存在"如果沙子变成泥土,就顺便把椅子放上去"这样的联动逻辑。
源码中,transform( map &m, const tripoint_bub_ms &location )会分别查询并处理四类元素(magic_ter_fur_transform.cpp):
- 读取目标格当前的地形
m.ter()、家具m.furn()、陷阱m.maptile_at().get_trap()与场m.field_at(); - 依次为每类元素寻找可用的变换结果;
- 独立执行设置:
m.ter_set()、m.furn_set()、m.trap_set(),场则先m.add_field()新场再m.remove_field()旧场,且保留原有的场强度(intensity)与年龄(age); - 四类结果之间没有任何依赖或联动关系。
对于场还有一个细节:一个格上可能存在多个场(如同时有烟雾与火焰),transform()会遍历该格的所有场,逐个尝试匹配并变换。
四、消息提示机制
每个变换条目都可以配置message(消息文本)与message_good(消息好坏标记):
message:当变换成功且玩家能看到该格时显示的消息。源码中,只有you.sees( m, location )为真且消息非空时才会输出,并通过add_msg_if_player发送给玩家(magic_ter_fur_transform.cpp);message_good:可选,默认true。为true时消息以正面颜色(m_good)显示,为false时以负面颜色(m_bad)显示。例如"The fires suddenly vanishes!"可以标记为message_good: true,而危险的陷阱触发则适合false。
这一机制让模组作者可以即时向玩家反馈变换结果,例如施法时显示"大地为你让路",或陷阱触发时警告玩家。
五、如何调用 ter_furn_transform
ter_furn_transform本身只是一份数据定义,它需要通过以下三种途径之一被实际触发。
5.1 在 effect on conditions(EoC)中使用
EoC 提供了两种相关效果,详细语法见 doc/JSON/EFFECT_ON_CONDITION.md:
u_transform_radius/npc_transform_radius:以玩家(u)、NPC(npc)或指定目标为中心,在半径范围内执行变换。
| 字段 | 必填 | 值 | 说明 |
|---|---|---|---|
| "u_transform_radius" / "npc_transform_radius" | 是 | int 或变量对象 | 变换发生的半径范围 |
| "ter_furn_transform" | 是 | string 或变量对象 | 使用的 ter_furn_transform 的 id |
| "target_var" | 否 | 变量对象 | 若指定,则使用变量中的目标位置代替玩家当前位置 |
| "time_in_future" | 否 | int/时长/变量对象/区间 | 延迟变换发生的时间;可用 "infinity" 使位置一直不更新,直到key事件发生 |
| "key" | 否 | string 或变量对象 | 外部可触发的事件 id,需配合 alter_timed_events 使用 |
示例一:将玩家周围 5 格内的地图元素按merc_spike_transform变换:
{ "u_transform_radius": 5, "ter_furn_transform": "merc_spike_transform" }示例二:在 2~10 秒后(或收到detonator事件时),将变量door_transform指向的位置周围 2 格按detonate_the_door变换:
{ "u_transform_radius": 2, "ter_furn_transform": "detonate_the_door", "target_var": { "global_val": "door_transform" }, "time_in_future": [ "2 seconds", "10 seconds" ], "key": "detonator" }transform_line:在两点之间画一条直线,并沿线逐格执行变换,常用于血迹、烧焦痕迹等轨迹效果(doc/JSON/EFFECT_ON_CONDITION.md)。
| 字段 | 必填 | 值 | 说明 |
|---|---|---|---|
| "transform_line" | 是 | string 或变量对象 | 使用的 ter_furn_transform 的 id |
| "first", "second" | 是 | 变量对象 | 由u_location_variable创建的两个坐标点,直线在二者之间 |
{ "transform_line": "blood_trail", "first": { "global_val": "point_0" }, "second": { "global_val": "point_1" } }5.2 在 mapgen 中使用
mapgen 中的ter_furn_transforms功能允许在指定的位置运行变换,可改变已生成的场、家具、地形或陷阱(包括置为空类型)。语法见 doc/JSON/MAPGEN.md:
transform(必填,string):要运行的ter_furn_transform的 id;place_ter_furn_transforms(配合 x/y):指定坐标执行变换,x、y可以是整数或[ min, max ]数组——若其中之一或两者是数组,则对该范围内的所有位置应用变换。
这使地形变换可以嵌入到建筑、区域或生物群落的生成流程中,例如生成岩浆湖时把范围内的地面统一变为岩浆地形。
5.3 在法术(spells)中使用
ter_furn_transform也可以作为法术效果使用。在 magic_spell_effect.cpp 中,法术通过sp.effect_data()读取变换 id,再将其转换为ter_furn_transform_id执行。法术效果(spell effects)的完整列表见 doc/JSON/MAGIC.md。这让法术能够"改造地形"——例如魔法流派 Mod Magiclysm 中的大地系法术。
六、真实案例:Magiclysm 中的 move_earth
仓库自带的 Magiclysm 魔法流派 Mod 提供了大量真实可用的ter_furn_transform定义,位于 data/mods/Magiclysm/ter_fur_transform/ter_fur_transform.json。以move_earth(移土术)为例,它演示了如何用一个变换定义完成多层地形递进:
{ "type": "ter_furn_transform", "id": "move_earth", "terrain": [ { "result": "t_open_air", "valid_terrain": [ "t_pit", "t_slope_down" ], "message": "The pit has deepened further." }, { "result": "t_pit", "valid_terrain": [ "t_pit_corpsed", "t_pit_covered", "t_pit_glass", "t_pit_spiked", "t_rootcellar", "t_pit_shallow" ], "message": "More debris shifts out of the pit." }, { "result": "t_pit_shallow", "valid_terrain": [ "t_grave", "t_dirt", "t_sand", "t_clay", "t_dirtmound", "t_grass", "t_dirtfloor", "t_sandbox" ], "message": "The earth moves out of the way for you." }, { "result": "t_sand", "valid_terrain": [ "t_pavement", "t_sidewalk", "t_concrete", "t_floor", "t_carpet_red", "t_linoleum_white", "t_slope_up", "t_rock_red" ], "message": "The rocks here are ground into sand." } ] }这个案例展示了三种实战技巧:
- 多条目并行:同一个变换可以包含多个 terrain 条目,每个条目对应一组不同的源地形和目标结果;
- 分类归纳:通过枚举大量相关地形 ID(各种坑、各种地面、各种岩石)实现"对整类地形生效";
- 消息差异化:每种变换都有独立的提示消息,如"The pit has deepened further.",配合默认
message_good: true显示为正面反馈。
类似的真实定义还存在于 data/mods/Magiclysm/ter_fur_transform/death_effects.json、data/mods/BombasticPerks/perkdata/perk_regions_and_mapgen/Closetland.json、data/mods/Isolation-Protocol/EOC/bossfight_mode.json 以及 data/mods/MindOverMatter/ter_transform/ter_transform.json 等文件中;测试数据见 data/mods/TEST_DATA/effect_on_condition.json 与 data/mods/TEST_DATA/magic.json。
七、编写与调试要点总结
- ID 前缀约定:地形用
t_前缀(如t_dirt)、家具用f_(如f_chair)、场用fd_(如fd_fire)、陷阱用tr_。使用不存在的 ID 会在加载时报错; - result 可以是空 ID:
fd_null、f_null、t_null、tr_null用于移除对应元素,是实现"灭火""拆家具""填坑"的标准做法; - 权重语法:
"result": [ "a", [ "b", 4 ] ]表示 a 权重 1、b 权重 4;单字符串隐式权重为 1; - ID 匹配优先于 flag 匹配:
valid_*与valid_flags可以共存于同一个变换(terrain/furniture/trap支持 flags,field 不支持); - 消息可选但建议提供:没有
message时玩家不会收到任何提示(源码中消息为空则跳过输出),但变换本身照常执行; - 四类元素独立:需要"同时改变地形与家具"时,在同一个变换中分别写
terrain和furniture条目即可,系统会分别处理。
以上要点均可在源码 src/magic_ter_fur_transform.cpp 与 src/magic_ter_furn_transform.h 中逐行验证。掌握 ter_furn_transform 之后,你便可以在事件、地图生成与法术系统中自由地塑造世界——无论是法师的移山填海,还是陷阱房的门户变幻,都只是几行 JSON 的事。
【免费下载链接】Cataclysm-DDACataclysm - Dark Days Ahead. A turn-based survival game set in a post-apocalyptic world.项目地址: https://gitcode.com/GitHub_Trending/ca/Cataclysm-DDA
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考