Prayer: bones, prayer points, combat prayers, temple offerings
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
568e61799d
commit
278c5f4283
6 changed files with 296 additions and 25 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");
|
||||
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()}`);
|
||||
}
|
||||
|
||||
|
|
@ -318,6 +320,7 @@ function skillPage(id) {
|
|||
${fast ? `<span class="tag gold">${cls.icon} ${cls.name} bonus</span>` : ""}
|
||||
</div></div>`;
|
||||
if (id === "combat") html += combatPanel();
|
||||
if (id === "prayer") html += prayerPanel();
|
||||
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);
|
||||
|
|
@ -347,6 +350,33 @@ function isLocked(a) {
|
|||
return Boolean(why && (why.startsWith("Needs") || why.startsWith("Only")));
|
||||
}
|
||||
|
||||
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);
|
||||
const cards = C.prayers.map((p) => {
|
||||
const on = me.prayers.includes(p.id), locked = level < p.level;
|
||||
const full = !on && me.prayers.length >= C.max_prayers;
|
||||
return `<button class="prayer ${on ? "on" : ""} ${locked ? "locked" : ""}" data-pray="${p.id}" ${locked ? "disabled" : ""}
|
||||
title="${locked ? `Needs prayer level ${p.level}` : full ? `Only ${C.max_prayers} at once: turn one off first` : ""}">
|
||||
<span class="ico">${p.icon}</span>
|
||||
<span class="grow"><b>${esc(p.name)}</b><span class="small muted">${esc(p.text)}</span></span>
|
||||
<span class="small ${locked ? "why" : "muted"}">${locked ? `Lv ${p.level}` : `${p.cost} pt${p.cost > 1 ? "s" : ""}/fight`}</span>
|
||||
</button>`;
|
||||
}).join("");
|
||||
const temple = city.buildings.temple.level;
|
||||
return `<div class="cols">
|
||||
<div class="panel"><h2>Prayers · ${me.prayers.length} / ${C.max_prayers} active</h2>
|
||||
<div class="prayer-points">🙏 <b>${fmt(me.pp)}</b> prayer points${cost ? ` · ${cost} per fight, enough for about ${fmt(me.pp / cost)} fights` : ""}</div>
|
||||
<div class="prayers">${cards}</div>
|
||||
<p class="small muted">Active prayers work in every fight while you have the points to pay for them, and each point spent trains Prayer. Bury bones for more points.</p>
|
||||
</div>
|
||||
<div class="panel"><h2>Temple</h2>
|
||||
${temple ? `<p>Devotion <b>${fmt(city.devotion)}</b> · divine ward <b>+${city.ward}</b> defence for the next raid (up to +${15 * temple} at temple level ${temple}).</p>
|
||||
<p class="small muted">Offering bones builds devotion; it wards the city through the next raid, then starts again. The temple also adds ${temple} prayer point${temple > 1 ? "s" : ""} to every bone you bury.</p>`
|
||||
: `<p class="muted">No temple yet. Build one on the Town square to offer bones for a divine ward in raids, and to get more prayer points from bones.</p>`}
|
||||
</div></div>`;
|
||||
}
|
||||
|
||||
function toolPanel(skill) {
|
||||
const tool = S.me.equip[`tool_${skill}`];
|
||||
return `<div class="panel tool-panel">${tool
|
||||
|
|
@ -372,6 +402,8 @@ function combatPanel() {
|
|||
<div class="bar thick hp"><span 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">🙏 ${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">(not enough points: prayers are off)</b>`}` : `<span class="muted">No prayers active.</span>`}
|
||||
<button class="linkish" data-nav="skill:prayer">Prayers</button></p>
|
||||
<p class="small muted">Below half health you eat from your bank automatically.
|
||||
${me.edible.length ? "" : `<b class="warn">You have no food in your bank.</b>`}
|
||||
Out of food and badly hurt, you retreat: the fight is lost, but nothing else is.</p>
|
||||
|
|
@ -405,7 +437,8 @@ function actionCard(a) {
|
|||
const time = me.times[a.id];
|
||||
let io = "";
|
||||
if (Object.keys(a.inputs).length) {
|
||||
io += Object.entries(a.inputs).map(([id, q]) => itemChip(id, q, me.bank[id] || 0)).join("") + `<span class="arrow">→</span>`;
|
||||
io += Object.entries(a.inputs).map(([id, q]) => itemChip(id, q, me.bank[id] || 0)).join("")
|
||||
+ (Object.keys(a.outputs).length ? `<span class="arrow">→</span>` : "");
|
||||
}
|
||||
io += Object.entries(a.outputs).map(([id, q]) => itemChip(id, q)).join("");
|
||||
let extra = "";
|
||||
|
|
@ -416,6 +449,9 @@ function actionCard(a) {
|
|||
if (a.special === "sabotage") extra += `<div class="small muted">Each win weakens the coming raid by 3%, up to 30%.</div>`;
|
||||
}
|
||||
if (a.special === "recon") extra = `<div class="small muted">+${a.recon} recon on the next event</div>`;
|
||||
const temple = S.city.buildings.temple?.level || 0;
|
||||
if (a.special === "bury") extra = `<div class="small makes">🙏 +${a.pp + temple} prayer points</div>`;
|
||||
if (a.special === "offer") extra = `<div class="small makes">⛪ +${+(a.devotion * (1 + 0.25 * temple)).toFixed(1)} devotion for the city's ward</div>`;
|
||||
const made = Object.keys(a.outputs).map(itemStats).filter(Boolean);
|
||||
if (made.length) extra += `<div class="small makes">${made.join(" · ")}</div>`;
|
||||
return `
|
||||
|
|
@ -657,7 +693,7 @@ function favourSpends() {
|
|||
|
||||
function raidPrep(ev) {
|
||||
const city = S.city, me = S.me, d = city.defence;
|
||||
const names = { rallied: "Rallied militia (favour)", townsfolk: "Townsfolk with pitchforks", walls: "Walls", militia: "Militia (barracks)", weapons: "Swords in the armoury", shields: "Shields in the armoury", arrows: "Arrows", garrison: "Garrison" };
|
||||
const names = { ward: "Divine ward (temple)", rallied: "Rallied militia (favour)", townsfolk: "Townsfolk with pitchforks", walls: "Walls", militia: "Militia (barracks)", weapons: "Swords in the armoury", shields: "Shields in the armoury", arrows: "Arrows", garrison: "Garrison" };
|
||||
const reqs = (ev.requests || []).map((r) => {
|
||||
const mine = me.bank[r.item] || 0;
|
||||
return `<div class="req"><span>${item(r.item).icon}</span><span>${esc(r.label)} <span class="muted small">${fmt(r.have)} / ${fmt(r.want)}</span></span>
|
||||
|
|
@ -758,6 +794,7 @@ function characterSheet(uid) {
|
|||
<div class="stats">
|
||||
<div class="stat"><div class="k">Health</div><div class="v">${fmt(p.hp)} <small>/ ${p.max_hp}</small></div><div class="bar hp" style="margin-top:6px"><span style="width:${p.hp / p.max_hp * 100}%"></span></div></div>
|
||||
<div class="stat"><div class="k">Combat power</div><div class="v">${p.power}</div><div class="note">${guard} in the garrison</div></div>
|
||||
${mine ? `<div class="stat"><div class="k">Prayer points</div><div class="v">${fmt(p.pp)}</div><div class="note">${p.prayers.length ? p.prayers.map((id) => esc(C.prayers.find((x) => x.id === id).name)).join(", ") : "No prayers active"}</div></div>` : ""}
|
||||
<div class="stat"><div class="k">Armour</div><div class="v">${p.armour}</div><div class="note">${Math.round(p.reduction * 100)}% less damage</div></div>
|
||||
<div class="stat"><div class="k">Given to the city</div><div class="v favour">${fmt(p.donated)}</div><div class="note">${fmt(p.favour)} favour to spend</div></div>
|
||||
${mine ? `<div class="stat"><div class="k">Purse</div><div class="v gold">${fmt(p.gold)}</div><div class="note">${Object.keys(p.bank).length} / ${p.slots} bank slots</div></div>` : ""}
|
||||
|
|
@ -861,6 +898,11 @@ 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.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");
|
||||
return act("/api/prayers", { ids: on ? S.me.prayers.filter((x) => x !== d.pray) : [...S.me.prayers, d.pray] });
|
||||
}
|
||||
if (d.overflow) return act("/api/settings", { overflow: d.overflow });
|
||||
if (d.class) { pickedClass = d.class; render(); return; }
|
||||
if (d.tab) { tabs[d.tab] = d.group; remember(`tab.${d.tab}`, d.group); render(); return; }
|
||||
|
|
|
|||
|
|
@ -379,3 +379,10 @@ button.sheet-skill:hover { background: var(--panel-2); }
|
|||
.tab.on { background: var(--gold); border-color: var(--gold); color: var(--bg); }
|
||||
.tab.dim:not(.on) { opacity: .5; }
|
||||
.makes { color: var(--muted); }
|
||||
.prayers { display: flex; flex-direction: column; gap: 6px; margin: 10px 0; }
|
||||
.prayer { display: flex; align-items: center; gap: 10px; text-align: left; padding: 8px 10px; border-radius: 10px; border: 1px solid var(--line); background: var(--panel-2); }
|
||||
.prayer .ico { font-size: 22px; width: 28px; text-align: center; }
|
||||
.prayer .grow { display: flex; flex-direction: column; }
|
||||
.prayer.on { border-color: var(--favour); box-shadow: 0 0 0 1px var(--favour) inset; background: rgba(185, 140, 232, .12); }
|
||||
.prayer.locked { opacity: .45; cursor: not-allowed; }
|
||||
.prayer-points { font-size: 15px; }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue