查看源码下载文档
CHAPTER / 08

单位与特殊机制:用有限的公共系统做出不同兵种

红警兵种丰富,并不是因为每种兵都有完全独立的引擎。多数差异来自类型配置、共享任务状态机和少量特殊分支;但工程师、军犬、运输、超时空等机制又确实需要改变公共流程。理解两者的边界,才知道怎样新增单位、怎样避免把某个兵种的例外误当成全局规则。

本文对应提交 0dc09bb1d6188d1ec79ab4765b22a48b75ffee32。仓库的 DEFINES.H 开启了 FIXIT_CSIIFIXIT_CARRIERFIXIT_PHASETRANSPORTFIXIT_ENGINEER 等分支,同时还存在 NewUnitsEnabled 运行时判断。这里分析的是该源码树,不能把所有资料片及实验能力都说成初版战役默认可用。DEFINES.H:90–129

1. 四大类单位解决四种不同问题

共享的核心能力 重点特化
InfantryClass 步行、目标、伤害、任务 站卧动作、射击关键帧、恐惧、占领、潜入
UnitClass 地面驱动、炮塔、任务 采矿、部署、碾压、载人、布雷
AircraftClass 目标、武器、任务、货舱 高度、起降、机场握手、攻击航线、补弹
VesselClass 地面驱动式转向、目标、任务 水域通行、鱼雷、反潜、上浮、登陆卸载

单位既有类型级 Class,也有实例级 Strength、Ammo、TarCom、NavCom、Mission、Status。类型描述“这类东西通常能做什么”,实例描述“这一辆现在在做什么”。特殊玩法经常只改变某个实例状态,而不用复制整个类型。

共享 TechnoClass::AI 中的自愈就是一例:若类型 IsSelfHealing,达到维修节奏且生命比例不高于黄色阈值,就加一点血。它不天然意味着持续修到满血;每次加一点也可能越过阈值一点后停止。该标志又能通过 INI 的 SelfHealing 设置。TECHNO.CPP:2331–2362TECHNO.CPP:6289–6295

2. 步兵:动画帧是玩法时序的一部分

步兵 Firing_AI 在允许攻击时先进入站立或卧倒开火动作,并设置 IsFiring;直到动画到达 FireLaunchProneLaunch 指定的关键帧才调用 Fire_At。这保证枪响与举枪动作吻合,也意味着只比较武器 ROF 无法完整描述步兵开火时序。INFANTRY.CPP:3559–3654

受伤会增长 Fear;Fear_AI 逐帧降低恐惧,恐惧高、没有移动目的地且未行走时可以卧倒,低于阈值再起身。胆小类型还会逃散。卧倒不仅换图片,Take_Damage 会把正伤害乘 Rule.ProneDamageBias。这样,姿态、移动意图、射击动作和生存能力被同一状态机串起来。INFANTRY.CPP:319–346INFANTRY.CPP:427–457INFANTRY.CPP:3465–3507

军犬尤其巧妙:攻击到关键帧时犬本体进入 Limbo,扑击由弹体承接;弹体析构时尝试在落点或邻格恢复同一犬对象并播放撕咬动作,无法落地则删除。犬的伤害又对指定目标直接使用当前生命值,对非指定对象归零。它看起来是动物跳跃,内部却复用了弹体生命周期。INFANTRY.CPP:3648–3653BULLET.CPP:112–169OBJECT.CPP:1582–1588

3. 工程师、间谍、小偷和 C4:进入建筑后的不同事务

这些能力主要在步兵抵达目标格的 Per_Cell_Process 中结算,不能只看鼠标光标就判断最后效果。

工程师对盟友对象调用 Renovate;对敌方,建筑可占领且生命比例达到阈值才 Captured,否则造成受上限约束的强制伤害,最多留下 1 点生命。工程师随后被消耗。当前开启的 FIXIT_ENGINEER 使用 EngineerCaptureLevel/EngineerDamage,旧分支用红血阈值及最大生命的三分之一。满血建筑被一个工程师瞬间占领,不能当成这个函数的普遍规则。INFANTRY.CPP:593–637

间谍设置建筑 SpiedBy 的 House 位,触发 TEVENT_SPIED;潜入雷达额外标记 RadarSpied,潜入潜艇基地开启声呐脉冲。小偷进入具备容量的建筑时,当前有效分支取敌方 Available_Money()/2,不是按这一个筒仓视觉上的装满比例取钱。二者在成功处理后同样删除步兵实例。INFANTRY.CPP:645–707

间谍伪装还延伸到表现层:不属于当前玩家的间谍获取 E1.SHP 图像。身份、自动索敌、模型显示和雷达情报是多个独立处理点,伪装并不是只把颜色改一下。INFANTRY.CPP:4042–4047

C4 通过 MISSION_SABOTAGE 设置建筑 IsGoingToBlow、CountDown 与责任目标,然后步兵逃开;铁幕保护或正在拆除时不设置炸药。建筑 AI 到时才按当前生命值发出强制伤害。因此放炸药、伤害发生、攻击者离开是三个不同事件,便于组合声音、闪烁和延时。INFANTRY.CPP:838–852BUILDING.CPP:969–975

4. 车辆:部署、碾压与地雷改变地图占用关系

MCV 展开先临时撤掉自身占位,再检查建造厂合法落点,调整朝向,创建 STRUCT_CONST 并传递生命比例;成功后删除 MCV。它不是把同一个对象的图标变成建筑,而是一次带状态交接的对象替换。反向收起建造厂也有对应任务与规则条件。UNIT.CPP:1477–1588BUILDING.CPP:3509–3548

碾压分为预警和真正压到:预警调用格子的 Incoming 让对象躲避;执行阶段筛选可碾压、非盟友且足够近的对象,直接记录击杀、解除占位并删除。它没有走普通炮弹伤害公式,护甲对枪炮的减伤不能自动成为防碾压能力。UNIT.CPP:4384–4448

布雷车的“卸载”其实创建地雷建筑,按 House 阵营选择反步兵或反车辆雷,成功后减弹药。车辆抵达雷格由车辆的过格逻辑触发;反车辆雷使用 AVMineDamage,反步兵雷对车辆有单独的小伤害分支。步兵踩反步兵雷时则遍历附近步兵并逐个施伤,不能假定所有雷都走普通九格 Explosion_Damage,也不能未经检查就宣称所有友军都免疫。UNIT.CPP:2578–2646UNIT.CPP:1807–1837INFANTRY.CPP:919–942

V2 又有自己的补弹计时:发射时设置 Reload,行驶时把计时往后推,停下且计时归零才补弹。武器 ROF、弹药数量、车辆 Reload 是不同的限制层。UNIT.CPP:611–625UNIT.CPP:4206–4231

5. 运输保留乘客对象,死亡结果由载具类型决定

CargoClass::Attach 让乘客进入 Limbo,并通过 Next 指针链入 CargoHold;Detach 取出链表头。乘客的生命值、所属方和对象身份保留,它只是暂时不占据地图。装载前后的协调仍通过 Radio 消息,步兵抵达载具并收到 RADIO_ATTACH 才正式进入货舱。CARGO.CPP:87–153INFANTRY.CPP:824–831

APC 卸载包含转向、开门、逐个寻找相邻落点、关门。若无法安放,乘客会重新 Attach,不会因为尝试了一次下车就丢失。登陆舰类似,但还通过 tether 握手等待刚离开的乘客走开,避免多辆车在出口叠住。UNIT.CPP:2458–2543VESSEL.CPP:1701–1834

“运输工具被打爆,乘客全部消失”也不是统一规则。车辆受毁会尝试让步兵乘客在原位出现并逃散,放不下或非步兵才记死亡删除;舰船受毁则遍历货舱直接记录乘客死亡。通用 Kill_Cargo 也直接删除货物,但不是所有载具死亡都无条件调用它。UNIT.CPP:1071–1088VESSEL.CPP:951–966

6. 飞机与舰船为何不能只换移动速度

直升机攻击任务先选攻击位置、起飞、飞到位置,再交替进入两个开火阶段,弹药用尽返回待机/返航处理;IsCurleyShuffle 决定连射间是否重选位置。固定翼则转到 Mission_Hunt 的飞越攻击流程,包含出击、投射和重整。二者攻击节奏并不共享一条“停下开炮”逻辑。AIRCRAFT.CPP:2383–2594AIRCRAFT.CPP:628–823

进机场也有多阶段:固定翼飞过等待、顺风、侧风等检查点再接近跑道,直升机省去对应航段;落地后以 RADIO_IM_IN 与建筑交接,建筑令飞机休眠并按节奏补弹。可见起降动画、地面占位、有限机场和补给都属于飞机玩法,不只是视觉修饰。AIRCRAFT.CPP:3216–3453BUILDING.CPP:3989–4030

舰船开火额外区分海面目标、潜艇、鱼雷和反潜弹体。水下弹发射前按格检查途中水域及友军阻挡;无炮塔舰船移动中不能像有炮塔单位那样自由开火。这些规则使“同样 Range=若干格”的鱼雷和炮弹有不同可用射线。VESSEL.CPP:1023–1139

7. 隐形、声呐、干扰和 GPS 是不同信息机制

隐形具有 UNCLOAKED → CLOAKING → CLOAKED → UNCLOAKING 状态。刚开火、显形延时未结束、有近距离攻击目标或派生类不允许时,不能重新隐形;重伤对象隐形还可能随机失败。完全隐形时会解除部分外部目标引用,因而既影响显示,也影响其他单位是否还能持续锁定。TECHNO.CPP:2427–2606

声呐脉冲遍历舰船列表,对潜艇及启用分支中的导弹潜艇设置 15 秒 PulseCountDown 并显形;本地玩家还显示雷达脉冲动画。VesselClass::Is_Allowed_To_Recloak 直接检查这个计时是否为零,所以画面上的扫描圈并不是碰到哪艘才让哪艘现形的碰撞体。HOUSE.CPP:2608–2628VESSEL.CPP:1951–1954

雷达干扰车由建筑 AI 周期扫描范围内敌对 IsJammer 单位,使雷达或 SAM 标记 IsJammed;裂缝产生器调用 Map.Jam_From 制造地图遮蔽,缺电则 UnJam_From。GPS 又是卫星弹体离场或完成后设置 House 的 IsGPSActive/IsVisionary 并显露地图;失去所需科技建筑时,House 会撤销 GPS 状态并重新遮蔽。它们分别作用于对象隐形、设施可用性、地图情报,不能用一个“看不见”属性概括。BUILDING.CPP:990–1027BULLET.CPP:401–415HOUSE.CPP:1416–1421

8. 超级武器共用充能器,实际效果由 House 分派

SuperClass 管 Present、Ready、OneTime 和倒计时:Enable 开始充能,AI 到零设 Ready,Discharged 使用后重充或移除一次性能力。House 定期检查来源建筑与条件,并暂停尚未就绪、需电、非一次性的能力。已经就绪与正在充能的处理并不相同,不能简单说一缺电所有超武都立即变成不可用。SUPER.CPP:102–297HOUSE.CPP:1381–1408

铁幕在点选格获取一个 Techno,对车辆、建筑、舰船或飞机写入保护倒计时;这段并未给步兵相同处理,也不是红警二那样按区域给一组单位统一罩上。爆破卡车分支还把持续时间从分钟 tick 换为秒 tick。同一效果由受害者 Take_Damage 的外层检查阻止扣血。HOUSE.CPP:2736–2766TECHNO.CPP:3803–3809

完整案例:超时空传送一辆普通坦克

  1. 第一阶段验证超级武器就绪、对象属于盟友、类型可传送且未在部署,将对象目标编码保存到 UnitToTeleport
  2. 第二次点击目的地,保存旧格到 MoebiusCell,调用 DriveClass::Teleport_To
  3. 传送清除驾驶轨迹、无线电联系、导航、攻击及任务;若规则启用 IsChronoKill,先杀死货舱乘客;目的格不能进入时查找附近位置,再重新登记占位。
  4. 设置 IsMoebius 和持续计时;Drive AI 到时再次 Teleport_To(MoebiusCell),因此返回原地也可能受新占位影响。
  5. 使用完成后充能器放电重充,另外按规则概率引发时间震荡或超时空漩涡。

这不是简单赋值 unit.position=clickedCell,而是必须维护对象在移动、目标、占位、载客和计时系统中的一致性。HOUSE.CPP:2769–2890DRIVE.CPP:382–413DRIVE.CPP:1290–1313

同一入口有明确例外:步兵移到目的格后受到以自身生命值为额的强制火焰伤害;运输舰及航母在第一阶段被排除;超时空坦克通过自身部署进入第二阶段,并取消普通“过一会儿返回”的 IsMoebius,使用自己的计时。不能把超时空坦克和建筑超时空仪画成同一个无条件瞬移技能。

9. 核弹与空中支援复用既有对象系统

核弹按钮通常找到导弹井、写 NukeDest,再让建筑执行 MISSION_MISSILE。建筑状态机控制开门、升空弹体及下降弹体;一次性核弹能力则可直接创建下降弹体。源码内还存在 TechLevel 不同的下降阶段分支,应保留具体路径,不能概括为所有任务都同样起飞落下。HOUSE.CPP:2632–2673BUILDING.CPP:4128–4255

核爆的大范围伤害还挂在原子爆炸动画 Middle 阶段。Do_Atom_Damage 在战役采用 4 格参数与 AtomDamage,非战役采用 3 格及 AtomDamage/5,再以 FIRE 弹头调用 Wide_Area_Damage;弹体创建核爆动画失败时也有直接执行伤害的兜底。因此不能只看核弹构造时的 200,就认定整场核爆只造成 200。ANIM.CPP:937–947ANIM.CPP:1063–1105BULLET.CPP:1038–1052

侦察机、伞兵和空投炸弹也不是按钮直接往地图写结果。侦察机/轰炸机调用空中增援创建飞机,伞兵构造临时 TeamType,组合运输机和步兵并设置目标航点。这让地图边缘入场、飞行、被防空击落和投放都能复用已有系统。HOUSE.CPP:2676–2733

10. 资料片特例与扩展的代价

部分新增机制进一步说明旧架构的复用方式:MAD 坦克在卸载任务中借用 Gold/Gems 字段保存部署阶段,等待 Arm,再设置 PendingTimeQuake;航母把飞机当货舱对象,所谓“开火”是 Detach 一架飞机并赋攻击任务,AI 定时向货舱飞机发送补弹消息。新玩法由旧状态、任务和消息组合而来,但字段名开始偏离原意。UNIT.CPP:2651–2722VESSEL.CPP:602–614VESSEL.CPP:2332–2353

这是一种在既有引擎和上市时间约束下很实用的扩展方式;代价是不能仅凭名字理解成员。读到矿石字段要先确认当前单位类型,读到 Fire_At 要确认究竟是发弹体还是放出飞机。源码能创建航母类,也不自动证明普通菜单、任务或所有发行版向玩家开放了它。

源码阅读路线与未验证部分

先选一个熟悉单位,读取类型及通用 Techno/Foot 能力,再按 Mission → Status → Radio → 地图占位/武器/货舱 追一次行为。工程师与间谍从 Infantry::Per_Cell_Process 开始;MCV、布雷、MAD 从 Unit::Mission_Unload 开始;超武从 House::Place_Special_Blast 开始。通用伤害见 06 战斗,生产补给见 07 经济与建筑

本章没有逐一运行所有兵种组合。重点未验证边界包括:传送目的区完全堵塞、装载者在握手中途被毁、核爆的多次局部伤害叠加、宏开关与最终 INI 的组合,以及字段复用造成的异常状态。它们应成为复现计划,而不是用玩家印象补齐的“已确认机制”。

红色警戒初代 · 源码剖析18 章正文 / 5 份附录
输入关键词,检索 24 份完整文档
全文检索 · 点击结果直达对应小节