Cataclysm-DDA 砸击系统(Smashing)完全指南:伤害档案、地形/家具/场域砸击与源码实现
Cataclysm-DDA 砸击系统Smashing完全指南伤害档案、地形/家具/场域砸击与源码实现【免费下载链接】Cataclysm-DDACataclysm - Dark Days Ahead. A turn-based survival game set in a post-apocalyptic world.项目地址: https://gitcode.com/GitHub_Trending/ca/Cataclysm-DDASmashing砸击是 Cataclysm-DDA 中一套让玩家、NPC 与怪物对地图上的**地形terrain、家具furniture和场域field**造成破坏的核心机制你可以砸碎木门、拆毁墙壁、打烂窗户、把蛛网一扫而空也能用工具敲开上锁的容器。本文以 doc/JSON/MAP_SMASHING.md 为骨架结合 src/map.cpp、src/map_accessories.cpp、src/mapdata.cpp 等源码与data/json下的真实配置完整讲解砸击的 HP 计算、伤害档案damage profile的定义与实现、地形/家具/场域的bash配置参数以及底层调用链帮助你理解并自定义任何可破坏对象。砸击是什么三种可破坏对象与统一流程Smashing 允许玩家对地图上的三类对象造成伤害地形terrain墙壁、门、窗户、地板等静态地块家具furniture桌子、柜子、床等可放置物品场域field蛛网、烟雾、火等区域效果。从 src/map.cpp 的入口函数map::bash()可以看到一次完整的砸击会按固定顺序依次处理五种目标检查SEALED标志如上锁的容器若存在则直接砸地形/家具本体bash_field清除可砸的场域bash_items砸碎地上的玻璃类物品如瓶子、Molotovbash_vehicle对格内的车辆部件造成伤害bash_ter_furn最后砸地形与家具若前面没有砸到实体。砸击的判定结果由bash_params结构记录did_bash、success、bashed_solid、can_bash等字段上层逻辑攻击命中、车辆撞击、爆炸、NPC AI 等都通过这一入口统一触发。HP 的计算str_min 与 str_max文档明确了砸击对象血量的定义对象的 HP 由str_max值减去对应的str_min值得出。在 src/mapdata.h 中map_common_bash_info结构体保存了这两个核心字段// min str(*) required to bash // max str required: bash succeeds if str random # between str_min str_max也就是说str_min表示破坏该对象所需的最小力量门槛str_max则表示力量达到这个值必然破坏的上限。两者之间是一个随机区间砸击成功与否取决于一次随机数落在区间内的位置。HPhp()函数见 src/map.cpp默认返回str_max而单次砸击造成的伤害则由伤害档案换算得到见下文。特殊状态下的替代力量值map_common_bash_info还提供两组情境化的力量参数默认均为-1表示不启用见 src/mapdata.cpp 的加载逻辑参数含义触发条件str_min/str_max基础力量门槛与上限常规状态str_min_blocked/str_max_blocked被支撑blocked时的门槛对象被支撑物顶住如承重墙str_min_supported/str_max_supported有支撑supported时的门槛下方有SUPPORTS_ROOF地形damage_to()src/map.cpp会优先采用情境化数值先判断supported再判断blocked最终确定本次砸击实际使用的min值并交给伤害档案换算。伤害档案Damage ProfileJSON 定义伤害档案bash_damage_profile定义了将每种伤害类型换算成砸击伤害的乘数。文档给出的标准定义格式为{ id: my_profile, type: bash_damage_profile, profile: { bash: 1.0, cut: 0.5, stab: 2.0 } }字段说明id档案的唯一标识供地形/家具/场域的profile字段引用type固定为bash_damage_profileprofile一个键值表键为伤害类型 id如bash、cut、stab、bullet、heat等值为该类型转换为砸击伤害的乘数。仓库内置了两个真实档案见 data/json/bash_damage_profiles.json[ { type: bash_damage_profile, id: default, profile: { bash: 1.0 } }, { type: bash_damage_profile, id: wooden_door, profile: { bash: 0.95, cut: 1.2 } } ]default档案只把钝击bash按 1:1 换算wooden_door则把钝击轻微削弱为 0.95 倍、切割cut放大到 1.2 倍——这正是木门怕刀砍的数值体现。伤害计算乘数、减伤与求和文档给出的计算规则是伤害点数 武器每种伤害 × 档案中对应乘数 −str_min然后对所有伤害类型求和。对应源码是 src/map_accessories.cpp 中bash_damage_profile::damage_from()的实现int bash_damage_profile::damage_from( const std::mapdamage_type_id, int str, int armor ) const { int ret 0; for( const std::pairconst damage_type_id, int type : str ) { if( profile.find( type.first ) profile.end() ) { debugmsg( Damage type %s not included in damage profile?, type.first.str() ); continue; } // finalize ensures any valid damage_type_id is in profile ret std::maxint( 0, ( type.second * profile.at( type.first ) ) - armor ); } return ret; }几点值得注意的细节这里的armor参数由调用方传入正是上文提到的情境化str_min支撑/阻挡时替换即每次伤害都要先扣掉力量门槛每种伤害类型独立计算后求和一把同时带钝击和切割的武器两类伤害会叠加生效单项结果为负时被std::max(0, ...)截断为 0不会产生负伤害抵消其他类型。未指定伤害类型的兜底bash_conversion_factor文档特别说明对于未在档案中指定的伤害类型乘数由该伤害类型自带的 bash 转换系数bash conversion factor决定。这一规则在finalize()中落地src/map_accessories.cppvoid bash_damage_profile::finalize() { for( const damage_type dam : damage_type::get_all() ) { // emplace fails if its already in the map profile.emplace( dam.id, dam.bash_conversion_factor ); } }即每个伤害类型定义里都有一个bash_conversion_factor如 data/json/damage_types.json 中bash类型自身的转换系数为1.0。finalize()阶段会把所有合法伤害类型的转换系数作为默认值补进档案若 JSON 中显式写了该类型则emplace不会覆盖已存在的键保留手写值。check()src/map_accessories.cpp还会在数据校验阶段检查档案引用的伤害类型必须有效且乘数不能为负否则输出debugmsg警告。这也意味着——乘数不能写成负数。地形与家具的 bash 配置地形terrain与家具furniture通过各自的 JSON 定义中的bash对象启用砸击行为完整字段见 src/mapdata.cpp 的加载函数。以 data/json/furniture_and_terrain/furniture-alien.json 中的实际条目为例bash: { str_min: 30, str_max: 60, sound: splorch!, sound_fail: whump!, furn_set: f_alien_scar }公共字段地形/家具/场域通用字段类型/默认值说明str_minint默认-1破坏所需最小力量门槛str_maxint默认-1破坏所需力量上限决定 HPstr_min_blocked/str_max_blockedint默认-1被支撑物顶住时的门槛如承重结构str_min_supported/str_max_supportedint默认-1有支撑下方有SUPPORTS_ROOF时的门槛profilestring默认default引用的伤害档案 idexplosiveint默认-1破坏时产生的爆炸威力如煤气罐、油桶soundstring默认smash!成功砸碎时播放的文本与音效sound_failstring默认thump!砸击失败未破坏时的文本sound_vol/sound_fail_volint默认-1成功/失败音效音量-1时按str_min自动推导collapse_radiusint默认1破坏支撑物后的塌方影响半径destroy_onlybool默认false仅允许被强制摧毁destroy模式砸掉普通砸击无效bash_belowbool默认false允许从上方砸穿作用于地板hit_field字段 id 强度砸击命中无论成败时生成指定场域destroyed_field字段 id 强度砸碎后生成指定场域itemsitem group砸碎后的掉落物组tent_centers坐标数组帐篷类结构体的中心点用于整体塌落地形专属字段地形在公共字段基础上还要求ter_set必填src/mapdata.cpp字段说明ter_set必填砸碎后替换为哪个地形ter_set_bashed_from_above从上方砸碎时替换为的地形默认取ter_set实际示例data/json/furniture_and_terrain/LIXA_furniture_and_terrain.jsonbash: { str_min: 120, str_max: 460, sound: scrrrash!, sound_fail: whump!, ter_set: t_folded_wall }这面墙需要至少 120 点力量门槛、上限 460HP砸碎后变成可折叠墙t_folded_wall。家具专属字段家具使用furn_set指定砸碎后替换的家具默认为空furn_f_null即直接消失见 src/mapdata.cpp。场域Field的砸击场域同样支持bash配置data/json/field_type.json例如蛛网bash: { str_min: 1, str_max: 3, sound_vol: 2, sound_fail_vol: 2, sound: hsh!, msg_success: You brush aside some webs. }场域专属字段字段默认值说明move_cost100砸击该场域需要消耗的行动点src/mapdata.cppmsg_success无成功清除场域时的消息对于场域文档指出其破坏判定是伤害/HP概率伤害与 HP 的比值作为概率依据而非像地形/家具那样累积伤害。从 src/map.cpp 的bash_field()实现看任何带bash_info且非indestructible的场域都会在砸击时被移除因此实际行为以清除为主。半持久伤害累积越砸越容易地形与家具的伤害是累积存储的。在 src/map.cpp 的bash_ter_furn()中// Semi-persistant map damage. Increment by one for each bash over smin // Gradually makes hard bashes easier int damage get_map_damage( tripoint_bub_ms( p ) ); int damage_dealt bash-damage_to( params.strength, supported, blocked ); ... if( damage_dealt damage bash-hp( supported, blocked ) ) { damage 0; success true; } else if( damage_dealt 0 ) { damage damage_dealt; params.can_bash true; } set_map_damage( p, damage );每次砸击先取出格子上已积累的map_damage本次换算伤害 已积累伤害 ≥ HP 时判定成功并清零积累值未达到但造成了伤害时把伤害累加回格子set_map_damage下次砸击更容易。这解释了游戏中的体感硬墙第一下可能纹丝不动但只要持续砸会出现裂缝并逐渐松动直至崩塌。破坏与后续效果掉落、塌方、爆炸与警报砸碎成功后bash_ter_furn()与相关函数会触发一系列连锁效果src/map.cpp 之后的部分清空植物若对象带PLANT标志先清空格子上的作物孢子扩散真菌类地形/家具被砸碎时会释放孢子除非格内有火焰掉落物drop_bash_results()src/map.cpp按优先级决定掉落——有底层物品时按其拆解组件掉落否则用items掉落组若未定义items但定义了拆解掉落组则以拆解组为基础打碎一部分后掉落塌方若砸碎的是带SUPPORTS_ROOF的地形可能触发上方结构坍塌collapse_radius控制范围爆炸explosive 0的对象煤气罐等会引发爆炸警报带ALARMED标志的对象被砸时直接触发警报声src/map.cpp。强制摧毁与无限循环防护map::destroy()与map::destroy_furn()src/map.cpp会以力量 999 反复调用bash()但上限25 次——防止出现A 砸成 B、B 砸成 A这类无限循环// Break if it takes more than 25 destructions to remove to prevent infinite loops // Example: A bashes to B, B bashes to A leads to A-B-A-... int count 0; while( count 25 bash( p, 999, silent, true ).success ) { count; }map::batter()src/map.cpp则提供以固定力量尝试 N 次的批量砸击入口。从源码看完整调用链与判定细节一次砸击map::bash的完整判定链条为map::bash(p, str) // 入口构造 bash_params ├── TFLAG_SEALED 检查 → bash_ter_furn // 密封容器直接砸本体 ├── bash_field // 清除可砸场域 ├── bash_items // 玻璃物品 50%×玻璃占比概率砸碎并掉出内容物 ├── bash_vehicle // 车辆部件直接按伤害类型扣血 └── bash_ter_furn // 地形/家具的伤害换算与累积判定 ├── damage_to(str, supported, blocked) // 情境化 str_min 门槛 ├── hp(supported, blocked) // 情境化 str_max 作为 HP ├── get_map_damage / set_map_damage // 半持久伤害累积 ├── drop_bash_results // 掉落 └── 塌方/爆炸/警报等后续效果AI 与 NPC 的砸击评分怪物与 NPC 在决定要不要砸墙时使用map::bash_rating_internal()src/map.cpp有可砸家具时优先评分家具否则评地形地板需bash_below且允许砸地板车辆部件一律返回 2 分NPC 倾向于绕开车辆而非砸车伤害 HP 返回满分 10否则返回max(10 * damage / hp, 1)保证绝望的 NPC 也会尝试砸墙。这套评分直接驱动了幸存者破墙逃生、怪物破门而入的 AI 行为。结语如何为你的 mod 添加可砸对象综合本文为一个新地形/家具添加砸击能力只需两步定义或复用伤害档案使用内置default、wooden_door或在 data/json/bash_damage_profiles.json 中新增bash_damage_profile注意乘数非负、伤害类型合法在对象 JSON 中写bash块至少给出str_min、str_max以及地形的ter_set/家具的furn_set作为破坏后的替换结果再按需补充profile、items、sound、explosive、destroy_only、bash_below等字段。所有字段均可在游戏数据加载期通过check_all()校验见 src/init.cpp 的加载顺序与 src/map_accessories.cpp 的校验入口配置错误会在加载时报错提示方便迭代调试。【免费下载链接】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),仅供参考