feedback3: merge WJ — 高台 only for elite 歌蕾蒂娅 with HOK-Y, live prep watch, silenced 折射, grant toasts, emote timeout glyph

This commit is contained in:
sganggs committed 2026-10-04 21:26:46 +08:00
commit c9dd2a6392
41 files changed
+700 -216

No files matched your search

+2 -1
View File
@@ -12,7 +12,7 @@
// (left_prepare / *_boss_prepare) clear of the folded shop's band (public
// issue #5: folding the shop did not grow the board)
// view.setPrep(privateState, { editable, canPlace }) hand/temp/board pieces; editable enables drag & drop; a
// new board piece flashes (fx.deploy); a merge's elite — on the tile of
// new board piece flashes (fx.deploy), and so does a card that just arrived in the hand or temp; a merge's elite — on the tile of
// the deployed copy it replaced, or on its bench slot — gets the
// promotion cue instead (render/promote.js, fx.promote)
// view.enterBattle(fieldMeta) m.field { fieldId, kind, rect, stageId, units: [UnitInfo] } — each
@@ -975,6 +975,7 @@ export async function createFieldView(host, options = {}) {
promotions.push({ uid: e.uid, id: e.piece.id, area: e.area, row: e.row ?? null, col: e.col ?? null, idx: e.idx ?? null, copies: promoFrom.get(e.uid).length });
if (promotions.length > 20) promotions.shift();
} else if (e.area === 'board' && prevBoard.size && !prevBoard.has(e.uid)) { v.onDeploy?.(); fx.deploy(v); }
else if (before.length && (e.area === 'hand' || e.area === 'temp') && !before.some((g) => g.uid === e.uid)) fx.deploy(v);
} else {
const prevHome = v._home;
const moved = !prevHome || prevHome.x !== w.x || prevHome.y !== w.y || prevHome.z !== w.z;
+14
View File
@@ -129,6 +129,8 @@ export const STATUS_ICON = Object.freeze({
substitute: 'doll',
// 禁疗 (sim status 'healFree': 史尔特尔's 余烬 — no heal reaches her until she leaves)
healFree: 'healFree',
// 折射 (sim buff 'ab:refraction', visible while the RES bonus is on). The tail of 'ab:refraction' hits this key.
refraction: 'refraction',
});
/** Keyword fallbacks for namespaced / content status keys ('ab:frost', 'reed2:scorch', 'skill:shotst_shred' …). */
@@ -140,6 +142,18 @@ const STATUS_GUESS = [
[/neural/i, 'neural'], [/necro|apopt|erosion/i, 'necrosis'],
];
/**
* The 折射 icon is not drawn while the unit is silenced: the RES bonus is already off (enemies.js refraction)
* and the status must not keep looking active. Other icons stay.
* @param {string} key a b.ev status key
* @param {Set<string>|string[]|null} statuses
*/
export function statusIconSuppressed(key, statuses) {
if (key !== 'ab:refraction' && key !== 'refraction') return false;
if (!statuses) return false;
return typeof statuses.has === 'function' ? statuses.has('silence') : Array.isArray(statuses) && statuses.includes('silence');
}
/** Icon key (textures STATUS_KEYS) for a b.ev status key or flag name; null when it has no icon. */
export function statusIconKey(key) {
if (typeof key !== 'string' || !key) return null;
+8 -1
View File
@@ -835,7 +835,7 @@ export function fxAtlas() {
return _fx;
}
export const STATUS_KEYS = ['stun', 'freeze', 'cold', 'stealth', 'shield', 'fragile', 'sleep', 'invuln', 'silence', 'slow', 'bind', 'fear', 'weaken', 'levitate', 'taunt', 'burn', 'neural', 'necrosis', 'blocked', 'skill', 'doll', 'healFree'];
export const STATUS_KEYS = ['stun', 'freeze', 'cold', 'stealth', 'shield', 'fragile', 'sleep', 'invuln', 'silence', 'slow', 'bind', 'fear', 'weaken', 'levitate', 'taunt', 'burn', 'neural', 'necrosis', 'blocked', 'skill', 'doll', 'healFree', 'refraction'];
function drawStatusIcon(c, key, x, y, s) {
const cx = x + s / 2, cy = y + s / 2;
@@ -883,6 +883,13 @@ function drawStatusIcon(c, key, x, y, s) {
c.fillStyle = '#d8e0ff'; c.font = 'bold 14px sans-serif'; c.textAlign = 'center'; c.textBaseline = 'middle'; c.fillText('Zz', cx, cy + 1);
break;
}
case 'refraction': {
// 折射: a split beam. Drawn only while the buff is on; units.js drops it under silence.
disc('rgba(12,36,48,0.92)', '#7ee0ff');
c.strokeStyle = '#d8f7ff'; c.lineWidth = 2;
c.beginPath(); c.moveTo(cx - 8, cy + 6); c.lineTo(cx - 1, cy - 8); c.lineTo(cx + 8, cy + 6); c.moveTo(cx + 2, cy - 2); c.lineTo(cx + 9, cy - 8); c.stroke();
break;
}
case 'silence': {
disc('rgba(45,20,55,0.92)', '#e08bff');
c.strokeStyle = '#f0c4ff'; c.lineWidth = 2.2;
+2 -1
View File
@@ -68,7 +68,7 @@
import { UF, ANIM } from '../../../shared/constants.js';
import { SpineActor } from './spine.js';
import { diamondTexture, shadowTexture, fxAtlas, tierChip, statusTexture, itemTexture, hudRings, ringArc, HUD_DISC, ELEMENT_RING } from './textures.js';
import { COLORS, TIER_COLORS, ENEMY_FRAME, UNIT, PROJ, statusIconKey } from './style.js';
import { COLORS, TIER_COLORS, ENEMY_FRAME, UNIT, PROJ, statusIconKey, statusIconSuppressed } from './style.js';
import { drawCrate, rowDepthKey, ROW_KEY, deviceBoxOf, DEVICE_BOX } from './tiles.js';
const clamp = (v, lo, hi) => (v < lo ? lo : v > hi ? hi : v);
@@ -1359,6 +1359,7 @@ export class UnitView {
// a burst's lock ('burnBurst', 'neuralBurst' … — the 爆发冷却) is shown by the element gauge row under the bars
// (b.snap `elem`); only a feed without gauges (an older recording) shows it as a status
if (this.el && k.endsWith('Burst')) continue;
if (statusIconSuppressed(k, this.statuses)) continue; // 折射 while silenced
const icon = statusIconKey(k);
// flag-driven states are authoritative (a stale 'stun' status must not outlive the flag)
if (!icon || icon === 'stun' || icon === 'freeze' || icon === 'sleep' || icon === 'stealth' || icon === 'invuln') continue;