feedback5 WK: 自选编队 in the browser — the 干员调配 overlay's third tab 自选编队 (5阶 ×2, 6阶 ×2 slots; the slot picker lists the kitted picks of the tier with avatar, name, class, derived bonds and a 原型 / 已持有 tag; an owned pick chooses its skill and module — 集成战略 modules left out —, a prototype shows its locked skill; sp.pref.diy, export / import, 全部清空, synced as room.diy through the shared engine, welcome.diyKitted kept), the operator on shop / reward cards, own pieces (「自选」 tag, its model on bench and board), the detail card (pick's skill and module), placement and range previews, bond popups (own and a teammate's 自选 member), the result screen; new UI strings through t() with English; the e2e fast server's SP_START_LEVEL hook

Bonds of 自选 pieces checked everywhere membership is decided (the owner's reminder of 2026-10-05): the match's counts through the player's data view (煌 in 炎 and 维多利亚, one member per slot, 绝技 elites), the sim's bond effects through def.bonds (叙拉古 on deployment, 协防 ×1.2 / elite ×1.4), the client's strip, popups, cards and detail card. Golden unchanged.
Tests: test/ui/diy-ui.test.js (15), test/match/diy-bonds.test.js (4), test/i18n.test.js pins screens/diy.js; test/ui/diy.e2e.test.js (SP_E2E=1, run once: pass).
This commit is contained in:
sganggs committed 2026-10-06 00:13:31 +08:00
1 parent e480b1cb26
commit 0def7babfd
31 files changed
+1637 -78

No files matched your search

+29 -7
View File
@@ -1,12 +1,15 @@
// public/js/screens/game/standInTags.js — 0.2.0 补位: the 「替补:X」 tag under the player's own prep pieces (hand,
// 临时整备区, board) of a chess they do not own (m.private.standIns). The hand keeps the chess's own model (the card shows
// the original operator); on the board the model is the stand-in's (render/app.js pieceInfo) — the tag says which
// chess it is either way. Anchored to each piece's drawn body (view.pieceScreenRect, followed every frame like the
// direction wheel follows its tile); never takes the pointer, so the pieces stay draggable.
// chess it is either way. 0.2.0 自选编队: the 「自选」 tag under the player's own pieces of a DIY slot it filled
// (m.private.diy) — the model is the operator's everywhere. Anchored to each piece's drawn body (view.pieceScreenRect,
// followed every frame like the direction wheel follows its tile); never takes the pointer, so the pieces stay
// draggable.
import { useEffect, useRef, useState } from '../../../vendor/hooks.module.js';
import { html } from '../../ui/components.js';
import { fieldsStandIn, standInOf, standInLabel } from '../../ui/gameLogic.js';
import { fieldsStandIn, standInOf, standInLabel, ownDiyRecord } from '../../ui/gameLogic.js';
import { t } from '../../../../shared/i18n.js';
const rawOf = (view) => (view && view.raw) || view || null;
@@ -30,9 +33,27 @@ export function standInPieces(priv, getChess, backups) {
return out;
}
/** @param {{ view: any, priv: any, getChess: (id: string) => any, backups: any }} props */
export function StandInTags({ view, priv, getChess, backups }) {
const items = standInPieces(priv, getChess, backups);
/**
* The player's own prep pieces of a DIY slot it filled (0.2.0 自选编队): [{ uid, label, name, area, diy: true }].
* @param {any} priv m.private @param {(id: string) => any} getChess @param {{ chess: any, backups: any }} data
*/
export function diyPieces(priv, getChess, data) {
const out = [];
if (!priv || !priv.diy || typeof priv.diy !== 'object' || !Object.keys(priv.diy).length) return out;
const add = (p, area) => {
if (!p || p.kind !== 'chess' || !Number.isInteger(p.uid)) return;
const rec = ownDiyRecord(getChess(p.id), priv, data);
if (rec) out.push({ uid: p.uid, label: t('自选'), name: rec.name, area, diy: true });
};
for (const p of Array.isArray(priv.hand) ? priv.hand : []) add(p, 'hand');
for (const p of Array.isArray(priv.temp) ? priv.temp : []) add(p, 'temp');
for (const p of Array.isArray(priv.board) ? priv.board : []) add(p, 'board');
return out;
}
/** @param {{ view: any, priv: any, getChess: (id: string) => any, backups: any, diyData?: { chess: any, backups: any } | null }} props */
export function StandInTags({ view, priv, getChess, backups, diyData = null }) {
const items = [...standInPieces(priv, getChess, backups), ...(diyData ? diyPieces(priv, getChess, diyData) : [])];
const [pos, setPos] = useState({});
const last = useRef('');
const key = items.map((it) => it.uid).join(',');
@@ -64,7 +85,8 @@ export function StandInTags({ view, priv, getChess, backups }) {
return html`<div class="sitags" aria-hidden="true" data-testid="standin-tags">
${items.map((it) => {
const p = pos[it.uid];
return p ? html`<span key=${it.uid} class=${`sitag sitag--${it.area}`} data-uid=${it.uid} title=${`未持有:由 ${it.name} 上场`}
return p ? html`<span key=${it.uid} class=${`sitag sitag--${it.area}${it.diy ? ' sitag--diy' : ''}`} data-uid=${it.uid} data-diy=${it.diy ? '1' : undefined}
title=${it.diy ? t('自选编队:{name}', { name: it.name }) : `未持有:由 ${it.name} 上场`}
style=${`left:${p.x.toFixed(1)}px;top:${p.y.toFixed(1)}px`}>${it.label}</span>` : null;
})}
</div>`;