Combat: refresh at every kill, seamless countdown to the next enemy
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
35491c6f4d
commit
57bcd812ee
2 changed files with 26 additions and 21 deletions
|
|
@ -14,7 +14,8 @@ let selected = null; // bank item
|
|||
let pickedClass = null; // registration
|
||||
const tabs = {}; // skill -> selected recipe tab
|
||||
let refreshing = false;
|
||||
let refreshedFor = null; // action start a completion refresh was sent for
|
||||
let refreshedFor = null; // action (start and length) a completion refresh was sent for
|
||||
let retriedAt = 0; // last time a fight past its kill asked again for the next one
|
||||
let version = null; // server build this page was loaded from
|
||||
let failures = 0; // polls in a row that didn't reach the server
|
||||
|
||||
|
|
@ -481,20 +482,19 @@ function animateFight(now) {
|
|||
const a = S.me?.action, f = a?.fight;
|
||||
if (!f) return;
|
||||
const t = now - a.start;
|
||||
{
|
||||
let i = -1;
|
||||
while (i + 1 < f.events.length && f.events[i + 1][0] <= t) i++;
|
||||
const left = i >= 0 ? f.events[i][5] : f.enemy_hp;
|
||||
for (const el of document.querySelectorAll("[data-progress]")) el.style.width = `${left / f.enemy_hp * 100}%`;
|
||||
}
|
||||
const panel = $("[data-fight]");
|
||||
if (!panel) return;
|
||||
// Between kills: the next enemy is on its way, both swing timers empty.
|
||||
panel.classList.toggle("waiting", t < 0);
|
||||
const over = t >= f.end;
|
||||
// From the kill until the next enemy turns up (the next fight's start, once the server sends it),
|
||||
// the panel is the same quiet countdown: no enemy, both swing timers empty.
|
||||
const waiting = t < 0 || (over && f.result === "won");
|
||||
const respawnIn = t < 0 ? -t : f.end + (a.respawn || 1) - t;
|
||||
let shown = -1;
|
||||
while (shown + 1 < f.events.length && f.events[shown + 1][0] <= t) shown++;
|
||||
const last = f.events[shown];
|
||||
const hp = last ? last[4] : f.start_hp, foeHp = last ? last[5] : f.enemy_hp;
|
||||
const hp = last ? last[4] : f.start_hp, foeHp = waiting ? 0 : last ? last[5] : f.enemy_hp;
|
||||
for (const el of document.querySelectorAll("[data-progress]")) el.style.width = `${foeHp / f.enemy_hp * 100}%`;
|
||||
const panel = $("[data-fight]");
|
||||
if (!panel) return;
|
||||
panel.classList.toggle("waiting", waiting);
|
||||
// Your HP everywhere follows the fight as it plays out.
|
||||
for (const el of document.querySelectorAll("[data-live-hp]")) el.textContent = fmt(hp);
|
||||
for (const el of document.querySelectorAll("[data-live-hpbar]")) el.style.width = `${Math.max(0, hp / f.top * 100)}%`;
|
||||
|
|
@ -502,21 +502,19 @@ function animateFight(now) {
|
|||
set("you-bar", (el) => { el.style.width = `${Math.max(0, hp / f.top * 100)}%`; });
|
||||
set("foe-bar", (el) => { el.style.width = `${Math.max(0, foeHp / f.enemy_hp * 100)}%`; });
|
||||
set("you-hp", (el) => { el.textContent = `${fmt(hp)} / ${fmt(f.top)}`; });
|
||||
set("foe-hp", (el) => { el.textContent = `${fmt(foeHp)} / ${fmt(f.enemy_hp)}`; });
|
||||
const over = t >= f.end;
|
||||
set("foe-hp", (el) => { el.textContent = waiting ? "" : `${fmt(foeHp)} / ${fmt(f.enemy_hp)}`; });
|
||||
for (const [who, every] of [["you", f.interval], ["foe", f.enemy_interval]]) {
|
||||
let swung = 0;
|
||||
for (let i = shown; i >= 0; i--) if (f.events[i][1] === who && f.events[i][2] !== "eat" && f.events[i][2] !== "wind") { swung = f.events[i][0]; break; }
|
||||
set(`${who}-swing`, (el) => { el.style.width = over || t < 0 ? "0%" : `${Math.min(100, (t - swung) / every * 100)}%`; });
|
||||
set(`${who}-swing`, (el) => { el.style.width = over || waiting ? "0%" : `${Math.min(100, (t - swung) / every * 100)}%`; });
|
||||
}
|
||||
set("log", (el) => {
|
||||
const key = `${a.start}:${shown}:${over}:${t < 0 ? Math.ceil(-t * 10) : ""}`;
|
||||
const key = `${a.start}:${shown}:${over}:${waiting ? Math.ceil(respawnIn * 10) : ""}`;
|
||||
if (el.dataset.key === key) return;
|
||||
el.dataset.key = key;
|
||||
const lines = f.events.slice(Math.max(0, shown - 5), shown + 1).reverse().map((e) => fightLine(e, f.enemy));
|
||||
if (over) lines.unshift(f.result === "won" ? `<li class="good"><b>${esc(f.enemy)} is beaten.</b> The next one is coming…</li>`
|
||||
: `<li class="bad"><b>You fall back.</b></li>`);
|
||||
el.innerHTML = t < 0 ? `<li class="good">Victory! Next enemy in ${(-t).toFixed(1)}s</li>`
|
||||
if (over && !waiting) lines.unshift(`<li class="bad"><b>You fall back.</b></li>`);
|
||||
el.innerHTML = waiting ? `<li class="good"><b>Victory!</b> ${respawnIn > 0.05 ? `Next enemy in ${respawnIn.toFixed(1)}s` : "Next enemy coming…"}</li>`
|
||||
: lines.join("") || `<li class="muted">Squaring up…</li>`;
|
||||
});
|
||||
}
|
||||
|
|
@ -1174,9 +1172,13 @@ function animate() {
|
|||
const pct = Math.min(1, (now - a.start) / a.duration);
|
||||
// In a fight the bar is the enemy's health (set by animateFight), not a timer.
|
||||
if (!a.fight) for (const el of document.querySelectorAll("[data-progress]")) el.style.width = (pct * 100) + "%";
|
||||
if (pct >= 1 && refreshedFor !== a.start) {
|
||||
refreshedFor = a.start;
|
||||
const key = `${a.start}:${a.duration}`;
|
||||
if (pct >= 1 && refreshedFor !== key) {
|
||||
refreshedFor = key;
|
||||
setTimeout(refresh, 150);
|
||||
} else if (a.fight && now - a.start > a.duration + (a.respawn || 1) && now - retriedAt > 2) {
|
||||
retriedAt = now; // the next enemy hasn't turned up yet: ask again
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
for (const el of document.querySelectorAll("[data-span]")) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue