feedback7 R22-LOADOUT: 干员调配's module card reads the module's talent changes at the operator's 潜能, as 局内数值 and the battle do; golden: unchanged
Grok's review of the branch (low): ModuleInfo read talentChanges from the data's module record — the full-potential one — so at 潜能1 余's 闲云隐市 still read 「生命上限2%(+0.5%)」 while 局内数值 and the battle use 1.5 % (异客 +10 %(+2 %) vs +8 %). ModuleInfo takes `potential` and composes the elite at it first (shared/potential.js atPotential; the module's traitOverride does not depend on potential); the detail passes the operator's setting. - Test: test/ui/loadout-rows.test.js — 余's module card at none / 潜能6 / 潜能1, 不装备's base 特性, the detail's prop.
This commit is contained in:
1 parent
4f8f5d2acf
commit
9f36db32e8
2 files changed
+31
-5
No files matched your search
@@ -34,11 +34,12 @@ import { PHASE } from '../../../shared/constants.js';
|
||||
import {
|
||||
MODULE_NONE, PROF_ORDER, PROF_NAME, rosterOf, filterRoster, recordsOf, chessOptions, effectiveChoice, setChoice, resetChoice,
|
||||
changedCount, skillLabel, moduleBadge, attrRows, skillTags, quickSkillTags, traitLines, serializeExport, parseImport, LOADOUT_IMPORT_MAX_BYTES,
|
||||
opsOf, setOps, resetOps,
|
||||
opsOf, setOps, resetOps, moduleRecord,
|
||||
} from '../ui/loadoutModel.js';
|
||||
import { loadoutStore, openLoadout, closeLoadout, setEntries, setOpsMap, applyLoadoutEntries, setNotOwned, applyOwnershipImport, setDiyPicks, applyDiyImport } from '../ui/loadoutSync.js';
|
||||
import { CultivationSelects, CultivationSection } from './cultivation.js';
|
||||
import { cultivationCharIds } from '../../../shared/protocol.js';
|
||||
import { atPotential } from '../../../shared/potential.js';
|
||||
import { setOwned, notOwnedCount, serializeOwnership, parseOwnershipImport, OWNERSHIP_IMPORT_MAX_BYTES } from '../ui/ownershipModel.js';
|
||||
import { OwnershipPanel, useOwnershipRoster } from './ownership.js';
|
||||
import { DiyPanel, diyData } from './diy.js';
|
||||
@@ -263,9 +264,16 @@ function SkillOption({ m, opt, on, level, onPick }) {
|
||||
</button>`;
|
||||
}
|
||||
|
||||
export function ModuleInfo({ m, golden, opt }) {
|
||||
/**
|
||||
* The chosen module's card: its stat bonus, the 特性 it fights with, its talent changes — read at the operator's 潜能
|
||||
* (`potential`, 0.2.2: the elite composed at it first, shared/potential.js atPotential, as 局内数值 and the battle do; the
|
||||
* data record is the full-potential one — 余's 闲云隐市 「生命上限2%(+0.5%)」 is 「1.5%」 at 潜能1). null = full potential.
|
||||
* @param {{ m: any, golden: any, opt: { id: string, rec: any, isDefault?: boolean }, potential?: number|null }} props
|
||||
*/
|
||||
export function ModuleInfo({ m, golden, opt, potential = null }) {
|
||||
if (!golden) return null;
|
||||
const rec = opt.id === MODULE_NONE ? null : opt.rec;
|
||||
const at = atPotential(golden, potential);
|
||||
const rec = opt.id === MODULE_NONE ? null : at === golden ? opt.rec : moduleRecord(at, opt.id) || opt.rec;
|
||||
if (!rec) {
|
||||
const traitBase = golden.traitBase || null;
|
||||
return html`<div class="lo-minfo lo-minfo--none">
|
||||
@@ -412,7 +420,7 @@ function Detail({ m, chess, golden, entries, ops = {}, onChange, onOps, onReset,
|
||||
${mo.isDefault ? html`<span class="lo-badge lo-badge--def lo-mod__def">${t('默认')}</span>` : null}
|
||||
</button>`)}
|
||||
</div>
|
||||
${modOpt ? html`<${ModuleInfo} m=${m} golden=${golden} opt=${modOpt} />` : null}
|
||||
${modOpt ? html`<${ModuleInfo} m=${m} golden=${golden} opt=${modOpt} potential=${opsOf(ops, chess.charId).potential} />` : null}
|
||||
</section>` : null}
|
||||
${notOwned ? html`<p class="lo-locknote lo-locknote--standin" data-testid="loadout-standin-note"><${Icon} name="info" />${standInName(chess) ? t('干员持有中标记为未持有:此棋子由替补干员 {name} 上场,技能与模组固定(补位干员技能不可更改);这里的调配在改回「持有」后生效', { name: standInName(chess) })
|
||||
: t('干员持有中标记为未持有:此棋子由替补干员上场,技能与模组固定(补位干员技能不可更改);这里的调配在改回「持有」后生效')}</p>` : null}
|
||||
|
||||
@@ -15,7 +15,7 @@ globalThis.fetch = async (url) => {
|
||||
const name = String(url).split('/').pop();
|
||||
try { const body = readFileSync(path.join(ROOT, 'data', name), 'utf8'); return { ok: true, status: 200, json: async () => JSON.parse(body) }; } catch { return { ok: false, status: 404, json: async () => ({}) }; }
|
||||
};
|
||||
const { RosterRow, RosterHead, statsPreview, LoadoutStats } = await import('../../public/js/screens/loadout.js');
|
||||
const { RosterRow, RosterHead, statsPreview, LoadoutStats, ModuleInfo } = await import('../../public/js/screens/loadout.js');
|
||||
const { CultivationSelects, CultivationSection, cultivateName } = await import('../../public/js/screens/cultivation.js');
|
||||
const { DiyPanelView } = await import('../../public/js/screens/diy.js');
|
||||
const { data } = await import('../../public/js/data.js');
|
||||
@@ -144,6 +144,24 @@ test('局内数值 at the operator\'s settings: 刺玫 at 潜能1 / 未精英化
|
||||
assert.match(cap(LoadoutStats({ base: v, golden: g, entries: {}, level: 'elite', onLevel() {}, getChess: get, ops: {} })), /^数值含所选模组;含潜能与练度;不含技能发动/);
|
||||
});
|
||||
|
||||
test('the module card reads its talent changes at the operator\'s 潜能, as 局内数值 and the battle do (余 闲云隐市: 2%(+0.5%) / 1.5%)', () => {
|
||||
// (Grok review of the branch: the card printed the full-potential line whatever the setting)
|
||||
const g = get('chess_char_6_03_b');
|
||||
const mod = g.modules.find((x) => x.uniEquipId === 'uniequip_002_yu');
|
||||
const opt = { id: mod.uniEquipId, rec: mod, isDefault: !!mod.isDefault };
|
||||
const texts = (tree) => find(tree, (v) => typeof v.type === 'function' && v.type.name === 'RichText').map((v) => String(v.props.text));
|
||||
const full = texts(ModuleInfo({ m: null, golden: g, opt }));
|
||||
assert.ok(full.some((x) => x.includes('生命上限2%<@ba.talpu>(+0.5%)</>')), 'full potential (none given)');
|
||||
assert.deepEqual(texts(ModuleInfo({ m: null, golden: g, opt, potential: 6 })), full);
|
||||
const p1 = texts(ModuleInfo({ m: null, golden: g, opt, potential: 1 }));
|
||||
assert.ok(p1.some((x) => x.includes('生命上限1.5%的生命')), '潜能1');
|
||||
assert.ok(!p1.some((x) => x.includes('(+0.5%)')));
|
||||
assert.deepEqual(texts(ModuleInfo({ m: null, golden: g, opt: { id: 'none', rec: null }, potential: 1 })), [g.traitBase.descRaw], '不装备: the base 特性');
|
||||
// the detail hands the operator's 潜能 to it
|
||||
const src = readFileSync(path.join(ROOT, 'public/js/screens/loadout.js'), 'utf8');
|
||||
assert.match(src, /<\$\{ModuleInfo\} m=\$\{m\} golden=\$\{golden\} opt=\$\{modOpt\} potential=\$\{opsOf\(ops, chess\.charId\)\.potential\} \/>/);
|
||||
});
|
||||
|
||||
test('css: the head and the rows share one grid (columns aligned), quick targets grow on a coarse pointer, the detail slides over below 1000 px', () => {
|
||||
const css = readFileSync(path.join(ROOT, 'public/css/screens/loadout.css'), 'utf8');
|
||||
assert.match(css, /\.lo-list__head, \.lo \.lo-card \{ display: grid; grid-template-columns: var\(--cols\);/);
|
||||
|
||||
Reference in new issue
Block a user