Jewelcrafting: gems, rings, amulets, trinkets

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rope 2026-10-20 19:59:45 +01:00
parent 0d33e09799
commit fb237cb686
5 changed files with 266 additions and 26 deletions

View file

@ -657,13 +657,20 @@ function animateFight(now) {
});
}
// A piece of jewellery's bonus, e.g. "+8% accuracy".
function jewelText(it) {
const st = C.jewel_stats[it.jewel];
return `+${it[it.jewel]}${st.unit} ${st.name.toLowerCase()}`;
}
// 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`];
const stats = [it.power && `+${it.power} power`, it.armour && `+${it.armour} armour`, it.ward && `+${it.ward} ward`,
it.jewel && !["power", "ward"].includes(it.jewel) && jewelText(it)];
return stats.filter(Boolean).join(", ") + (it.weight ? ` (${C.weights[it.weight].name.toLowerCase()})` : "");
}
const GEAR_SLOTS = ["weapon", "shield", "helmet", "body", "legs"];
const GEAR_SLOTS = ["weapon", "shield", "helmet", "body", "legs", "ring", "amulet", "trinket"];
// A weapon's or armour piece's stats in a small grid. Weapons: damage type and power | 1H or 2H | swing
// time; armour: armour | weight | ward. Below: what it needs to wear, and any bonus for you.
@ -684,6 +691,9 @@ function gearChips(it, who = S.me) {
row = [cell(glyph("checked-shield", "#d4dae2"), it.armour, `+${it.armour} armour: less damage from blows`),
cell("", C.weights[it.weight].name, `${C.weights[it.weight].name} armour`, "muted"),
cell(glyph("magic-shield", "#a77be8"), it.ward, `+${it.ward} ward: less damage from magic`)];
} else if (it.jewel) {
const st = C.jewel_stats[it.jewel];
row = [cell(glyph(st.glyph, st.tint), `+${it[it.jewel]}${st.unit}`, jewelText(it)), "<span></span>", "<span></span>"];
}
let req = "", bonus = "";
if (it.req) {
@ -708,7 +718,8 @@ function itemStats(id) {
parts.push(`${it.two_handed ? "two-handed " : ""}${st.name}, ${st.interval}s${trade ? ` (${C.classes[trade].name.toLowerCase()})` : ""}`);
}
if (it.armour || it.ward) parts.push(gearStats({ ...it, power: 0 }));
if (it.reagent) parts.push(`Reagent for ${C.weights[it.reagent].name.toLowerCase()} armour`);
if (it.reagent) parts.push(it.reagent === "jewel" ? "Reagent for jewellery" : `Reagent for ${C.weights[it.reagent].name.toLowerCase()} armour`);
if (it.jewel && !["power", "ward"].includes(it.jewel)) parts.push(jewelText(it));
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`);
@ -735,15 +746,17 @@ function actionCard(a) {
let io = "";
if (Object.keys(a.inputs).length) {
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>` : "");
+ (Object.keys(a.outputs).length || a.finds ? `<span class="arrow">→</span>` : "");
}
io += Object.entries(a.outputs).map(([id, q]) => itemChip(id, q)).join("");
const chance = ([id, q, odds]) => `<span class="item-chip" title="${esc(item(id).name)}${q > 1 ? ` ×${q}` : ""}: ${Math.round(odds * 100)}% chance">${item(id).icon} ${Math.round(odds * 100)}%</span>`;
if (a.finds) io += a.finds.map(chance).join("");
let extra = "";
if (a.enemy) {
const o = me.fight_odds[a.id], e = a.enemy;
extra = `${e.boss ? `<div><span class="tag warn">Boss</span></div>` : ""}${variantRows(e, o)}`;
if (a.gold) io += `<span class="item-chip" title="Gold: ${a.gold[0]}–${a.gold[1]} a kill">${COIN} ${a.gold[0]}–${a.gold[1]}</span>`;
if (a.drops) io += a.drops.map(([id, q, odds]) => `<span class="item-chip" title="${esc(item(id).name)}${q > 1 ? ` ×${q}` : ""}: ${Math.round(odds * 100)}% chance">${item(id).icon} ${Math.round(odds * 100)}%</span>`).join("");
if (a.drops) io += a.drops.map(chance).join("");
if (a.special === "sabotage") extra += `<div class="small muted">-3% raid strength per win (max 30%)</div>`;
}
if (a.special === "drill") {
@ -1419,7 +1432,7 @@ const DOLL = [
["cape", "Cape", "cape"], ["helmet", "Head", "visored-helm"], ["amulet", "Amulet", "gem-pendant"],
["weapon", "Main hand", "pointy-sword"], ["body", "Body", "breastplate"], ["shield", "Off hand", "round-shield"],
["gloves", "Gloves", "gauntlet"], ["legs", "Legs", "leg-armor"], ["ring", "Ring", "ring"],
["ammo", "Ammo", "quiver"], ["boots", "Boots", "leather-boot"], ["food", "Food", "hot-meal"],
["trinket", "Trinket", "crystal-ball"], ["boots", "Boots", "leather-boot"], ["food", "Food", "hot-meal"],
];
function dollSlot(p, slot, label, outline, picked) {