feedback6 R21P: every operator fights at full potential (潜能 6) — the owner's decision of 2026-10-07 (GitHub #252, PR #255; no host slider): tools/build-data.mjs OPERATOR_POTENTIAL adds every character_table potentialRanks attribute step (部署费用 / 再部署时间 / ATK / HP / DEF / RES / ASPD, all ADDITION, before rounding) to every chess (normal and elite), 补位 and 自选 unit form and picks talent / trait / module-talent candidates up to requiredPotentialRank 5 (「天赋效果增强」); a summon picks its talent candidates at its owner's potential (their requiredPotentialRank mirrors the owner's — PRTS 凯尔希: Mon3tr 3.5 s / 1400) and takes none of the owner's attribute steps; the 原型干员 stand-ins have no potential ranks (unchanged). 刺玫 413 / 17 → 435 / 15 as in the tournament video, 砾 18 → 16 s, 至简 80 → 70 s. data/chess.json (256 records), backups.json (the 71 owned picks, 8 summons), tokens.json (5 summons) and the data translations en / ja / ko / zh-TW rebuilt — only potential-driven fields (JSON-compared); the 干员调配 局内数值 and detail cards read the records. op-wang.js reads her stock cap 「最多拥有8(+1)枚」 through the potential step (the pattern fell back to 7: she sat at the cap from the start). Docs: DATA §2 / §18 / tokens, PLAYING (detail card), research 03, kits README, shared/diy.js / standIn.js, the 71 自选 kit headers and the kit comments that quoted potential-0 numbers. Tests: test/full-potential.test.js (new, fails on the parent); data.test.js re-derivation adds the potential steps (+ respawnTime); every test that hard-coded a potential-0 number carries the full-potential one (from the data and the test's own formula; no check loosened); golden: moves almost everywhere — roster 49/49, bonds 46/46, fields 22/22, matches 18/18, diy 133/138 (each battle parts on a stat: roster-001 / roster-020 at tick 1 on 角峰's HP (生命上限+250), bond-arcaneShip-high at 4.73 s and diy-001 at 3.70 s on an enemy's HP from a higher ATK; diy-110 / 111 also by 望's cap; the matches through those battles); unchanged: standins 10/10 and diy-135…138 (no potential ranks), diy-012 (twelve 夜莺 never hit, never redeployed)
This commit is contained in:
1 parent
783bd30afd
commit
806a047717
178 files changed
+7616
-7268
No files matched your search
+15
-10
@@ -121,9 +121,10 @@ test('chess: every non-DIY chess has stats, range, classification and a resolvab
|
||||
assert.ok(c.assets && c.assets.avatar && c.assets.spine, `${c.chessId}: assets`);
|
||||
if (c.isGolden) assert.equal(c.module ? c.module.active || c.module.id === null : true, true);
|
||||
}
|
||||
// Spot checks against official numbers (隐现 E1 Lv55: HP 1123, ATK 399).
|
||||
// Spot checks against official numbers (隐现 E1 Lv55: HP 1123, ATK 399 + 攻击力+23 at full potential = 422; cost 15 − 3).
|
||||
assert.equal(chess.chess_char_1_01_a.stats.maxHp, 1123);
|
||||
assert.equal(chess.chess_char_1_01_a.stats.atk, 399);
|
||||
assert.equal(chess.chess_char_1_01_a.stats.atk, 422);
|
||||
assert.equal(chess.chess_char_1_01_a.stats.cost, 12);
|
||||
assert.equal(chess.chess_char_1_01_a.targetPriority, 'fly');
|
||||
});
|
||||
|
||||
@@ -577,7 +578,7 @@ test('chess: golden modules[] (+ statsBase/traitBase/talentsBase) compose back t
|
||||
const m = chess.chess_char_6_11_b;
|
||||
assert.deepEqual(m.modules.map((x) => [x.uniEquipId, x.typeName, x.isDefault]), [['uniequip_002_mlyss', 'TAC-X', true], ['uniequip_003_mlyss', 'TAC-Y', false]]);
|
||||
assert.deepEqual(m.modules[1].attr, { maxHp: 170, atk: 28, def: 28 });
|
||||
assert.deepEqual([m.statsBase.maxHp, m.statsBase.atk, m.statsBase.def], [1703, 467, 111]);
|
||||
assert.deepEqual([m.statsBase.maxHp, m.statsBase.atk, m.statsBase.def], [1703, 492, 111]); // ATK 467 + 攻击力+25 (full potential)
|
||||
// module-less goldens: no choices, base = stats
|
||||
assert.deepEqual(chess.chess_char_5_14_b.modules, []);
|
||||
assert.deepEqual(chess.chess_char_5_14_b.statsBase, chess.chess_char_5_14_b.stats);
|
||||
@@ -702,12 +703,13 @@ test('stages: the 联防 maps (GitHub #41) — the escaped template levels, one
|
||||
});
|
||||
|
||||
test('official spot checks (hard-coded values from the zh_CN client data)', () => {
|
||||
// at full potential (the owner's decision of 2026-10-07): the client's numbers plus each operator's potential steps
|
||||
const st = (id) => { const s = chess[id].stats; return [s.maxHp, s.atk, s.def, s.res, s.blockCnt, s.cost]; };
|
||||
assert.deepEqual(st('chess_char_6_11_b'), [1893, 492, 141, 0, 1, 15]); // 缪尔赛思 精锐 (E2 60 + module Lv3)
|
||||
assert.deepEqual(st('chess_char_6_17_b'), [3593, 1108, 279, 0, 1, 19]); // 耀骑士临光 精锐
|
||||
assert.deepEqual(st('chess_char_3_08_a'), [1594, 629, 170, 15, 1, 25]); // 薄绿 (E2 1)
|
||||
assert.deepEqual(st('chess_char_2_11_a'), [1816, 547, 254, 0, 2, 15]); // 风丸 (E1 60)
|
||||
assert.deepEqual(st('chess_char_4_07_b'), [2383, 604, 359, 0, 1, 13]); // 风笛 精锐
|
||||
assert.deepEqual(st('chess_char_6_11_b'), [1893, 517, 141, 0, 1, 13]); // 缪尔赛思 精锐 (E2 60 + module Lv3): 492 + 25 ATK, 15 − 2 cost
|
||||
assert.deepEqual(st('chess_char_6_17_b'), [3593, 1143, 279, 0, 1, 17]); // 耀骑士临光 精锐: 1108 + 35, 19 − 2
|
||||
assert.deepEqual(st('chess_char_3_08_a'), [1594, 629, 170, 15, 1, 22]); // 薄绿 (E2 1): 25 − 3
|
||||
assert.deepEqual(st('chess_char_2_11_a'), [1816, 574, 254, 0, 2, 13]); // 风丸 (E1 60): 547 + 27, 15 − 2
|
||||
assert.deepEqual(st('chess_char_4_07_b'), [2383, 629, 359, 0, 1, 11]); // 风笛 精锐: 604 + 25, 13 − 2
|
||||
assert.equal(chess.chess_char_6_11_b.module.level, 3);
|
||||
assert.deepEqual([chess.chess_char_4_07_b.skill.skillId, chess.chess_char_4_07_b.skill.level, chess.chess_char_4_07_b.skill.spCost], ['skchr_bpipe_2', 7, 5]);
|
||||
const es = (k) => { const s = enemies[k].stats; return [s.maxHp, s.atk, s.def, s.res]; };
|
||||
@@ -738,8 +740,11 @@ test('independent re-derivation of every chess and enemy stat from the raw offic
|
||||
const k0 = P.attributesKeyFrames[0], k1 = P.attributesKeyFrames[P.attributesKeyFrames.length - 1];
|
||||
const t = (cd.status.charLevel - k0.level) / (k1.level - k0.level || 1);
|
||||
const f = (key) => k0.data[key] + (k1.data[key] - k0.data[key]) * t;
|
||||
const exp = { maxHp: f('maxHp'), atk: f('atk'), def: f('def'), res: f('magicResistance'), aspd: f('attackSpeed'), blockCnt: f('blockCnt'), cost: f('cost') };
|
||||
const map = { max_hp: 'maxHp', atk: 'atk', def: 'def', magic_resistance: 'res', attack_speed: 'aspd', block_cnt: 'blockCnt', cost: 'cost' };
|
||||
const exp = { maxHp: f('maxHp'), atk: f('atk'), def: f('def'), res: f('magicResistance'), aspd: f('attackSpeed'), blockCnt: f('blockCnt'), cost: f('cost'), respawnTime: f('respawnTime') };
|
||||
const map = { max_hp: 'maxHp', atk: 'atk', def: 'def', magic_resistance: 'res', attack_speed: 'aspd', block_cnt: 'blockCnt', cost: 'cost', respawn_time: 'respawnTime' };
|
||||
// full potential (the owner's decision of 2026-10-07): every potentialRanks attribute step (all ADDITION)
|
||||
const POT = { MAX_HP: 'maxHp', ATK: 'atk', DEF: 'def', MAGIC_RESISTANCE: 'res', ATTACK_SPEED: 'aspd', COST: 'cost', RESPAWN_TIME: 'respawnTime' };
|
||||
for (const r of CT[shop.charId].potentialRanks || []) for (const m of r.buff?.attributes?.attributeModifiers || []) exp[POT[m.attributeType]] += m.value;
|
||||
if (cd.status.equipLevel > 0 && shop.defaultUniEquipId) {
|
||||
const mp = BE[shop.defaultUniEquipId]?.phases.find((p) => p.equipLevel === cd.status.equipLevel);
|
||||
for (const b of mp?.attributeBlackboard || []) if (map[b.key]) exp[map[b.key]] += b.value;
|
||||
|
||||
Reference in new issue
Block a user