Skip to content

Latest commit

 

History

History
643 lines (487 loc) · 33.7 KB

File metadata and controls

643 lines (487 loc) · 33.7 KB

国策编写指南(*.warf-decision)

国策是纯 JSON 文件,写在代码之外,放进 decisions/ 就能用。本文是格式的完整参考:每个字段、每个属性、每处会失败的地方。


1. 快速开始

  1. 在 decisions/ 下新建 任意名字.warf-decision(扩展名必须完全一致)。
  2. 写入下面的最小可用内容:
{
  "Country": "CHN",
  "Decisions": [
    {
      "Type": "Decisions",
      "Name": "五年计划",
      "Description": "集中力量办大事。",
      "Require": [
        { "Type": "Economy", "To": "self", "Condition": ">=", "Num": 50 }
      ],
      "Time": 90,
      "Result": [
        { "Type": "Result", "Rewards": [
          { "Type": "Reward", "Rewards": [["Economy", "+", 8]] }
        ] }
      ]
    }
  ]
}
  1. 桌面版:打开游戏 → 右侧面板「国家国策」→ 点「从磁盘重新载入」。不需要重新编译。
  2. 浏览器版(npm run dev / 网页版)没有文件系统,国策在构建时打包进去,改完要重新构建。

三条必须先知道的事

事实 说明
是严格 JSON 不支持 // 注释、不支持尾逗号、不支持单引号。写坏了不会崩游戏,但那条国策不会出现。
出错不静默 解析失败的文件会连同原因列在国策面板顶部(dec.err.* 文案),其余文件照常加载。
文件可以在开局前开关 标题界面的「游戏设置」里有一份决策文件清单,可以在开始战役前关掉不想玩的文件。all.warf-decision 是基础文件,不可关闭;其余文件的开关状态会记住,下一局继续生效。战役开始后清单不再可改。
加载是「替换」不是「合并」 桌面版只要在磁盘上找到 decisions/ 目录且有文件,就只用磁盘上的文件,构建时打包的那份被整个忽略。所以从仓库里删掉 all.warf-decision 会让它的国策一起消失。

桌面版查找顺序(src-tauri/src/lib.rs 的 decisions_dir):环境变量 WARFIRE_DECISIONS_DIR → 当前工作目录的 decisions/ → ../decisions/(tauri dev 用)→ 可执行文件同级的 decisions/ 与 resources/decisions/ → 用户数据目录。第一个存在的目录胜出。


2. 文件结构

// 结构示意 —— 注释只是说明,真文件里不能有它们
{
  "Country": "CHN",        // 必需。国家 id、"all" 或 "*"
  "Decisions": [ /* … */ ],// 必需。数组,可以放任意多项国策
  "News": [ /* … */ ]      // 可选。新闻条目,见 §10
}
字段 类型 必需 说明
Country string ✅ 生效范围。"all" / "*" = 所有国家;否则是国家 id(USA CHN RUS GBR FRA DEU JPN IND PRK IRN ISR BRA)。大小写不敏感。
Decisions array ✅ 国策定义列表。空数组合法(等于这个文件不提供任何国策)。
News array — 新闻条目,供 EffectNews 按 id 发布(§10)。每条需要唯一的 NewsId。

国策的唯一标识

运行时状态(已完成次数、阶段游标)按 「Country」::「Name」 记录。因此:

  • 同一个文件里不要出现两个同名国策 —— 它们会共用同一份运行时状态,读起来像 bug。
  • 不同 Country 文件里的同名国策是两项不同的国策(all::五年计划 ≠ CHN::五年计划)。但两个文件同时对同一个国家生效、且国策重名时,面板会出现两项同名国策、共享状态。别这么写。
  • 改名 = 换了一项新国策:旧的进度(completed、phaseIndex)不会跟过来。

3. 一项国策的字段

{
  "Type": "Decisions",            // 约定俗成,解析器不校验
  "Name": "对敌宣传攻势",          // 必需
  "Description": "对关系恶劣的国家发动宣传战。\n动摇其民心。",
  "Require": [ /* 见 §4 */ ],
  "Time": 30,                     // 数字(1…180)或 "inf"
  "PlayTime": "resm",             // 属于哪个时代;省略 = "resm"
  "InfinityPhases": [ /* 仅 Time:"inf" 需要,见 §6 */ ],
  "Result": [ /* 见 §5 */ ]
}
字段 类型 必需 说明
Name string ✅ 国策名。面板显示、日志、任务名都用它。同名同国家 = 同一项国策。
Description string — 面板里的说明文字。\n 会真的换行,\t、\"、\\ 同 JSON 规则。
Require array — 执行条件。省略或 [] = 无条件。
Time number | "inf" ✅ 天数。见下。
PlayTime string | array — 只在哪些时代出现。省略 = "resm"(21 世纪初)。见 §3.2。
InfinityPhases array Time:"inf" 时 ✅ 持续性国策的阶段列表,至少一项。
Result array — 结算奖励。一次性国策是唯一收益;持续性国策是每阶段的额外收益。

PlayTime:这项国策属于哪个时代

战役开始时选的时代,决定哪些国策存在。代码取自时代英文名的首字母:

代码 时代 开局
allt 所有时代(all time) —
ngtm 二战前夕 · 梦魇(nightmare) 1938 年 1 月
dfig 二战战争 · 死斗(die fight) 1942 年 1 月
waku 二战战争 · 初醒(wake up) 1945 年 1 月
splt 二十世纪 · 解体(split) 1994 年 1 月
resm 二一世纪 · 复兴(resume) 2000 年 1 月
"PlayTime": "ngtm"                       // 只在 1938 年
"PlayTime": ["dfig", "waku"]             // 1942 与 1945 都出现
"PlayTime": "allt"                       // 哪个时代都有
// 不写 → "resm",即 21 世纪初那一局

不属于任何年份的国策用 allt:战时经济动员、对敌宣传攻势、邀请军事指挥家这些 跟具体哪一年无关的国策,写 allt 而不是把五个代码列一遍——将来加第六个时代时, 它们不会因为漏改而被落下。

规则:

  • 大小写与空格不影响:" NGTM " 与 "ngtm" 相同。
  • allt 与别的代码混写(["allt","ngtm"])等于 allt。
  • 写错就报错并丢弃这条国策,不会静默当成 resm——把一条 1938 年的国策放进 2000 年的战役,比少一条国策更难查。错误会出现在国策面板顶部(dec.err.play_time)。
  • 不是当前时代的国策根本不出现:面板不列、控制台不认、startDecision 也会拒绝。
  • 打了一半换时代不存在:时代在开局时定死,一局之内不会变,所以不会出现「执行到一半时代变了」。

这一条只作用于国策。同文件里的 News(§10)目前不受 PlayTime 限制,任何时代都可能被 EffectNews 发布。

Time 的规则

写法 含义
30 一次性国策:任务条走 30 天后结算 Result。
1 … 180 合法区间。Time 非法(非数字、<= 0)→ 这条国策被丢弃,并在面板报错。
> 180 会被截断到 180(不报错,静默截断)。MAX_DECISION_DAYS = 180。
"inf" 持续性国策:每阶段结束后可再次执行,永不「完成」。

天数是游戏内天数,受时间流速影响(5× 时 1 天 ≈ 0.2 秒)。


4. 条件(Require)

{ "Type": "Stability", "To": "self", "Condition": ">=", "Num": 60 }
字段 类型 必需 说明
Type string ✅ 属性名(§7 总表)或六个成对属性之一(§8)。大小写不敏感。
To string — 读谁的值。self(默认)/ target / enemy / 国家 id。
Condition string — 比较运算符,默认 >=。
Num number — 比较的右值,省略按 0 算(建议总是写)。

运算符:>= <= > < == !=,另外 = 与 == 等价。写错运算符不会报错,这条条件恒为 false(面板上会一直显示 ✘)。

To 的解析规则:

写法 解析为
"self" / "player" / 省略* 执行国策的国家(通常是玩家)
"target" 面板上选中的目标国
"enemy" 执行国正在交战的第一个国家;没有则条件失败
"usa" / "USA" 该国 id(大小写不敏感)

* 严格说:省略 To 时,若面板已选中目标国则用目标国,否则用 self。建议总是显式写 To。

多项条件之间是「与」:全部满足才能执行。

条件只在点击执行的瞬间检查一次,任务开始后不再复查(startDecision 注释:"a decision, once begun, runs to completion")。

面板会为每条条件显示 ✔/✘、解析后的文字,以及当前值(ui.req.current),所以写国策时不用猜自己算得对不对。


5. 奖励(Result / InfinityPhases[].Get)

奖励三元组:

["Economy", "+", 5]
["Relation", "-", 10]
["PublicSupport", "=", 50]
位置 路径
结算奖励 Result[].Rewards[].Rewards[]
阶段奖励(仅 "inf") InfinityPhases[].Get[].Rewards[]

Rewards 里可以有多个 { "Type": "Reward", "Rewards": [...] } 对象,它们的元组会被按顺序拼成一条列表执行。

运算符:+ - * / =(= 是直接赋值)。

  • 结果会按属性的上下限截断(例如 Economy 上限 100,Treasury 下限 0)。
  • ["X", "/", 0] 会保持原值(不报错)。
  • 写错属性名或运算符 → 这条奖励被静默丢弃,日志里不会出现。这是最常见的「明明写了却没生效」。核对办法:面板上的奖励标签会原样显示属性名(§7 的规范名拼写一致才算命中)。

奖励作用于谁?

奖励 作用对象
普通属性(§7) 执行国策的国家(actor)——不是 To 指定的国家
Relation / NukedCountry / GetCasusBelli / War / SideCountry / FullDestroy 成对属性:由元组第四项或面板选中的目标国决定(§8)
ToCountry 重定向:把同一元组里排在它后面的奖励转给指定国家(§9)
EffectNews 发布一条新闻;元组第四项是 NewsId,不是国家(§10)

奖励元组的第四项(可选)就是国家:["GetCasusBelli", "+", 1, "JPN"]。它和条件里的 To 写法一致(self / target / enemy / 国家 id)。

面板只会为「需要目标国」的国策显示目标选择器:条件里写了 To: "target",或奖励里有不带第四项的成对属性。带第四项的国策不需要选目标。

目标国是点击执行时冻结进任务的:一项 120 天的国策在第 1 天选定目标,第 120 天结算时还是打这个国家。


6. 持续性国策(Time: "inf")

{
  "Time": "inf",
  "InfinityPhases": [
    { "Type": "Phase", "Get": [ { "Type": "Get", "Rewards": [["Military", "+", 4]] } ],
      "Content": "工厂昼夜不停地运转。", "Time": 45 },
    { "Type": "Phase", "Get": [ { "Type": "Get", "Rewards": [["Economy", "+", 3]] } ],
      "Content": "产能开始反哺民生。", "Time": 60 }
  ],
  "Result": [ { "Type": "Result", "Rewards": [ { "Type": "Reward", "Rewards": [["ForceValue", "+", 3]] } ] } ]
}
  • 执行一次 = 走完当前阶段的 Time 天,然后:应用该阶段的全部奖励 + 应用 Result(每次结算都应用)+ phaseIndex 前进一格(循环)。
  • 面板按钮文案是「开始下一阶段」,随时可以再次点。
  • InfinityPhases 为空或缺席 → 整条国策被丢弃并报错(dec.err.inf_no_phase)。
  • 阶段 Time 缺失时按 30 天算,并按 1…180 截断。
  • 阶段文本 Content 会以弹窗形式呈现给玩家。

7. 属性总表

大小写不敏感;范围 是写入时的截断区间。

全局(描述玩家国家)

属性 范围 单位 说明
CountryEndurance 0–100 % 国家耐力。To 指向 AI 国家时,改用该国的稳定度读写。
ArmyEndurance 0–100 % 军队耐力。To 指向 AI 国家时,改用该国的军力。
DiplomaticPoints 0–100 — 外交点。AI 国家读作 100,写入无效。
WorldCollapse 0–100 % 世界崩坏度。与 To 无关,永远是世界的那一个值。
Day ≥1 天 当前天数。写入是跳时钟:= 100 直接跳到第 100 天(中间的每日结算照常发生)。

国家属性

属性 范围 单位 写 说明
Economy 0–100 — ✅ 经济
Military 0–100 — ✅ 军力
Stability 0–100 — ✅ 稳定度
PublicSupport 0–100 % ✅ 民众支持率(发动战争、核打击都有门槛)
ForceValue 0–100 % ✅ 迫使值 / 胁迫意愿
Treasury ≥0 B ✅ 国库(十亿)
Population ≥0 M ✅ 人口(百万)
Manpower ≥0 K ✅ 人力池(千)
Nuke ≥0 枚 ✅ 核弹头库存(写入自动取整)
Nuclear 0/1 — ✅ 是否核武国家;= 1 授予核能力
AutoArmy 0/1 — ✅ 把军队交给参谋部(= 1 开启)
Divisions ≥0 个 ✅ 师的数量。写入会真的增减师团:新增的师按征募的标准状态创建(命名、50K 人力、强度 70、组织度 100),减员会同时清理指向它们的集团军编成。
DivisionStrength 0–100 % ✅ 平均师强度(读取时取平均)。写入会把该值套到每一个师上,= 100 就是全军满编。

Nuclear 只代表「拥有核武能力」,不等于有弹头:投放需要 Nuke >= 1。


8. 成对属性(需要国家)

这六个属性同时读取执行国与另一国,因此需要指定国家:条件里用 To,奖励里用元组第四项(省略则用面板选中的目标国)。

Relation —— 好感度

  • 条件:{ "Type": "Relation", "To": "RUS", "Condition": "<=", "Num": -30 } 读的是执行国对目标国的好感度(-100…100)。
  • 奖励:["Relation", "-", 6] 双向写入(双方互相的好感度一起变),+ 增加、= 直接赋值、- 减少;结果截断在 ±100。写其他运算符等同 +(* / 不生效,别用)。
  • 注意:奖励只改数值,不会同步「盟友 / 敌人」名单(那套逻辑属于 adjustRelation,只有游戏内行动会调用)。

NukedCountry —— 核打击 / 是否被我核过

作为条件:读取「执行国对 To 国投放核弹的次数」。

{ "Type": "NukedCountry", "To": "JPN", "Condition": ">=", "Num": 1 }
  • 只统计执行国自己投下的核弹。别国(含 AI)核打击同一个国家不会让这条条件成立 —— 这是一条「我干过没有」的检查,不是「那里挨过核没有」。
  • 数值是次数,所以 >= 1(干过)、== 0(没干过)、>= 2(干过两次以上)都能写。
  • 反过来说:没有「别国核了我没有」的写法。To: "self" 读的是「我方对我方投放核弹的次数」,恒为 0,不要用。

作为奖励:["NukedCountry", "+", 1, "JPN"] —— 向该国投放一枚核弹。

  • 运算符和数值被忽略(["NukedCountry", "*", 3] 和 ["NukedCountry", "+", 1] 完全一样,都只投一枚)。要投多次就写多条。
  • 第四项省略时打面板选中的目标国;两者都没有 → 跳过,并在奖励日志里写一行说明。
  • 不做任何前置检查:不要求执行国有核能力、有弹头、民众支持够高,也不消耗弹头。想要代价就自己加条件和奖励,例如:
"Require": [
  { "Type": "Nuclear", "To": "self", "Condition": "==", "Num": 1 },
  { "Type": "Nuke", "To": "self", "Condition": ">=", "Num": 1 }
],
"Result": [ { "Type": "Result", "Rewards": [ { "Type": "Reward", "Rewards": [
  ["Nuke", "-", 1],
  ["NukedCountry", "+", 1, "JPN"]
] } ] } ]
  • 效果与游戏内的「核打击」行动完全一致(同一个 detonateNuke):
    • 受害国:Military Economy Stability 归零,人口 ×0.3,师只剩 30%(强度 ×0.4、组织度归零),集团军编成清理。
    • 双方好感度 -100。
    • 执行国若是玩家:军队耐力 -40、国家耐力 -30、民众支持 -25。
    • 世界崩坏度 +25。

GetCasusBelli —— 战争理由

作为条件:To 国是否在执行国的宣战理由名单里。

{ "Type": "GetCasusBelli", "To": "JPN", "Condition": "==", "Num": 1 }
  • 读数为 1(有)或 0(没有)。
  • 「有战争理由」正是游戏里宣战的硬性前提(面板「宣战理由」、行动「正当化宣战理由」)。宣战一旦完成,该理由会被消耗掉,这条条件随之变回 0。

作为奖励:

元组 效果
["GetCasusBelli", "+", 1, "JPN"] 获得对日本的战争理由(已有则不重复)
["GetCasusBelli", "-", 1, "JPN"] 撤销对日本的战争理由
  • 数值同样被忽略,只有运算符的 - 有意义。
  • 只写 ["GetCasusBelli", "+", 1] 时作用于面板选中的目标国。

War —— 战争状态与宣战

作为条件:To 国是否正与执行国交战。

{ "Type": "War", "To": "JPN", "Condition": "==", "Num": 1 }
  • 读数为 1(正在交战)或 0(没有)。
  • 读的是双方的 atWarWith 名单 —— 宣战、被宣战、AI 之间的战争都算;签了和约就变回 0。

作为奖励:宣战。方向由运算符决定:

元组 效果
["War", "+", 1, "JPN"] 我方向日本宣战(我方是进攻方)
["War", "-", 1, "JPN"] 日本向我方宣战(日本是进攻方,即 reverse)
["War", "+", 1] 对面板选中的目标国宣战
  • 数值是占位符,只有运算符的 - 有意义。
  • 这是剧本化的宣战,会跳过所有外交门槛:不需要战争理由、不看民众支持率、不看军队耐力与兵力、不看双方态度。国策文件就是它自己剧本的权威。
  • 它仍然遵守世界的硬规则:不能对自己宣战、已经在交战的两国不会重复宣战(此时奖励会写一行「无法宣战」并跳过)。同一时间只跑一场战争,旧的那场会被归档进战史。
  • 宣战的附带效果与游戏内行动一致:双方好感度 -30、世界崩坏度 +8、进攻方的战争理由被消耗。想做「先正当化再宣战」的剧本,就用 GetCasusBelli 条件当门槛:
"Require": [ { "Type": "GetCasusBelli", "To": "target", "Condition": "==", "Num": 1 } ],
"Result": [ { "Type": "Result", "Rewards": [ { "Type": "Reward", "Rewards": [
  ["War", "+", 1]
] } ] } ]

SideCountry —— 附属国

作为条件:To 国是否从属于执行国。

{ "Type": "SideCountry", "To": "JPN", "Condition": "==", "Num": 1 }

判定有两条路,满足其一即为 1:

  1. 有人这么写过 —— SideCountry 奖励给该国打上了附属标记(记在该国自己的 overlordId 上)。
  2. 我方占着它的土地 —— To 国任何一个行政区的实际归属是执行国,就算数,占多少都不重要。

第二条是算出来的,不落盘:把土地还回去,附属状态随之消失。反过来,「解除」按钮解除不了被占领的土地 —— 先打下来再解除,条件照样成立;归还土地才是解除。

作为奖励:

元组 效果
["SideCountry", "+", 1, "JPN"] 使日本成为我方附属国
["SideCountry", "-", 1, "JPN"] 清除附属标记(我方仍占着它的地时,条件依然成立)
["SideCountry", "+", 1] 对面板选中的目标国
  • 数值是占位符,只有运算符的 - 有意义。
  • 不能把自己变成自己的附属国,也不能凭空指定不存在的国家(都会写一行日志并跳过)。
  • 全屏地图的提示框会标出「我方附属国 / 我方宗主国」—— 这是游戏里唯一能看到它的地方。

目前它只是状态与判定:附属国不会自动跟随宗主国参战,双方也不会因此不能开战,AI 行为不受影响。想要这些效果,得在国策里自己写(例如用 ToCountry 把资源抽走,或用 War 让宗主国替它出头)。

FullDestroy —— 彻底毁灭

作为条件:To 国是否已经被彻底毁灭。

{ "Type": "FullDestroy", "To": "JPN", "Condition": "==", "Num": 1 }

作为奖励:把 To 国从地图上抹掉。

["FullDestroy", "+", 1, "JPN"]
  • 数值和运算符都是占位符。
  • 它的地块直接沉入海底:地图上不再绘制该国的国土、行政区边界、城市与国名,那片海就是原来的陆地;它也不能再被点选、悬停。
  • 同时:军队与集团军清空,经济 / 军力 / 稳定度 / 民众支持 / 人力 / 人口 / 弹头归零,交战国列表清空、进行中的战争直接结束(对手记为胜者),盟友 / 敌人 / 战争理由一并抹去,它占过别国的行政区归还给原主。
  • 不可逆:没有任何属性可以把它变回来。
  • 不能毁灭自己的国家(To: self 会被拒绝并写一行日志)——玩家亡国走的是被征服结局,那条路需要一个征服者来接收领土。
  • 已经毁灭过的国家再毁灭一次只会写一行「此前已毁灭」。
  • 毁灭后对方仍然留在国家名册里(关系、战争史、国策运行时都用它的 id),但地图、AI 与所有国家列表都会跳过它。

与 SideCountry 的区别:附属国是「活着但听命于你」,FullDestroy 是「不存在了」。想先打服再傀儡化用 SideCountry,想彻底抹掉用 FullDestroy。


9. 奖励重定向(ToCountry)

只支持奖励,不能当条件。 它把排在它后面的奖励转给别的国家 —— 援助盟友、惩罚第三方、让两个别国打起来,都靠它。

["ToCountry", "+", 1, "JPN", "Economy", "-", 20]

读法:ToCountry 是属性名,元组头的 "+", 1 是占位符(与其它动作型奖励一致,运算符和数值都被忽略),第 4 项是目标国,从第 5 项开始是一串指令,从左到右执行:

指令 含义
<属性> <运算符> <数值> 作用于当前目标国
<属性> <运算符> <数值> <国家> 同上;成对属性(§8)可以在这里指定它的另一方
ToCountry <国家> 换一个目标国,后续指令都跟着走(也接受 ToCountry + 1 <国家> 这种与元组头对称的写法)

实例:

["ToCountry", "+", 1, "target", "Treasury", "+", 500, "Stability", "+", 5]
["ToCountry", "+", 1, "DEU", "Economy", "-", 6, "ToCountry", "+", 1, "RUS", "Economy", "-", 6]
["ToCountry", "+", 1, "JPN", "War", "+", 1, "CHN"]
["ToCountry", "+", 1, "JPN", "NukedCountry", "+", 1, "CHN"]

要点:

  • 目标国写法与 To 完全一致:self / target / enemy / 国家 id。target 指面板选中的国家,没选就整条跳过(不会像别处那样退化成执行国自己);写成不存在的国家同样跳过,并在日志里说明。
  • 效果全部落在指定国家身上,不会顺带作用于执行国。想两边都给,就写两条。
  • 成对属性在里面以当前目标国为执行方:["ToCountry", "+", 1, "JPN", "Relation", "+", 10] 改的是日本与面板目标国之间的好感度;带上第四项就能指定另一方,如上面的 War / NukedCountry 例子。
  • 双方都不是玩家的战争只记进交战名单,不会顶掉玩家正在打的那场战争 —— 这条对 ["ToCountry", … , "War", …] 尤其重要。
  • 指令按 属性 运算符 数值 三元组切分;只有在确实是一个国家时,后面的第四个令牌才会被当成国家(所以 "Economy" 不会被误认成国家名)。
  • 整条重定向共用一行奖励日志,按国家逐个列出各自的结果。
  • 里面的属性名、运算符、上下限规则与普通奖励完全相同(§5):拼错的属性依然会被静默丢弃。

10. 新闻(News 与 EffectNews)

在国策文件的顶层写 News 数组,就能准备一批新闻稿;用 EffectNews 属性发布它们。发布过什么记在战役里(存盘保留),所以「这条新闻发过了没有」可以被条件读到。

{
  "Country": "all",
  "Decisions": [ /* … */ ],
  "News": [
    {
      "Type": "News",
      "Title": "日本陷落",
      "Countries": ["CHN", "JPN"],
      "Content": "中华人民共和国 对 日本国 宣战过后,日本国 被其重创,今已成为其附属国。",
      "Effects": [ ["WorldCollapse", "+", 30] ],
      "NewsId": "chinatfr_japdie"
    }
  ]
}
字段 类型 必需 说明
NewsId string ✅ 稳定 id,国策文件用它引用这条新闻。全局唯一:不同文件重名会报错,且后一条不生效。
Title string ✅ 标题。气泡的抬头就是它。
Content string ✅ 正文。\n 换行。
Countries string[] — 涉及的国家 id,显示在气泡底部。
Effects 奖励元组数组 — 发布瞬间结算的奖励,写法与 Result 里的完全一样(§5),连成对属性、ToCountry 都能用。
Type string — 约定俗成写 "News",解析器不校验。

EffectNews —— 发布新闻 / 是否发布过

作为条件:这条新闻是否已经发布过。To 在这里填的是 NewsId(条件里唯一的字符串槽位),不是国家。

{ "Type": "EffectNews", "To": "chinatfr_japdie", "Condition": "==", "Num": 1 }

作为奖励:发布这条新闻。元组第四项是 NewsId,不是国家:

["EffectNews", "+", 1, "chinatfr_japdie"]
  • 运算符和数值都是占位符。
  • 发布是幂等的:已经发布过的新闻再发一次只会写一行「此前已发布」,效果不会重复结算(所以 ["WorldCollapse", "+", 30] 只会加一次)。
  • 发布时会:立刻结算 Effects → 记录 id → 把这条新闻推成一张地图气泡(📰 新闻),同时进入事件记录。
  • 效果作用于执行国策的国家(与普通奖励一致),新闻只是叙事外壳。
  • NewsId 写错(没有这条新闻)时:条件恒为 ✘ 并显示「未知新闻:xxx」;奖励写一行日志并跳过。
  • 气泡的颜色按 Effects 实际造成的世界崩坏度变化分档(≥25 红、≥12 橙、≥5 金、否则绿)。

循环是安全的:id 在效果结算之前就被记下,所以「新闻 A 的效果发布新闻 B、B 又发布 A」不会无限递归 —— 第二次遇到同一个 id 直接跳过。


11. 执行、时间与取消

  • 点击执行后国策进入任务队列(右上角),按 Time 天推进;队列里会显示国策名与目标国。
  • 国策任务可以取消(✕)。取消不退款、也不结算奖励 —— 条件与代价在执行时就付出去了,不会回来(startedAt 也不会清掉,但那不影响再次执行)。
  • 同一项国策同时只能有一个任务在跑,重复点击会提示「已在进行中」。
  • 时间只在游戏未暂停时流动。

一次性国策只做一次

Time 为数字的国策结算后就从面板消失 —— 它已经用掉了。运行时记录(decisionStates 里的 completed)仍留在存档里,条件依然读得到,但没人能再执行一次。

持续性国策(Time: "inf")不受影响:永远留在面板上,按钮是「开始下一阶段」。


12. 模板

decisions/templates/ 下有九份可直接抄的文件。它们在子目录里,加载器只读 decisions/ 顶层的 *.warf-decision,所以放在那里不会出现在游戏里;要用的时候复制到上一层即可。

模板 演示
one-shot.warf-decision 一次性国策:条件 + Result 奖励 + 国家限定
infinite.warf-decision 持续性国策:两个阶段 + 阶段奖励 + 每阶段结算
targeted.warf-decision 面板选择目标国(To: "target")+ Relation 双向读写
nuclear-and-casus-belli.warf-decision 核打击、战后索赔、战争理由的获得与撤销
war.warf-decision 宣战(+ 我方 / - 对方)+ War 作为条件
to-country.warf-decision 奖励重定向:援助盟友、制裁第三方、挑动别国开战
subject.warf-decision 附属国:建立傀儡、从附属国抽资源、归还土地
news.warf-decision 新闻:声明新闻稿、发布、按「是否发布过」解锁后续国策
annihilation.warf-decision 彻底毁灭:抹掉一个国家、发新闻、按毁灭状态解锁后续

13. 调试技巧

  • 国名后面看 id:控制台(`)输入 debug,国名旁会追加显示国家 id —— 🇨🇳 中华人民共和国 (CHN)。写 Country 和 To 时不用猜。
  • 看数值:控制台 status(别名 s)打印玩家国家的一整份数据 —— 经济 / 军力 / 稳定度、国库 / 人口、盟友敌人、外交点、军队耐力、国家耐力、崩坏度、迫使值、民众支持率、人力、师数、弹头数、交战国、汇率。改国策前后各来一次,奖励有没有落地一眼可见。
  • 条件面板自带当前值:展开国策时每条条件右边就是执行国视角的当前读数,比反复改文件猜快得多。
  • 直接跑一段奖励:控制台 debug execute [["Economy","+",5],["Nuke","+",1]] —— 与国策 Rewards 同一套格式,写完国策可以把奖励原样粘进控制台验证;也接受外面那层 Result 包装([{"Type":"Reward","Rewards":[…]}])。成对属性要带上国家(["GetCasusBelli","+",1,"JPN"]),否则会提示缺少目标国。
  • 派生字段也能写:Divisions + 5 会真的多出 5 个师(按征募的标准状态创建),DivisionStrength = 100 会让每个师满编,Day = 100 直接跳时钟。见 §7 的写列。
  • 加速:控制台 speed 5,或点顶部速度条的第 5 格(5 天/秒,1 天 ≈ 0.2 秒)。用来快速跑完长国策。
  • 重新载入只重读文件:已经结算过的运行时状态(已完成次数、阶段游标)不会重置;要干净复现就重开一局。

14. 错误与排查

现象 原因
面板顶部列出 Invalid JSON — … 语法错误。注释、尾逗号、中文引号、缺逗号是最常见的四种。
第 N 项国策格式错误(缺少 Name) 该数组项不是对象,或 Name 不是字符串。
"X" 的 Time 无效 Time 缺失、非数字或 <= 0。该条国策被丢弃。
"X" 的 Time 为 "inf",但没有提供 InfinityPhases inf 国策必须有阶段。
国策没出现在面板里 ①Country 不是你的国家或 all;②文件不在加载目录(桌面版看「来源」一行:disk / bundled / none);③解析失败被丢弃。
条件一直 ✘,标签是 未知条件:XXX 属性名拼错(对照 §7/§8 的规范拼写),或运算符写错。
奖励没生效,日志里也没有 属性名或运算符拼错 → 整条被静默丢弃。对照面板里奖励标签的属性名。
数值和写的不一样 被属性的上下限截断了(如 Treasury 不能为负、Economy 不超过 100)。
奖励日志出现「未指定目标国,未执行」 成对属性的奖励既没有第四项,面板也没有选目标国。
改完文件没反应 桌面版要点「从磁盘重新载入」;浏览器版要重新构建。
目标国不是我想的那个 To: "target" 用的是面板选中的国家(地图点击 / 外交面板列表)。

15. 完整示例:核报复链路

一份「先核打击、再拿到战争理由」的两步国策,Country: "all",任何核武国家都能用:

{
  "Country": "all",
  "Decisions": [
    {
      "Type": "Decisions",
      "Name": "先发制人核打击",
      "Description": "对目标国实施核打击。\n世界会记住这一天。",
      "Require": [
        { "Type": "Nuclear", "To": "self", "Condition": "==", "Num": 1 },
        { "Type": "Nuke", "To": "self", "Condition": ">=", "Num": 1 },
        { "Type": "NukedCountry", "To": "target", "Condition": "==", "Num": 0 }
      ],
      "Time": 2,
      "Result": [
        { "Type": "Result", "Rewards": [
          { "Type": "Reward", "Rewards": [
            ["Nuke", "-", 1],
            ["NukedCountry", "+", 1]
          ] }
        ] }
      ]
    },
    {
      "Type": "Decisions",
      "Name": "战后索赔",
      "Description": "以核打击为由,向受害国索取政治让步。",
      "Require": [
        { "Type": "NukedCountry", "To": "target", "Condition": ">=", "Num": 1 },
        { "Type": "GetCasusBelli", "To": "target", "Condition": "==", "Num": 0 }
      ],
      "Time": 30,
      "Result": [
        { "Type": "Result", "Rewards": [
          { "Type": "Reward", "Rewards": [
            ["GetCasusBelli", "+", 1],
            ["PublicSupport", "+", 5]
          ] }
        ] }
      ]
    }
  ]
}

两项国策都通过 To: "target" 引用面板上选中的国家,所以面板会显示目标选择器;奖励元组省略第四项,跟着同一个目标走。