Work orders: anyone can pool reagents, paid by value
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
f7daa4e44d
commit
9ff7a22a5d
3 changed files with 161 additions and 44 deletions
|
|
@ -16,6 +16,7 @@ let orderTab = "take"; // work orders: for you, all open, yours
|
|||
let draft = null; // a work order being written
|
||||
let seenOrders = null; // order ids already known, so only new ones toast
|
||||
let deliveredSeen = {}; // how much of each order had arrived
|
||||
let earnedSeen = {}; // gold each order had paid you for reagents
|
||||
let pickedClass = null; // registration
|
||||
const tabs = {}; // skill -> selected recipe tab
|
||||
let refreshing = false;
|
||||
|
|
@ -1478,28 +1479,39 @@ function orderNews(state) {
|
|||
}
|
||||
const was = deliveredSeen[o.id];
|
||||
if (o.by === uid && was !== undefined && o.delivered > was) toast(`📦 ${item(o.item).name} ×${fmt(o.delivered - was)}`, "good");
|
||||
const got = (o.earned || {})[uid] || 0, had = earnedSeen[o.id];
|
||||
if (had !== undefined && got > had) toast(`🤝 ${item(o.item).name} · +${fmt(got - had)} gold`, "good");
|
||||
}
|
||||
}
|
||||
seenOrders = new Set(orders.map((o) => o.id));
|
||||
deliveredSeen = Object.fromEntries(orders.map((o) => [o.id, o.delivered]));
|
||||
earnedSeen = Object.fromEntries(orders.map((o) => [o.id, (o.earned || {})[uid] || 0]));
|
||||
}
|
||||
|
||||
// Recipe slots on a crafting order. Anyone can put reagents in the pool; the crafter brings the rest.
|
||||
function orderSlots(o) {
|
||||
const a = C.actions.find((x) => x.id === o.action), out = a.outputs[o.item];
|
||||
const left = Math.ceil((o.qty - o.delivered) / out), all = Math.ceil(o.qty / out);
|
||||
const worth = Object.entries(a.inputs).reduce((sum, [id, per]) => sum + item(id).value * per, 0);
|
||||
return Object.entries(a.inputs).map(([id, per]) => {
|
||||
const name = item(id).name;
|
||||
if (id in o.provided) {
|
||||
return `<div class="ro-slot filled" title="${esc(`${name}: provided`)}"><span class="doll-slot">${item(id).icon}
|
||||
<span class="swap-qty">${fmt(per * all)}</span><span class="tick">✓</span></span></div>`;
|
||||
const name = item(id).name, pool = o.provided[id] || 0, have = S.me.bank[id] || 0;
|
||||
if (o.status !== "open") {
|
||||
return `<div class="ro-slot open"><span class="doll-slot empty-req" title="${esc(name)}">${item(id).icon}</span>
|
||||
<span class="ro-have muted">×${fmt(per * all)}</span></div>`;
|
||||
}
|
||||
const need = per * (o.status === "open" ? left : all), have = S.me.bank[id] || 0;
|
||||
// On your own open order, an empty slot can still be filled from your bank.
|
||||
const fill = o.by === S.uid && o.status === "open";
|
||||
return `<div class="ro-slot open">${fill
|
||||
? `<button class="doll-slot empty-req" data-order-fill="${o.id}" data-item="${id}" ${have < need ? "disabled" : ""} title="${esc(`${name}: ${fmt(have)} / ${fmt(need)}`)}">${item(id).icon}</button>`
|
||||
: `<span class="doll-slot empty-req" title="${esc(`${name}: crafter brings`)}">${item(id).icon}</span>`}
|
||||
<span class="ro-have ${o.status !== "open" ? "muted" : have < need ? "bad" : "good"}">${o.status !== "open" ? `×${fmt(need)}` : `${fmt(have)} / ${fmt(need)}`}</span></div>`;
|
||||
const need = per * left;
|
||||
const who = Object.entries(o.gave || {}).filter(([, items]) => items[id]).map(([uid, items]) => `${playerName(uid)} ${fmt(items[id])}`);
|
||||
const tip = [`${name}: ${fmt(pool)} / ${fmt(need)}`, ...who].join(" · ");
|
||||
if (pool >= need) {
|
||||
return `<div class="ro-slot filled" title="${esc(tip)}"><span class="doll-slot">${item(id).icon}
|
||||
<span class="swap-qty">${fmt(need)}</span><span class="tick">✓</span></span></div>`;
|
||||
}
|
||||
const give = Math.min(have, need - pool);
|
||||
// What putting them in pays you once they're crafted: your value's slice of the reagents' share.
|
||||
const share = o.by === S.uid ? 0 : Math.round(o.reward / o.qty * out * C.input_share * item(id).value / worth * give);
|
||||
const fill = give ? `${tip} · +${fmt(give)}${share ? ` → ${fmt(share)} gold` : ""}` : `${tip} · 🎒 0`;
|
||||
return `<div class="ro-slot open"><button class="doll-slot empty-req" data-order-fill="${o.id}" data-item="${id}" ${give ? "" : "disabled"} title="${esc(fill)}">${item(id).icon}</button>
|
||||
<span class="ro-have ${pool + have < need ? "bad" : "good"}">${fmt(pool)} / ${fmt(need)}</span></div>`;
|
||||
}).join("");
|
||||
}
|
||||
|
||||
|
|
@ -1536,6 +1548,7 @@ function orderCard(o) {
|
|||
<div class="sub">${mine ? "Yours" : esc(playerName(o.by))} · ${ago(o.at)}${o.held ? ` · ${fmt(o.held)} waiting for bank space` : ""}</div></div>
|
||||
<span class="ro-req ${short && !mine ? "bad" : ""}" title="${esc(`${skill.name} ${o.level}`)}">${skill.icon} ${o.level}</span></div>
|
||||
${o.kind === "craft" ? `<div class="ro-slots">${orderSlots(o)}</div>` : ""}
|
||||
${Object.keys(o.earned || {}).length ? `<div class="small muted">🤝 ${Object.entries(o.earned).map(([uid, n]) => `${esc(playerName(uid))} ${fmt(n)}`).join(", ")}</div>` : ""}
|
||||
${progress}
|
||||
<div class="foot"><span class="item-chip ro-reward">${COIN} ${reward}</span>${status}<span class="btns">${buttons}</span></div>
|
||||
</div>`;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue