diff --git a/shared/diy.js b/shared/diy.js index 23de7849..c3cb24bc 100644 --- a/shared/diy.js +++ b/shared/diy.js @@ -146,8 +146,9 @@ export function checkDiyPick(slotId, pick, data) { if (proto) { const lk = lockedSelection(data, slot.tier, charId); if (!lk) return { error: `${charId}: no locked selection at tier ${slot.tier}` }; + // omitted / null = its locked selection; another skill, another module or an explicit 'none' is refused if (want !== null && want !== lk.skillIndex) return { error: `${charId}: a prototype carries skill ${lk.skillIndex} (与系统补位时一致)` }; - if (pick.uniEquipId !== undefined && wantMod !== lk.uniEquipId) return { error: `${charId}: a prototype carries module ${lk.uniEquipId ?? 'none'}` }; + if (pick.uniEquipId != null && wantMod !== lk.uniEquipId) return { error: `${charId}: a prototype carries module ${lk.uniEquipId ?? 'none'}` }; ({ skillIndex, uniEquipId } = lk); } else { if (want === null) return { error: `${charId}: an owned pick needs a skill index` }; diff --git a/test/content/kits_layout.test.js b/test/content/kits_layout.test.js index 77f97ff0..0b073d7b 100644 --- a/test/content/kits_layout.test.js +++ b/test/content/kits_layout.test.js @@ -14,6 +14,8 @@ import { join, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; import { KIT_FILES, TIER_KITS, KITS, STANDIN_KIT_FILES, STANDIN_KITS, OPERATOR_KIT_FILES, OPERATOR_KITS, GENERIC_KIT_CHARS, KITTED_CHARS } from '../../server/sim/content/kits/index.js'; import { KITS as CONTENT_KITS } from '../../server/sim/content/index.js'; +import { diyRecord } from '../../shared/diy.js'; +import { normalizeChess } from '../../server/sim/simdata.js'; const ROOT = join(dirname(fileURLToPath(import.meta.url)), '../..'); const KIT_DIR = join(ROOT, 'server/sim/content/kits'); @@ -54,10 +56,15 @@ test('a 自选 kit file op-.js registers the kit of that owned-6★ pi assert.equal(typeof mod.default[charId], 'function', `${file}: kit builder`); assert.equal(OPERATOR_KITS[charId], mod.default[charId], `${file}: registered`); assert.equal(KITS[charId], mod.default[charId], `${file}: in the merged registry`); - // every skill of every form is authored (a 自选 piece has no default skill: the pick chooses any) - for (const [key, form] of Object.entries(BACKUPS.units[charId].forms)) { - const kit = mod.default[charId](form.skills[0].bb, { ...form, skill: form.skills[0], charId }, {}); - assert.deepEqual(Object.keys(kit.skills || {}).sort(), form.skills.map((s) => s.skillId).sort(), `${file}@${key}: skills`); + // every skill of every slot form is authored (a 自选 piece has no default skill: the pick chooses any), built from the + // composed record and its def as the sim does (shared/diy.js diyRecord, simdata normalizeChess) + for (const slot of ['chess_char_5_diy1_a', 'chess_char_6_diy1_a']) { + for (const elite of [false, true]) { + const rec = diyRecord(slot, { charId, skillIndex: 0 }, { elite, data: { chess: CHESS, backups: BACKUPS } }); + assert.ok(rec, `${file}: a legal pick of ${slot}`); + const kit = mod.default[charId](rec.skill.bb, rec, normalizeChess(rec)); + assert.deepEqual(Object.keys(kit.skills || {}).sort(), rec.skills.map((s) => s.skillId).sort(), `${file}@${slot}${elite ? ' elite' : ''}: skills`); + } } } assert.deepEqual([...KITTED_CHARS].sort(), [...new Set([...GENERIC_KIT_CHARS, ...Object.keys(STANDIN_KITS), ...Object.keys(OPERATOR_KITS)])].sort()); diff --git a/test/diy.test.js b/test/diy.test.js index cccb32cb..a9e141f0 100644 --- a/test/diy.test.js +++ b/test/diy.test.js @@ -70,6 +70,8 @@ test('checkDiyPick: a prototype carries its locked skill / module (与系统补 assert.deepEqual(lockedSelection(DATA, 6, 'char_617_sharp2'), { skillIndex: 0, uniEquipId: 'uniequip_002_sharp2' }); assert.deepEqual(lockedSelection(DATA, 5, 'char_605_cmedic'), { skillIndex: 2, uniEquipId: null }); assert.match(checkDiyPick(T5, { charId: 'char_609_acguad', uniEquipId: 'none' }, DATA).error, /prototype carries module/); + assert.match(checkDiyPick(T5, { charId: 'char_609_acguad', uniEquipId: 'uniequip_001_acguad' }, DATA).error, /prototype carries module/); + assert.deepEqual(checkDiyPick(T5, { charId: 'char_609_acguad', skillIndex: null, uniEquipId: null }, DATA).pick, { charId: 'char_609_acguad', skillIndex: 2, uniEquipId: 'uniequip_002_acguad' }, 'null = the locked selection'); assert.equal(lockedSelection(DATA, 5, SIEGE), null, 'an owned operator has no lock'); });