diff --git a/app.py b/app.py
index 6d81e58..a616e49 100644
--- a/app.py
+++ b/app.py
@@ -413,6 +413,8 @@ class Handler(BaseHTTPRequestHandler):
return game.buy_perk(player, str(body.get("perk")))
if path == "/api/pin":
return game.toggle_pin(player, str(body.get("skill")))
+ if path == "/api/track":
+ return game.toggle_track(player, str(body.get("id")))
if path == "/api/settings":
if body.get("overflow") not in ("city", "stop"):
return "Bad setting"
diff --git a/game.py b/game.py
index 6c96772..b0adba1 100644
--- a/game.py
+++ b/game.py
@@ -1090,7 +1090,7 @@ RAID_TRAITS = ["lightly armoured, heavy on archers", "mounted and fast, few bows
# --- world ---------------------------------------------------------------
-WORLD_VERSION = 21
+WORLD_VERSION = 22
def new_world(now: float, speed: float = 1.0) -> dict:
@@ -1253,6 +1253,10 @@ def migrate(world: dict, now: float | None = None) -> dict:
for slot in EQUIP_SLOTS:
player["equip"].setdefault(slot, None)
world["version"] = 21
+ if world["version"] < 22:
+ for player in world["players"].values():
+ player.setdefault("tracked", None)
+ world["version"] = 22
return world
@@ -1513,6 +1517,7 @@ def new_player(uid: str, name: str, cls: str, now: float) -> dict:
"garrison": False,
"donated": 0,
"pinned": [], # skills pinned to the top of the sidebar
+ "tracked": None, # recipe shown under the top bar
}
@@ -3962,6 +3967,14 @@ def toggle_pin(player: dict, skill: str) -> str | None:
return None
+def toggle_track(player: dict, action_id: str) -> str | None:
+ action = ACTION_BY_ID.get(action_id)
+ if not action or not action["inputs"]:
+ return "No such recipe"
+ player["tracked"] = None if player["tracked"] == action_id else action_id
+ return None
+
+
# --- icons ------------------------------------------------------------------------
# Every item, skill, class, building, prayer and output-less action gets a
# game-icons.net glyph (CC BY 3.0) and a tint. Families share a shape and are
diff --git a/static/app.js b/static/app.js
index eb63973..bcbf497 100644
--- a/static/app.js
+++ b/static/app.js
@@ -384,7 +384,21 @@ function topbar() {
const w = S.happenings?.weather;
if (w) chips += ``;
if (S.upcoming) chips += ``;
- return `
${esc(title)}
${chips}`;
+ return `
${esc(title)}
${chips}${S.me ? trackedBar() : ""}`;
+}
+
+// The recipe you're after, under the top bar on every page: have / need for each input.
+function trackedBar() {
+ const me = S.me, a = me.tracked && C.actions.find((x) => x.id === me.tracked);
+ if (!a) return "";
+ const ready = Object.entries(a.inputs).every(([id, q]) => (me.bank[id] || 0) >= q);
+ const go = !me.blocked[a.id] && me.action?.id !== a.id;
+ return `