Combat in one frame: you, prayers and gear left; the enemy, its traits and the log right. Fight chip in enemy red
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
2f2681ba17
commit
308c3a3c01
2 changed files with 67 additions and 33 deletions
|
|
@ -360,7 +360,7 @@ function topbar() {
|
|||
let chips = "";
|
||||
if (S.me?.action) {
|
||||
const action = C.actions.find((a) => a.id === (S.me.action.growing || S.me.action.id));
|
||||
chips += `<button class="chip act" data-nav="skill:${action.skill}">${skillOf(action.skill).icon} <span class="label">${esc(action.name)}</span><span class="bar mini"><span data-progress></span></span></button>`;
|
||||
chips += `<button class="chip act" data-nav="skill:${action.skill}">${skillOf(action.skill).icon} <span class="label">${esc(action.name)}</span><span class="bar mini ${S.me.action.fight ? "foe" : ""}"><span data-progress></span></span></button>`;
|
||||
} else if (S.me) {
|
||||
chips += `<span class="chip muted">Idle${S.me.stopped ? ` · ${esc(S.me.stopped)}` : ""}</span>`;
|
||||
}
|
||||
|
|
@ -421,7 +421,7 @@ function skillPage(id) {
|
|||
</div>
|
||||
${fast ? `<span class="tag gold">${cls.icon} ${cls.name} bonus</span>` : ""}
|
||||
</div></div>`;
|
||||
if (id === "combat") html += fightPanel() + combatPanel();
|
||||
if (id === "combat") html += combatFrame();
|
||||
if (id === "prayer") html += prayerPanel();
|
||||
if (id === "construction") html += S.city.construction ? siteBanner()
|
||||
: `<div class="panel muted">Nothing under construction.</div>`;
|
||||
|
|
@ -550,21 +550,45 @@ function variantRows(e, o) {
|
|||
}).join("")}</div>`;
|
||||
}
|
||||
|
||||
// The fight in progress, replayed from the server's swing-by-swing record.
|
||||
function fightPanel() {
|
||||
const f = S.me.action?.fight;
|
||||
if (!f) return "";
|
||||
const side = (who, name, foe) => `<div class="fighter">
|
||||
<div class="fighter-name"><b>${esc(name)}</b>${foe ? `<span class="v-res">${resistIcons(foe)}</span>` : ""}<span class="small muted" data-f="${who}-hp"></span></div>
|
||||
<div class="bar hp ${who}"><span data-f="${who}-bar"></span></div>
|
||||
<div class="bar swing"><span data-f="${who}-swing"></span></div>
|
||||
// Everything about fighting in one frame. Left: you, your prayers (switch them mid-fight) and your
|
||||
// gear (swap it mid-fight). Right: what you're fighting, what it attacks with, what it's weak to and
|
||||
// resists (your weapon's type outlined), and the fight as it plays out.
|
||||
function combatFrame() {
|
||||
const me = S.me, a = me.action, f = a?.fight;
|
||||
const encounter = f && C.actions.find((x) => x.id === a.id);
|
||||
const bars = (who) => `<div class="bar hp ${who}"><span data-f="${who}-bar"${who === "you" ? ` data-live-hpbar style="width:${me.hp / me.max_hp * 100}%"` : ""}></span></div>
|
||||
<div class="bar swing"><span data-f="${who}-swing"></span></div>`;
|
||||
const you = `<div class="fighter">
|
||||
<div class="fighter-name"><b>${esc(me.name)}</b><span class="small muted">${HEART} <span data-f="you-hp">${fmt(me.hp)} / ${me.max_hp}</span></span></div>
|
||||
${bars("you")}
|
||||
${me.edible.length ? "" : `<div class="small warn">No food in your bank.</div>`}
|
||||
</div>`;
|
||||
return `<div class="panel fight" data-fight="${S.me.action.start}">
|
||||
<div class="fighters">${side("you", S.me.name)}<span class="vs">⚔️</span>${side("foe", f.enemy, f.foe)}</div>
|
||||
<ul class="fight-log" data-f="log"></ul>
|
||||
let foe = `<div class="foe-idle muted">Not fighting.</div>`;
|
||||
if (f) {
|
||||
const mine = C.styles[(me.equip.weapon && item(me.equip.weapon).style) || "sword"].damage;
|
||||
const pills = (label, kinds, how) => kinds?.length ? `<div class="trait"><span class="muted small">${label}</span>
|
||||
${kinds.map((k) => `<span class="pill ${how} ${k === mine && how !== "attack" ? "yours" : ""}">${dmgIcon(k, how)}${C.damage_types[k].name}</span>`).join("")}</div>` : "";
|
||||
foe = `<div class="foe-head"><span class="foe-ico">${encounter?.icon || ""}</span>
|
||||
<div class="fighter"><div class="fighter-name"><b>${esc(f.enemy)}</b><span class="small muted" data-f="foe-hp"></span></div>${bars("foe")}</div></div>
|
||||
${f.foe ? `<div class="traits">${pills("Attacks with", f.foe.attack, "attack")}${pills("Weak to", f.foe.vulnerable, "vuln")}${pills("Resists", f.foe.resists, "resist")}</div>` : ""}
|
||||
<ul class="fight-log" data-f="log"></ul>`;
|
||||
}
|
||||
return `<div class="panel fight combat-frame" ${f ? `data-fight="${a.start}"` : ""}>
|
||||
<div class="you-side">${you}${prayerBar(me)}${paperDoll(me, true)}</div>
|
||||
<div class="foe-side">${foe}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// Prayers you can switch on and off between swings, and the points left to pay for them.
|
||||
function prayerBar(me) {
|
||||
const level = me.levels.prayer;
|
||||
const buttons = C.prayers.filter((p) => p.level <= level).map((p) => {
|
||||
const on = me.prayers.includes(p.id);
|
||||
return `<button class="pray-btn ${on ? "on" : ""}" data-pray="${p.id}" title="${esc(`${p.name}: ${p.text} · ${p.cost} pt${p.cost > 1 ? "s" : ""}/fight`)}">${p.icon}</button>`;
|
||||
}).join("");
|
||||
return `<div class="prayer-bar"><span class="pp ${me.prayers.length && !me.blessing.cost ? "warn" : ""}" title="Prayer points">${skillOf("prayer").icon} ${fmt(me.pp)}</span>${buttons}</div>`;
|
||||
}
|
||||
|
||||
function fightLine(e, enemy) {
|
||||
const [, who, kind, amount, , , food] = e;
|
||||
const type = kind === "hit" || kind === "crit" ? (C.damage_types[food] ? ` ${dmgIcon(food)}` : "") : "";
|
||||
|
|
@ -617,24 +641,6 @@ function animateFight(now) {
|
|||
});
|
||||
}
|
||||
|
||||
function combatPanel() {
|
||||
const me = S.me;
|
||||
const foods = me.edible.map((id) => `<option value="${id}" ${me.food === id ? "selected" : ""}>${esc(item(id).name)} (heals ${item(id).heal}, ${fmt(me.bank[id])} left)</option>`).join("");
|
||||
const hpPct = me.hp / me.max_hp * 100;
|
||||
return `<div class="cols">
|
||||
<div class="panel"><h2>Health</h2>
|
||||
<div class="hp-line"><b>${HEART} <span data-live-hp>${fmt(me.hp)}</span> / ${me.max_hp}</b><span class="muted small">recovers ${Math.round(C.regen_per_hour * 100)}% an hour</span></div>
|
||||
<div class="bar thick hp"><span data-live-hpbar style="width:${hpPct}%"></span></div>
|
||||
<div class="row" style="margin-top:12px"><label class="small muted">Food in fights
|
||||
<select data-food-pick>${`<option value="">Cheapest first</option>` + foods}</select></label></div>
|
||||
<p class="small">${skillOf("prayer").icon} ${me.prayers.length ? `${me.prayers.map((id) => esc(C.prayers.find((p) => p.id === id).name)).join(" + ")} · ${fmt(me.pp)} points${me.blessing.cost ? "" : ` <b class="warn">(out of points)</b>`}` : `<span class="muted">No prayers active.</span>`}
|
||||
<button class="linkish" data-nav="skill:prayer">Prayers</button></p>
|
||||
<p class="small muted">Eats below half HP, retreats when out of food.
|
||||
${me.edible.length ? "" : `<b class="warn">No food in your bank.</b>`}</p>
|
||||
</div>
|
||||
${equipmentPanel(me, true, false)}</div>`;
|
||||
}
|
||||
|
||||
// Power, armour and ward, and the armour's weight.
|
||||
function gearStats(it) {
|
||||
const stats = [it.power && `+${it.power} power`, it.armour && `+${it.armour} armour`, it.ward && `+${it.ward} ward`];
|
||||
|
|
@ -1439,7 +1445,6 @@ document.addEventListener("click", (e) => {
|
|||
});
|
||||
|
||||
document.addEventListener("change", (e) => {
|
||||
if (e.target.matches("[data-food-pick]")) act("/api/food", { item: e.target.value || null });
|
||||
});
|
||||
|
||||
$("#modal").addEventListener("click", (e) => { if (e.target.id === "modal") e.target.hidden = true; });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue