运行交战引擎:行军、会战、围城与渡河
本教程运行军事参考起步示例。一支军队借助参考世界的移动插件行军,抵达后由宿主放置它的部队,随后一场野战先按日、再按半小时一个战斗步推进;与此同时,一座城被围、被轰开、被强攻,一支渡河部队绕过敌方船队渡过河去。你会了解 canwu-military 如何接收你的规则册和输入、何时推进一场战斗,以及哪些后果留给你的游戏处理。
军事模拟扩展分为三个 crate:
canwu-military:交战引擎,负责部队、交战、围城、渡河作战及其记录和报告,构建在canwu-api之上。canwu-military-reference-content:一份明确标为合成的战斗规则册。canwu-military-reference:可替换的参考整合包,在参考世界上安装这份规则册和六支部队。
在仓库根目录执行:
cargo run -p canwu-military-reference --example military_starterengagement=canwu:military:crossing:capital-ford/ferry-0 kind=FerryFight outcome=Stalled steps=1 site_fell=false losses=0/0engagement=canwu:military:engagement:eastern-field kind=Field outcome=AttackerPrevailed steps=121 site_fell=false losses=16/424engagement=canwu:military:siege:westford/assault-1 kind=Assault outcome=AttackerPrevailed steps=5 site_fell=true losses=10/100siege=Fell cause=Some(Breach) crossing=Complete landed=2000…military_starter=complete engagements=3 signals=3 checkpoint=…military_starter=complete 表示所有断言都通过了:每一场结束的战斗都向宿主发出了信号,被围的城已经陷落,恢复和重放得到的副本与实时运行的检查点哈希相同。中间几行打印的是一个政府持有的报告。
一场战斗如何推进
Section titled “一场战斗如何推进”查看图表源码
flowchart TB
Inputs["宿主:每方的 SupplyInput、<br/>PlaceForce、OpenEngagement"] --> Apply["第 7 阶段:应用命令"]
Wake["每日的日历结算边界<br/>唤醒系统"] --> Packet["military_step_v1"]
Packet --> Step["第 7 阶段:推进到期的工作<br/>结算器、损失、溃退规则"]
Apply --> Step
Step --> Summary["结束:engagement_summary<br/>结果信号"]
Step --> Reports["第 13 阶段:持有人报告"]
Summary --> Host["宿主处理控制、<br/>库存、人口"]
起步示例的规则册信任参考上层应用的 System 签发者。每条命令都是一个 MilitaryCommandV1,通过 military_command() 经规范化输入发送;修改记录的命令要写明该记录的 expected_host_revision。引擎只在接收了战斗步包的结算边界推进战斗:这里由默认的唤醒系统在每个有到期工作的每日日历结算边界排入一个战斗步包。想自己驱动推进的上层应用可以构造 MilitaryPlugin::new().with_step_cadence(None),并在发送输入之后发送 Step { up_to };也可以为自己本来就要结算的每日结算边界排入一个定时战斗步(timed_step_ingress),这样推进不会额外增加结算边界。
起步示例做了什么
Section titled “起步示例做了什么”demo_engagement_scenario() 从参考世界的演示场景出发,加入一个敌对政府,并安装 synthetic_rulebook_v1() 和六支部队:都城里与野战军对应的部队、东方的敌方野战部队、西方的一对围城与守城部队,以及都城里面对敌方船队的渡河部队。
| 天 | 宿主命令 | 引擎做了什么 |
|---|---|---|
| 0 | 军队的移动命令(参考世界);在 Westford InvestSite,storm_on: BreachOrGate;从都城向 Westford 越过船队的 OrderCrossing |
开始围城和渡河 |
| 1 | 为已抵达的军队发送 PlaceForce;在东方 OpenEngagement { kind: Field } |
第一批人马被截住,打一场渡口之战;围城火炮就位 |
| 2 | 双方的 SupplyInput;ReviseCrossing { night: true } |
野战推进一步;渡口之战结束;夜间的一批登岸 |
| 3 | SupplyInput;ReviseStepProfile 改为半小时一步 |
城墙破开,当天开始强攻 |
| 4 及以后 | 交战持续期间的 SupplyInput |
野战在半小时战斗步中结束;攻城击溃守军,城池陷落,守军溃散 |
起步示例这样开启野战:
MilitaryCommandV1::OpenEngagement(Box::new(OpenEngagementV1 { id: field(), kind: EngagementKind::Field, profile: None, site: place(ids.eastern_territory), fort: None, attackers: vec![force_id("yun-field")], defenders: vec![force_id("rival-field")], holders: [ vec![ours.clone(), KnowledgeHolderRef::Person(ids.commander)], vec![theirs.clone()], ], inputs: [condition("field", 720), condition("camp", 680)], environment: environment(false), max_minutes: None,}))宿主把 PlaceForce 和 OpenEngagement 放在同一个准入顺序里发送:引擎按放置之后的部队检查是否同处一地,所以两者在同一个结算边界里就能衔接。
留给你的游戏的部分
Section titled “留给你的游戏的部分”引擎只写它自己的记录。城池陷落体现为攻城总结里的 site_fell,以及围城记录上带 Breach 原因的 Fell;起步示例的宿主用自己的一个小插件接收每一场结束战斗的 EngagementOutcomeSignalV1。控制与占领、作为人口的伤亡和物资库存,都由你的游戏依据这些事实去改变;军事学说、内应、配给和水上巡逻,由你的游戏决定并作为输入传进来。军事系统列出了每条命令检查和写入的内容、战斗步公式以及各项上限。
参考规则册是合成的
Section titled “参考规则册是合成的”synthetic_rulebook_v1() 包含六种地形、五个城防等级、五条分档线、四种姿态、按日和按半小时两种战斗步配置、六种交战类型的规则,以及参考上限。这些数值是演示用的,选择它们只是为了让起步示例里的战斗在几天内结束,不代表任何历史结论。游戏应当从自己的内容编译自己的规则册。
阅读完整的起步示例源码,可以看到每一条命令以及快照和重放检查。