查看源码下载文档
CHAPTER / 07

经济、生产与建筑:从一格矿到一辆坦克

红警的经济系统不是一个不断加减的“金钱变量”。地图保存矿石,矿车保存货物,House 保存可消费资产和存储容量,工厂对象保存生产中的商品及尚未支付余额,建筑负责让商品进入地图。把这些状态分开,才能解释断矿、爆仓、缺电、暂停建造、出口堵塞和占领建筑为何产生不同后果。

本文以提交 0dc09bb1d6188d1ec79ab4765b22a48b75ffee32 为依据。这里的 Tiberium 是沿用前作的内部命名,在本作对应矿石经济;不能因变量名相同,就把前作的逐包卸矿或数值规则直接套过来。

1. 钱有两种:Credits 与受容量约束的矿石资金

HouseClass::Available_Money 返回 Tiberium + CreditsHarvested 将矿车收入加入 Tiberium,并按 Capacity 截断;Refund_Money 则直接增加 Credits。Spend_Money 优先用 Tiberium,不足部分再从 Credits 扣除。HOUSE.CPP:1802–1922

状态 可以怎样变化 玩家可观察的意义
矿车 Gold/Gems/Tiberium 采矿增加,卸货清空 车里有货不等于钱到账
House Tiberium 卸货增加、消费减少、容量不足截断 筒仓及炼矿厂关系到这部分财富
House Credits 退钱增加、矿石资金不足时扣除 退款不受当前矿石容量直接限制
House Capacity 建筑启用/移除/易主改变 容量与资金是两种独立状态

Adjust_Capacity 对容量减少提供两种处理:若减少后矿石资金超过容量,inanger=true 会取走超额部分;否则把超额转入 Credits。但必须再查调用者:当前 BuildingClass::Limbo 明确传 true,出售最终也调用 Limbo,因此不能从参数名推断“主动出售必定保住超额矿石资金”。HOUSE.CPP:1942–1962BUILDING.CPP:2255–2274

例如当前 Credits=200、Tiberium=900、Capacity=1000,一车交来 500:可储矿石资金只从 900 到 1000,超出部分在此入口被截掉;总可用钱为 1200。之后花 300,只把 Tiberium 减到 700。这里的数值是演示状态变化,非某关初始资金设定。

2. 矿车不是每帧“找最近矿”的单一函数

Mission_HarvestLOOKING → HARVESTING → FINDHOME → HEADINGHOME 等状态构成的任务。没有己方活动炼矿厂时会转守卫;装满先回家;旧采矿点可保存在 ArchiveTarget,交货后继续使用。找不到矿也有闲置及通知 House 矿源短缺的处理。UNIT.CPP:2749–2922

Goto_Tiberium 从当前格向外按方形环扫描,而非给整个地图所有矿格计算全局最短路。候选检查包括地图边界、同一移动区域、没有其他 Techno 占位、地形为矿石;人类战役玩家还受已探明条件限制。找到候选只是设置导航目的地,实际走过去仍由移动系统处理。UNIT.CPP:2161–2248

这解释了为什么“看起来最近”和“源码首先找到”不总相同。扫描顺序、通行区域、占位和当前保存的采矿点都参与决策。它是成本可控的局部策略,不是实时物流优化器。

Harvesting 每轮减少当前格的一份矿层,再增加货物计数。普通矿石增加 Gold;宝石先增加 Gems,并在容量允许时额外增加最多三份 Gems 与总装载计数,所以宝石会更快填满矿车。总价值由 Credit_Load 计算为 Gold × Rule.GoldValue + Gems × Rule.GemValueUNIT.CPP:2267–2329UNIT.CPP:4790–4793

规则构造默认值为容量 28、GoldValue=35、GemValue=110,随后又允许 INI 覆盖。只在没有覆盖的假设下,满车普通矿是 980,满车宝石是 3080;这应叫“构造默认值演算”,不应作为所有地图和发行版固定收入。RULES.CPP:237–239RULES.CPP:466–478

3. 进厂与卸货通过对象间握手完成

回程先 Find_Docking_Bay,再向炼矿厂发送 RADIO_HELLO。这里的 Radio 是游戏对象之间的同步消息接口,不是联网数据包。炼矿厂用 RADIO_DOCKING 指定南侧接近点,必要时要求倒车;收到 RADIO_IM_IN 后让矿车执行 MISSION_UNLOADBUILDING.CPP:215–325

矿车的卸货流程先转到西向、播放卸载动作,动画达到指定阶段后一次调用 House->Harvested(Credit_Load()),清空 Gold、Gems、Tiberium,结束联络,再回采矿任务。这就是当前主体路径的到账点。UNIT.CPP:2348–2390

仓库还保留 BuildingClass::Mission_Harvest 逐包索取货物的代码,但 UnitClass::Offload_Tiberium_Bail 的价值返回部分在 TOFIX 内,常规路径返回零。若只搜索函数名、看到“Offload”就写成逐包现金流,会得到错误的当前机制。这也是阅读老项目必须追真实调用者的原因。BUILDING.CPP:3735–3795UNIT.CPP:4299–4312

4. 生产对象与生产建筑不是同一个东西

玩家点击生产后,HouseClass::Begin_Production 按 RTTI 类别获取工厂对象,必要时新建 FactoryClass。House 分别保存步兵、车辆、建筑、飞机、舰船的工厂索引,因此玩家这条路径是按类别维护当前生产槽,而不是每一座兵营天然拥有独立的玩家订单队列。HOUSE.CPP:2394–2434HOUSE.CPP:6945–6988

FactoryClass::Set 会立即创建实际的产品对象,但它尚未摆上地图;保存总价到 Balance,并记录 PurchasePrice。这样,生产中的坦克已经有身份和所属 House,工厂只是掌管它的完成度、计费和交付。FACTORY.CPP:290–329

生产划分为 54 个进度阶段。每次推进按“剩余余额÷剩余阶段”计算本次费用;若钱不足,把刚推进的阶段退回去;否则花钱并减余额。完成时停表并结清剩余余额。因此资金短缺通常体现为进度卡住,继续到账又能推进,不需要一开始付清全价。FACTORY.H:92FACTORY.CPP:201–239FACTORY.CPP:615–626

举例:商品价格 540、没有价格修正,54 阶段平均每步 10。完成 20 步时已付 200、Balance=340;暂停只是停进度,取消则按完整成本减余额退回已支付部分,并删除未交付对象。取消不是给一辆未出厂坦克按二手比例折价。FACTORY.CPP:382–391FACTORY.CPP:469–500

5. 建造速度包含多层系数

基础 TechnoTypeClass::Time_To_Build 按成本、全局 BuildSpeedBias 和分钟 tick 数计算。对象层再引入 House 建造速度、多人单位惩罚、电力及工厂数量。其中启用 CSII 代码且 NewUnitsEnabled 为真时,工厂数量除数最多计 2;其他路径可以按实际数量除。不能无条件说“多造工厂无限加速”,也不能把“双工厂封顶”套到所有运行模式。TECHNO.CPP:6075–6078TECHNO.CPP:653–698

值得单列的是两层电力折算:对象建造时间先把供电比例限制并分段处理,低于一半按一半、四分之三至满电之间按四分之三,然后取倒数;FactoryClass::Start 又将此时间除以限制在 1/16~1 的供电比例,再除以 54,最后把每阶段间隔限制到 1~255。这是当前代码真实的两次处理。FACTORY.CPP:411–447

在其他条件相同、忽略截断和上下限时,半电会在前层乘 2,后层再乘 2,总计约四倍的启动计算时间。这个推导针对启动时计算出的 rate;不要据此宣称已在进行的每笔订单都会每帧按最新电力连续重算。当前 FactoryClass::AI 推进主体没有重算上述 rate。

另一个易混点是 AI:建筑 Factory_AI 还维护建筑自己的工厂并向 House 询问建议商品。这条电脑生产路径与玩家 House 分类槽并不完全相同,分析作弊、排队或多厂效率时要先确定研究哪条路径。BUILDING.CPP:5281–5325

6. 建造资格与成品交付是两道关

HouseClass::Can_Build 先排除 TechLevel=-1,按运行模式过滤资料片新增类型,再检查所属阵营、前置建筑位图和科技等级。高级电厂可满足普通电厂前置;非战役模式中两种科技中心位图有互通处理;非人类战役 House 还有提前放行分支。这是任务设计与遭遇战规则共存的结果。HOUSE.CPP:784–876

生产完成并不保证成功生成在地面。HouseClass::Place_Object 查找能够生产该商品的建筑:单位经 Exit_Object 出厂,建筑通过指定格子 Unlimbo 落位。放置失败时,完成产品仍可能等待;成功后才把产品从工厂移交并清理生产记录。出口被堵与造不出来因此属于不同问题。HOUSE.CPP:2918–3045

电脑建筑工厂的出口返回值进一步区分取消、暂时阻塞及成功:阻塞会设置三秒 PlacementDelay 再试。这是一种简单的重试协议,而不是让生产线程一直等出口清空。BUILDING.CPP:5200–5275

7. 建筑在开业、受损、出售和被占领时改变 House

Grand_Opening 增加耗电与容量、标记统计需要重算,并处理炼矿厂附带矿车、部分规则下直升机场附带飞机等特殊效果。炼矿厂送车有 ScenarioInit、captured、Debug_Map、玩家及 PurchasePrice 条件,不能说“任何方式得到炼矿厂都送一辆车”。生成失败时还有退款补偿。BUILDING.CPP:2383–2456

发电量按最大生命值比例折算。建筑 AI 发现 Strength 与 LastStrength 不同,就计算新旧发电差并更新 House。这使攻击电厂可以先产生断电效果,不必等它被摧毁。BUILDING.CPP:929–934BUILDING.CPP:4607–4615

缺电除了影响建造,也会使标记为需电的防御建筑停止开火;House 还周期性向高于黄色生命阈值、存在耗电的己方建筑施加小额损伤。飞机补弹间隔则使用另一套最低一半供电比例,不能直接沿用建造的倍数。BUILDING.CPP:2853–2862HOUSE.CPP:1066–1086BUILDING.CPP:3989–4030

维修建筑按全局帧取模的维修节奏支付一笔费用、增加固定生命步长,达到上限封顶;钱不够关闭维修状态。维修费又从 Raw_Cost、最大生命、RepairStep、RepairPercent 推导,移动单位有另一组 URepair 参数。BUILDING.CPP:5462–5481TECHNO.CPP:6139–6169

出售是逆向施工任务:先结束维修并通知占用者离开,再处理幸存人员、播放拆除状态,最终退款和删除。人类退款采用 Raw_Cost × 阵营成本倍率 × RefundPercent;当前有效函数体没有按剩余生命值比例折算。炼矿厂 Raw_Cost 会扣除附带矿车成本,避免把整套购买价都当成空建筑残值。BUILDING.CPP:3385–3586TECHNO.CPP:5743–5761BDATA.CPP:3672–3682

占领更不是简单改颜色:BuildingClass::Captured 从旧 House 移除发电、耗电和容量,处理附属单位及停靠者,清理建筑工厂,调整活动计数、地基归属、视野和雷达,再以 captured 模式重新开业。工程师的一次点击之所以能改变生产网络,是因为易主函数维护了这些跨系统约束。BUILDING.CPP:2936–3098

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

Unit::Mission_Harvest/Harvesting/Mission_Unload → House::Harvested/Spend_Money → Factory::Set/Start/AI → House::Place_Object → Building::Grand_Opening 追一车矿换成一件产品,再读 Can_Build、维修、出售与易主。单位如何进厂和载人,可继续读 08 单位与特殊机制

本章没有把静态公式包装为已运行的速度测试。缺少最终加载规则时,各兵种价格、真实满车收入、科技树、发电量及造速只能按具体数据集确认。生产中的 rate 是否被外围事件触发重启、极端容量变化及占领瞬间的经济顺序,适合进一步配合断点和录像验证。

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