Queue: gathering too

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rope 2026-10-27 23:02:57 +00:00
parent a723558fb2
commit 45a51476a9
3 changed files with 19 additions and 9 deletions

View file

@ -3185,15 +3185,15 @@ def stop(player: dict, reason: str | None = None) -> None:
def queueable(action: dict) -> bool:
"""Crafting: turns materials into something. Only these can be queued."""
return bool(action["inputs"] and action["outputs"]) and not (
"""Crafting and gathering: makes or gathers something. Only these can be queued."""
return bool(action["outputs"]) and not (
action.get("enemy") or action.get("special") or action.get("together"))
def queue_reason(world: dict, player: dict, action: dict) -> str | None:
"""Why it can't go in the queue. Materials are checked when its turn comes."""
if not queueable(action):
return "Only crafting can be queued"
return "Only crafting and gathering can be queued"
if skill_level(player, action["skill"]) < action["level"]:
return f"Needs level {action['level']}"
if action.get("pattern") and action["pattern"] not in player.get("patterns", []):
@ -6286,6 +6286,7 @@ ORDER_RECIPES, ORDER_RAW = _order_sources()
# line here (new / balance / fix), and a new release a new entry on top.
PATCH_NOTES = [
{"date": "2026-10-27", "title": "Prayer", "notes": [
("new", "Gathering can be queued."),
("new", "Make X and queue counts go up to 9,999. Queue has no length cap."),
("balance", "Rebalanced favour values of items"),
("balance", "Prospecting: 3 ore per try (was 5), 50% gem chance (was 15%)."),

View file

@ -20,7 +20,7 @@ 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
let queueOpen = false; // the crafting queue panel under the top bar
let queueOpen = false; // the queue panel under the top bar
const tabs = {}; // skill -> selected recipe tab
let refreshing = false;
let refreshedFor = null; // action (start and length) a completion refresh was sent for
@ -777,7 +777,7 @@ function topbar() {
chips += `<span class="chip muted">Idle${S.me.stopped ? ` · ${esc(S.me.stopped)}` : ""}</span>`;
}
if (S.me?.action?.left || S.me?.queue.length) {
chips += `<button class="chip ${queueOpen ? "on" : ""}" data-queue-menu title="Crafting queue">⏳ ${S.me.queue.length}</button>`;
chips += `<button class="chip ${queueOpen ? "on" : ""}" data-queue-menu title="Queue">⏳ ${S.me.queue.length}</button>`;
}
const fires = S.me ? Object.keys(C.fires).map((id) => [id, fireNow(id)]) : [];
if (fires.some(([, n]) => n.heat >= 0)) {
@ -791,7 +791,7 @@ function topbar() {
return `<button class="menu-btn" data-menu aria-label="Menu">☰</button><h1>${esc(title)}</h1>${chips}${S.me ? trackedBar() : ""}${S.me && queueOpen ? queuePanel() : ""}`;
}
// The crafting queue, opened from the ⏳ chip: what's being made, what's next, and in what order.
// The queue, opened from the ⏳ chip: what's being made, what's next, and in what order.
function queuePanel() {
const me = S.me, q = me.queue, recipe = (id) => C.actions.find((x) => x.id === id);
const now = me.action?.left && recipe(me.action.id), back = me.action?.resume && recipe(me.action.resume);
@ -805,10 +805,10 @@ function queuePanel() {
<button class="btn sm danger" data-unqueue="${i}" title="Remove">✕</button></li>`;
}).join("");
return `<div class="queue-panel">
<div class="qp-head"><b>⏳ Crafting queue</b><span class="muted small">${q.length}</span><button class="x" data-queue-menu title="Close">✕</button></div>
<div class="qp-head"><b>⏳ Queue</b><span class="muted small">${q.length}</span><button class="x" data-queue-menu title="Close">✕</button></div>
${now ? `<div class="qp-row now">${now.icon}<span class="qp-name">${esc(now.name)}<span class="muted small">${fmt(me.action.left)} left</span></span>
<button class="btn sm" data-queue-skip title="Drop this make, on to the next">Skip</button></div>` : ""}
${rows ? `<ol class="qp-list">${rows}</ol>` : `<p class="muted small">Empty. +Q on a recipe adds to it.</p>`}
${rows ? `<ol class="qp-list">${rows}</ol>` : `<p class="muted small">Empty. +Q on a recipe or gathering adds to it.</p>`}
<div class="qp-foot"><span class="muted small qp-then">After queue: ${back ? `${esc(back.name)}${now ? `<button class="x" data-queue-then title="Idle after the queue">✕</button>` : ""}` : "idle"}</span>
${q.length ? `<button class="btn sm" data-queue-clear>Clear</button>` : ""}</div>
</div>`;

View file

@ -103,6 +103,15 @@ class ActionTest(unittest.TestCase):
self.assertEqual(summary["resumed"], "mine_stone")
self.assertEqual(p["queue"], [])
def test_queue_gathering(self):
world, p = self._smith(4)
self.assertIsNone(game.queue_add(world, p, "cut_logs", 3, T0)) # starts it, mining is what comes after
self.assertIsNone(game.queue_add(world, p, "smith_copper_legs", 1, T0))
self.assertIsNone(game.queue_add(world, p, "cut_logs", 2, T0))
game.process_player(world, p, T0 + 3600, random.Random(1))
self.assertEqual((p["bank"].get("logs"), p["bank"].get("copper_legs")), (5, 1))
self.assertEqual((p["action"]["id"], p["queue"]), ("mine_stone", []))
def test_queue_skips_what_you_cant_make(self):
world, p = self._smith(4)
game.start_action(world, p, "smith_copper_helmet", T0, once=1)
@ -133,7 +142,7 @@ class ActionTest(unittest.TestCase):
def test_queue_rules(self):
world, p = self._smith(4)
self.assertEqual(game.queue_add(world, p, "cut_logs", 1, T0), "Only crafting can be queued")
self.assertEqual(game.queue_add(world, p, "bury_bones", 1, T0), "Only crafting and gathering can be queued")
self.assertIsNone(game.queue_add(world, p, "smith_copper_helmet", 1, T0))
self.assertEqual(p["action"]["id"], "smith_copper_helmet") # nothing being made: it just starts
self.assertEqual(p["action"]["resume"], "mine_stone")