Skill pages: potion and tool side by side with swaps; happenings as red and green banners with a fix button
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
29ed3780da
commit
89fd9b3c40
3 changed files with 54 additions and 29 deletions
|
|
@ -426,8 +426,7 @@ function skillPage(id) {
|
|||
if (id === "construction") html += S.city.construction ? siteBanner()
|
||||
: `<div class="panel muted">Nothing under construction.</div>`;
|
||||
if (id === "farming" && me.cls === "homesteader") html += plotsPanel();
|
||||
html += potionPanel(id);
|
||||
if (`tool_${id}` in me.equip) html += toolPanel(id);
|
||||
html += skillGear(id);
|
||||
if (id === "reconnaissance") html += `<div class="panel small muted">Raises intel on the coming threat. Higher levels open as it nears.</div>`;
|
||||
html += skillHappenings(id);
|
||||
const live = new Set(S.happenings.problems.map((p) => p.kind));
|
||||
|
|
@ -473,19 +472,32 @@ function plotsPanel() {
|
|||
}
|
||||
|
||||
// The skill's active potion, or the ones in the bank that could be drunk for it.
|
||||
function potionPanel(skill) {
|
||||
const me = S.me;
|
||||
// A skill's potion and tool side by side, each a slot with what you could switch to beside it.
|
||||
function skillGear(skill) {
|
||||
const me = S.me, cards = [];
|
||||
const kinds = Object.entries(C.items).filter(([, it]) => it.potion?.skill === skill);
|
||||
if (!kinds.length) return "";
|
||||
const active = me.potions[skill];
|
||||
const owned = kinds.filter(([id]) => me.bank[id]);
|
||||
const drink = owned.map(([id, it]) => `<button class="btn sm" data-potion="${id}" title="${esc(it.potion.text)}">${it.icon} ${esc(it.name)} (${fmt(me.bank[id])})</button>`).join("");
|
||||
return `<div class="panel tool-panel">${active
|
||||
? `${item(active.item).icon} <b>${esc(item(active.item).name)}</b> <span class="muted">${esc(item(active.item).potion.text)} · ${fmt(active.charges)} uses left${me.bank[active.item] ? `, ${fmt(me.bank[active.item])} more in the bank` : ""}</span>`
|
||||
: `<span class="muted">No potion.</span>`}
|
||||
${!active && drink ? `<span class="btns">Drink: ${drink}</span>` : ""}
|
||||
${!active && !drink ? `<span class="small muted">Brewed in Herblore.</span>` : ""}
|
||||
</div>`;
|
||||
if (kinds.length) {
|
||||
const active = me.potions[skill], it = active && item(active.item);
|
||||
const others = kinds.filter(([id]) => me.bank[id] && id !== active?.item).map(([id, p]) =>
|
||||
`<button class="doll-slot swap" data-potion="${id}" title="${esc(`${p.name}: ${p.potion.text}`)}">${p.icon}<span class="swap-qty">${fmt(me.bank[id])}</span></button>`).join("");
|
||||
cards.push(`<div class="panel gear-card">
|
||||
<span class="doll-slot ${it ? "" : "empty"}" title="${esc(it ? `${it.name}: ${it.potion.text}` : "No potion")}">${it ? it.icon : glyph("round-bottom-flask", "#98a1b4")}</span>
|
||||
<div class="gear-text">${it ? `<b>${esc(it.name)}</b><span class="muted small">${esc(it.potion.text)} · ${fmt(active.charges)} uses</span>`
|
||||
: `<span class="muted">No potion</span>`}</div>
|
||||
${others ? `<div class="swap-grid">${others}</div>` : ""}</div>`);
|
||||
}
|
||||
if (`tool_${skill}` in me.equip) {
|
||||
const id = me.equip[`tool_${skill}`], it = id && item(id);
|
||||
const others = Object.keys(me.bank).filter((x) => item(x).tool === skill).sort((a, b) => item(b).speed - item(a).speed).map((x) => {
|
||||
const [s, level] = item(x).req || [], why = s && me.levels[s] < level ? `Needs ${skillOf(s).name.toLowerCase()} ${level}` : "";
|
||||
return `<button class="doll-slot swap" data-equip="${x}" ${why ? "disabled" : ""} title="${esc([`${item(x).name}: ${Math.round(item(x).speed * 100)}% faster`, why].filter(Boolean).join(" · "))}">${item(x).icon}</button>`;
|
||||
}).join("");
|
||||
cards.push(`<div class="panel gear-card">
|
||||
<span class="doll-slot ${it ? "" : "empty"}" title="${esc(it ? it.name : "No tool")}">${it ? it.icon : skillOf(skill).icon}</span>
|
||||
<div class="gear-text">${it ? `<b>${esc(it.name)}</b><span class="muted small">${Math.round(it.speed * 100)}% faster</span>` : `<span class="muted">No tool</span>`}</div>
|
||||
${others || it ? `<div class="swap-grid">${others}${it ? `<button class="doll-slot swap off" data-unequip="tool_${skill}" title="Take it off">${glyph("cancel", "#98a1b4")}</button>` : ""}</div>` : ""}</div>`);
|
||||
}
|
||||
return cards.length ? `<div class="gear-row">${cards.join("")}</div>` : "";
|
||||
}
|
||||
|
||||
function prayerPanel() {
|
||||
|
|
@ -515,14 +527,6 @@ function prayerPanel() {
|
|||
</div></div>`;
|
||||
}
|
||||
|
||||
function toolPanel(skill) {
|
||||
const tool = S.me.equip[`tool_${skill}`];
|
||||
return `<div class="panel tool-panel">${tool
|
||||
? `${item(tool).icon} <b>${esc(item(tool).name)}</b> <span class="muted">${Math.round(item(tool).speed * 100)}% faster</span>
|
||||
<button class="btn sm" data-unequip="tool_${skill}">Unequip</button>`
|
||||
: `<span class="muted">No tool.</span>`}</div>`;
|
||||
}
|
||||
|
||||
// A damage type's icon: what an enemy attacks with, or (tinted) what it's vulnerable to or resists.
|
||||
function dmgIcon(type, how = "") {
|
||||
const t = C.damage_types[type], name = t.name.toLowerCase();
|
||||
|
|
@ -975,17 +979,22 @@ function happeningsPanel() {
|
|||
|
||||
// What's speeding up, slowing down or blocking this skill right now.
|
||||
function skillHappenings(skill) {
|
||||
const h = S.happenings, lines = [];
|
||||
const note = (icon, name, text) => lines.push(`<span>${icon} <b>${esc(name)}</b> <span class="muted">${esc(text)}</span></span>`);
|
||||
const h = S.happenings, banners = [];
|
||||
const touches = (src) => ["speed", "yield", "xp"].some((k) => src[k] && skill in src[k]) || (src.block && skill in src.block);
|
||||
if (touches(h.weather)) note(h.weather.icon, h.weather.name, h.weather.text);
|
||||
for (const e of h.effects) if (touches(e)) note(e.icon, e.name, e.text);
|
||||
const good = (src) => (src.speed?.[skill] ?? 1) < 1 || (src.yield?.[skill] ?? 1) > 1 || (src.xp?.[skill] ?? 1) > 1;
|
||||
const banner = (tone, icon, name, text, button = "") => banners.push(`<div class="banner ${tone} happening-banner">
|
||||
<span class="banner-ico">${icon}</span><div class="grow"><b>${esc(name)}</b><div class="small">${esc(text)}</div></div>${button}</div>`);
|
||||
if (touches(h.weather)) banner(good(h.weather) ? "good" : "bad", h.weather.icon, h.weather.name, h.weather.text);
|
||||
for (const e of h.effects) if (touches(e)) banner(good(e) ? "good" : "bad", e.icon, e.name, e.text);
|
||||
for (const p of h.problems) {
|
||||
if (!C.problems[p.kind] || !touches(C.problems[p.kind])) continue;
|
||||
const fix = p.action && C.actions.find((x) => x.id === p.action);
|
||||
note(p.icon, p.name, !fix ? p.text : fix.skill === skill ? "Clear it below." : `${skillOf(fix.skill).name} clears it (Town square).`);
|
||||
const button = fix ? (fix.skill === skill ? `<button class="btn primary" data-start="${fix.id}">${esc(fix.name)}</button>`
|
||||
: `<button class="btn" data-nav="skill:${fix.skill}">${skillOf(fix.skill).icon} ${esc(fix.name)}</button>`)
|
||||
: `<button class="btn" data-nav="town">Help on the Town square</button>`;
|
||||
banner("bad", p.icon, p.name, p.text, button);
|
||||
}
|
||||
return lines.length ? `<div class="panel tool-panel skill-happenings">${lines.join("")}</div>` : "";
|
||||
return banners.join("");
|
||||
}
|
||||
|
||||
function buildingCard(key, b) {
|
||||
|
|
|
|||
|
|
@ -555,3 +555,19 @@ button.sheet-skill:hover { background: var(--panel-2); }
|
|||
.doll-slot.starving { border-color: rgba(226, 100, 92, .7); background: rgba(226, 100, 92, .12); }
|
||||
.doll-slot.starving .gi { opacity: .9; }
|
||||
@media (max-width: 520px) { .doll-col { justify-items: center; } }
|
||||
|
||||
/* A skill's potion and tool, side by side */
|
||||
.gear-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); gap: 14px; margin-bottom: 14px; }
|
||||
.gear-row .panel { margin: 0; }
|
||||
.gear-card { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 12px 14px; }
|
||||
.gear-card > .doll-slot { cursor: default; flex: none; }
|
||||
.gear-text { display: grid; gap: 2px; min-width: 0; flex: 1; }
|
||||
.gear-card .swap-grid { margin-left: auto; }
|
||||
/* Happenings that touch this skill */
|
||||
.happening-banner { margin-bottom: 14px; align-items: center; }
|
||||
.happening-banner .banner-ico { font-size: 30px; }
|
||||
.happening-banner b { font-size: 15px; }
|
||||
.banner.good { border-color: rgba(95, 174, 90, .55); background: rgba(95, 174, 90, .08); }
|
||||
.happening-banner { flex-wrap: wrap; }
|
||||
.happening-banner .grow { min-width: min(260px, 70%); }
|
||||
.happening-banner > .btn { margin-left: auto; }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue