feedback7 UIBACK: the top-bar capsule counts like the official — resolved = killed + leaked among the enemies the round scheduled (opening 0/3, a leak 1/3, a kill 2/3, the splitter 3/3); a split child, a summon and a boss summon are in neither number but still cost LP and break 完美作战 (PR #308 by @xcdoge, the capsule half of PR #157 — authored by them: the sim / protocol / HUD hunks applied as reviewed): inTotal / killedInTotal / leakedInTotal / resolved, b.snap / BattleResult / b.progress (optional field) / m.public progress carry resolved, killed may exceed total (validation bounds it by the content-spawn allowance); dropped the PR's stray design-doc line — it had replaced the S→C b.end bullet of docs/design/network.md, which is back; the 联防 live-counter change mark follows the enemy list now that a spawn no longer moves total (uniteLeftMark + test); golden: only total / players.*.total / snaps move, outcomes bit-identical — roster 23 (roster-002 003 007 008 009 010 011 012 013 015 017 018 020 023 024 025 026 027 037 045 047 048 049), bonds 2 (bond-maniShip-low, bond-soloShip-high), fields 11 (boss-boss_1 3 5 pair+solo, boss-boss_6-solo, hidden-boss_8 10 pair+solo), diy 11 (diy-036 040 056 062 068 073 083 091 095 114 115), matches 12 (solo-FUNNY-1 NORMAL-1 HARD-1 ABYSS-1 ABYSS-2, coop2-NORMAL-3 coop2-HARD-4 coop3-ABYSS-5 coop4-ABYSS-7 coop2-NORMAL-8-serverrun solo-HARD-9-boosted coop2-NORMAL-70-diy: the total column of 36 round rows), standins unchanged
This commit is contained in:
1 parent
d7c665dbf6
commit
b4bc89ad4e
39 files changed
+836
-252
No files matched your search
@@ -100,7 +100,7 @@ export function followedScout({ field = null, watching = null, alive = true, spe
|
||||
return fid;
|
||||
}
|
||||
|
||||
/** Teammates' progress for the waiting pill: [{ playerId, name, killed, total, done, isBot }]. */
|
||||
/** Teammates' progress for the waiting pill: [{ playerId, name, killed, resolved, total, done, isBot }]. */
|
||||
export function teammateProgress(pub, myId) {
|
||||
const out = [];
|
||||
for (const f of fields(pub)) {
|
||||
@@ -111,7 +111,9 @@ export function teammateProgress(pub, myId) {
|
||||
const pr = isObj(f.progress) ? f.progress : null;
|
||||
out.push({
|
||||
playerId: pid, name: p?.name || t('队友'), isBot: !!p?.isBot,
|
||||
killed: Number.isFinite(pr?.killed) ? pr.killed : null, total: Number.isFinite(pr?.total) ? pr.total : null,
|
||||
killed: Number.isFinite(pr?.killed) ? pr.killed : null,
|
||||
resolved: Number.isFinite(pr?.resolved) ? pr.resolved : (Number.isFinite(pr?.killed) ? pr.killed : null),
|
||||
total: Number.isFinite(pr?.total) ? pr.total : null,
|
||||
done: f.live === false || !!pr?.done,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -119,6 +119,14 @@ export const LOST_RESULT_CODES = Object.freeze(['DISCONNECTED', 'OFFLINE', 'TIME
|
||||
/** Ticks per frame at a speed (same cap as the server pacing: server/match/fields.js maxTicksPerInterval). */
|
||||
export const ticksPerFrameCap = (speed) => Math.max(8, Math.ceil((Number(speed) || 2) * 4));
|
||||
|
||||
/**
|
||||
* What the runner compares between two looks at a 联防 battle (noteUniteLeft) before it reads the leakers' enemies still
|
||||
* standing again: a knock-out, a leak, a spawn, the end. `total` stands for the spawns no longer — a split child or a summon
|
||||
* is outside the capsule's denominator (DESIGN §14 顶栏胶囊) — so the length of the enemy list does.
|
||||
* @param {any} b the battle
|
||||
*/
|
||||
export const uniteLeftMark = (b) => `${Number(b.killed) || 0}:${Number(b.leakedCount) || 0}:${Number(b.total) || 0}:${Array.isArray(b.enemies) ? b.enemies.length : 0}:${b.finished ? 1 : 0}`;
|
||||
|
||||
function deepFreeze(root) {
|
||||
const stack = [root];
|
||||
while (stack.length) {
|
||||
@@ -288,7 +296,7 @@ export function createBattleRunner(deps) {
|
||||
|
||||
function noteUniteLeft(e) {
|
||||
const b = e.battle;
|
||||
const mark = `${Number(b.killed) || 0}:${Number(b.leakedCount) || 0}:${Number(b.total) || 0}:${b.finished ? 1 : 0}`;
|
||||
const mark = uniteLeftMark(b);
|
||||
if (mark === e.leakMark) return;
|
||||
e.leakMark = mark;
|
||||
let left = e.left;
|
||||
@@ -428,6 +436,10 @@ export function createBattleRunner(deps) {
|
||||
e.lastProgressAt = t;
|
||||
const p = e.sim.spec.battleProgress(e.battle);
|
||||
const msg = { battleId: e.battleId, gt: Math.min(1e5, p.gt), killed: Math.min(p.killed, p.total), total: Math.min(1e5, p.total), done: !!p.done };
|
||||
// the HUD capsule's numerator of this field (shared/protocol.js b.progress `resolved`): the field's own scheduled
|
||||
// enemies knocked out or leaked. Sent only when the battle reports one (Battle.resolved) — an absent field leaves
|
||||
// the teammate UI on its `resolved ?? killed` fallback
|
||||
if (Number.isFinite(p.resolved)) msg.resolved = Math.max(0, Math.min(msg.total, p.resolved));
|
||||
if (bossLike(e)) {
|
||||
const pool = e.battle.sharedBoss;
|
||||
msg.leaks = Math.min(1e6, e.meter.lp);
|
||||
|
||||
@@ -25,7 +25,7 @@ function ProgressList({ list }) {
|
||||
<b>${p.name}</b>
|
||||
${p.done
|
||||
? html`<span class="chud__prog__ok" aria-label=${t('作战结束')}>✓</span>`
|
||||
: html`<span class="num">${p.killed != null && p.total != null ? `${p.killed}/${p.total}` : '•••'}</span>`}
|
||||
: html`<span class="num">${(p.resolved ?? p.killed) != null && p.total != null ? `${p.resolved ?? p.killed}/${p.total}` : '•••'}</span>`}
|
||||
</span>`)}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,9 @@ export const STATUS_META = Object.freeze({
|
||||
// ---- snapshots / battle HUD ---------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* HUD numbers from a b.snap: { killed, total, dp, boss } (boss: { hp, max } when present).
|
||||
* HUD numbers from a b.snap: { killed, resolved, total, dp, boss } (boss: { hp, max } when present).
|
||||
* `resolved` is the capsule's numerator — knocked out + leaked among the round's own enemies (Battle.leakedInTotal);
|
||||
* an older snapshot without it falls back to the kill count.
|
||||
* @param {any} snap
|
||||
*/
|
||||
export function snapHud(snap) {
|
||||
@@ -30,7 +32,8 @@ export function snapHud(snap) {
|
||||
const n = (v) => (Number.isFinite(v) ? v : null);
|
||||
let boss = null;
|
||||
if (isObj(snap.boss) && Number.isFinite(snap.boss.hp)) boss = { hp: snap.boss.hp, max: n(snap.boss.max) ?? n(snap.boss.maxHp) };
|
||||
return { killed: n(snap.killed), total: n(snap.total), dp: n(snap.dp), boss };
|
||||
const killed = n(snap.killed);
|
||||
return { killed, resolved: n(snap.resolved) ?? killed, total: n(snap.total), dp: n(snap.dp), boss };
|
||||
}
|
||||
|
||||
/** Boss HP fraction 0..1 (null when unknown). */
|
||||
|
||||
+2
-2
@@ -88,7 +88,7 @@ export function PhaseCapsule({ pub, hud, miss = null }) {
|
||||
const frac = bossFrac(boss);
|
||||
return html`<div class="capsule capsule--boss" role="status">
|
||||
<${Sprite} k="hudPanel/icon_boss" class="capsule__icon" fallback=${html`<${GIcon} name="skull" class="capsule__icon" />`} />
|
||||
${hud?.total != null ? html`<span class="capsule__kills num"><b>${hud.killed ?? 0}</b>/${hud.total}</span>` : null}
|
||||
${hud?.total != null ? html`<span class="capsule__kills num"><b>${hud.resolved ?? hud.killed ?? 0}</b>/${hud.total}</span>` : null}
|
||||
<div class="bossbar" title=${boss ? `${fmtNum(boss.hp)} / ${fmtNum(boss.max)}` : t('敌方领袖')}>
|
||||
<div class="bossbar__fill" style=${`width:${frac == null ? 100 : frac * 100}%`}></div>
|
||||
<span class="bossbar__txt num">${frac == null ? t('敌方领袖') : bossPctText(frac)}</span>
|
||||
@@ -99,7 +99,7 @@ export function PhaseCapsule({ pub, hud, miss = null }) {
|
||||
return html`<div class=${cx('capsule', 'capsule--combat', phase === PHASE.UNITE && 'capsule--unite')} role="status">
|
||||
<${Sprite} k=${phase === PHASE.UNITE ? 'hudPanel/icon_coop' : 'hudPanel/icon_battle'} class="capsule__icon"
|
||||
fallback=${html`<${Icon} name="sword" class="capsule__icon" />`} />
|
||||
<span class="capsule__kills num"><b>${hud?.killed ?? 0}</b>/${hud?.total ?? '--'}</span>
|
||||
<span class="capsule__kills num"><b>${hud?.resolved ?? hud?.killed ?? 0}</b>/${hud?.total ?? '--'}</span>
|
||||
${phase === PHASE.UNITE ? html`<span class="capsule__tag">${t('联防')}</span>` : null}
|
||||
${phase === PHASE.UNITE && Number.isFinite(miss) ? html`<${MissTag} n=${miss} />` : null}
|
||||
</div>`;
|
||||
|
||||
Reference in new issue
Block a user