feedback7: merge fb7-hud — HP-bar readouts (#303 / #378 / #383: ammo cells, 伺夜's wolf-shadow pips, 斩业星熊's 我执 bar), #302 renderer abort, #292 held skill cue, #334 atlas slots, #354 梓兰's per-card frost
- Grok review: MERGE (no defect). Golden unchanged (union OK); broad node --test 5327 (5326 pass, 0 fail, 1 skipped); lint 0 errors.
This commit is contained in:
commit
7d6fefe8f5
33 files changed
+1751
-77
No files matched your search
@@ -4,7 +4,7 @@
|
||||
|
||||
import { EVENT_BUFFER_CAP } from '../constants.js';
|
||||
import { elementView } from '../damage.js';
|
||||
import { unitInfo, snapshotUnits } from '../snapshot.js';
|
||||
import { unitInfo, snapshotUnits, ammoView, wolfView, negView } from '../snapshot.js';
|
||||
|
||||
export class BattleEvents {
|
||||
fx(kind, params = {}) {
|
||||
@@ -37,7 +37,11 @@ export class BattleEvents {
|
||||
* down: [[id, respawnAt, respawnTime, state, row, col]] — operators that left the field waiting to redeploy (isDown): the
|
||||
* game time their respawn timer ends, its length (s), constants.js DOWN_STATE and the tile they lie on (and
|
||||
* come back on: _layBody — where they fell, or their home);
|
||||
* elem: [[id, element, fill, cooldownEnd, cooldown]] — the element gauge each unit shows (damage.js elementView).
|
||||
* elem: [[id, element, fill, cooldownEnd, cooldown]] — the element gauge each unit shows (damage.js elementView);
|
||||
* ammo: [[id, rounds left, rounds in the magazine]] — a running ammo skill, whole rounds (snapshot.js ammoView): the segmented bar;
|
||||
* wolves: [[id, 狼影 left, the talent's maximum]] — 伺夜's 狼群 (snapshot.js wolfView): the pips under the HP bar;
|
||||
* neg: [[id, fill]] — the share of its cap a negative-HP pool holds (snapshot.js negView; 斩业星熊's 我执): the red bar.
|
||||
* The last three are display only: no sim state reads them, and the nine-field unit tuples are unchanged.
|
||||
*/
|
||||
snapshot() {
|
||||
const snap = {
|
||||
@@ -60,13 +64,22 @@ export class BattleEvents {
|
||||
(down || (down = [])).push([u.id, r2(u.respawnAt), r2(Math.max(0, u.respawnAt - u.deathAt)), this._downState(u), ...this.restTile(u)]);
|
||||
}
|
||||
if (down) snap.down = down;
|
||||
let elem = null;
|
||||
let elem = null, ammo = null, wolves = null, neg = null;
|
||||
for (const u of this.units) {
|
||||
if (!u.alive || !u.deployed || u.hidden) continue;
|
||||
const v = elementView(u, this.time);
|
||||
if (v) (elem || (elem = [])).push([u.id, v[0], v[1], v[2], v[3]]);
|
||||
const am = ammoView(u);
|
||||
if (am) (ammo || (ammo = [])).push([u.id, am[0], am[1]]);
|
||||
const wv = wolfView(u);
|
||||
if (wv) (wolves || (wolves = [])).push([u.id, wv[0], wv[1]]);
|
||||
const ng = negView(u);
|
||||
if (ng) (neg || (neg = [])).push([u.id, ng]);
|
||||
}
|
||||
if (elem) snap.elem = elem;
|
||||
if (ammo) snap.ammo = ammo;
|
||||
if (wolves) snap.wolves = wolves;
|
||||
if (neg) snap.neg = neg;
|
||||
return snap;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ export default {
|
||||
skill: null,
|
||||
trait: { hitsFn: (b, w) => Math.max(1, w.mem.wolves || 1) },
|
||||
install(battle, w) {
|
||||
w.mem.wolfCapacity = maxWolves; // (b.snap `wolves`, snapshot.js wolfView: the pips under the HP bar)
|
||||
const tal = w.def.talents || [];
|
||||
const wb = tal[0]?.bb ?? {};
|
||||
const per = num(wb.block_cnt, num(wb['vigil_wolf_t_1_enhance[trigger].block_cnt'], 1));
|
||||
|
||||
@@ -180,6 +180,9 @@ export default {
|
||||
const capOf = () => num(t0.max_minus_hp_ratio, 2) * unit.s.maxHp;
|
||||
const quiet = num(t0['hsgma2_t_1[heal].interval'], 5);
|
||||
const regen = num(t0['hsgma2_t_1[heal].hp_recovery_per_sec_by_max_hp_ratio']);
|
||||
// the pool as a share of its cap, read when a snapshot is taken (b.snap `neg`, snapshot.js negView): the green HP bar
|
||||
// sits on the 1-HP floor in 我执, so the client draws the pool as the red bar — display only
|
||||
unit.negFill = () => (unit.mem.hsEgo && unit.mem.hsEgo.pool > 0 ? unit.mem.hsEgo.pool / Math.max(1e-9, capOf()) : 0);
|
||||
const enter = (pool) => {
|
||||
unit.mem.hsEgo = { pool: Math.max(0, pool), lastHurt: battle.time };
|
||||
battle.addBuff(unit, { key: EGO, status: 'healFree', flags: { noHeal: true, healFree: true }, tags: ['talent'] });
|
||||
|
||||
@@ -773,6 +773,7 @@ function wolfPack(bb, raw, def) {
|
||||
skill: null,
|
||||
trait: { hitsFn: (b, u) => Math.max(1, u.mem.shadows ?? 1) },
|
||||
install(battle, unit) {
|
||||
unit.mem.wolfCapacity = maxShadows; // (b.snap `wolves`, snapshot.js wolfView: the pips under the HP bar)
|
||||
// initial shadows: 伺夜's talent text ("初始两只"), else one below the maximum
|
||||
const ot = (ownerOf(unit)?.def?.talents || []).map((t) => t.description || '').join(' ');
|
||||
const mi = ot.match(/初始(两|二|\d+)只/);
|
||||
|
||||
+44
-1
@@ -3,7 +3,8 @@
|
||||
// b.snap = { fieldId, t, units: [[id, x, y, hp, maxHp, sp, spMax, flags, anim]], dp, killed, total }
|
||||
// (hp of a countdown summon — unit.countdown, content/tokens.js startCountdown — is maxHp × the share of its life left)
|
||||
// UnitInfo = { id, kind, side, ownerId, defId, name, tier, golden, spine, avatar, x, y, facing, dir, maxHp, motion?, boss?, uid?,
|
||||
// form?, skillIndex?, moduleId?, items?, standInFor?, diy? } (standInFor = the replaced operator's charId of a 补位
|
||||
// form?, skillIndex?, ammoSkill?, moduleId?, items?, standInFor?, diy? } (ammoSkill = an ally whose skill is an ammo
|
||||
// magazine: the HP bar shows its rounds as cells, b.snap `ammo`; standInFor = the replaced operator's charId of a 补位
|
||||
// stand-in; diy = a 自选 piece's pick { charId, skillIndex, uniEquipId } — defId is its slot, spine / avatar the operator's;
|
||||
// form = the unit's current model form — an enemy's, content/enemies/helpers.js setForm:
|
||||
// 掠海漂移体 'crawl', 暴鸰 'bombed', 转译基底·α's forms …; a 傀儡师 fighting as its 替身 'doll', professions.js — a view built
|
||||
@@ -48,6 +49,9 @@ export function unitInfo(u) {
|
||||
uid: u.uid ?? undefined,
|
||||
// DESIGN §16: the equipped skill's index (the renderer / audio pick that skill's Spine clip and sound)
|
||||
skillIndex: u.side === 'ally' && Number.isInteger(d.skill?.index) ? d.skill.index : undefined,
|
||||
// an ally whose skill is an ammo magazine: the renderer shows it as the segmented bar under the HP bar (b.snap `ammo`) and
|
||||
// draws no sustained skill aura for it — known from the unit's first appearance, before any snapshot or skill event
|
||||
ammoSkill: u.side === 'ally' && u.skill?.kind === 'ammo' ? true : undefined,
|
||||
// DESIGN §16: an elite ally's equipped module (uniEquipId | 'none'; display only — a teammate's unit in a shared
|
||||
// field shows its owner's module in the detail card)
|
||||
moduleId: u.side === 'ally' && d.golden && typeof d.loadout?.moduleId === 'string' ? d.loadout.moduleId : undefined,
|
||||
@@ -118,6 +122,45 @@ export function unitTuple(u, t) {
|
||||
return [u.id, r2(u.x), r2(u.y), hp, maxHp, r1(sp), spMax, flagsOf(u), animOf(u, t)];
|
||||
}
|
||||
|
||||
/**
|
||||
* The three optional HUD readouts of b.snap (events.js snapshot(): `ammo`, `wolves`, `neg`; each only for a living,
|
||||
* deployed, visible unit and only when it has one). They ride beside the 9-field tuples — the tuple, and the SP
|
||||
* fraction the ammo skill still puts in it, stay as they are.
|
||||
*
|
||||
* `ammoView(u)` = `[rounds left, rounds in the magazine]` of a running ammo skill (whole numbers; the magazine is the
|
||||
* activation's real total — extra bullets and refills included — that unitTuple's SP fraction runs on): the segmented
|
||||
* yellow bar under the HP bar (隐现's S2 holds 14 rounds, skill_table skchr_inside_2 `attack@trigger_time`).
|
||||
*/
|
||||
export function ammoView(u) {
|
||||
const sk = u.skill;
|
||||
if (u.side !== 'ally' || !sk || !sk.active || sk.kind !== 'ammo') return null;
|
||||
const left = Math.max(0, Math.ceil(sk.ammoLeft - 1e-9));
|
||||
return [left, Math.max(1, Math.ceil((sk.ammoMax || sk.ammo || 0) - 1e-9), left)];
|
||||
}
|
||||
|
||||
/**
|
||||
* `wolfView(u)` = `[狼影 left, the talent's maximum]` of 伺夜's 狼群 pack (content/tokens.js wolfPack and the managed kit
|
||||
* chess_char_3_19-vigil.js keep the count in `mem.shadows` and the maximum in `mem.wolfCapacity`): the pips under the HP
|
||||
* bar. A pack in its 战术点形态 is not alive and shows nothing.
|
||||
*/
|
||||
export function wolfView(u) {
|
||||
const cap = u.mem?.wolfCapacity, n = u.mem?.shadows;
|
||||
if (u.side !== 'ally' || !(cap >= 1) || !Number.isFinite(n)) return null;
|
||||
const max = Math.round(cap);
|
||||
return [Math.max(0, Math.min(max, Math.round(n))), max];
|
||||
}
|
||||
|
||||
/**
|
||||
* `negView(u)` = the share (0.01–1) of its cap that a unit's negative-HP pool holds, null without a pool: 斩业星熊's 我执
|
||||
* (kits/ops/op-hsgma2.js sets `unit.negFill`, the pool read when the snapshot is taken) — the red bar over the drained HP
|
||||
* bar. The pool itself, and the 1-HP floor she stands on, are the sim's and are not touched.
|
||||
*/
|
||||
export function negView(u) {
|
||||
if (typeof u.negFill !== 'function') return null;
|
||||
const v = u.negFill();
|
||||
return v > 0 ? Math.min(1, Math.max(0.01, r2(v))) : null;
|
||||
}
|
||||
|
||||
/** Units included in a snapshot: deployed & visible, plus recently dead ones (DIE animation). */
|
||||
export function snapshotUnits(units, t) {
|
||||
const out = [];
|
||||
|
||||
@@ -84,6 +84,9 @@ export class Unit {
|
||||
// a countdown summon's life on the field ({ from, until } battle times; content/tokens.js startCountdown, cleared by
|
||||
// every deployment): its bar shows the time left (snapshot.js unitTuple), its HP never moves (无敌 + 禁疗)
|
||||
this.countdown = null;
|
||||
// a unit with a negative-HP pool (斩业星熊's 我执, kits/ops/op-hsgma2.js) sets this to a function returning the share of the
|
||||
// pool's cap it holds (0–1): b.snap's `neg` list (snapshot.js negView) draws it as the red bar; display only
|
||||
this.negFill = null;
|
||||
// knocked out, the unit lies — and redeploys — on its home tile instead of where it fell (Battle._layBody) while content
|
||||
// holds this: 乌尔比安 moved by his S3 (the owner's decision of 2026-10-07, a deviation from PRTS's "where it fell");
|
||||
// every deployment clears it (battle/deploy.js _deploy)
|
||||
|
||||
Reference in new issue
Block a user