diff --git a/app.py b/app.py index 0947fdd..e39aab4 100644 --- a/app.py +++ b/app.py @@ -454,6 +454,8 @@ class Handler(BaseHTTPRequestHandler): if path == "/api/prayers": ids = body.get("ids") return game.set_prayers(player, ids) if isinstance(ids, list) else "Bad request" + if path == "/api/eat": + return game.set_eat_below(player, body.get("below")) if path == "/api/food": food = body.get("item") if food is not None and "heal" not in game.ITEMS.get(str(food), {}): diff --git a/game.py b/game.py index 089a343..e5196e6 100644 --- a/game.py +++ b/game.py @@ -26,6 +26,8 @@ MAX_LOG = 200 REGEN_PER_HOUR = 0.25 # share of max HP recovered per game hour REST_RATE = 0.01 # share of max HP back per game second resting between fights... BITE = 2.0 # ...and each bite of food takes this long, so food rests you faster +EAT_BELOW = 0.5 # unless you choose otherwise, you only eat while under this share of your HP... +EAT_CHOICES = (0, 0.25, 0.5, 0.75, 1.0) # ...and these are the choices (never, up to always) KNOCKED_OUT_HP = 0.3 # share of max HP you come round with after being knocked out KNOCKOUTS_TO_STOP = 3 # in a row, and you stop fighting @@ -2018,7 +2020,7 @@ def new_player(uid: str, name: str, cls: str, now: float) -> dict: "hp": 44, "hp_at": now, "action": None, "stopped": None, "last_active": now, "overflow": "city", - "perks": {"bank_slot": 0}, "manual": [], "patterns": [], + "perks": {"bank_slot": 0}, "manual": [], "patterns": [], "eat_below": EAT_BELOW, "rented": 0, "boosts": [], "bought": None, "deeds": {}, "owned": [], "look": dict(DEFAULT_LOOK), "garrison": False, "donated": 0, @@ -2211,14 +2213,16 @@ def _average_hit(max_hit: float, crit: float = CRIT_CHANCE) -> float: def rest_plan(world: dict | None, player: dict, hp: float | None = None) -> tuple[float, dict]: """Resting back to full before a fight: how many game seconds, and the food eaten on the way. - A bite is only taken when it's quicker than resting, so better food makes for shorter rests.""" + You eat while you're under the share of your HP you chose, and only when a bite is quicker than + resting, so better food makes for shorter rests. The rest of the way you rest.""" top = max_hp(player) missing = top - (player["hp"] if hp is None else hp) rate = top * REST_RATE * regen_boost(world, player) / ware(player, "rest", "combat") + hungry = top * (1 - player.get("eat_below", EAT_BELOW)) # eat while more than this is missing seconds, eaten = 0.0, {} for food in edible(player): heal, left = ITEMS[food]["heal"], player["bank"].get(food, 0) - while missing > 0 and left and min(heal, missing) / rate > BITE: + while missing > hungry and missing > 0 and left and min(heal, missing) / rate > BITE: eaten[food] = eaten.get(food, 0) + 1 left -= 1 missing -= heal @@ -2398,6 +2402,14 @@ def _input(world: dict, player: dict, now: float, kind: str, *args) -> str | Non return None +def set_eat_below(player: dict, share) -> str | None: + """Below what share of your HP you eat between fights (0: never, 1: whenever it's quicker).""" + if share not in EAT_CHOICES: + return "Pick one of the choices" + player["eat_below"] = share + return None + + def learn_pattern(player: dict, item: str) -> str | None: """Read a pattern: from then on you can reforge its masterwork. The pattern's used up.""" spec = ITEMS.get(item, {}) @@ -5964,6 +5976,8 @@ PATCH_NOTES = [ ("new", "One unique per dungeon from its last boss (8% each), worn as a ring, amulet or trinket, that changes an " "ability: One-Eye's fang, Ogre's knuckle, Barrow king's pendant, Warlord's whistle, Forgeheart, Rimeglass " "lens, Wyrmscale quiver. Abilities it changes get a ✦."), + ("new", "Choose when you eat between fights: below 25 / 50 / 75% of your HP, always, or never (the food slot, " + "under \"Eat below\"). Default 50%: above it you just rest, so a scratch doesn't cost food."), ]}, {"date": "2026-10-24", "title": "A dungeon in every area", "notes": [ ("new", "Six more dungeons for 2–4, seven pulls each with two mini-bosses and a final boss: Goblin Warren " @@ -6081,7 +6095,7 @@ def content() -> dict: "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()), - "equip_slots": EQUIP_SLOTS, "class_speed": CLASS_SPEED, "rest_rate": REST_RATE, "bite": BITE, + "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, "styles": STYLES, "damage_types": DAMAGE_TYPES, "weights": WEIGHTS, "vulnerable": VULNERABLE, diff --git a/static/app.js b/static/app.js index fa820e1..a0b8e76 100644 --- a/static/app.js +++ b/static/app.js @@ -2180,8 +2180,12 @@ function foodPanel(p) { const tiles = [...p.edible].sort((a, b) => item(a).heal - item(b).heal).map((f) => ``); const cheap = ``; + const below = p.eat_below ?? C.eat_below; + const label = (x) => x === 0 ? "Never" : x === 1 ? "Always" : `${Math.round(x * 100)}%`; + const when = `