feedback5 WY: 华法琳's granted trait stops at the data's 7 / 14 layers per battle, not 12 / 24 (GitHub #175; from PR #192 by @kukiC; the owner's decision of 2026-10-06)

PRTS 卫戍协议:盟约 下半, 3月27日更新#2调整 (revision 439189): 「[Ⅳ阶]华法琳:赋予的特质的叠层上限从 初始12/精锐24
降低至 初始7/精锐14」 — a nerf; the current 参战干员 record says 至多7层 / 14层 and the client blackboard is 7 / 14.
Research 02 Addendum 1 had read the line backwards, so the remake overrode garrison_95's cap to 12 / 24.

Ported from PR #192 by @kukiC: GRANTED_CAP_OVERRIDE is removed from server/sim/content/garrisons/battle.js and from
the client-result layer bound (server/match/fields.js layerAllowanceOf); a granted trait keeps its data cap like a
native one. Tests from the PR: garrisons_battle (updated on purpose: the 7th cast reaches 7 / 14, the 8th adds
nothing), feedback1-gaps (the bound with 华法琳 + 古米), playtest3 (both cards show 7 / 14); the first two fail without
the fix. Docs: research 00 / 01 / 02 corrected (02-bonds.json's note; data/bonds.json regenerated offline — only
miraShip.spec.layerGain changed), history 0.1.1's pointer, DESIGN §25.13.2 and the §25.10 table.
Golden unchanged (no corpus battle casts 8 times in front of 华法琳).
This commit is contained in:
sganggs committed 2026-10-06 21:03:56 +08:00
1 parent 463352d9bc
commit 269d7b0bcf
12 files changed
+77 -31

No files matched your search

+1 -1
View File
@@ -1 +1 @@
{"yanShip":{"bondId":"yanShip","name":"炎","identifier":1,"isCore":true,"bondType":"SEASON","bondOrder":2,"powerIdList":["yan","sui","lungmen","lee","lgd"],"iconId":"icon_yanShip","activeCount":3,"thresholds":[3,6,9],"maxCount":null,"thresholdTemplate":"count_threshold_upward","countMode":"BOARD","countsHand":false,"countsGoldenOnly":false,"activeType":"BATTLE","isActiveInDeck":false,"noStack":false,"weight":10,"maxInactiveBondCount":-1,"layerMilestones":[],"desc":"【炎】干员攻击力提升(受层数影响)\n<在场6名不同【炎】干员>战斗开始时召唤可自主行动的“炎佑”,其攻击力生命值分别为开战时【炎】干员攻击力生命值总和的30%,“炎佑”同时攻击3个目标,可使用“祛恶之焰”,攻击时附带攻击力一定比例的灼燃损伤且周围敌人受到一定比例的元素脆弱\n<在场9名不同【炎】干员>改为召唤2只“炎佑”,攻击力提升至1.5倍,受到的伤害-90%","descRaw":"【炎】干员<@ba.vup>攻击力</>提升<@autochess.gray>(受层数影响)</>\n<在场<@autochess.dgreen>6</>名不同【炎】干员>战斗开始时召唤可自主行动的<@ba.vup>“炎佑”</>,其攻击力生命值分别为开战时【炎】干员攻击力生命值总和的30%,“炎佑”同时攻击3个目标,可使用<@ba.vup>“祛恶之焰”</>,攻击时附带攻击力一定比例的灼燃损伤且周围敌人受到一定比例的元素脆弱\n<在场<@autochess.dgreen>9</>名不同【炎】干员>改为召唤2只<@ba.vup>“炎佑”</>,攻击力提升至<@ba.vup>1.5</>倍,受到的伤害<@ba.vup>-90%</>","effectId":"bondeffect_yan","effectName":"炎","effectDesc":"【炎】干员攻击力+{0:0%}(受层数影响)\n<在场6名不同【炎】干员>战斗开始时收集【炎】干员部分属性召唤“炎佑”(其攻击力和生命值为【炎】干员攻击力和生命值总和的30%);“炎佑”同时攻击3个目标,攻击附带20%攻击力的灼燃损伤,周围敌人受到20%的元素脆弱,周期性使用“祛恶之焰”\n<在场9名不同【炎】干员>改为召唤2只“炎佑”;“炎佑”攻击力提升至1.5倍,受到的伤害-90%","effectDescRaw":"【炎】干员攻击力<@ba.vup>+{0:0%}</><@ba.acrem>(受层数影响)</>\n<在场<@ba.vup>6</>名不同【炎】干员>战斗开始时收集【炎】干员部分属性召唤“炎佑”(其攻击力和生命值为【炎】干员攻击力和生命值总和的30%);“炎佑”同时攻击3个目标,攻击附带20%<@ba.vup>攻击力</>的<@ba.vup>灼燃损伤</>,周围敌人受到20%的<@ba.vup>元素脆弱</>,周期性使用“祛恶之焰”\n<在场<@ba.vup>9</>名不同【炎】干员>改为召唤2只“炎佑”;“炎佑”攻击力提升至1.5倍,受到的伤害-90%","effectDescParams":[{"index":0,"format":"0%","base":"base_atk","perStack":"atk_per_stack"}],"bb":{"base_atk":0.23,"atk_per_stack":0.009,"power_bond_char_cnt":6,"ex_bond_char_cnt":9,"damage_resistance":0.9,"atk":1.5},"bbStr":{"key":"act1autochess_bond_eff_yan"},"buffs":[{"key":"env_gbuff_new","bb":{"base_atk":0.23,"atk_per_stack":0.009,"power_bond_char_cnt":6,"ex_bond_char_cnt":9,"damage_resistance":0.9,"atk":1.5},"bbStr":{"key":"act1autochess_bond_eff_yan"}}],"baseParams":["base_atk"],"perStackParams":["atk_per_stack"],"members":["chess_char_1_03_a","chess_char_2_04_a","chess_char_3_03_a","chess_char_3_04_a","chess_char_4_15_a","chess_char_4_17_a","chess_char_5_03_a","chess_char_5_12_a","chess_char_5_23_a","chess_char_6_03_a","chess_char_6_15_a"],"visibleMembers":["chess_char_1_03_a","chess_char_2_04_a","chess_char_3_03_a","chess_char_3_04_a","chess_char_4_17_a","chess_char_5_03_a","chess_char_5_12_a","chess_char_5_23_a","chess_char_6_03_a","chess_char_6_15_a"],"spec":{"tiers":[{"need":"3 distinct 炎 on board","effect":"炎 members (+调和 members) ATK x(1 + 0.23 + 0.009*L)."},{"need":"6 distinct","effect":"At battle start spawn 1 \"炎佑\" (enemy_9012_acloon template, allied flying ranged unit, not blockable, moves on its own). ATK = 0.30 x sum(ATK of 炎 operators on board at battle start), maxHP = 0.30 x sum(maxHP of those operators). Template: DEF 200, RES 0, BAT 2.5 s, ASPD 100, attack range radius 2.0 tiles, magic damage, hits up to 3 targets; every hit adds 灼燃损伤 (burn elemental damage) = 20% of its ATK and applies 元素脆弱 (elemental damage taken x1.2) to enemies within 0.8 tiles of the target. Skill \"祛恶之焰\": cooldown 15 s (first cast 15 s after spawn), channels a flame on one enemy in range dealing 60% ATK magic damage per second to it and to enemies within 1.0 tile, for up to 20 s."},{"need":"9 distinct","effect":"Spawn 2 炎佑 instead; each has ATK x1.5 and takes 90% less damage (damage_resistance 0.9)."}],"formulas":{"atkMultiplier":"1 + 0.23 + 0.009*L"},"caps":"none","layerGain":"Economy-driven: 惊蛰 获得时 +shop level (no activation needed); 小满 休整期结束 +1 per operator gained this rouLine truncated
{"yanShip":{"bondId":"yanShip","name":"炎","identifier":1,"isCore":true,"bondType":"SEASON","bondOrder":2,"powerIdList":["yan","sui","lungmen","lee","lgd"],"iconId":"icon_yanShip","activeCount":3,"thresholds":[3,6,9],"maxCount":null,"thresholdTemplate":"count_threshold_upward","countMode":"BOARD","countsHand":false,"countsGoldenOnly":false,"activeType":"BATTLE","isActiveInDeck":false,"noStack":false,"weight":10,"maxInactiveBondCount":-1,"layerMilestones":[],"desc":"【炎】干员攻击力提升(受层数影响)\n<在场6名不同【炎】干员>战斗开始时召唤可自主行动的“炎佑”,其攻击力生命值分别为开战时【炎】干员攻击力生命值总和的30%,“炎佑”同时攻击3个目标,可使用“祛恶之焰”,攻击时附带攻击力一定比例的灼燃损伤且周围敌人受到一定比例的元素脆弱\n<在场9名不同【炎】干员>改为召唤2只“炎佑”,攻击力提升至1.5倍,受到的伤害-90%","descRaw":"【炎】干员<@ba.vup>攻击力</>提升<@autochess.gray>(受层数影响)</>\n<在场<@autochess.dgreen>6</>名不同【炎】干员>战斗开始时召唤可自主行动的<@ba.vup>“炎佑”</>,其攻击力生命值分别为开战时【炎】干员攻击力生命值总和的30%,“炎佑”同时攻击3个目标,可使用<@ba.vup>“祛恶之焰”</>,攻击时附带攻击力一定比例的灼燃损伤且周围敌人受到一定比例的元素脆弱\n<在场<@autochess.dgreen>9</>名不同【炎】干员>改为召唤2只<@ba.vup>“炎佑”</>,攻击力提升至<@ba.vup>1.5</>倍,受到的伤害<@ba.vup>-90%</>","effectId":"bondeffect_yan","effectName":"炎","effectDesc":"【炎】干员攻击力+{0:0%}(受层数影响)\n<在场6名不同【炎】干员>战斗开始时收集【炎】干员部分属性召唤“炎佑”(其攻击力和生命值为【炎】干员攻击力和生命值总和的30%);“炎佑”同时攻击3个目标,攻击附带20%攻击力的灼燃损伤,周围敌人受到20%的元素脆弱,周期性使用“祛恶之焰”\n<在场9名不同【炎】干员>改为召唤2只“炎佑”;“炎佑”攻击力提升至1.5倍,受到的伤害-90%","effectDescRaw":"【炎】干员攻击力<@ba.vup>+{0:0%}</><@ba.acrem>(受层数影响)</>\n<在场<@ba.vup>6</>名不同【炎】干员>战斗开始时收集【炎】干员部分属性召唤“炎佑”(其攻击力和生命值为【炎】干员攻击力和生命值总和的30%);“炎佑”同时攻击3个目标,攻击附带20%<@ba.vup>攻击力</>的<@ba.vup>灼燃损伤</>,周围敌人受到20%的<@ba.vup>元素脆弱</>,周期性使用“祛恶之焰”\n<在场<@ba.vup>9</>名不同【炎】干员>改为召唤2只“炎佑”;“炎佑”攻击力提升至1.5倍,受到的伤害-90%","effectDescParams":[{"index":0,"format":"0%","base":"base_atk","perStack":"atk_per_stack"}],"bb":{"base_atk":0.23,"atk_per_stack":0.009,"power_bond_char_cnt":6,"ex_bond_char_cnt":9,"damage_resistance":0.9,"atk":1.5},"bbStr":{"key":"act1autochess_bond_eff_yan"},"buffs":[{"key":"env_gbuff_new","bb":{"base_atk":0.23,"atk_per_stack":0.009,"power_bond_char_cnt":6,"ex_bond_char_cnt":9,"damage_resistance":0.9,"atk":1.5},"bbStr":{"key":"act1autochess_bond_eff_yan"}}],"baseParams":["base_atk"],"perStackParams":["atk_per_stack"],"members":["chess_char_1_03_a","chess_char_2_04_a","chess_char_3_03_a","chess_char_3_04_a","chess_char_4_15_a","chess_char_4_17_a","chess_char_5_03_a","chess_char_5_12_a","chess_char_5_23_a","chess_char_6_03_a","chess_char_6_15_a"],"visibleMembers":["chess_char_1_03_a","chess_char_2_04_a","chess_char_3_03_a","chess_char_3_04_a","chess_char_4_17_a","chess_char_5_03_a","chess_char_5_12_a","chess_char_5_23_a","chess_char_6_03_a","chess_char_6_15_a"],"spec":{"tiers":[{"need":"3 distinct 炎 on board","effect":"炎 members (+调和 members) ATK x(1 + 0.23 + 0.009*L)."},{"need":"6 distinct","effect":"At battle start spawn 1 \"炎佑\" (enemy_9012_acloon template, allied flying ranged unit, not blockable, moves on its own). ATK = 0.30 x sum(ATK of 炎 operators on board at battle start), maxHP = 0.30 x sum(maxHP of those operators). Template: DEF 200, RES 0, BAT 2.5 s, ASPD 100, attack range radius 2.0 tiles, magic damage, hits up to 3 targets; every hit adds 灼燃损伤 (burn elemental damage) = 20% of its ATK and applies 元素脆弱 (elemental damage taken x1.2) to enemies within 0.8 tiles of the target. Skill \"祛恶之焰\": cooldown 15 s (first cast 15 s after spawn), channels a flame on one enemy in range dealing 60% ATK magic damage per second to it and to enemies within 1.0 tile, for up to 20 s."},{"need":"9 distinct","effect":"Spawn 2 炎佑 instead; each has ATK x1.5 and takes 90% less damage (damage_resistance 0.9)."}],"formulas":{"atkMultiplier":"1 + 0.23 + 0.009*L"},"caps":"none","layerGain":"Economy-driven: 惊蛰 获得时 +shop level (no activation needed); 小满 休整期结束 +1 per operator gained this rouLine truncated
+1 -1
View File
@@ -309,7 +309,7 @@ Where: `ui/gameLogic.js morphPairings / HARMONY_BOND / harmonyMembers`, `ui/deta
- **调和's +1 explained** (issue #1: "一些不考虑整备区的盟约计算人数时也会把整备区的干员算进去" — the +1 read as a miscount; §21.11 had left "调和's +1 raises 在场 for core bonds without a member row"). The rule is unchanged (§6.3; one +1 total, research 02 §2.1 [ASSUMED]). **Now** a bond state whose count holds the +1 carries `harmony: 1` (`computeBonds`) and both views send it — m.private `bonds[]` and m.public `players[].bonds[]` entries `{ …, harmony? }` (absent otherwise; not in the battle input, `bondSnapshot`) — so the client never re-derives the rule. The popup says 在场 n(含调和 +1) and a 调和 row heads its member list, naming the 调和 operators on that board (`harmonyMembers`: 缪尔赛思, Pith's 盟约·辅助干员; a tap opens the first one's card; with that board not on screen "调和已激活:核心盟约激活人数 +1") — for your own strip and a teammate's (§20.15: the entry from m.public, the operators from the field on screen). The strip disc's and the card chip's titles add (含调和 +1).
- **Bots skip strategies built around a switched-off bond**: in 标准 the bot took 潘格尼尼 (【定制铳械】 an elite <拉特兰>), 克莱门莎 (<阿戈尔>) or 玛恩纳 (<卡西米尔>) in 5 of 200 solo and 23 of 800 co-op picks (seeds 1–200). bands.json `bondIds` holds a band's tie, read from its own data at build time (`tools/build-data.mjs` through `shared/bandBonds.js`; `GameData.bandBondIds` and the client read the field) — the bond names in <…> of its text (the official note "在<X>部分干员缺席时体验可能不完整" among them; a bracketed item or enemy adds nothing) and the bond ids / bond pools of its blackboards (`bond`, `bond_id`; `pool` → choices.json `pools[pool].bond`, 潘格尼尼's pool_char_later) — nine bands are tied (杜遥夜 炎, 佩佩 / 娜仁图亚 萨尔贡, 哈洛德 维多利亚, 休露丝 谢拉格, 潘格尼尼 拉特兰, 克莱门莎 阿戈尔, 玛恩纳 卡西米尔, 贾维 叙拉古). `botPickBand` weighs a band tied to a bond of `gd.modeInactiveBonds` 0 — one rng draw per pick, the default band when every offered band is excluded: 0 of 200 and 0 of 800 in 标准, while the other modes keep exactly the earlier picks (绝境: 5 / 200, 23 / 800). `tools/botbench.mjs --band` still forces any band.
- **The strategy draft marks them 本局禁用** for human players (the owner's follow-up): a band whose `bondIds` meet the mode's inactive bonds (`gameLogic.bandOffBonds`, the same field and rule as the bot — a test pins the two in every mode) carries a 本局禁用 tag on its card and, in the detail pane, "本局禁用【拉特兰】盟约,此策略效果可能无法发挥" with the bond struck through (§21.7's look); it stays selectable — the official game lets you pick it.
- **Found on the way — the client-result layer bound** (`match/fields.js layerAllowanceOf / validateClientResult`): with the bots' new picks the full-match suite's coop/FUNNY/107 (4 humans on AI 托管) reached a 6-谢拉格 freeze board at R12 (初雪 + 银灰: garrison_28, 50 % +1 per freeze in range, no per-battle cap in the data; 凛御银灰 hands out a 60 % +2) whose honest +112 谢拉格 layers passed the flat 60 + 4·12 = 108, so the server rejected the client and re-simulated (SP_VERIFY=all agrees with the client on all 57 battles of that match). Uncapped traits are common (菲莱 / 百炼嘉维尔 per skill, 斯卡蒂 per 2 kills, 莫斯提马 per 6 bullets …) and capped ones reach 100–200 (史尔特尔, 荒芜拉普兰德), so the flat bound now adds, per bond, what the player's IN_BATTLE layer 特质 can add to it — each trait of its units and each one their ADD_BOND traits hand out (once per operator), on the bonds it names, up to the cap the sim applies (`max_add_count_per_battle`, the handed-out 华法琳 trait's `GRANTED_CAP_OVERRIDE`); a trait without a cap — or a 魔王, whose +extra counts toward none — leaves its bonds bounded only by the 999 room. A forged gain on any other bond meets the flat bound as before.
- **Found on the way — the client-result layer bound** (`match/fields.js layerAllowanceOf / validateClientResult`): with the bots' new picks the full-match suite's coop/FUNNY/107 (4 humans on AI 托管) reached a 6-谢拉格 freeze board at R12 (初雪 + 银灰: garrison_28, 50 % +1 per freeze in range, no per-battle cap in the data; 凛御银灰 hands out a 60 % +2) whose honest +112 谢拉格 layers passed the flat 60 + 4·12 = 108, so the server rejected the client and re-simulated (SP_VERIFY=all agrees with the client on all 57 battles of that match). Uncapped traits are common (菲莱 / 百炼嘉维尔 per skill, 斯卡蒂 per 2 kills, 莫斯提马 per 6 bullets …) and capped ones reach 100–200 (史尔特尔, 荒芜拉普兰德), so the flat bound now adds, per bond, what the player's IN_BATTLE layer 特质 can add to it — each trait of its units and each one their ADD_BOND traits hand out (once per operator), on the bonds it names, up to the cap the sim applies (`max_add_count_per_battle`, the handed-out 华法琳 trait's `GRANTED_CAP_OVERRIDE` — removed in 0.2.0, §25.13.2); a trait without a cap — or a 魔王, whose +extra counts toward none — leaves its bonds bounded only by the 999 room. A forged gain on any other bond meets the flat bound as before.
- Normative lines rewritten with it: §6.3 (`harmony` in the views), §6.6 (the band pick), §8.2 / §8.3 (`bonds[]` entries `harmony?`), §14 / §20.12 (the layer bound), §21.11 (its 调和 note).
- Tests: `test/ui/feedback1-gaps.test.js`, `test/match/feedback1-gaps.test.js`; opt-in `test/ui/feedback1-gaps.e2e.test.js` (`SP_E2E=1`; 1920×1080, 844×390, 756×366; `SP_E2E_OUT` moves the screenshots).
+25 -2
View File
@@ -22,7 +22,8 @@ rules (GitHub #181, #187 / #170, follow-ups) → §25.7; WE2, data / match / UI
what stays [ASSUMED] and the questions left to the owner → §25.10. The closing additions: the owner's decisions of
2026-10-06 (望's stock, skill triggers through summon areas, the 突袭 landing, GitHub #148) and the items the sources
settled (泥岩's 闭锁; 忍冬, 深靛, 阿) → §25.11; release packaging and the asset manifest (the 自选 voices, the summons
with no Spine model) → §25.12; round 18 — 拟态物质 at the copy cap (GitHub #207) → §25.13.
with no Spine model) → §25.12; round 18 — 拟态物质 at the copy cap (GitHub #207), 华法琳's 7 / 14 (GitHub #175, PR #192)
→ §25.13.
Decisions of the owner: 2026-10-04 — the server stays on plain `node:http` + `ws`, no web framework (§25.1);
2026-10-05 — the 补位 plan (§25.3); the 自选编队 rules, the collab operators out of the pool, ISW-A refused (§25.4);
@@ -31,7 +32,8 @@ Chinese by default with an English switch, official English game texts, PR #70's
enemy in range, 纯烬艾雅法拉 S1 at full SP, 伺夜 S1 only with his pack on the field (§25.5); 2026-10-06 — a chess the player
does not own shows as its stand-in everywhere (the owner's recall of the official mode, §25.3); the enemy damage frame
kept (§25.7.2); 望's placed 棋子 count in her stock, skills that act through summons cast on an enemy in a summon's area,
a melee 突袭 member lands where it can block (§25.11).
a melee 突袭 member lands where it can block (§25.11); 华法琳's granted trait at the data's 7 / 14, replacing the 12 / 24
lock (§25.13.2).
### 25.1 Refactor and tooling: the big files split, golden results, lint, types, import boundaries (refactor plan; owner's decision of 2026-10-04: no web framework) — `server/http/`, `server/match/match/`, `server/match/player/`, `server/sim/battle/`, `content/enemies/`, `kits/ops/`, `public/js/ui/gameLogic/`, `render/fx/`, `render/app/`, `screens/game/`, `tools/golden.mjs`, `tools/check-imports.mjs`, `eslint.config.js`, `jsconfig.json`
@@ -659,6 +661,7 @@ its tile, §18.1; §25.9.2). Where a 0.1.4 change met 0.2.0 work:
| 望's placed 棋子 take and return her stock, so S1 / S2 keep cycling (§25.11.1) | the owner, 2026-10-06 (deliberate deviation from 「不消耗持有数量」) |
| Skills that act through summons cast on an enemy in a summon's area: 麦哲伦 S1, 令 S3, 电弧 S2 / S3 (§25.11.2) | the owner, 2026-10-06 (the larger-range rule) |
| A melee 突袭 member lands on a tile where it can block before one where it cannot (GitHub #148, §25.11.3) | the owner, 2026-10-06 |
| 华法琳's granted trait stops at the data's 7 / 14 (PRTS 3/27: lowered from 12 / 24; GitHub #175, PR #192, §25.13.2) | the owner, 2026-10-06 (replaces the 12 / 24 lock) |
Settled by the sources in W5V, no longer questions: 泥岩 S3's dormancy is 闭锁 — she lets go and blocks nobody (PRTS,
§25.11.4); 阿 S2 / S3 keep the data's DEFAULT, 忍冬 S2's and 深靛 S1's notes are what the kits do (§25.11.5).
@@ -853,3 +856,23 @@ ported PR credits its author there and here.
report — an elite and 2 normal 溯光星源, normal and golden item: nothing granted, no other operator, consumed; 2 copies
with the pool drained: nothing; 2 copies with copies left: the elite), `test/match/bot-smarter.test.js` (no bot carrier
on a pair whose pool is out). Docs: docs/META.md (built-ins, the bots' items), research 04 / 06.
#### 25.13.2 华法琳's granted trait stops at the data's 7 / 14 layers (GitHub #175; PR #192 by @kukiC; the owner's decision of 2026-10-06) — `server/sim/content/garrisons/battle.js`, `server/match/fields.js layerAllowanceOf`
- **Seen** (GitHub #175): the card says 至多7层, but the operator in front of 华法琳 gained up to 12 layers per bond in a
battle (the elite: 24). The cause was the remake's own override `GRANTED_CAP_OVERRIDE` (12 / 24 for garrison_95_a /
_b, in the battle and in the server's client-result layer bound), taken from research 02 Addendum 1. Also in 0.1.4.
- **Official**: PRTS 卫戍协议:盟约 下半, 3月27日更新#2调整 (revision 439189): 「[Ⅳ阶]华法琳:赋予的特质的叠层上限从 初始12/精锐24
降低至 初始7/精锐14」 — a nerf; the current 参战干员 record (下半/PRTS盟约记录) says 至多7层 / 14层, and the client
blackboard (`max_add_count_per_battle` 7 / 14) carries it with the other 3/27 numbers. The official notice 8584 says
only 「调整每场战斗至多获得的层数」. Addendum 1 had read the PRTS line backwards (7 / 14 raised to 12 / 24).
- **Decision**: the owner's decision of 2026-10-06 — follow the current page and the data: 7 / 14 (it replaces the
earlier 12 / 24 lock).
- **Now** (ported from PR #192 by @kukiC): the override is gone; a granted trait keeps its data cap like a native one —
the 7th cast in front of 华法琳 reaches 7 (elite: 14) and later casts add nothing; the client-result bound counts the
handed-out trait at the same cap. Research 00 / 01 / 02 corrected (02-bonds.json's note, copied into
data/bonds.json `miraShip.spec.layerGain`, regenerated offline — the only data change).
- Golden: unchanged (no corpus battle casts 8 times in front of 华法琳). Tests (from PR #192):
`test/content/garrisons_battle.test.js` (updated on purpose: the cap is the data's 7 / 14 — the 7th cast reaches it,
the 8th adds nothing), `test/match/feedback1-gaps.test.js` (the client-result bound with 华法琳 + 古米: 60 + 4·3 +
2 × cap passes, one more is refused), `test/ui/playtest3.test.js` (both cards show 至多7层 / 至多14层).
+2 -2
View File
@@ -11,7 +11,7 @@ Tags: **[DATA]** client game data · **[VERIFIED]** official or wiki rule text,
| File | What it holds | Key machine paths |
|---|---|---|
| `01-core-rules.md` / `01-core-data.json` | Modes, round schedule and timers, economy, shop, merging, LP, Final Assault and Hidden Core, 40 strategies (bands), 机变 choice events, scoring and tips. **Addendum A1–A5** has the verified rules, the ban rule and the enemy-multiplier table. | `modes`, `rounds`, `shopLevels`, `bands`, `choiceEvents`, `choiceAndEnemyEffects`, **`_criticAddendum`** (income, freeze, hand, bandDraft, bannedOperators, enemyStatMultipliers, specialPhase…) |
| `02-bonds.md` / `02-bonds.json` | All 23 bonds (8 core, 15 add-on): activation, layer formulas, tier effects, layer sources and readers, band/choice/item interactions. Addendum: 华法琳 cap 12/24, ban interplay. | `bonds[*]`, `layerGarrisons`, `layerScalingGarrisons`, `bondGrantingItems` |
| `02-bonds.md` / `02-bonds.json` | All 23 bonds (8 core, 15 add-on): activation, layer formulas, tier effects, layer sources and readers, band/choice/item interactions. Addendum: 华法琳 cap 7/14 (corrected 2026-10-06), ban interplay. | `bonds[*]`, `layerGarrisons`, `layerScalingGarrisons`, `bondGrantingItems` |
| `03-operators.md` / `03-operators.json` | 133 chess ×2 (normal/elite): stats at the tier's status, range grids, the default skill with blackboard, talents, module, 特质 (garrison) blackboards, tokens, distribution. Addendum: **skill-trigger correction**, summons and hand, deploy rules, DIY. | `chess[*].stats/rangeGrid/skill/talents/garrisons/module`, `tokensUsedByPool`, `skillTriggerDataList` |
| `04-items.md` / `04-items.json` | 56 equipment ×2 + 3 Arts: effects, formulas, merge, sources, 变形同构体 table, VI-tier combos. Addendum: **items can't be sold**, 机变 item card formats. | `items[*]`, `itemSources`, `bondGrantTable_for_变形同构体` |
| `05-enemies-levels.md` / `05-enemies.json` / `05-maps.json` | Stage terrains (8 active), 38 wave templates with routes, 326 enemies, special-enemy pools, generation algorithm, bosses, bounties, LP rules. Addendum: DP, hidden-core thresholds, multi FUNNY rounds, stat scaling. | `05-maps.json: stages, roundLevels`; `05-enemies.json: enemies, specialEntries, bosses, bountyEffects, generation, _criticAddendum` |
@@ -131,7 +131,7 @@ The server draws a disabled bond set **D**: **3 core + 4 add-on** in NORMAL/HARD
| TANK skill rule | 03: any skill index | **Skill 1 only** |
| 联防 partner rule | 01: fewest leaks | Meaningless; **highest LP, then seat** [ASSUMED] |
| FUNNY income bonus | 01: +2 | **None** (official intro shows 4 at R1 in 标准) |
| 华法琳 granted cap | data 7/14 vs patch 12/24 | **12/24** (official 3/27 notice) |
| 华法琳 granted cap | data 7/14 vs patch 12/24 | **7/14** (the PRTS 3/27 note lowers 12/24 to 7/14; first misread, corrected 2026-10-06 — GitHub #175) |
## 8. Remaining open questions, with recommended defaults
+1 -1
View File
@@ -930,6 +930,6 @@ Machine-readable copy: `01-core-data.json → _criticAddendum.enemyStatMultiplie
- [ASSUMED] 道具补给 tier windows: R3 I–IV, R6 II–V, R9 III–VI, R11 IV–VI. Solo draws 3 cards from the same pools.
### A5. Other resolved items
- The **华法琳** granted-trait cap should be **12 / 24 per battle**. The official 3/27 notice changed it and PRTS lists 7/14 → 12/24. The client data still shows 7/14, even though the other 3/27 changes (奇迹 18 %, 远见 80/150, 商业包装方案 8/7) are already in it. See 02.
- The **华法琳** granted-trait cap is the data's **7 / 14 per battle**. The official 3/27 notice adjusted it and PRTS lists 初始12/精锐24 降低至 初始7/精锐14 — the numbers the client data carries with the other 3/27 changes (奇迹 18 %, 远见 80/150, 商业包装方案 8/7). Corrected 2026-10-06 (GitHub #175): this line first read the PRTS note backwards as 7/14 → 12/24. See 02 Addendum 1.
- ~~The skill auto-cast rows with `skillIndex` 0 mean skill 1 only (BWIKI "重装职业干员 技能1,受到伤害后自动释放").~~ Superseded by user playtest #6 (DESIGN §20): the class rows apply to **every MANUAL skill** of the class and never to an AUTO skill (PRTS 卫戍协议/帮助 names whole classes; the 阵法术师 row must cover 薄绿's default S2, a phalanx that never attacks with its skill off); a MANUAL skill with its own 技能范围 (not an attack-range change) uses SKILL_RANGE; automatic operations have a 3 s cooldown. See 03 C1.
- Asset URLs: 40 random URLs from `07-assets.json` were re-checked on 2026-09-27; 40/40 returned `200`.
+1 -1
View File
@@ -7601,7 +7601,7 @@
"p": "min(1, 0.18 + 0.003*L)",
"goldPayouts": "20 * floor(L/100)"
},
"layerGain": "维娜·维多利亚 +2 per distinct-tier 奇迹; 录武官 获得时 +3; 伺夜 kills +1; 引星棘刺 +4 self & front; 华法琳 (奇迹 member) gives the operator in front \"开技能时 +1 own active bonds\" (data: <=7/battle, elite +2 <=14; PRTS 3/27 patch note says cap raised to 12/24).",
"layerGain": "维娜·维多利亚 +2 per distinct-tier 奇迹; 录武官 获得时 +3; 伺夜 kills +1; 引星棘刺 +4 self & front; 华法琳 (奇迹 member) gives the operator in front \"开技能时 +1 own active bonds\" (data: <=7/battle, elite +2 <=14 — the 3/27 update lowered it from 12/24, PRTS 下半 3月27日更新#2; Addendum 1).",
"notes": [
"blackboard probk = 0.3: meaning unknown (not in any description) [UNKNOWN]."
]
+3 -3
View File
@@ -842,7 +842,7 @@ Members (9 chess, 8 in current shop pool; by tier in shop: {'2': 1, '3': 2, '4':
- **[2 distinct (board + bench)]** After each shop refresh, if the player has 0 free refreshes: with p = 0.18 + 0.003*L gain 1 free refresh.
- **[every 100 layers]** +20 gold.
- Formulas: `p = min(1, 0.18 + 0.003*L)`; `goldPayouts = 20 * floor(L/100)`
- How layers are gained: 维娜·维多利亚 +2 per distinct-tier 奇迹; 录武官 获得时 +3; 伺夜 kills +1; 引星棘刺 +4 self & front; 华法琳 (奇迹 member) gives the operator in front "开技能时 +1 own active bonds" (data: <=7/battle, elite +2 <=14; PRTS 3/27 patch note says cap raised to 12/24).
- How layers are gained: 维娜·维多利亚 +2 per distinct-tier 奇迹; 录武官 获得时 +3; 伺夜 kills +1; 引星棘刺 +4 self & front; 华法琳 (奇迹 member) gives the operator in front "开技能时 +1 own active bonds" (data: <=7/battle, elite +2 <=14 — the 3/27 update lowered it from 12/24, PRTS 下半 3月27日更新#2; Addendum 1).
- Note: blackboard probk = 0.3: meaning unknown (not in any description) [UNKNOWN].
- Garrisons that explicitly add layers to this bond: `garrison_54`, `garrison_153`, `garrison_156`, `garrison_157` (see section 4; plus the generic ones)
@@ -1325,7 +1325,7 @@ Bond items for 变形同构体 (`chess_item_6_09_e`, "携带者获得额外盟
## 8. Open questions / discrepancies
1. 华法琳 granted-trait cap: data garrison_95 = 7 (elite 14) per battle, PRTS 3/27 patch note says 12/24. Recommend the patch-note value if we want "latest official"; data value otherwise.
1. 华法琳 granted-trait cap: data garrison_95 = 7 (elite 14) per battle; the PRTS 3/27 patch note lowers it from 12/24 to 7/14, so the data value is the latest official one (resolved: Addendum 1, corrected 2026-10-06).
2. 调和 +1: applies to every core bond or only those with >=1 real member on board? (assumed the latter).
3. 奇迹 blackboard `probk` 0.3 has no known meaning.
4. `pool_equip_vict` content (assumed the 4 special 维式重锤).
@@ -1341,7 +1341,7 @@ Bond items for 变形同构体 (`chess_item_6_09_e`, "携带者获得额外盟
Written 2026-09-27 by the completeness critic. Sources: the official 3/27 notice (`ak.hypergryph.com/news/8584`), PRTS `卫戍协议:盟约_下半` 更新记录, BWIKI `盟约` / `盟约/S.W.E.E.P.报告`, and the screenshot `i.meee.com.tw/9H7Kugy.png`.
1. **§8 Q1 (华法琳 cap): resolved → use 12 per battle normal, 24 elite.** The official 3/27 notice says "调整每场战斗至多获得的层数". PRTS gives the numbers: "付与的特质的叠层上限从初始7/精锐14提升至初始12/精锐24". The client data (`garrison_72_*` → `garrison_95_*`, `max_add_count_per_battle` 7/14) was not updated for this line, although it already carries the other 3/27 changes: 奇迹 `baseprob` 0.18, 远见 80/150, 商业包装方案 8/7. Override `max_add_count_per_battle` to 12/24.
1. **§8 Q1 (华法琳 cap): resolved → the data's 7 per battle normal, 14 elite (corrected 2026-10-06, GitHub #175).** The official 3/27 notice says only "调整每场战斗至多获得的层数". PRTS `卫戍协议:盟约 下半`, 3月27日更新#2 (revision 439189), gives the numbers: "[Ⅳ阶]华法琳:赋予的特质的叠层上限从 初始12/精锐24 降低至 初始7/精锐14" — a nerf. The current 参战干员 record says 至多7层 / 14层, and the client data (`garrison_72_*` → `garrison_95_*`, `max_add_count_per_battle` 7/14) carries it with the other 3/27 changes (奇迹 `baseprob` 0.18, 远见 80/150, 商业包装方案 8/7). This addendum first read the line backwards (7/14 raised to 12/24), and until 0.2.0 the remake overrode the cap to 12/24 (`GRANTED_CAP_OVERRIDE`, removed with PR #192 by @kukiC: DESIGN §25.13.2). The 12/24 above is the pre-3/27 number.
2. **Per-match incomplete bonds (new, verified).** See 01 Addendum A2.
- Each match disables a random set D: 3 core + 4 add-on in NORMAL+; the static list + 1 add-on in FUNNY.
- Every operator whose bonds are **all** in D leaves the pool.
+6 -8
View File
@@ -32,7 +32,6 @@
import { TICK, SNAPSHOT_EVERY } from '../sim/constants.js';
import { layerGainRoom } from '../../shared/constants.js';
import { uniteLeft } from '../sim/spec.js';
import { GRANTED_CAP_OVERRIDE } from '../sim/content/garrisons/battle.js';
import { diyTokenOwner } from '../../shared/diy.js';
export const MAX_TICKS_PER_INTERVAL = 8;
@@ -589,8 +588,8 @@ function layerBondsOf(p, gd) {
* §21.26): the traits of its units (garrisons with `bond_add_count` / `bond_add_count_multi`) and the ones their ADD_BOND
* traits hand out (`give_garrison_id`, counted for every operator of the player — "所有【X】" reaches them all), each on
* the bonds it names (`bond_by_id` ids; `bond_self` / `bond_actived_maxstack`: every bond of the player's snapshot and
* units), up to the per-battle cap the sim applies (content/garrisons/battle.js: `max_add_count_per_battle`, the handed-out
* 华法琳 trait's GRANTED_CAP_OVERRIDE). A trait the data gives no cap (初雪 / 银灰's freeze trait, 菲莱 / 百炼嘉维尔's per-skill
* units), up to the per-battle cap the sim applies (content/garrisons/battle.js: `max_add_count_per_battle` — a handed-out
* trait's too, 华法琳's 7 / 14, GitHub #175). A trait the data gives no cap (初雪 / 银灰's freeze trait, 菲莱 / 百炼嘉维尔's per-skill
* 萨尔贡, 斯卡蒂's per-kill …) — or a 魔王, whose +extra on every trait gain counts toward no cap — leaves its bonds bounded
* only by the room under 999 (Infinity here): such boards legitimately gain hundreds of layers a battle.
* @returns {Map<string, number>} bondId → extra allowance (Infinity: uncapped)
@@ -602,23 +601,22 @@ function layerAllowanceOf(p, gd) {
const lineup = new Set(Object.keys(p.bonds && typeof p.bonds === 'object' ? p.bonds : {}));
for (const u of units) for (const b of gd.chess(u.chessId)?.bonds || []) lineup.add(b);
let extra = false;
const credit = (g, times, handedOut) => {
const credit = (g, times) => {
const bb = g.bb || {};
if (Number.isFinite(bb.extra_cnt)) { extra = true; return; }
if (!Number.isFinite(bb.bond_add_count) && !Number.isFinite(bb.bond_add_count_multi)) return;
const s = g.bbStr || {};
const bonds = s.bond_type === 'bond_by_id' ? String(s.bond_id ?? '').split(',').map((x) => x.trim()).filter((x) => gd.bond(x)) : [...lineup];
const override = handedOut ? GRANTED_CAP_OVERRIDE[g.garrisonId] : undefined;
const cap = override ?? (Number(bb.max_add_count_per_battle) > 0 ? Number(bb.max_add_count_per_battle) : Infinity);
const cap = Number(bb.max_add_count_per_battle) > 0 ? Number(bb.max_add_count_per_battle) : Infinity;
for (const b of bonds) out.set(b, (out.get(b) || 0) + cap * times);
};
for (const u of units) {
for (const gid of gd.chess(u.chessId)?.garrisonIds || []) {
const g = gd.garrison(gid);
if (!g || g.eventType !== 'IN_BATTLE') continue;
if (g.effectKey !== 'ADD_BOND') { credit(g, 1, false); continue; }
if (g.effectKey !== 'ADD_BOND') { credit(g, 1); continue; }
const given = gd.garrison(g.bbStr?.give_garrison_id);
if (given && given.eventType === 'IN_BATTLE') credit(given, units.length, true);
if (given && given.eventType === 'IN_BATTLE') credit(given, units.length);
}
}
if (extra) for (const b of out.keys()) out.set(b, Infinity);
+4 -7
View File
@@ -5,8 +5,9 @@
//
// ADD_BOND grants `give_garrison_id` to other operators before the first deployment (targets parsed from
// the text: 身前一格 / 身前一格【X】/ 自身和身前一格 / 同一行最右边 / 所有【X】). A unit owns a garrison id
// at most once (耀骑士临光 already carries the 144/159 it grants to itself). The 华法琳-granted trait
// (garrison_95) is capped 12 / 24 per battle instead of the data's 7 / 14 (research 02 Addendum 1).
// at most once (耀骑士临光 already carries the 144/159 it grants to itself). A granted trait keeps
// its data cap like a native one: 华法琳's garrison_95 stops at 7 / 14 layers per battle (PRTS 下半
// 3月27日更新#2: 「从 初始12/精锐24 降低至 初始7/精锐14」; GitHub #175, PR #192 by @kukiC).
// layer events act1autochess_gar_event_useskill (skillStart) · _selfkillenemy (kill, every check_cnt) ·
// _selfdead (death 'killed'; texts with 替身 also on every substitute ⇄ body swap, read from the
// dollkeeper flag unit.trait.doll) · _consume_ammo (ammoUsed; range_id 0-1 self, 1-1 front tile,
@@ -43,9 +44,6 @@ import * as S from '../support/index.js';
import { mitigate } from '../../damage.js';
import { frontOf } from '../../dir.js';
/** 华法琳's granted trait: per-battle cap override (research 02 Addendum 1: PRTS 3/27 "初始7/精锐14 → 初始12/精锐24"). */
export const GRANTED_CAP_OVERRIDE = Object.freeze({ garrison_95_a: 12, garrison_95_b: 24 });
const ids = (s) => String(s ?? '').split(',').map((x) => x.trim()).filter(Boolean);
const EMPTY = Object.freeze([]);
/** Status keys for check_ab_flag values used by the data (16 = 冻结). */
@@ -68,8 +66,7 @@ export function install(battle) {
if (set.has(g.garrisonId)) return null;
set.add(g.garrisonId);
const bb = g.bb || {};
const override = grantedBy ? GRANTED_CAP_OVERRIDE[g.garrisonId] : undefined;
const cap = override ?? (S.num(bb.max_add_count_per_battle, 0) > 0 ? S.num(bb.max_add_count_per_battle) : Infinity);
const cap = S.num(bb.max_add_count_per_battle, 0) > 0 ? S.num(bb.max_add_count_per_battle) : Infinity;
const it = {
unit, g, gid: g.garrisonId, key: g.effectKey, bb, bbStr: g.bbStr || {}, grantedBy,
cap, capKey: `gar:${g.garrisonId}:${unit.id}`, cnt: 0, k: -1,
+11 -5
View File
@@ -5,13 +5,12 @@ import { test } from 'node:test';
import assert from 'node:assert/strict';
import { makeBattle, chessRec, enemyRec, checkInvariants } from '../helpers/battleHarness.js';
import { getData } from '../../server/data.js';
import { GRANTED_CAP_OVERRIDE } from '../../server/sim/content/garrisons/battle.js';
const D = getData({ log: { warn() {}, error() {}, info() {} } });
const GR = (gid) => D.garrisons[gid];
const ids = (s) => String(s ?? '').split(',').map((x) => x.trim()).filter(Boolean);
const num = (v, d = 0) => (Number.isFinite(+v) && v !== '' && v != null ? +v : d);
const capOf = (gid) => GRANTED_CAP_OVERRIDE[gid] ?? (num(GR(gid).bb.max_add_count_per_battle) > 0 ? num(GR(gid).bb.max_add_count_per_battle) : Infinity);
const capOf = (gid) => (num(GR(gid).bb.max_add_count_per_battle) > 0 ? num(GR(gid).bb.max_add_count_per_battle) : Infinity);
/** IN_BATTLE garrison ids carried by visible chess, and the ids those grant. */
const VISIBLE_IDS = new Set();
@@ -358,8 +357,10 @@ test('缇缇 125 + S2 封护 (GitHub #162): every 0.25 s sleep pulse of the ward
// ---------------------------------------------------------------------------------------------------------------------
// ADD_BOND grants
test('ADD_BOND 华法琳 72: the front operator gets garrison_95 (own active bonds +1 / +2), cap overridden to 12 / 24', () => {
for (const [gid, per, cap] of [['garrison_72_a', 1, 12], ['garrison_72_b', 2, 24]]) {
// PRTS 卫戍协议:盟约 下半, 3月27日更新#2: 「[Ⅳ阶]华法琳:赋予的特质的叠层上限从 初始12/精锐24 降低至 初始7/精锐14」 — the
// data's 7 / 14 (GitHub #175; from PR #192 by @kukiC)
test('ADD_BOND 华法琳 72: the front operator gets garrison_95 (own active bonds +1 / +2), capped at the data\'s 7 / 14 (GitHub #175)', () => {
for (const [gid, per, cap] of [['garrison_72_a', 1, 7], ['garrison_72_b', 2, 14]]) {
const give = GR(gid).bbStr.give_garrison_id;
const h = battle({
units: [{ id: 'warfarin', g: [gid], row: 10, col: 4 }, { id: 'front', row: 10, col: 5, bonds: ['yanShip', 'kjeragShip', 'egirShip'] }, { id: 'side', row: 11, col: 4, bonds: ['yanShip'] }],
@@ -369,8 +370,13 @@ test('ADD_BOND 华法琳 72: the front operator gets garrison_95 (own active bon
assert.equal(num(GR(give).bb.bond_add_count), per);
skill(h, f);
assert.deepEqual(gains(h), { yanShip: per, kjeragShip: per });
assert.equal(num(GR(give).bb.max_add_count_per_battle), cap, `${give}: the data cap`);
for (let i = 1; i < 7; i++) skill(h, f);
assert.deepEqual(gains(h), { yanShip: cap, kjeragShip: cap }, `${gid}: the seventh activation reaches the cap`);
skill(h, f);
assert.deepEqual(gains(h), { yanShip: cap, kjeragShip: cap }, `${gid}: the eighth adds no layers`);
for (let i = 0; i < 30; i++) skill(h, f);
assert.deepEqual(gains(h), { yanShip: cap, kjeragShip: cap }, `${gid}: 12/24 override, not the data's 7/14`);
assert.deepEqual(gains(h), { yanShip: cap, kjeragShip: cap }, `${gid}: later activations stay capped`);
skill(h, h.unit('warfarin'));
skill(h, h.unit('side'));
assert.equal(gains(h).yanShip, cap, '华法琳 herself / other operators do not carry the trait');
+11
View File
@@ -229,6 +229,17 @@ describe('§21.26 found on the way — the client-result layer bound reads the l
const check = (s, gains, opts = { gd }) => { const v = validateClientResult(s, res(gains), opts); return v.ok ? 'ok' : v.reason; };
const SEED_107_R12 = ['chess_char_1_06_b', 'chess_char_4_14_a', 'chess_char_4_13_a', 'chess_char_3_14_a', 'chess_char_3_11_a', 'chess_char_4_22_a', 'chess_char_3_20_a', 'chess_char_5_14_b'];
// GitHub #175 (from PR #192 by @kukiC): the handed-out 华法琳 trait counts at the data cap, 7 / 14
test('华法琳\'s handed-out trait allows its data cap of 7 / 14 (GitHub #175)', () => {
for (const [id, cap] of [['chess_char_4_26_a', 7], ['chess_char_4_26_b', 14]]) {
const s = spec(3, [id, 'chess_char_1_10_a']); // 华法琳 + 古米
// a handed-out trait is counted once per operator of the player (2 here), on top of the flat 60 + 4·round
const bound = 60 + 4 * 3 + 2 * cap;
assert.equal(check(s, { steadShip: bound }), 'ok');
assert.equal(check(s, { steadShip: bound + 1 }), 'layer bound');
}
});
test('an uncapped freeze trait leaves 谢拉格 bounded only by 999: the honest +112 passes; a bond without such a trait keeps 60 + 4·round', () => {
const s = spec(12, SEED_107_R12, { kjeragShip: { count: 6, active: true, tier: 2, layers: 39 }, swiftShip: { count: 2, active: true, tier: 1, layers: 0 } });
assert.equal(check(s, { kjeragShip: 112 }), 'ok', 'the board of coop/FUNNY/107 R12');
+11
View File
@@ -269,6 +269,17 @@ describe('3: the temp overflow row (临时整备区)', () => {
// ---- 8: the operator's own effect first --------------------------------------------------------------------------------
describe('8: 特质 right under the detail card\'s header', () => {
// GitHub #175 (from PR #192 by @kukiC): the card's 7 / 14 is the cap the battle applies
test('华法琳\'s normal and elite cards show the 7 / 14 layer caps (GitHub #175)', async () => {
await data.loadAll('chess', 'garrisons', 'assets', 'bonds', 'items');
for (const [id, cap] of [['chess_char_4_26_a', 7], ['chess_char_4_26_b', 14]]) {
const blocks = ChessDetail({ chess: data.lookup('chess', id), piece: null, editable: false, bonds: [], loadout: null });
const block = blocks.find((b) => b.key === 'garrison');
const rich = [...walk(block.type(block.props))].find((n) => n.props?.text != null);
assert.match(rich.props.text, new RegExp(`每场战斗至多${cap}层`));
}
});
test('the block order', () => {
assert.deepEqual(CHESS_SECTIONS, ['head', 'garrison', 'trait', 'stats', 'skill', 'module', 'equip', 'talents', 'actions']);
});