军事系统
军事模拟扩展 canwu-military 是一个交战引擎。它在你的世界所拥有的地点上结算带类型的部队之间的战斗,依据是你的游戏安装的战斗规则册和一个战斗步结算器;结算的结果是记录和信号,由你的游戏来应用。它不拥有军事学说、兵种目录、地形表、天气、控制或占领状态、物资库存,也不撰写报告文本。决定是否用它驱动你的战斗,或者怎样把你自己的战斗模型接进来之前,请先读这一页。
简要来说:决定战斗结果的每一个数值都是你的规则册里的参数;你的游戏负责放置部队,并每天提供它们的状况;引擎推进交战,记录每一个战斗步的全部数值,最后把交战收束为一份 engagement_summary 总结记录。一个地点陷落,只是这份总结里的一项事实,引擎从不因此创建占领。
crate 与所有权
Section titled “crate 与所有权”| crate | 拥有什么 | 发布情况 |
|---|---|---|
canwu-military |
MilitaryPlugin<R>、StepResolver trait 与 ParametricStepResolver、canwu.military 命名空间下的八种记录(rulebook、registry、force、engagement、engagement_steps、siege、crossing、engagement_summary)、MilitaryCommandV1 命令,以及 engagement_report 和 engagement_outcome 两种知识 schema |
已发布到 crates.io |
canwu-military-reference-content |
synthetic_rulebook_v1(),一份明确标为合成的规则册 |
已发布到 crates.io |
canwu-military-reference |
demo_engagement_scenario()(在参考世界上安装这份规则册和六支部队)以及 military_starter 示例 |
只在仓库中 |
| 关注点 | 由谁拥有 |
|---|---|
| 战斗所知的部队:所属方、控制方、位置、各组成部分的在场与损失人数、一方条件、战斗状态 | canwu-military(force 记录);由你的游戏登记、修订和放置 |
| 部队的其他一切:粮饷、自主度、战备、学说、指挥官、库存 | 你的游戏,每个交战日作为条件输入传入 |
| 交战过程:开战、预判、战斗步、溃退、追击、结束、总结 | canwu-military,依据你的规则册和一个结算器 |
| 围城与渡河作战 | canwu-military;城防、补给、水面控制,以及按你自己的规则打开的城门,都是你的输入 |
| 移动、接敌、侦察、敌对关系 | 你的游戏,以及 canwu-movement 或 canwu-knowledge;引擎只检查部队是否站在该地点 |
| 控制、占领、作为人口的伤亡、物资库存 | 你的游戏,依据总结和结果信号 |
- 战斗规则册(
CombatRulebookV1):战斗步配置、六种交战类型(Field、Assault、Sortie、Relief、FerryFight、BridgeheadFight)各自的规则、预判项与分档、四条战线、凝聚力、士气、溃退、追击、地形、夜战、城防、围城和渡河规则,以及引擎保存的每种集合的上限。规则册带哈希;由初始场景安装,或由System签发方发送InstallRulebook。一次运行只有一份。 - 部队(
ForceStateV1):部队站在你的世界所拥有的一个地点上(LocationV1::At { site });部队抵达时由你的游戏用PlaceForce放置。各组成部分带着你的游戏从兵种资料编译出的数值;一方条件包括凝聚力上限、士气崩溃线、临阵指挥、判断力、可信度和特征。 - 交战(
EngagementStateV1):一个地点上的双方,每一方都有自己的部队、报告持有人、最近一次的战斗条件输入(ConditionInputV1)、本场战斗的士气冲击、凝聚力和损失;还有依据规则册的预判项和比值分档得出的战前预判(ForecastV1)。 - 战斗步(
StepV1):一次战斗步公式的结算,包含每一个战斗步数值、输入的摘要、随机抽样和结算器的身份。 - 围城(
SiegeStateV1)与渡河作战(CrossingStateV1):按日和按批推进的过程,会自行开启交战。
查看图表源码
flowchart TB
Cmd["MilitaryCommandV1<br/>规范化命令输入"] --> Admit["准入<br/>静态检查、持有人"]
Admit --> Apply["military_apply_v1 包"]
Wake["按你的节奏运行的唤醒系统、<br/>可信的 Step 命令,<br/>或你的定时战斗步"] --> StepPacket["战斗步包"]
Apply --> Settle["第 7 阶段:settle-engagements-v1<br/>先应用命令,再推进战斗步"]
StepPacket --> Settle
Settle --> Records["force、engagement、steps、<br/>summary、siege、crossing"]
Settle --> Signal["可选的结果信号<br/>发往你的插件"]
Records --> Publish["第 13 阶段:向各方<br/>持有人发布报告"]
- 你的游戏发送命令;准入阶段检查它的结构和报告持有人,并为同一时刻的下一个结算边界排入一个应用包。
- 只有接收了战斗步包的结算边界才会推进战斗。使用
with_step_cadence(Some(cadence))(默认按日)时,唤醒系统在有到期工作时排入一个战斗步包,在同一时刻的下一个结算边界推进;使用with_step_cadence(None)时,由你的游戏在发送输入之后发送Step { up_to },在应用这些输入的结算边界推进。无论哪种方式,你的游戏都可以改为给本来就要结算的结算边界(例如下一个每日结算边界)排入一个定时战斗步(timed_step_ingress(due_at)):由那个结算边界推进,推进本身不再增加结算边界。该结算边界准入的命令在下一个切点、也就是战斗步之后才生效;要让当天的命令先于战斗步生效,你的游戏随命令一起发送Step,或者让命令在定时战斗步到期之前的某个结算边界准入,这样它们的应用包就落在推进的那个结算边界里、先于战斗步应用。 - 第 7 阶段,结算系统按准入顺序应用本结算边界的命令,然后推进到此为止到期的每一场交战、每一个围城日和每一批渡河。
- 每个战斗步都从双方的组成部分、最近的输入、环境、该类型的规则和地点构造结算器的输入;调用结算器;按最大余数法(先按比例取整数部分,再把剩下的名额按余数从大到小分给各组成部分)把伤亡分配到各组成部分;应用溃退规则和追击;然后追加这个战斗步。
- 一方撤退或溃散时交战结束;交战的
max_minutes(该类型的时限,或你的游戏开战时指定的更短时限)到期而无人溃退时,以Stalled结束。结束时写出一份只能创建一次的engagement_summary总结记录,如果你要求了,还会发送结果信号。 - 第 13 阶段,报告系统在知识发布预算内向每一方的持有人发布持有人相对的报告。
战斗步与结算器
Section titled “战斗步与结算器”结算器是一个类型:MilitaryPlugin<R: StepResolver>。ParametricStepResolver 依据组成部分、地形、夜战、城防和补给,为每一方计算四条战线(火力、冲击、近战、防御四个分项,每项一个整数),以及每一方每个战斗步造成的损伤率;每一个战斗步数值(造成的比例、凝聚力损失、士气冲击)都只按配置的 step_scale_bp(以基点计,万分之一为一基点)缩放一次,所以按日的配置可以把许多短战斗步压缩成一步。使用自己公式的游戏只需实现一次 StepResolver,并可以通过 StepDraws 请求定址抽样。规则册指定的结算器与插件运行的不同时,激活会拒绝它。
调用之外的一切由引擎负责:它把结果限定在规则册的范围内,应用溃退规则(守在规则册所定等级、尚未破开的城墙后的一方从不溃退),在溃散时应用追击,并决定战斗是否继续。战斗步配置让一场交战按日推进,另一场则可以提升为半小时一步(ReviseStepProfile)。
每个围城日都依据围城方人数和可能的水面控制计算合围程度,先架设火炮再轰击城墙,直到达到该等级的破城阈值;当下令攻城,或在 storm_on: BreachOrGate 下城墙破开或城门打开的当天,开启一次攻城交战。下令出城反击且守军的冷却时间已过时,开启一次出城反击;援军解围则是你针对围城方开启的一场交战。守军在城下溃退,围城即告陷落,仍留在城内的守军随之溃散;围城方溃散则围城解除。第二支部队可以用 JoinSiege 加入已被围的地点,如果攻城或出城反击正在进行,它也在自己一方加入这场交战;ReviseSiege { release } 则让任一方不在交战中的部队撤出围城,好让你把它放到别处。最后一支守军撤出后,围城在下一个围城日以 Abandoned 陷落;最后一支围城部队离开时,用 LiftSiege 结束围城。
渡河作战每隔一个批次间隔发出一批人马。控制力足够高的拦截方按阈值或按一次定址抽样截住一批;被截住的一批打一场渡口之战后折返;登岸的一批可能在对岸与守军打一场滩头之战;对岸守军由渡河命令指定,后来抵达对岸的部队可以用 ReviseCrossing { defenders } 加入。渡河完成后由你的游戏移动这支部队。
在准入阶段失败的命令是一次被拒绝的命令尝试。在第 7 阶段失败的命令(未知或已在交战中的部队、过期的 expected_host_revision、签发方不控制任何部队、超出上限、未知的姿态或战斗步配置)会以一条 canwu.military.command_rejected.v1 事件记录下来,事件摘要以错误代码开头。引擎无法完成的战斗步(战斗步过大、结算器出错、抽样过多)会让该交战进入挂起状态并发出 canwu.military.engagement_held.v1,直到收到 SupplyInput、ReviseStepProfile 或 OrderAction { Abandon }。这些情况都不会让结算边界失败。
知识与可见性
Section titled “知识与可见性”每一方指定自己的报告持有人:人物、政府,或部队记录本身。持有人在命令准入时检查一次,在发布报告时再检查一次,所以已经去世的持有人会被跳过。进度报告(engagement_report)会取代该方上一份报告;结束报告(engagement_outcome)会保留。双方都没有指定持有人的交战、围城或渡河不会排入报告,所以自己依据记录撰写报告的游戏不必为报告系统付出任何开销。一方能准确看到自己的损失,而敌方的数字只按规则册的分档显示。
随机性、持久化与重放
Section titled “随机性、持久化与重放”两条随机流 crossing-interception 和 step-contingency 都按操作地址定址抽样,并以已准入的战斗步包作为证据,所以实时运行、封存后的运行、恢复的运行和重放的运行抽到的结果相同。每一个修改记录的命令都要写明该记录的 expected_host_revision,它只会被上层应用的命令推进:同一时刻的战斗步从不会让你的命令过期。唤醒节奏、信号目标和发布预留都是插件描述的一部分,所以只有做出同样选择的运行才能恢复和重放。
你的应用需要提供什么
Section titled “你的应用需要提供什么”- 从你的内容编译出的规则册。
- 部队及其修订(征集、逃亡、伤员归队),以及部队抵达时的放置。
- 依据你自己的接敌规则开启的交战、围城和渡河,并写明每一方的报告持有人。
- 每天每一方一份条件输入和一份环境输入,从你的补给、粮饷、指挥和天气记录推导,并作为证据引用。
- 引擎不替你决定的规则:内应(
ReviseSiege { gate_open_steps })、攻城学说、配给、水上巡逻、提升一场交战的精度。 - 战斗之后的事情:依据总结或结果信号处理控制、占领、人口、库存和玩家文本。
引擎保存的每种集合都在规则册的上限中有一个上限:部队数、每支部队的组成部分、每方的部队和持有人、同时进行的交战、围城和渡河、每个战斗步的字节数、每个战斗步的抽样次数,以及报告队列。没有战斗步包的结算边界不做任何推进工作;推进战斗的结算边界,其开销与进行中的交战及其中的部队成正比。一场结束的交战会在整个运行期间留下约 2 KB 的记录。每场交战只有两方,引擎不会自动接敌,报告里的敌方数字是分档的。
cargo run -p canwu-military-reference --example military_starter教程运行交战引擎逐步讲解了起步示例。想了解以资源为基础的补给,可以运行 cargo run -p canwu-economy-reference --example grain_loop,并阅读粮食、生产与军事补给案例。随机流的声明和抽样证据见随机性。