diff --git a/app.py b/app.py index 24dc624..ea42487 100644 --- a/app.py +++ b/app.py @@ -358,7 +358,10 @@ class Handler(BaseHTTPRequestHandler): if not isinstance(qty, int): return "Bad quantity" if path == "/api/action": - return game.start_action(world, player, str(body.get("id")), now, once=bool(body.get("once"))) + once = body.get("once") or 0 + if not isinstance(once, int) or not 0 <= once <= game.MAKE_MAX: + return f"1 to {game.MAKE_MAX:,}" + return game.start_action(world, player, str(body.get("id")), now, once=int(once)) if path == "/api/join": return game.join_fight(world, player, str(body.get("id")), now) if path == "/api/press": diff --git a/game.py b/game.py index a9edc37..1fa80af 100644 --- a/game.py +++ b/game.py @@ -3801,8 +3801,11 @@ def working_order(world: dict, player: dict, action: dict) -> dict | None: return order_by_id(world, current["order"]) -def start_action(world: dict, player: dict, action_id: str, now: float, once: bool = False) -> str | None: - """Start an action. `once` does it a single time, then goes back to what you were doing.""" +MAKE_MAX = 1000 # most you can ask for in one "Make X" + + +def start_action(world: dict, player: dict, action_id: str, now: float, once: int = 0) -> str | None: + """Start an action. `once` does it that many times, then goes back to what you were doing.""" action = ACTION_BY_ID.get(action_id) if not action: return "No such action" @@ -3813,7 +3816,7 @@ def start_action(world: dict, player: dict, action_id: str, now: float, once: bo current = player["action"] if once: back = current and (current.get("resume") or current["id"]) - entry["left"] = 1 + entry["left"] = int(once) if back and back != action_id: entry["resume"] = back plot2 = current.get("resume_plot2") or current.get("plot2") @@ -6081,6 +6084,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-26", "title": "Fittings", "notes": [ + ("new", "Make X: \"Make one\" now takes a count (1 by default, up to 1,000), then you go back to what you were doing."), ("new", "Construction › Fittings: ✓ Fitted on the ones the town already has (✓ Paused while the building is damaged)."), ]}, {"date": "2026-10-25", "title": "Titles", "notes": [ @@ -6236,7 +6240,7 @@ def content() -> dict: "city_uses": {item: city_uses(item) for item in ITEMS}, "prayers": [{**prayer, "text": describe_prayer(prayer)} for prayer in PRAYERS], "max_prayers": MAX_PRAYERS, "potion_charges": POTION_CHARGES, "order_recipes": ORDER_RECIPES, "order_raw": ORDER_RAW, - "order_limit": ORDER_LIMIT, "order_max": ORDER_MAX, "input_share": INPUT_SHARE, "problems": PROBLEMS, "fittings": list(FITTING_BY_ID.values()), + "order_limit": ORDER_LIMIT, "order_max": ORDER_MAX, "make_max": MAKE_MAX, "input_share": INPUT_SHARE, "problems": PROBLEMS, "fittings": list(FITTING_BY_ID.values()), "equip_slots": EQUIP_SLOTS, "class_speed": CLASS_SPEED, "rest_rate": REST_RATE, "bite": BITE, "eat_below": EAT_BELOW, "eat_choices": EAT_CHOICES, "abilities": combat.ABILITIES, "enemy_abilities": combat.ENEMY_ABILITIES, "unlock": combat.UNLOCK, "auto_cd": combat.AUTO_CD, "watch": combat.WATCH, "gcd": combat.GCD, "weapon_kits": combat.WEAPON_KITS, "group_size": GROUP_SIZE, "dungeon_size": DUNGEON_SIZE, "dungeon_wipes": DUNGEON_WIPES, "trade_ability": combat.TRADE_ABILITY, "potion_below": combat.POTION_BELOW, diff --git a/static/app.js b/static/app.js index 30495a3..e7e5c48 100644 --- a/static/app.js +++ b/static/app.js @@ -1712,12 +1712,14 @@ function actionCard(a) { ${me.action?.plot2 && (active || second) ? `