feedback3 WJ: grey the 折射 line and drop its icon while silenced
The RES bonus was already off. The enemy card now greys a SILENCE-format line from the live silenced flag, and the status row leaves the 折射 icon out while silence is up.
This commit is contained in:
1 parent
6e59b5e4dd
commit
2723a3daea
9 files changed
+74
-3
No files matched your search
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// Enemy-card ability lines (detailPanel EnemyDetail). A line whose data format is SILENCE
|
||||
// (折射: `<$ba.refraction>折射</>`, format "SILENCE") is off while the enemy is silenced: the card greys it.
|
||||
// The sim already drops the RES bonus; this is only the line.
|
||||
|
||||
/**
|
||||
* @param {any[]|null} abilities enemies.json abilities
|
||||
* @param {boolean} silenced live unit (`unitStatsEntry.silenced`)
|
||||
* @returns {Array<{ text: string, off: boolean }>}
|
||||
*/
|
||||
export function abilityRows(abilities, silenced) {
|
||||
const list = Array.isArray(abilities) ? abilities : [];
|
||||
return list.map((a) => {
|
||||
const text = typeof a === 'string' ? a : (a && (a.textRaw || a.text)) || '';
|
||||
const off = !!(silenced && a && typeof a === 'object' && a.format === 'SILENCE');
|
||||
return { text, off };
|
||||
});
|
||||
}
|
||||
@@ -35,6 +35,7 @@ import { html, Icon, TierChip, MicroLabel, Button, confirmDialog, useTicker } fr
|
||||
import { Img, RichText, UnitThumb, BondGlyph, GIcon } from './gameComponents.js';
|
||||
import { attackInterval, rangeGridBox, fmtNum, tileKey, chessLoadout, nextThreshold, bondTier, briefingBondTip, pieceBondIds, grantedBonds, morphPairings } from './gameLogic.js';
|
||||
import { chessPortraitUrl, skillIconUrl, skillRecordIconUrl, profIconUrl, subProfIconUrl, itemIconUrl, enemyIconUrl, tokenAvatarUrl, factionIconUrl, uiUrl, moduleTypeIconUrl } from './assetUrls.js';
|
||||
import { abilityRows } from './abilityLines.js';
|
||||
import { data } from '../data.js';
|
||||
import { attackRangeGrid } from '../../../shared/loadoutRecord.js';
|
||||
import { SKILL_SUMMON_START_DEPLOY } from '../../../shared/constants.js';
|
||||
@@ -522,7 +523,7 @@ function EnemyDetail({ enemy, snapHp, count, live = null }) {
|
||||
</div>
|
||||
${imm.length ? html`<p class="dhint"><${Icon} name="shield" />免疫:${imm.join('、')}</p>` : null}
|
||||
${Array.isArray(enemy.abilities) && enemy.abilities.length ? html`<${Section} title="能力" micro="ABILITIES">
|
||||
<ul class="dabil">${enemy.abilities.map((a, i) => html`<li key=${i}><${RichText} text=${typeof a === 'string' ? a : a.textRaw || a.text} /></li>`)}</ul>
|
||||
<ul class="dabil">${abilityRows(enemy.abilities, !!live?.silenced).map((a, i) => html`<li key=${i} class=${a.off ? 'is-off' : null}><${RichText} text=${a.text} /></li>`)}</ul>
|
||||
<//>` : enemy.descRaw || enemy.desc ? html`<${Section} title="说明"><${RichText} as="p" text=${enemy.descRaw || enemy.desc} class="dtext" /><//>` : null}`;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user