# 03｜战场对象如何存在：继承、类型表、内存池与身份

一辆坦克在源码里不只是“坐标加贴图”。它有类型、阵营、生命值、任务、导航、攻击目标、朝向、动画，以及与地图、显示层、逻辑列表的关系。本章把这些关系拆开，解释红警如何在同一个游戏世界中管理大量相互引用、随时生成或被摧毁的对象。

分析固定于提交 `0dc09bb1d6188d1ec79ab4765b22a48b75ffee32`；除明确标为推论的部分外，以下均依据字段和函数实现。

## 1. 先认识对象主干，别被英文名字误导

主体沿着 `AbstractClass → ObjectClass → MissionClass → RadioClass → TechnoClass` 逐层增加能力。到 `TechnoClass` 分成建筑和可移动物体；可移动物体归 `FootClass`，进一步分为步兵、飞机及带驾驶逻辑的车船。

```mermaid
flowchart TD
    A[AbstractClass] --> O[ObjectClass]
    O --> M[MissionClass]
    M --> R[RadioClass]
    R --> T[TechnoClass]
    T --> B[BuildingClass]
    T --> F[FootClass]
    F --> I[InfantryClass]
    F --> P[AircraftClass]
    F --> D[DriveClass]
    D --> U[UnitClass]
    D --> V[VesselClass]
```

图中只画主继承链。`TechnoClass` 同时继承 `FlasherClass`、`StageClass`、`CargoClass`、`DoorClass`；`AircraftClass` 还继承 `FlyClass`。这是一套面向对象、部分多继承的设计，并非现代 ECS 那种“所有实体只是 ID，所有能力都是独立组件数组”的结构。[TechnoClass 声明](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/TECHNO.H#L51-L61)、[FootClass](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/FOOT.H#L46-L56)、[DriveClass](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/DRIVE.H#L41-L51)、[AircraftClass](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/AIRCRAFT.H#L46-L61)。

| 层次 | 新增的主要概念 | 游戏里对应什么 |
|---|---|---|
| `AbstractClass` | RTTI、池槽 ID、位置、高度、活动标志 | 世界中可被识别和定位的对象 |
| `ObjectClass` | 生命值、选择、地图占用、显示、Limbo、触发器 | 可以进入地图、受伤和被绘制的物体 |
| `MissionClass` | 当前任务、待接纳任务、任务状态、任务计时器 | 守卫、攻击、移动、采矿等行为调度 |
| `RadioClass` | 对象间联络及消息应答 | 运输、停靠、装卸等双方协作 |
| `TechnoClass` | 所属阵营、攻击目标、装填、弹药、朝向等 | 建筑与作战单位的共性 |
| `FootClass` | 导航目标、路径、导航队列、队伍关系 | 所有可移动单位，不只是步兵 |
| `DriveClass`、具体子类 | 行驶轨迹及载具、船只、飞机、步兵特殊处理 | 坦克转弯、船只航行、飞机飞行等差异 |

前三层字段可分别核对 [ABSTRACT.H](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/ABSTRACT.H#L52-L95)、[OBJECT.H](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/OBJECT.H#L58-L127)、[MISSION.H](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/MISSION.H#L48-L66)。`UnitClass` 在这个项目中主要指地面载具，而不是“游戏中所有单位”；步兵有自己的 `InfantryClass`，船只则是 `VesselClass`。[载具](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/UNIT.H#L50-L60)、[步兵](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/INFANTRY.H#L39-L48)、[舰船](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/VESSEL.H#L44-L54)。

也不是所有东西都走完这条链。对象类型体系中子弹、动画、地形直接派生自 `ObjectTypeClass`，并不必具备阵营经济、运输和武器等完整“科技物体”能力。阅读一个具体系统，应先确认其真实基类和覆盖函数，而不是机械套用坦克路径。

## 2. “中型坦克是什么”与“眼前这辆坦克怎样了”分开保存

项目有两组平行概念：

| 共享类型对象 | 独立实例对象 |
|---|---|
| `UnitTypeClass`、`BuildingTypeClass` 等 | `UnitClass`、`BuildingClass` 等 |
| 最高生命、成本、速度类型、射程相关数据、图片指针 | 当前生命、当前弹药、当前位置、所属阵营 |
| 例如“这一型号坦克的定义” | 例如“池槽 37 中，玩家 A 的那辆坦克” |
| `UnitTypes` 等池 | `Units` 等池 |

两组池都在 `GLOBALS.CPP` 定义，不能把 `UnitTypes` 和 `Units` 当作同一个容器。[全局实例池与类型池](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/GLOBALS.CPP#L64-L93)。共享字段能在 `ObjectTypeClass` 的 `Armor/MaxStrength/ImageData` 和 `TechnoTypeClass` 的 `Cost/MaxSpeed/Speed/MaxAmmo` 中找到。[通用类型数据](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/TYPE.H#L247-L273)、[作战物体类型数据](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/TYPE.H#L437-L495)。

`UnitClass` 的构造函数把 `Class` 指向 `UnitTypes.Ptr(classid)`，把 `Ammo` 从 `Class->MaxAmmo` 初始化，把 `Strength` 从 `Class->MaxStrength` 初始化。由此能看见一个很重要的边界：图片和型号规则可以共享，当前损伤、弹药等必须每个实例独立保存。[UnitClass 构造函数](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/UNIT.CPP#L284-L319)。

这些类型也并非运行时任意发现。`UnitTypeClass::Init_Heap` 按 `UnitType` 枚举次序复制预定义类型，例如重型、中型、轻型坦克、APC、矿车、MCV。注释明确要求分配顺序匹配枚举，因为池槽同时承担类型编号；扩展单位还受编译宏控制。随后规则文件可以调整定义，但“增加一个完全新型号”不能简单理解为随意多写一个 INI 小节即可。[类型池初始化顺序](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/UDATA.CPP#L926-L961)。

工程上，这类似共享只读定义与实例状态的分离，能减少重复数据。但这里的“共享”不等于永远不可修改；规则加载阶段确实会处理已有类型，不能把它想成编译后绝对不可变的常量数据库。

## 3. 内存池：每类对象预留一排固定槽位

实际对象不是每次产生都直接向通用堆申请任意大小内存。`FixedHeapClass::Set_Heap(count)` 分配 `count * Size` 字节，并创建布尔占用表。`Allocate` 查找第一个空闲槽，置占用标记并返回该槽地址；没有容量时返回零。`Free` 只清占用标记，整块池内存仍在。[建立池与分配](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/HEAP.CPP#L135-L203)、[释放槽位](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/HEAP.CPP#L222-L236)。

`TFixedIHeapClass<T>` 在底层固定池之上提供类型化接口；中间的 `FixedIHeapClass` 维护 `ActivePointers`，用于只遍历已分配对象。分配时把指针加入这个列表，并将槽内字节清零；释放时从列表移除。[可遍历池实现](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/HEAP.CPP#L412-L445)。

这里有两个完全不同的“索引”：

| 访问方式 | 含义 | 删除其他对象后是否可能改变 |
|---|---|---|
| 固定槽 ID、`Raw_Ptr(id)` | 对象在整块池中的位置 | 对象尚存活时保持原槽 |
| `Ptr(index)`、`ActivePointers[index]` | 活动对象列表的第几项 | 可能改变 |

`ID(pointer)` 直接计算 `(pointer - Buffer) / Size`；`Ptr` 从活动列表取指针，`Raw_Ptr` 从固定槽取地址。两者不能混用：列表中第 3 辆存活坦克，未必位于池的第 3 号槽。[ID 换算](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/HEAP.CPP#L256-L262)、[类型化访问接口](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/HEAP.H#L174-L193)。

车辆重载的 `operator new` 调用 `Units.Alloc()`，成功后设置 `IsActive=true`；`operator delete` 设置它为假，再归还池槽。构造函数收到的 ID 就来自 `Units.ID(this)`。[车辆分配与释放](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/UNIT.CPP#L188-L220)。

这种安排的工程收益是对象地址稳定、容量可预测、同类对象大小统一，也便于用编号表示对象。代价是容量上限真实存在，分配失败必须处理；分配过程需要找空闲位，不能不经实现检查就宣称它严格 O(1)。更不能把注释里的“数据段数组”理解成所有池都静态编译进可执行文件，当前 `Set_Heap` 明确可以动态分配整块缓冲区。

## 4. TARGET：跨对象类型的地址簿

坦克既能攻击步兵，也能攻击建筑，还能强制攻击地面。若每种情况使用不同指针类型，很多命令和存档结构就会变得笨重。`TARGET` 因而将身份统一打包：在原 32 位目标 ABI 下，它由 **8 位种类 + 24 位值**组成。对象目标的值通常是相应池的固定槽 ID，格子目标的值则表示格子；`AbstractClass::As_Target()` 调用 `Build_Target(RTTI, ID)`。[TARGET 布局与打包](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/DEFINES.H#L559-L593)、[对象转目标](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/ABSTRACT.H#L91-L95)。

`As_Object(target)` 读取种类，选择 `Units`、`Infantry`、`Buildings` 等池，再用 `Raw_Ptr(value)` 找回地址。其末尾会拒绝 `IsActive=false` 的对象，注释特别提到“网络消息发送后、接收前目标已经死亡”的情况。[目标解析与活动检查](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/TARGET.CPP#L276-L329)。

`TargetClass`/`xTargetClass` 是另一层包装。拆成两个类有具体的老编译器缘由：Watcom 对 union 中带构造函数的类有限制；事件联合体需要没有构造函数的 `xTargetClass`，便利构造则放在派生包装中。[TARGET 包装及 Watcom 限制](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/TARGET.H#L97-L174)。

必须保留两个边界：

- `TARGET` 不是网络传输的内存指针，也不是跨平台无条件安全的序列化格式。其布局依赖原项目的整数宽度、位域和 ABI。
- 这个标识没有现代常用的“代数 generation”。槽位释放后可以复用；如果某处保留了旧 ID，仅凭种类和槽号无法知道它是否已经对应另一对象。当前代码靠活动标记、对象状态检查及销毁时主动断开引用维持一致性。由结构可推出这种限制，但不能仅凭它断言存在某个可复现的游戏漏洞。

尤其不能把所有 `As_*` 当成同等严格的验证器：例如自由函数 `As_Unit` 只按种类返回固定槽地址，没有复制 `As_Object` 末尾的活动检查。[As_Unit](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/TARGET.CPP#L348-L351)。

## 5. 三类“指针”，没有现代自动内存管理的保证

| 表达方式 | 实际保存什么 | 它不负责什么 |
|---|---|---|
| `T*` | 直接内存地址 | 自动失效、序列化修复、所有权 |
| `SmartPtr<T>` | 一个 `T*`，解引用时带断言等便利操作 | 引用计数、自动删除目标 |
| `CCPtr<T>` | 某类型固定池中的 ID | 自动验证目标仍存活、区分槽复用 |

源码中的 `SmartPtr` 与现代 `std::shared_ptr`、`std::unique_ptr` 不是同一种机制：析构只把自己的 `Pointer` 置零，没有 `delete Pointer`，也没有引用计数。[SmartPtr 完整定义](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/JSHELL.H#L446-L470)。

`CCPtr` 的类型参数对应一个静态池指针，访问时用 ID 查池。它的 `Is_Valid()` 只检查 `ID != -1`；虽然访问处有范围断言，也并不等于“目标一定活着”。这种设计让这类引用无需像原始地址一样做存取档地址修复，但不能因此推断全游戏没有指针编码/解码需求。[CCPtr](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/CCPTR.H#L42-L90)。

读代码时，一次“判空”到底验证了地址、ID，还是对象活动状态，应当按这个表逐项区分。项目中三种形式并存，它们承担的保证明显不同。

## 6. 实例：一辆坦克从分配到消失

下面以一辆普通车辆为例，完整跟踪其存在状态。

**第一步，分配和构造。** `new UnitClass(type, house)` 先取得池槽并置 `IsActive`，再初始化父类和类型引用、加入阵营跟踪、设置生命与弹药。但基类 `ObjectClass` 构造时设定 `IsInLimbo=true`、`IsDown=false`。此时“内存中的对象已经活着”，不代表它已经出现在地图上。[对象初始状态](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/OBJECT.CPP#L131-L145)、[车辆初始状态](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/UNIT.CPP#L284-L311)。

**第二步，放入世界。** `ObjectClass::Unlimbo` 检查正在游戏、对象处于 Limbo 且尚未占地图；场景初始化期间允许特殊处理，否则要求目标格可进入。它修正坐标、`Mark(MARK_DOWN)`，随后加入相应显示层；类型若有 `IsSentient`，再 `Logic.Submit(this)` 加入更新列表。放置可能失败，调用者必须看返回值。[Unlimbo](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/OBJECT.CPP#L1391-L1421)。

**第三步，参与模拟。** `Logic` 遍历时通过虚函数到达 `UnitClass::AI`。车辆接纳任务，调用 `DriveClass::AI → FootClass::AI → TechnoClass::AI` 等父层处理，再处理射击、炮塔等。注意这个版本 `FootClass::AI` 的有效函数体基本只调用 `TechnoClass::AI`，后面的旧逻辑藏在 `OBSOLETE` 内；不能仅因文件名叫 `FOOT`，就认定所有移动步骤都在它的 `AI` 函数里。真正的车船轨迹推进在 `DriveClass::AI` 中调用 `While_Moving`、`Start_Of_Move`。[车辆 AI](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/UNIT.CPP#L397-L451)、[Foot AI](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/FOOT.CPP#L2440-L2452)、[Drive AI](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/DRIVE.CPP#L1290-L1329)。

**第四步，离开地图但仍存在。** `Limbo` 取消选择、断开引用、标记抬起，移出显示层与逻辑列表，最后设 `IsInLimbo=true`。它没有归还池槽，适合“已创建但尚未投放”或“装进运输工具”等状态。`IsSentient` 的名字也不应直译成人工智能：它实际表示该类型是否参加逻辑更新，地形为了动画也可能设置它。[Limbo](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/OBJECT.CPP#L1340-L1369)、[IsSentient 定义](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/TYPE.H#L232-L237)。

**第五步，彻底销毁。** 正常活动游戏中，车辆析构移出队伍与阵营跟踪，删除所带乘员，并调用 `Limbo`；最后令自身 `ID=-1`。随后重载 `delete` 归还池槽。`Detach_All` 还会通知地图，并以 `As_Target()` 调用 `Detach_This_From_All`，防止其他单位继续把这辆坦克当攻击或导航目标。[车辆析构](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/UNIT.CPP#L238-L263)、[断开引用入口](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/OBJECT.CPP#L1466-L1484)。

由此可以明确区分四件事：**池槽是否分配、对象是否在地图上、是否参加逻辑更新、是否需要显示**。它们分别由活动状态、Limbo/占用状态、逻辑列表、显示层及重绘标记管理，不能用一个“可见”布尔值代替。

## 7. 攻击、移动和对象协作是不同关系

`TechnoClass::TarCom` 保存攻击目标；`FootClass::NavCom` 保存导航目标。坦克可以朝一个位置行驶，同时让炮塔攻击另一个目标，两者必须独立。`FootClass` 还持有 `NavQueue[10]`、固定长度方向路径、队伍引用和下一个成员指针；这些分别表达玩家路径队列、当前寻路结果和编队关系。[攻击状态](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/TECHNO.H#L184-L228)、[导航与队伍状态](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/FOOT.H#L176-L223)。

`RadioClass` 则用于两个对象协商：默认把消息发给当前联络对象；`RADIO_HELLO` 尝试建立联系，对方回复 `RADIO_ROGER` 才接受；`RADIO_OVER_OUT` 切断关系。其核心是直接调用 `to->Receive_Message(...)` 并立刻取得返回值，所以它是同步的对象协作机制，不能因“Radio”就理解成音频播放或网络通信。[消息发送实现](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/RADIO.CPP#L204-L241)、[握手接收实现](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/RADIO.CPP#L155-L180)。

这让“矿车到精炼厂卸矿”“乘员进入运输工具”具备双方可拒绝、可确认、可结束的交互基础。具体业务消息由派生类解释；基类提供的是联系和应答框架，而非完整采矿流程。

## 8. 坐标、定点数与随机数：可重复世界的基础

### 格子与格内精度

`CELL` 是紧凑的地图格索引；`COORDINATE` 表示更细的位置。每个轴由一个 16 位 `LEPTON` 值表示，拆成 8 位格坐标与 8 位格内位置，因此一格对应 256 个 lepton。这个“逻辑长度”与屏幕像素分开：地图索引用格子，平滑行驶、距离计算用更细坐标。[坐标数据布局](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/DEFINES.H#L499-L535)。

对象的 `Coord` 语义还因种类而异：车辆通常用中心点，建筑以左上参考位置为基础。因此提供 `Center_Coord`、`Target_Coord`、`Render_Coord` 等虚函数，避免各系统把某个原始坐标一概当成目标中心。[基础坐标说明](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/ABSTRACT.H#L64-L73)、[对象坐标接口](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/OBJECT.H#L164-L173)。

### 比例使用有限精度的 `fixed`

`fixed` 由两个无符号字节组成整数与小数部分，共 16 位，也就是常说的无符号 8.8 定点表示。构造 `fixed(n,d)` 时计算 `n*256/d`；整数乘比例时则加 128 再除 256，体现其取整规则。例：`fixed(1,2)` 的内部值为 128，乘 75 按该运算符得到 38，而不是直接截成 37。[比例构造](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/FIXED.CPP#L59-L66)、[转换及乘法](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/FIXED.H#L84-L121)、[内部布局](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/FIXED.H#L216-L229)。

这不是无限精度数学，也不是所有运算都采用同一种舍入。改用浮点后即使公式“看上去一样”，临界血量、速度、伤害也可能变动。定点与整数有利于控制计算结果，但不能单独保证跨平台确定性；数据宽度、运算顺序、随机数调用和容器遍历顺序同样重要。

### 随机数必须分用途

`RandomClass` 用线性同余更新：`Seed = Seed * 0x41C64E6D + 0x3039`，再右移 10 位提取 15 位结果。区间版本构造位掩码，若抽中值大于范围就重抽，而非简单 `% 范围`。因此某次区间调用可能消耗多个原始随机数。[常量](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/RANDOM.H#L55-L83)、[原始与区间生成](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/RANDOM.CPP#L89-L181)。

`Random_Pick` 使用 `Scen.RandomNumber`，用于会影响战局的随机性；`Sim_Random_Pick` 使用 `NonCriticalRandomNumber`，用于不影响游戏结果、无需各机一致的用途。这里 `Sim` 的名字尤其容易误导，必须看实现和注释。若给本地特效多抽一次战局随机数，后面的单位行为抽样就会错位，联机结果可能分叉。[关键与非关键随机源](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/INLINE.H#L955-L1009)。

## 9. 世界是共享全局状态，设计取舍也在这里

项目把 `Scen`、`Logic`、`Map`、`Options`、`PlayerPtr`、`Session` 和各对象池作为全局状态组织。非编辑器情况下 `Map` 的实际类型还是继承了输入及界面能力的 `MouseClass`；它不仅是一张格子数组。[场景与帧](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/GLOBALS.CPP#L314-L348)、[全局选项、逻辑与地图](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/GLOBALS.CPP#L415-L442)。

这种结构让单位很容易查询地图、修改阵营状态、生成动画，符合一个进程主要运行一个世界的组织方式。工程代价也明显：单独测试一个对象需要准备许多全局依赖；同时运行两个独立战局、进行多线程更新或移植 64 位，都不能只替换一个外层循环。这是根据结构得出的工程判断，不是说这些改造在技术上不可能。

继承层次也没有形成严格独立模块：`TechnoClass::AI` 同时做后坐恢复、货物、任务、自修复、隐形和动画重绘标记。这种集中共享逻辑减少了重复实现，却增加跨行为耦合；修一个通用基类函数可能同时影响建筑、飞机、船只和坦克。[TechnoClass::AI](https://github.com/electronicarts/CnC_Red_Alert/blob/0dc09bb1d6188d1ec79ab4765b22a48b75ffee32/CODE/TECHNO.CPP#L2331-L2408)。

## 10. 阅读路线与尚未验证的边界

建议路线：先并排阅读 `ABSTRACT.H / OBJECT.H / TYPE.H`，把类型数据和实例状态分清 → 沿 `UnitClass::operator new` 进入 `HEAP.CPP` → 跟踪构造、`Unlimbo`、`Limbo`、析构 → 比较 `TARGET`、`CCPtr`、`SmartPtr` → 回到 `UnitClass::AI` 追父类调用 → 读定点、坐标与随机数实现。与[主循环章节](02-runtime-and-game-loop.md)配合，才能同时看清“对象是什么”和“对象什么时候更新”。

本章未执行原二进制，未以压力场景测出对象上限，也未证明每个销毁路径都完全正确清理引用。固定槽复用、不同 `As_*` 验证强度不一致、旧编译器和 32 位 ABI 依赖，是需要进一步测试的具体边界；不能把结构分析包装成已确认的崩溃、漏洞或跨平台兼容性结论。
