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

+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']);
});