From 2597a1d7e7fdfa01fee5f97e17b3861325314d3e Mon Sep 17 00:00:00 2001 From: rope Date: Tue, 27 Oct 2026 21:23:00 +0000 Subject: [PATCH] Bank: drink all potions Co-Authored-By: Claude Opus 5.5 --- app.py | 3 ++- game.py | 17 ++++++++++++----- static/app.js | 3 ++- tests/test_game.py | 12 ++++++++++++ 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/app.py b/app.py index ce0a9ff..9f032ce 100644 --- a/app.py +++ b/app.py @@ -482,7 +482,8 @@ class Handler(BaseHTTPRequestHandler): if path == "/api/statue": return game.carve_statue(world, player, str(body.get("of")), now) if path == "/api/potion": - return game.use_potion(player, str(body.get("item"))) + qty = body.get("qty", 1) + return game.use_potion(player, str(body.get("item")), qty if qty == "all" or isinstance(qty, int) else -1) if path == "/api/prayers": ids = body.get("ids") return game.set_prayers(player, ids) if isinstance(ids, list) else "Bad request" diff --git a/game.py b/game.py index 0c2ee3d..c95b496 100644 --- a/game.py +++ b/game.py @@ -2692,18 +2692,24 @@ def potion_effect(player: dict, skill: str) -> dict: return ITEMS[active["item"]]["potion"] if active else {} -def use_potion(player: dict, item: str) -> str | None: - """Drink a potion from the bank into its skill's slot (topping up the same potion).""" +def use_potion(player: dict, item: str, qty: int | str = 1) -> str | None: + """Drink potions from the bank into their skill's slot (topping up the same potion).""" spec = ITEMS.get(item, {}).get("potion") if not spec: return "That isn't a potion" - if player["bank"].get(item, 0) < 1: + have = player["bank"].get(item, 0) + if have < 1: return "You don't have one" - bank_take(player, item, 1) + qty = have if qty == "all" else qty + if not isinstance(qty, int) or qty < 1: + return "Bad amount" + if have < qty: + return "You don't have that many" + bank_take(player, item, qty) current = player["potions"].get(spec["skill"]) charges = current["charges"] if current and current["item"] == item else 0 fresh = round(POTION_CHARGES * (1.25 if player["cls"] == "homesteader" else 1)) - player["potions"][spec["skill"]] = {"item": item, "charges": charges + fresh} + player["potions"][spec["skill"]] = {"item": item, "charges": charges + fresh * qty} return None @@ -6270,6 +6276,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", "Added \"Drink all\" option to potions in the bank."), ("new", "Firekeeping: Tend all fires. Each log goes on the lowest fire with room in your pile."), ("new", "Added resource dumps for Leatherworking: Guard Jerkins"), ("fix", "+Q button: rounded corners."), diff --git a/static/app.js b/static/app.js index 22a192f..adfe9ac 100644 --- a/static/app.js +++ b/static/app.js @@ -1903,6 +1903,7 @@ function itemPanel(id) { ${it.slot ? equipRow(id, it) : ""} ${it.potion ? `
${esc(it.potion.text)} for ${C.potion_charges} ${it.potion.skill === "combat" ? "fights" : "actions"}.
+ ${have > 1 ? `` : ""} ${S.me.potions[it.potion.skill] && S.me.potions[it.potion.skill].item !== id ? `
Replaces your ${esc(item(S.me.potions[it.potion.skill].item).name.toLowerCase())} (${S.me.potions[it.potion.skill].charges} uses left)
` : ""}
` : ""} ${it.heal ? `
Heals ${it.heal} resting between fights.
${S.me.food === id ? `Your fighting food` : ``}
` : ""} @@ -2984,7 +2985,7 @@ document.addEventListener("click", (e) => { if (d.track) return act("/api/track", { id: d.track }); if (d.favour) return act("/api/favour", { kind: d.favour }); if (d.food) return act("/api/food", { item: d.food === "cheapest" ? null : d.food }); - if (d.potion) return act("/api/potion", { item: d.potion }); + if (d.potion) return act("/api/potion", { item: d.potion, qty: d.qty === "all" ? "all" : 1 }); if (d.unpost) { if (!confirm("Delete this message?")) return; return act("/api/board/delete", { id: Number(d.unpost) }); diff --git a/tests/test_game.py b/tests/test_game.py index 9915576..4d903ae 100644 --- a/tests/test_game.py +++ b/tests/test_game.py @@ -1636,6 +1636,18 @@ class HerbloreTest(unittest.TestCase): self.assertEqual(game.ITEMS["mining_potion_6"]["name"], "Mining potion VI") self.assertFalse(any("heal" in p[6] or "double" in p[6] for p in game.POTIONS)) + def test_drink_all_potions_stacks_charges(self): + p = self.p + p["bank"] = {"mining_potion_1": 3, "mining_potion_2": 1} + game.use_potion(p, "mining_potion_2") + self.assertIsNone(game.use_potion(p, "mining_potion_1", "all")) + self.assertEqual(p["potions"]["mining"], {"item": "mining_potion_1", "charges": 3 * game.POTION_CHARGES}) + self.assertNotIn("mining_potion_1", p["bank"]) + self.assertEqual(game.use_potion(p, "mining_potion_1", "all"), "You don't have one") + p["bank"]["mining_potion_1"] = 2 + self.assertEqual(game.use_potion(p, "mining_potion_1", 0), "Bad amount") + self.assertEqual(game.use_potion(p, "mining_potion_1", 3), "You don't have that many") + def test_skill_potion_speeds_up_and_uses_charges(self): world, p = self.world, self.p plain = game.action_time(world, p, game.ACTION_BY_ID["cut_logs"])