Herblore: herbs, potions with charges, one per skill
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
278c5f4283
commit
090b202188
5 changed files with 245 additions and 19 deletions
|
|
@ -137,6 +137,8 @@ function showSummary(sum) {
|
|||
if (sold.length) toast(`📦 Bank full: sold ${sold.map(([id, q]) => `${fmt(q)} ${item(id).name.toLowerCase()}`).join(", ")} for ${fmt(sum.sold_gold)} gold`);
|
||||
for (const [skill, level] of sum.levels) toast(`🎉 ${skillOf(skill).name} level ${level}!`, "level");
|
||||
if (sum.stopped) toast(`Stopped: ${sum.stopped}`, "bad");
|
||||
for (const id of sum.potion_out || []) toast(`🧪 Your ${item(id).name.toLowerCase()} ran out`, "bad");
|
||||
for (const id of Object.keys(sum.potions_drunk || {})) toast(`🧪 Drank another ${item(id).name.toLowerCase()}`);
|
||||
if (sum.prayer_out) toast("🙏 Out of prayer points: your prayers have gone quiet", "bad");
|
||||
if (sum.second_wind) toast("🌬️ Second Wind! Back on your feet at half health", "level");
|
||||
if (sum.resumed) toast(`Back to ${C.actions.find((x) => x.id === sum.resumed).name.toLowerCase()}`);
|
||||
|
|
@ -321,6 +323,7 @@ function skillPage(id) {
|
|||
</div></div>`;
|
||||
if (id === "combat") html += combatPanel();
|
||||
if (id === "prayer") html += prayerPanel();
|
||||
html += potionPanel(id);
|
||||
if (`tool_${id}` in me.equip) html += toolPanel(id);
|
||||
if (id === "reconnaissance") html += `<div class="panel small muted">Every ${8} reconnaissance points on an upcoming event raises its intel by one level, on top of what the spymaster's guild provides. The council & threats page shows the progress.</div>`;
|
||||
const actions = C.actions.filter((a) => a.skill === id);
|
||||
|
|
@ -333,13 +336,10 @@ function skillPage(id) {
|
|||
const bar = groups.map((g) => `<button class="tab ${g === current ? "on" : ""} ${open(g) ? "" : "dim"}" data-tab="${id}" data-group="${esc(g)}">${esc(g)}</button>`).join("");
|
||||
return html + `<div class="tabs">${bar}</div><div class="grid">${actions.filter((a) => a.group === current).map(actionCard).join("")}</div>`;
|
||||
}
|
||||
let group = null, cards = "";
|
||||
for (const a of actions) {
|
||||
if (a.group && a.group !== group) {
|
||||
group = a.group;
|
||||
cards += `<div class="section-title grid-head">${esc(group)}</div>`;
|
||||
}
|
||||
cards += actionCard(a);
|
||||
// A few groups: each under its own heading, in the order they first appear.
|
||||
let cards = actions.filter((a) => !a.group).map(actionCard).join("");
|
||||
for (const g of groups) {
|
||||
cards += `<div class="section-title grid-head">${esc(g)}</div>` + actions.filter((a) => a.group === g).map(actionCard).join("");
|
||||
}
|
||||
return html + `<div class="grid">${cards}</div>`;
|
||||
}
|
||||
|
|
@ -350,6 +350,22 @@ function isLocked(a) {
|
|||
return Boolean(why && (why.startsWith("Needs") || why.startsWith("Only")));
|
||||
}
|
||||
|
||||
// The skill's active potion, or the ones in the bank that could be drunk for it.
|
||||
function potionPanel(skill) {
|
||||
const me = S.me;
|
||||
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">Herblore can brew ${kinds.length === 1 ? "one" : "some"} for ${skill === "combat" ? "fighting" : esc(skillOf(skill).name.toLowerCase())}.</span>` : ""}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function prayerPanel() {
|
||||
const me = S.me, city = S.city, level = me.levels.prayer;
|
||||
const cost = C.prayers.filter((p) => me.prayers.includes(p.id)).reduce((sum, p) => sum + p.cost, 0);
|
||||
|
|
@ -424,6 +440,7 @@ function itemStats(id) {
|
|||
if (it.armour) parts.push(`+${it.armour} armour`);
|
||||
if (it.tool) parts.push(`${Math.round(it.speed * 100)}% faster ${skillOf(it.tool).name.toLowerCase()}`);
|
||||
if (it.req && it.req[1] > 1) parts.push(`needs ${skillOf(it.req[0]).name.toLowerCase()} ${it.req[1]}`);
|
||||
if (it.potion) parts.push(`${it.potion.text}, ${C.potion_charges} uses`);
|
||||
return parts.join(", ");
|
||||
}
|
||||
|
||||
|
|
@ -514,6 +531,9 @@ function itemPanel(id) {
|
|||
<h3>${esc(it.name)}</h3>
|
||||
<div class="muted small">You have ${fmt(have)} · worth ${price % 1 ? price.toFixed(1) : price} gold each${merchant ? " (merchant in town!)" : ""}</div>
|
||||
${it.slot ? equipRow(id, it) : ""}
|
||||
${it.potion ? `<div class="row"><div class="small">${esc(it.potion.text)} for ${C.potion_charges} ${it.potion.skill === "combat" ? "fights" : "actions"}.</div>
|
||||
<button class="btn gold" data-potion="${id}">Drink</button>
|
||||
${S.me.potions[it.potion.skill] && S.me.potions[it.potion.skill].item !== id ? `<div class="small why">Replaces your ${esc(item(S.me.potions[it.potion.skill].item).name.toLowerCase())} (${S.me.potions[it.potion.skill].charges} uses left)</div>` : ""}</div>` : ""}
|
||||
${it.heal ? `<div class="row"><div class="small muted">Heals ${it.heal} in fights.</div>
|
||||
${S.me.food === id ? `<span class="tag good">Your fighting food</span>` : `<button class="btn sm" data-food="${id}">Eat this first in fights</button>`}</div>` : ""}
|
||||
${it.food ? `<div class="row small muted">Feeds ${it.food} townsfolk for two days in the city granary.</div>` : ""}
|
||||
|
|
@ -898,6 +918,7 @@ document.addEventListener("click", (e) => {
|
|||
if (d.perk) return act("/api/perk", { perk: d.perk });
|
||||
if (d.favour) return act("/api/favour", { kind: d.favour });
|
||||
if (d.food) return act("/api/food", { item: d.food });
|
||||
if (d.potion) return act("/api/potion", { item: d.potion });
|
||||
if (d.pray) {
|
||||
const on = S.me.prayers.includes(d.pray);
|
||||
if (!on && S.me.prayers.length >= C.max_prayers) return toast(`Only ${C.max_prayers} prayers at once: turn one off first`, "bad");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue