Shorter descriptions throughout
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
5898e2006f
commit
61d8b2fce3
2 changed files with 146 additions and 153 deletions
175
static/app.js
175
static/app.js
|
|
@ -150,8 +150,8 @@ function showSummary(sum) {
|
|||
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.prayer_out) toast("🙏 Out of prayer points", "bad");
|
||||
if (sum.second_wind) toast("🌬️ Second Wind! Back to half HP", "level");
|
||||
if (sum.resumed) toast(`Back to ${C.actions.find((x) => x.id === sum.resumed).name.toLowerCase()}`);
|
||||
}
|
||||
|
||||
|
|
@ -169,8 +169,8 @@ function awayModal(sum) {
|
|||
${sum.gold ? `<p class="gold">+${fmt(sum.gold)} gold</p>` : ""}
|
||||
${xp ? `<ul>${xp}</ul>` : ""}
|
||||
${levels ? `<ul>${levels}</ul>` : ""}
|
||||
${sold ? `<p class="muted small">Your bank was full, so this was sold for ${fmt(sum.sold_gold)} gold:</p><ul>${sold}</ul>` : ""}
|
||||
${spilled ? `<p class="muted small">Your bank was full, so this went to the city stockpile (for half the favour of giving it yourself):</p><ul>${spilled}</ul>` : ""}
|
||||
${sold ? `<p class="muted small">Bank full, sold for ${fmt(sum.sold_gold)} gold:</p><ul>${sold}</ul>` : ""}
|
||||
${spilled ? `<p class="muted small">Bank full, sent to the stockpile:</p><ul>${spilled}</ul>` : ""}
|
||||
${sum.stopped ? `<p class="tag bad">Stopped: ${esc(sum.stopped)}</p>` : ""}
|
||||
<p><button class="btn primary" data-close>Back to work</button></p>`);
|
||||
}
|
||||
|
|
@ -230,7 +230,7 @@ function sidebar() {
|
|||
<div class="me">
|
||||
<div class="who">${esc(me.name)}</div>
|
||||
<div class="cls">${cls.icon} ${cls.name} · ${HEART} <span data-live-hp>${fmt(me.hp)}</span>/${me.max_hp}</div>
|
||||
<div class="purse"><span class="gold" title="Gold: from fights and selling. Fund buildings with it or give it to the treasury">${COIN} ${fmt(me.gold)}</span><span class="favour" title="Favour: earned by giving to the city. Spend it on the Council page, or on extra bank slots">${FAVOUR} ${fmt(me.favour)}</span></div>
|
||||
<div class="purse"><span class="gold" title="Gold">${COIN} ${fmt(me.gold)}</span><span class="favour" title="Favour: earned by donating">${FAVOUR} ${fmt(me.favour)}</span></div>
|
||||
</div>
|
||||
<div class="navhead">City</div>
|
||||
${nav("town", "🏘️", "Town square", `${S.city.population}`, "", `Population ${S.city.population}`)}
|
||||
|
|
@ -342,11 +342,11 @@ function skillPage(id) {
|
|||
if (id === "combat") html += fightPanel() + combatPanel();
|
||||
if (id === "prayer") html += prayerPanel();
|
||||
if (id === "construction") html += S.city.construction ? siteBanner()
|
||||
: `<div class="panel muted">Nothing is being built right now. Fund a building on the Town square and its site opens here.</div>`;
|
||||
: `<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);
|
||||
if (id === "reconnaissance") html += `<div class="panel small muted">Reconnaissance on a coming threat raises its intel. Each level takes more than the last, a bigger band takes longer to read, and the sharpest news (the exact day and their camp) only comes in the last hours before they strike. The spymaster's guild adds to every point and gathers some on its own. The council & threats page shows the progress.</div>`;
|
||||
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));
|
||||
const listed = C.actions.filter((a) => a.skill === id && (a.special !== "problem" || live.has(a.problem)));
|
||||
|
|
@ -384,9 +384,9 @@ function isLocked(a) {
|
|||
function plotsPanel() {
|
||||
const a = S.me.action, farming = a && C.actions.find((x) => x.id === a.id)?.skill === "farming";
|
||||
const name = (id) => esc(C.actions.find((x) => x.id === id).name);
|
||||
const status = !farming ? `<span class="muted">Start a crop to plant your first plot.</span>`
|
||||
: a.plot2 ? `Plot 1: <b>${name(a.id)}</b> · Plot 2: <b>${name(a.plot2)}</b> <span class="muted">· harvests alternate, together 20% faster than one crop</span>`
|
||||
: `Plot 1: <b>${name(a.id)}</b> · Plot 2: <span class="muted">empty, so you're focused: 30% faster. Use "Plot 2" on another crop to grow both.</span>`;
|
||||
const status = !farming ? `<span class="muted">Start a crop to plant plot 1.</span>`
|
||||
: a.plot2 ? `Plot 1: <b>${name(a.id)}</b> · Plot 2: <b>${name(a.plot2)}</b> <span class="muted">· alternating, 20% faster</span>`
|
||||
: `Plot 1: <b>${name(a.id)}</b> · Plot 2: <span class="muted">empty · focused, 30% faster</span>`;
|
||||
return `<div class="panel tool-panel"><span>🌱 ${status}</span></div>`;
|
||||
}
|
||||
|
||||
|
|
@ -402,7 +402,7 @@ function potionPanel(skill) {
|
|||
? `${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>` : ""}
|
||||
${!active && !drink ? `<span class="small muted">Brewed in Herblore.</span>` : ""}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
|
|
@ -413,7 +413,7 @@ function prayerPanel() {
|
|||
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` : ""}">
|
||||
title="${locked ? `Needs prayer level ${p.level}` : full ? `Max ${C.max_prayers} active` : ""}">
|
||||
<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>
|
||||
|
|
@ -422,14 +422,14 @@ function prayerPanel() {
|
|||
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">${skillOf("prayer").icon} <b>${fmt(me.pp)}</b> prayer points${cost ? ` · ${cost} per fight, enough for about ${fmt(me.pp / cost)} fights` : ""}</div>
|
||||
<div class="prayer-points">${skillOf("prayer").icon} <b>${fmt(me.pp)}</b> prayer points${cost ? ` · ${cost} per fight (~${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>
|
||||
<p class="small muted">Prayers cost points each fight. Bury bones for more.</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>`}
|
||||
${temple ? `<p>Devotion <b>${fmt(city.devotion)}</b> · ward <b>+${city.ward}</b> defence (max +${15 * temple})</p>
|
||||
<p class="small muted">Offerings ward the next raid. +${temple} prayer point${temple > 1 ? "s" : ""} per bone buried.</p>`
|
||||
: `<p class="muted">No temple yet.</p>`}
|
||||
</div></div>`;
|
||||
}
|
||||
|
||||
|
|
@ -438,7 +438,7 @@ function toolPanel(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. A smith can make you one that speeds this up.</span>`}</div>`;
|
||||
: `<span class="muted">No tool.</span>`}</div>`;
|
||||
}
|
||||
|
||||
// What a monster is weak to and shrugs off, and how your weapon fares against it.
|
||||
|
|
@ -525,13 +525,13 @@ function animateFight(now) {
|
|||
function styleNote(me) {
|
||||
const weapon = me.equip.weapon, styleId = (weapon && item(weapon).style) || "sword", st = C.styles[styleId];
|
||||
const own = C.class_style[me.cls], traits = {
|
||||
sword: `a swing every ${st.interval}s${me.equip.shield ? `, and your shield blocks ${Math.round(C.block_chance * 100)}% of hits` : ""}`,
|
||||
hammer: `slow (${st.interval}s) heavy blows, twice the crits`,
|
||||
staff: `a cast every ${st.interval}s that rarely misses and goes through armour`,
|
||||
bow: `fast (${st.interval}s), and you shoot first`,
|
||||
sword: `${st.interval}s${me.equip.shield ? `, shield blocks ${Math.round(C.block_chance * 100)}%` : ""}`,
|
||||
hammer: `${st.interval}s, double crits`,
|
||||
staff: `${st.interval}s, accurate, ignores armour`,
|
||||
bow: `${st.interval}s, shoots first`,
|
||||
};
|
||||
return `<p class="small">Fighting with ${weapon ? `a ${esc(st.name)}` : "your fists"}: ${traits[styleId]}.
|
||||
${own === styleId ? `<b class="good">Your trade's weapon: +15% damage.</b>` : `As a ${esc(C.classes[me.cls].name.toLowerCase())} you hit 15% harder with a ${esc(C.styles[own].name)}.`}</p>`;
|
||||
return `<p class="small">${weapon ? st.name[0].toUpperCase() + st.name.slice(1) : "Fists"}: ${traits[styleId]}.
|
||||
${own === styleId ? `<b class="good">Trade weapon: +15% damage.</b>` : `Trade weapon: ${esc(C.styles[own].name)} (+15%).`}</p>`;
|
||||
}
|
||||
|
||||
function combatPanel() {
|
||||
|
|
@ -551,15 +551,14 @@ function combatPanel() {
|
|||
<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">(not enough points: prayers are off)</b>`}` : `<span class="muted">No prayers active.</span>`}
|
||||
<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">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>
|
||||
<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>
|
||||
<div class="panel"><h2>Equipment</h2>
|
||||
<div class="gear">${slot("weapon", "weapon")}${slot("shield", "shield")}${slot("helmet", "helmet")}${slot("body", "body armour")}${slot("legs", "leg armour")}</div>
|
||||
<p class="small muted">Power ${me.power} (level + weapon + shield + half your armour) · armour ${me.armour} takes ${Math.round(me.reduction * 100)}% off the damage you take. Equip gear from the bank.</p>
|
||||
<p class="small muted">Power ${me.power} · armour ${me.armour} (-${Math.round(me.reduction * 100)}% damage)</p>
|
||||
${styleNote(me)}
|
||||
</div></div>`;
|
||||
}
|
||||
|
|
@ -569,20 +568,20 @@ function itemStats(id) {
|
|||
const it = item(id);
|
||||
const parts = [];
|
||||
if (it.heal) parts.push(`❤️ Heals ${it.heal}`);
|
||||
if (it.food) parts.push(`feeds ${it.food} ${it.food === 1 ? "person" : "people"} for two days`);
|
||||
if (it.food) parts.push(`feeds ${it.food} for 2 days`);
|
||||
if (it.power) parts.push(`+${it.power} power`);
|
||||
if (it.style) {
|
||||
const st = C.styles[it.style], trade = Object.entries(C.class_style).find(([, s]) => s === it.style)?.[0];
|
||||
parts.push(`${it.two_handed ? "two-handed " : ""}${st.name}, a swing every ${st.interval}s${trade ? ` (the ${C.classes[trade].name.toLowerCase()}'s weapon)` : ""}`);
|
||||
parts.push(`${it.two_handed ? "two-handed " : ""}${st.name}, ${st.interval}s${trade ? ` (${C.classes[trade].name.toLowerCase()})` : ""}`);
|
||||
}
|
||||
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`);
|
||||
if (it.trophy) parts.push(`An expedition trophy: stronger weapons will need these`);
|
||||
if (it.trophy) parts.push(`Trophy for stronger weapons`);
|
||||
if (it.fitting) {
|
||||
const f = C.fittings.find((x) => x.id === id), done = S.city.buildings[f.building].fittings.includes(id);
|
||||
parts.push(`${C.buildings[f.building].icon} For the ${C.buildings[f.building].name.toLowerCase()} (Lv ${f.level}): ${f.effect}${done ? " · already fitted" : ""}`);
|
||||
parts.push(`${C.buildings[f.building].icon} ${C.buildings[f.building].name} Lv ${f.level}: ${f.effect}${done ? " · fitted" : ""}`);
|
||||
}
|
||||
return parts.join(", ");
|
||||
}
|
||||
|
|
@ -609,23 +608,22 @@ function actionCard(a) {
|
|||
if (a.enemy) {
|
||||
const o = me.fight_odds[a.id], e = a.enemy, pct = (x) => `${Math.round(x * 100)}%`;
|
||||
extra = `<div class="small">${HEART} ${fmt(e.hp)} HP · hits up to <b>${o.their_max}</b> every ${e.interval}s${e.boss ? ` · <span class="tag warn">Boss</span>` : ""}</div>
|
||||
<div class="small">You land <b>${pct(o.hit)}</b> of swings (up to ${o.max_hit}) · it lands <b>${pct(o.their_hit)}</b></div>
|
||||
<div class="small">About <b>${dur(o.kill_time)}</b> a kill, <b>${Math.round(o.damage)}</b> damage taken${a.gold ? ` · ${a.gold} gold` : ""}</div>
|
||||
<div class="small">You hit <b>${pct(o.hit)}</b> (max ${o.max_hit}) · it hits <b>${pct(o.their_hit)}</b></div>
|
||||
<div class="small">~<b>${dur(o.kill_time)}</b> a kill · <b>${Math.round(o.damage)}</b> damage taken${a.gold ? ` · ${a.gold} gold` : ""}</div>
|
||||
${weaknessLine(e.kind, o)}`;
|
||||
if (a.drops) io += a.drops.map(([id, q, odds]) => `<span class="item-chip" title="${esc(item(id).name)}: ${Math.round(odds * 100)}% chance">${item(id).icon} ${q} <span class="have">${Math.round(odds * 100)}%</span></span>`).join("");
|
||||
if (a.special === "sabotage") extra += `<div class="small muted">Each win weakens the coming raid by 3%, up to 30%.</div>`;
|
||||
if (a.special === "sabotage") extra += `<div class="small muted">-3% raid strength per win (max 30%)</div>`;
|
||||
}
|
||||
if (a.special === "drill") {
|
||||
const s = S.favours.sergeant, boost = s.boost;
|
||||
extra = !s.needed ? `<div class="small muted">Fighters drill the townsfolk while a raid is coming. A fully drilled militia adds one defence per townsperson.</div>`
|
||||
: `<div class="small makes">${skillOf("combat").icon} +${+me.drill_per_action.toFixed(2)} drill · ${fmt(s.points)} / ${fmt(s.needed)} points, worth ${fmt(s.defence)} of ${fmt(s.cap)} defence${boost ? ` <b class="boosted">×2 drill and XP for the next ${fmt(boost)}</b>` : ""}</div>
|
||||
<div class="small muted">A bigger band needs more drilling. A fully drilled militia adds one defence per townsperson.</div>`;
|
||||
extra = !s.needed ? `<div class="small muted">Only while a raid is coming.</div>`
|
||||
: `<div class="small makes">${skillOf("combat").icon} +${+me.drill_per_action.toFixed(2)} drill · ${fmt(s.points)} / ${fmt(s.needed)} points, worth ${fmt(s.defence)} of ${fmt(s.cap)} defence${boost ? ` <b class="boosted">×2 drill and XP for the next ${fmt(boost)}</b>` : ""}</div>`;
|
||||
}
|
||||
if (a.special === "recon") {
|
||||
const boost = S.favours.informant.boost;
|
||||
const ev = S.upcoming, full = ev && ev.recon >= ev.recon_full;
|
||||
extra = full ? `<div class="small muted">The coming threat is fully scouted: this only trains the skill for now.</div>`
|
||||
: `<div class="small makes">${skillOf("reconnaissance").icon} +${+me.recon_gain[a.id].toFixed(2)} recon on the coming threat${boost ? ` <b class="boosted">×2 recon and XP for the next ${fmt(boost)}</b>` : ""}</div>`;
|
||||
extra = full ? `<div class="small muted">Fully scouted: XP only.</div>`
|
||||
: `<div class="small makes">${skillOf("reconnaissance").icon} +${+me.recon_gain[a.id].toFixed(2)} recon${boost ? ` <b class="boosted">×2 recon and XP for the next ${fmt(boost)}</b>` : ""}</div>`;
|
||||
}
|
||||
const temple = S.city.buildings.temple?.level || 0;
|
||||
if (a.special === "labour") {
|
||||
|
|
@ -633,7 +631,7 @@ function actionCard(a) {
|
|||
extra = `<div class="small makes">${skillOf("construction").icon} +${me.labour_per_action} labour per action${boost ? ` <b class="boosted">×2 labour and XP for the next ${fmt(boost)}</b>` : ""}</div>`;
|
||||
}
|
||||
if (a.special === "bury") extra = `<div class="small makes">${skillOf("prayer").icon} +${a.pp + temple} prayer points</div>`;
|
||||
if (a.special === "offer") extra = `<div class="small makes">${C.buildings.temple.icon} +${+(a.devotion * (1 + 0.25 * temple)).toFixed(1)} devotion for the city's ward</div>`;
|
||||
if (a.special === "offer") extra = `<div class="small makes">${C.buildings.temple.icon} +${+(a.devotion * (1 + 0.25 * temple)).toFixed(1)} devotion</div>`;
|
||||
const made = Object.keys(a.outputs).map(itemStats).filter(Boolean);
|
||||
if (made.length) extra += `<div class="small makes">${made.join(" · ")}</div>`;
|
||||
return `
|
||||
|
|
@ -652,8 +650,8 @@ function actionCard(a) {
|
|||
<span class="why">${!active && !second && why ? esc(why) : ""}</span>
|
||||
${second ? `<button class="btn" data-plot="">Clear plot 2</button>`
|
||||
: active ? `<button class="btn danger" data-stop>Stop</button>`
|
||||
: `<span class="btns"><button class="btn" data-once="${a.id}" ${why ? "disabled" : ""} title="Do this once, then go back to what you were doing">${a.enemy ? "Fight once" : Object.keys(a.outputs).length ? "Make one" : "Once"}</button>
|
||||
${canPlot ? `<button class="btn" data-plot="${a.id}" ${why ? "disabled" : ""} title="Grow this in your second plot as well">Plot 2</button>` : ""}
|
||||
: `<span class="btns"><button class="btn" data-once="${a.id}" ${why ? "disabled" : ""} title="Once, then resume">${a.enemy ? "Fight once" : Object.keys(a.outputs).length ? "Make one" : "Once"}</button>
|
||||
${canPlot ? `<button class="btn" data-plot="${a.id}" ${why ? "disabled" : ""} title="Grow in plot 2">Plot 2</button>` : ""}
|
||||
<button class="btn primary" data-start="${a.id}" ${why ? "disabled" : ""}>Start</button></span>`}
|
||||
</div>
|
||||
</div>`;
|
||||
|
|
@ -682,11 +680,10 @@ function bank() {
|
|||
<div class="panel">
|
||||
<h2>Bank · ${me.slots_used} / ${me.slots} slots · ${fmt(me.stack)} per stack</h2>
|
||||
<div class="slots">${slots}</div>
|
||||
<p class="small muted">When an item doesn't fit:
|
||||
<p class="small muted">Bank full:
|
||||
<button class="btn sm ${me.overflow === "city" ? "gold" : ""}" data-overflow="city">Send it to the city</button>
|
||||
<button class="btn sm ${me.overflow === "stop" ? "gold" : ""}" data-overflow="stop">Stop working</button>
|
||||
<br>Raids never touch your bank. Overflow sent to the city earns half the favour of giving it yourself;
|
||||
anything the city has no use for is sold instead.</p>
|
||||
<br>Overflow earns half favour; what the city can't use is sold.</p>
|
||||
</div>
|
||||
${favourPanel()}
|
||||
</div>
|
||||
|
|
@ -711,11 +708,11 @@ function itemPanel(id) {
|
|||
${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>` : ""}
|
||||
${it.food ? `<div class="row small muted">Feeds ${it.food} townsfolk for 2 days.</div>` : ""}
|
||||
${C.city_uses[id].length
|
||||
? `<div class="row"><div class="k">Donate to the city · +${it.value} favour each</div><div class="btns">${qtyBtns("data-donate")}</div>
|
||||
<div class="small muted">Used for ${esc(listing(C.city_uses[id]))}.</div></div>`
|
||||
: `<div class="row small muted">The city has no use for this. Sell it, or give it to someone who can use it.</div>`}
|
||||
: `<div class="row small muted">The city has no use for this.</div>`}
|
||||
${others.length ? `<div class="row"><div class="k">Give to <select data-keep="give-to">${others.map((p) => `<option value="${esc(p.id)}">${esc(p.name)}</option>`).join("")}</select></div><div class="btns">${qtyBtns("data-give")}</div></div>` : ""}
|
||||
<div class="row"><div class="k">Sell</div><div class="btns">${qtyBtns("data-sell")}</div></div>
|
||||
<div class="row"><button class="btn sm" data-select="">Close</button></div>
|
||||
|
|
@ -743,7 +740,7 @@ function goldPanel() {
|
|||
return `<div class="panel"><h2>Gold</h2>
|
||||
<p class="gold">${COIN} ${fmt(S.me.gold)}</p>
|
||||
<form data-form="gold" class="btns"><input type="number" min="1" data-keep="gold" placeholder="Amount" style="width:120px"><button class="btn gold">Donate to treasury</button></form>
|
||||
<p class="small muted">The treasury pays the gold part of new buildings. 1 favour per gold.</p></div>`;
|
||||
<p class="small muted">1 favour per gold.</p></div>`;
|
||||
}
|
||||
|
||||
function favourPanel() {
|
||||
|
|
@ -751,7 +748,7 @@ function favourPanel() {
|
|||
return `<div class="panel"><h2>Favour with the council</h2>
|
||||
<p class="favour">${FAVOUR} ${fmt(me.favour)} favour · ${fmt(me.donated)} given in total</p>
|
||||
<div class="btns"><button class="btn" data-perk="bank_slot" ${me.favour < me.perk_costs.bank_slot ? "disabled" : ""}>+1 bank slot (${fmt(me.perk_costs.bank_slot)} favour)</button></div>
|
||||
<p class="small muted">Favour can also hire master builders, informants and drill sergeants: see <button class="linkish" data-nav="council">Council & threats</button>.</p></div>`;
|
||||
<p class="small muted">More to spend on in <button class="linkish" data-nav="council">Council & threats</button>.</p></div>`;
|
||||
}
|
||||
|
||||
// --- town -----------------------------------------------------------------
|
||||
|
|
@ -761,8 +758,8 @@ function town() {
|
|||
const growth = city.starving ? `<span class="bad">Hungry: ${city.growth} a day</span>`
|
||||
: city.growth > 0 ? `+${city.growth} a day`
|
||||
: city.growth < 0 ? `<span class="bad">Overcrowded: ${city.growth} a day</span>`
|
||||
: `<span class="warn">Full. Build more houses to grow</span>`;
|
||||
const next = city.next_tier ? `${city.next_tier.name} at ${city.next_tier.pop} people` : "The largest a settlement can be";
|
||||
: `<span class="warn">Full: build houses</span>`;
|
||||
const next = city.next_tier ? `${city.next_tier.name} at ${city.next_tier.pop} people` : "Largest tier";
|
||||
let html = `<div class="stats">
|
||||
<div class="stat"><div class="k">Settlement</div><div class="v">${city.tier_name}</div><div class="note">${next}</div></div>
|
||||
<div class="stat"><div class="k">Population</div><div class="v">${city.population} <small>/ ${city.housing} homes</small></div><div class="note">${growth}</div></div>
|
||||
|
|
@ -772,16 +769,16 @@ function town() {
|
|||
${weatherCard()}
|
||||
</div>`;
|
||||
html += happeningsPanel();
|
||||
if (city.starving) html += `<div class="banner bad">🍞<div class="grow"><b>The granary is empty.</b> People are leaving and the city defends worse. Donate bread, cooked meat or stew.</div></div>`;
|
||||
if (city.merchant_until > serverNow()) html += `<div class="banner">🐪<div class="grow"><b>A merchant caravan is in town.</b> Everything sells for 50% more for another ${countdown(city.merchant_until)}.</div></div>`;
|
||||
if (city.starving) html += `<div class="banner bad">🍞<div class="grow"><b>Granary empty.</b> People are leaving, defence is down. Donate food.</div></div>`;
|
||||
if (city.merchant_until > serverNow()) html += `<div class="banner">🐪<div class="grow"><b>Merchant in town.</b> Sell prices +50% for ${countdown(city.merchant_until)}.</div></div>`;
|
||||
if (city.construction) html += siteBanner();
|
||||
html += `<div class="section-title">Buildings</div>
|
||||
<p class="small muted">Each building collects its own fund. Materials in a fund are safe from raiders, and building starts on its own once the fund is complete.</p>
|
||||
<p class="small muted">Funds are safe from raiders. Building starts once a fund is full.</p>
|
||||
<div class="grid wide">${Object.entries(city.buildings).map(([key, b]) => buildingCard(key, b)).join("")}</div>`;
|
||||
html += `<div class="section-title">City stockpile</div><div class="panel">`;
|
||||
const stock = Object.entries(city.stockpile);
|
||||
html += stock.length ? `<div class="stock">${stock.map(([id, q]) => itemChip(id, q)).join("")}</div>` : `<p class="muted">Empty.</p>`;
|
||||
html += `<p class="small muted">Food, weapons and anything not yet in a fund. The granary and armoury draw from here, and so do raiders.</p></div>`;
|
||||
html += `<p class="small muted">Feeds the granary and armoury. Raiders can loot it.</p></div>`;
|
||||
return html;
|
||||
}
|
||||
|
||||
|
|
@ -798,10 +795,10 @@ function siteBanner() {
|
|||
<span class="muted small">${fmt(work.labour)} / ${fmt(work.needed)} labour</span>
|
||||
<span class="btns">
|
||||
${working ? `<span class="btn sm working">Working</span>` : `<button class="btn sm primary" data-start="labour">Work on it</button>`}
|
||||
${hire.why ? "" : `<button class="btn sm gold" data-favour="builders" ${me.favour < hire.cost ? "disabled" : ""} title="The next ${hire.labour} labour on the site counts double, with double XP">Master builder ${FAVOUR} ${fmt(hire.cost)}</button>`}
|
||||
${hire.why ? "" : `<button class="btn sm gold" data-favour="builders" ${me.favour < hire.cost ? "disabled" : ""} title="×2 labour and XP for the next ${hire.labour}">Master builder ${FAVOUR} ${fmt(hire.cost)}</button>`}
|
||||
</span></div>
|
||||
<div class="bar gold" style="margin-top:6px"><span style="width:${Math.min(100, work.labour / work.needed * 100)}%"></span></div>
|
||||
<div class="small muted" style="margin-top:6px">${builders.length ? `On site: ${builders.join(", ")}. ` : ""}${hire.boost ? `<b class="boosted">Master builder on site: the next ${fmt(hire.boost)} labour counts double, with double XP.</b> ` : ""}The townsfolk add about ${fmt(city.townsfolk_labour)} labour an hour.${queued ? ` Next: ${queued}.` : ""}</div>
|
||||
<div class="small muted" style="margin-top:6px">${builders.length ? `On site: ${builders.join(", ")}. ` : ""}${hire.boost ? `<b class="boosted">Master builder: ×2 for the next ${fmt(hire.boost)}.</b> ` : ""}Townsfolk +${fmt(city.townsfolk_labour)}/h.${queued ? ` Next: ${queued}.` : ""}</div>
|
||||
</div></div>`;
|
||||
}
|
||||
|
||||
|
|
@ -812,7 +809,7 @@ function weatherTip(w) {
|
|||
|
||||
function weatherCard() {
|
||||
const h = S.happenings, w = h.weather;
|
||||
return `<div class="stat" title="${esc(`${weatherTip(w)} Weather changes every few hours; each season lasts a week.`)}"><div class="k">${h.season.icon} ${h.season.name}, day ${h.season.day}</div>
|
||||
return `<div class="stat" title="${esc(weatherTip(w))}"><div class="k">${h.season.icon} ${h.season.name}, day ${h.season.day}</div>
|
||||
<div class="v">${w.icon} <small>${esc(w.name)}</small></div>
|
||||
<div class="note">${w.next_icon} ${esc(w.next_name)} in ${countdown(w.until)}</div></div>`;
|
||||
}
|
||||
|
|
@ -822,7 +819,7 @@ function happeningsPanel() {
|
|||
const h = S.happenings, me = S.me, city = S.city;
|
||||
const rows = [];
|
||||
if (h.weather.kind !== "clear") rows.push(`<div class="happening"><span class="ico">${h.weather.icon}</span><span class="grow"><b>${esc(h.weather.name)}</b><span class="small muted">${esc(h.weather.text)}</span></span></div>`);
|
||||
if (h.cold) rows.push(`<div class="happening bad"><span class="ico">🥶</span><span class="grow"><b>Out of firewood</b><span class="small muted">People are cold and eating half again as much. Donate logs.</span></span></div>`);
|
||||
if (h.cold) rows.push(`<div class="happening bad"><span class="ico">🥶</span><span class="grow"><b>Out of firewood</b><span class="small muted">City eats 50% more. Donate logs.</span></span></div>`);
|
||||
for (const e of h.effects) {
|
||||
const hire = e.offer ? `<button class="btn sm gold" data-hire ${city.treasury < e.cost ? "disabled" : ""}>Hire for ${fmt(e.cost)} gold</button>` : "";
|
||||
rows.push(`<div class="happening"><span class="ico">${e.icon}</span><span class="grow"><b>${esc(e.name)}</b>
|
||||
|
|
@ -860,7 +857,7 @@ function skillHappenings(skill) {
|
|||
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 ? "Help clear it below." : `${skillOf(fix.skill).name} can clear it: see the Town square.`);
|
||||
note(p.icon, p.name, !fix ? p.text : fix.skill === skill ? "Clear it below." : `${skillOf(fix.skill).name} clears it (Town square).`);
|
||||
}
|
||||
return lines.length ? `<div class="panel tool-panel skill-happenings">${lines.join("")}</div>` : "";
|
||||
}
|
||||
|
|
@ -869,9 +866,9 @@ function buildingCard(key, b) {
|
|||
const spec = C.buildings[key], city = S.city, me = S.me;
|
||||
const tags = {
|
||||
building: `<span class="tag gold">Under construction</span>`,
|
||||
queued: `<span class="tag good">Funded · waiting for builders</span>`,
|
||||
queued: `<span class="tag good">Funded · queued</span>`,
|
||||
max: `<span class="tag good">Fully built</span>`,
|
||||
capped: `<span class="tag">Next level when the settlement grows</span>`,
|
||||
capped: `<span class="tag">Needs a bigger settlement</span>`,
|
||||
};
|
||||
let body = tags[b.status] ? `<div>${tags[b.status]}</div>` : "";
|
||||
if (b.status === "funding" || b.status === "queued") {
|
||||
|
|
@ -888,10 +885,10 @@ function buildingCard(key, b) {
|
|||
}).join("")}</div>`;
|
||||
}
|
||||
body += fittingRows(key, b);
|
||||
const levelText = b.cap < spec.max ? `Lv ${b.level} · max ${b.cap} for a ${city.tier_name.toLowerCase()}` : `Lv ${b.level} / ${spec.max}`;
|
||||
const levelText = b.cap < spec.max ? `Lv ${b.level} / ${b.cap} (${city.tier_name.toLowerCase()} cap)` : `Lv ${b.level} / ${spec.max}`;
|
||||
return `<div class="action building ${b.damaged ? "damaged" : ""}">
|
||||
<div class="top"><div class="ico">${spec.icon}</div><div>
|
||||
<div class="name">${esc(spec.name)} <span class="lv muted">${levelText}</span> ${b.damaged ? `<span class="tag bad">Damaged: repairs needed</span>` : ""}</div>
|
||||
<div class="name">${esc(spec.name)} <span class="lv muted">${levelText}</span> ${b.damaged ? `<span class="tag bad">Damaged</span>` : ""}</div>
|
||||
<div class="sub">${esc(spec.effect)}</div>
|
||||
</div></div>${body}</div>`;
|
||||
}
|
||||
|
|
@ -927,17 +924,17 @@ function pips(level) {
|
|||
|
||||
function reconText(ev) {
|
||||
const points = `${fmt(ev.recon)} / ${fmt(ev.recon_full)} points`;
|
||||
if (ev.intel >= 3) return `${points} · nothing more to learn`;
|
||||
const boost = S.favours.informant.boost ? ` · <b class="boosted">informant's leads: ×2 for the next ${fmt(S.favours.informant.boost)}</b>` : "";
|
||||
if (ev.recon_next <= 0) return `${points} · enough for the next level, but they're too far off to read yet${boost}`;
|
||||
return `${points} · ${fmt(ev.recon_next)} more for the next intel level${boost}`;
|
||||
if (ev.intel >= 3) return `${points} · complete`;
|
||||
const boost = S.favours.informant.boost ? ` · <b class="boosted">informant: ×2 for the next ${fmt(S.favours.informant.boost)}</b>` : "";
|
||||
if (ev.recon_next <= 0) return `${points} · next level opens closer to the threat${boost}`;
|
||||
return `${points} · ${fmt(ev.recon_next)} to the next level${boost}`;
|
||||
}
|
||||
|
||||
function council() {
|
||||
const ev = S.upcoming, city = S.city, me = S.me;
|
||||
let html = "";
|
||||
if (!ev) {
|
||||
html += `<div class="panel"><h2>All quiet</h2><p class="muted">No news on the roads. The spymasters will report as soon as something stirs.</p></div>`;
|
||||
html += `<div class="panel"><h2>All quiet</h2><p class="muted">No news on the roads.</p></div>`;
|
||||
} else {
|
||||
const threat = ["raid", "dragon"].includes(ev.kind);
|
||||
html += `<div class="panel ${threat ? "threat-card" : ""}">
|
||||
|
|
@ -947,10 +944,9 @@ function council() {
|
|||
<span class="k">Expected</span><span>${eventWhen(ev)}</span>
|
||||
${ev.range ? `<span class="k">Strength</span><span>${ev.strength ?? `${ev.range[0]}–${ev.range[1]}`}</span>` : ""}
|
||||
${ev.trait ? `<span class="k">Scouts say</span><span>${esc(ev.trait)}</span>` : ""}
|
||||
${ev.camp ? `<span class="k">Their camp</span><span>Located. Fighters can sabotage it from the combat page${ev.sabotage ? ` (weakened ${ev.sabotage}% so far)` : ""}.</span>` : ""}
|
||||
${ev.camp ? `<span class="k">Their camp</span><span>Found · sabotage it in Combat${ev.sabotage ? ` (-${ev.sabotage}% so far)` : ""}</span>` : ""}
|
||||
<span class="k">Reconnaissance</span><span>${reconText(ev)}</span>
|
||||
</div>
|
||||
${ev.intel < 3 ? `<p class="small muted">Better intel comes from the spymaster's guild and anyone's scouting (rangers are best at it), and the sharpest only as the threat draws near.</p>` : ""}`;
|
||||
</div>`;
|
||||
if (threat) {
|
||||
const strength = ev.strength ?? (ev.range ? `${ev.range[0]}–${ev.range[1]}` : "?");
|
||||
html += `<div class="vs"><div class="side"><span class="muted small">Our defence</span><b>${fmt(city.defence.total)}</b></div><span class="muted">vs</span><div class="side"><span class="muted small">Raiders</span><b>${strength}</b></div></div>`;
|
||||
|
|
@ -988,9 +984,9 @@ function favourSpends() {
|
|||
</div>`;
|
||||
};
|
||||
return `<div class="panel"><h2>Spend favour on the city · you have ${FAVOUR} ${fmt(me.favour)}</h2>
|
||||
${option("builders", "🏗️", "Hire a master builder", f.builders.why ? esc(f.builders.why) + "." : `The next ${f.builders.labour} labour anyone puts into the site counts double, with double Construction XP${f.builders.boost ? ` (${fmt(f.builders.boost)} still to use)` : ""}. Costs more each time on the same site.`)}
|
||||
${option("informant", "🕵️", "Pay an informant", f.informant.why ? esc(f.informant.why) + "." : `Leads to follow up: the next ${f.informant.recon} reconnaissance on the coming threat counts double, with double XP${f.informant.boost ? ` (${fmt(f.informant.boost)} still to use)` : ""}. Costs more each time for the same threat.`)}
|
||||
${option("sergeant", "📯", "Hire a drill sergeant", f.sergeant.why ? esc(f.sergeant.why) + "." : `The next ${f.sergeant.drill} drill points fighters put into the militia count double, with double combat XP${f.sergeant.boost ? ` (${fmt(f.sergeant.boost)} still to use)` : ""}. Costs more each time for the same threat.`)}
|
||||
${option("builders", "🏗️", "Hire a master builder", f.builders.why ? esc(f.builders.why) + "." : `×2 labour and XP for the next ${f.builders.labour}${f.builders.boost ? ` (${fmt(f.builders.boost)} left)` : ""}. Price rises per hire.`)}
|
||||
${option("informant", "🕵️", "Pay an informant", f.informant.why ? esc(f.informant.why) + "." : `×2 recon and XP for the next ${f.informant.recon}${f.informant.boost ? ` (${fmt(f.informant.boost)} left)` : ""}. Price rises per hire.`)}
|
||||
${option("sergeant", "📯", "Hire a drill sergeant", f.sergeant.why ? esc(f.sergeant.why) + "." : `×2 drill and XP for the next ${f.sergeant.drill}${f.sergeant.boost ? ` (${fmt(f.sergeant.boost)} left)` : ""}. Price rises per hire.`)}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
|
|
@ -1005,15 +1001,15 @@ function raidPrep(ev) {
|
|||
}).join("");
|
||||
return `<div class="cols">
|
||||
<div class="panel"><h2>Council requests</h2>${reqs}
|
||||
<p class="small muted">Arrows, swords and shields in the stockpile arm the defenders: one sword and shield each, best first. Every fight uses up arrows and breaks some of the gear, more when it's close. Food keeps them fighting.</p></div>
|
||||
<p class="small muted">Stockpile gear arms the defenders, best first. Fights use up arrows and break some gear.</p></div>
|
||||
<div class="panel"><h2>Defence</h2>
|
||||
<table class="parts">${Object.entries(d.parts).filter(([k, v]) => k !== "rallied" || v).map(([k, v]) => `<tr><td>${names[k]}</td><td>${fmt(v)}</td></tr>`).join("")}
|
||||
${d.starving ? `<tr><td class="bad">Hungry (×0.8)</td><td></td></tr>` : ""}</table>
|
||||
<h2 style="margin-top:16px">Garrison</h2>
|
||||
<p class="small">${d.garrison.length ? d.garrison.map((g) => `${esc(g.name)} (${g.power})`).join(", ") : `<span class="muted">Nobody has signed up yet.</span>`}</p>
|
||||
<p class="small">${d.garrison.length ? d.garrison.map((g) => `${esc(g.name)} (${g.power})`).join(", ") : `<span class="muted">Nobody yet.</span>`}</p>
|
||||
${me.garrison ? `<button class="btn" data-garrison="0">Leave the garrison</button>`
|
||||
: `<button class="btn primary" data-garrison="1">Stand guard (+${me.power})</button>`}
|
||||
<p class="small muted">You can keep working while you're signed up. Victory earns the garrison favour and combat XP.</p>
|
||||
<p class="small muted">Keep working while on guard. Victory earns favour and combat XP.</p>
|
||||
</div></div>`;
|
||||
}
|
||||
|
||||
|
|
@ -1026,7 +1022,7 @@ function board() {
|
|||
const ledger = [...S.ledger].reverse().map((l) => `<li>${esc(l.text)}<span class="when">${ago(l.at)}</span></li>`).join("");
|
||||
return `<div class="cols">
|
||||
<div class="panel"><h2>Town board</h2>
|
||||
<div class="msgs">${msgs || `<p class="muted">No messages yet. Say hello, or ask for that iron you need.</p>`}</div>
|
||||
<div class="msgs">${msgs || `<p class="muted">No messages yet.</p>`}</div>
|
||||
<form data-form="board"><textarea data-keep="board" maxlength="500" placeholder="Write to the guild…"></textarea>
|
||||
<p><button class="btn primary">Post</button></p></form></div>
|
||||
<div class="panel"><h2>Town ledger</h2><ul class="ledger">${ledger || `<li class="muted">Nothing recorded yet.</li>`}</ul></div>
|
||||
|
|
@ -1117,7 +1113,7 @@ function characterSheet(uid) {
|
|||
<div class="sheet-gear">${Object.entries(SLOT_NAMES).map(([slot, label]) => gearSlot(slot, label, "Nothing")).join("")}</div>
|
||||
<h2 style="margin-top:16px">Tools</h2>
|
||||
<div class="sheet-gear">${C.skills.filter((s) => `tool_${s.id}` in p.equip).map((s) => gearSlot(`tool_${s.id}`, s.name, "No tool")).join("")}</div>
|
||||
${mine ? `<p class="small muted">Equip gear and tools from the <button class="linkish" data-nav="bank">bank</button>.</p>` : ""}
|
||||
${mine ? `<p class="small muted">Equip from the <button class="linkish" data-nav="bank">bank</button>.</p>` : ""}
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel"><h2>Skills</h2><div class="sheet-skills">${skillRows}</div></div>
|
||||
|
|
@ -1137,12 +1133,10 @@ function classCards() {
|
|||
function repickPage() {
|
||||
return `<div class="welcome">
|
||||
<h2>Pick your trade</h2>
|
||||
<p class="muted">Classes are now whole trades, from gathering through to the finished goods, so you can play
|
||||
the quiet gathering side and the busier crafting side yourself. You keep every level and item. This choice
|
||||
is free, once.</p>
|
||||
<p class="muted">Classes are now trades, gathering through crafting. You keep every level and item. Free, once.</p>
|
||||
<div class="classes">${classCards()}</div>
|
||||
<div class="btns"><button class="btn primary" data-repick ${pickedClass ? "" : "disabled"}>Take up this trade</button></div>
|
||||
<p class="small muted">Every class can still train every skill, scout and drill. A trade makes its skills faster and better.</p>
|
||||
<p class="small muted">Any class can train any skill.</p>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
|
|
@ -1150,15 +1144,14 @@ function register() {
|
|||
const cards = classCards();
|
||||
return `<div class="welcome">
|
||||
<h2>A new city on the frontier</h2>
|
||||
<p class="muted">You're one of a handful of adventurers settling a young city at the edge of the kingdom.
|
||||
Train your skills, supply the council, and keep the place standing when the bandits come.
|
||||
Your adventurer keeps working while you're away, for up to a day.</p>
|
||||
<p class="muted">Settle a frontier city together. Train, build, and hold off the bandits.
|
||||
You keep working while away, for up to a day.</p>
|
||||
<div class="classes">${cards}</div>
|
||||
<form data-form="register" class="btns">
|
||||
<input type="text" data-keep="name" maxlength="20" placeholder="Your adventurer's name" style="flex:1;min-width:200px">
|
||||
<button class="btn primary" ${pickedClass ? "" : "disabled"}>Enter the city</button>
|
||||
</form>
|
||||
<p class="small muted">Every class can train every skill. Your class is a trade: its skills are faster, its crafts sometimes make double, and it has one bonus of its own.</p>
|
||||
<p class="small muted">Any class can train any skill.</p>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
|
|
@ -1236,7 +1229,7 @@ document.addEventListener("click", (e) => {
|
|||
if (d.devHappen) return act("/api/dev/happen", { track: d.devHappen });
|
||||
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");
|
||||
if (!on && S.me.prayers.length >= C.max_prayers) return toast(`Max ${C.max_prayers} prayers active`, "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 });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue