diff --git a/game.py b/game.py index eaacd3b..669e9da 100644 --- a/game.py +++ b/game.py @@ -281,14 +281,14 @@ def describe_potion(skill: str, effect: dict) -> str: if "speed" in effect: return f"{name.title()} {round(effect['speed'] * 100)}% faster" if "double" in effect: - return f"{name.title()}: {round(effect['double'] * 100)}% chance of double yield" + return f"{name.title()}: {round(effect['double'] * 100)}% double yield" if "keep" in effect: - return f"{name.title()}: {round(effect['keep'] * 100)}% chance to keep your materials" + return f"{name.title()}: {round(effect['keep'] * 100)}% save materials" if "bones" in effect: - return f"+{round(effect['bones'] * 100)}% prayer points and devotion from bones" + return f"+{round(effect['bones'] * 100)}% from bones" if "power" in effect: - return f"+{effect['power']} combat power" - return f"-{round(effect['reduce'] * 100)}% damage taken in fights" + return f"+{effect['power']} power" + return f"-{round(effect['reduce'] * 100)}% damage taken" for _id, _name, _icon, _level, _time, _xp, _value in HERBS: @@ -328,31 +328,31 @@ CLASSES = { "miner": { "name": "Miner", "icon": "⛏️", "fast": ["mining", "smithing"], "craft": ["smithing"], "bonus": "Mining and smithing 20% faster", - "masterwork": "Masterwork: 1 in 10 smithing crafts make double", - "signature": "Prospector: 1 in 10 mining trips bring back double", - "combat": "Warhammer: +15% damage with one. Slow, heavy, crushes golems", + "masterwork": "Masterwork: 10% double smithing crafts", + "signature": "Prospector: 10% double ore", + "combat": "Warhammer: +15% damage", }, "builder": { "name": "Builder", "icon": "🏗️", "fast": ["woodcutting", "construction"], "craft": ["construction"], - "bonus": "Woodcutting and construction (masonry included) 20% faster", - "masterwork": "Masterwork: 1 in 10 construction crafts make double", - "signature": "Foreman: +1 labour every time you work a building site", - "combat": "Sword and shield: +15% damage with a sword. The shield blocks, and trolls fall to blades", + "bonus": "Woodcutting and construction 20% faster", + "masterwork": "Masterwork: 10% double construction crafts", + "signature": "Foreman: +1 labour per site action", + "combat": "Sword: +15% damage", }, "homesteader": { "name": "Homesteader", "icon": "🌾", "fast": ["cooking", "herblore"], "craft": ["cooking", "herblore"], - "bonus": "Cooking and herblore 20% faster. Two plots: farm one crop 30% faster, or two crops at once", - "masterwork": "Masterwork: 1 in 10 cooking and herblore crafts make double", - "signature": "Steady hand: potions you drink last 25% longer", - "combat": "Staff: +15% damage with one. Rarely misses, ignores armour, and spirits fear it", + "bonus": "Cooking and herblore 20% faster, two farm plots", + "masterwork": "Masterwork: 10% double cooking and herblore crafts", + "signature": "Steady hand: potions last 25% longer", + "combat": "Staff: +15% damage", }, "ranger": { "name": "Ranger", "icon": "🏹", "fast": ["hunting", "fishing", "leatherworking", "cooking"], "craft": ["leatherworking", "cooking"], "bonus": "Hunting, fishing, leatherworking and cooking 20% faster", - "masterwork": "Masterwork: 1 in 10 leatherworking and cooking crafts make double", - "signature": "Pathfinder: your scouting gathers 50% more reconnaissance", - "combat": "Bow: +15% damage with one. Fast, strikes first, and brings down flyers", + "masterwork": "Masterwork: 10% double leatherworking and cooking crafts", + "signature": "Pathfinder: +50% recon", + "combat": "Bow: +15% damage", }, } # Classes from before the trades, as a starting point until their player re-picks. @@ -639,7 +639,7 @@ BUILDINGS = { "advanced": {"oak_logs": 100, "leather": 80, "rope": 40, "iron_nails": 150}, "late": {"willow_logs": 80, "oak_plank": 35, "hide_glue": 50, "steel_hinges": 20}, "final": {"maple_logs": 50, "willow_plank": 30, "hard_leather": 60, "mithril_brackets": 15}, - "effect": "Room for 20 more people per level", + "effect": "+20 homes per level", }, "walls": { "name": "Walls", "icon": "🏰", "max": 5, "gold": 300, @@ -647,7 +647,7 @@ BUILDINGS = { "advanced": {"cut_stone": 150, "beam": 20, "rope": 50, "iron_nails": 200}, "late": {"steel_bar": 60, "oak_plank": 25, "hide_glue": 50, "steel_hinges": 30}, "final": {"mithril_brackets": 60, "maple_plank": 20, "hide_glue": 60, "adamant_chain": 10}, - "effect": "+20 defence per level, and raiders carry off less", + "effect": "+20 defence per level, less plunder lost", }, "barracks": { "name": "Barracks", "icon": "⛺", "max": 5, "gold": 300, @@ -655,7 +655,7 @@ BUILDINGS = { "advanced": {"iron_bar": 60, "hard_leather": 40}, "late": {"steel_bar": 40, "oak_plank": 30, "hide_glue": 40}, "final": {"mithril_bar": 25, "willow_plank": 25, "bear_leather": 40}, - "effect": "Trains townsfolk as militia: more defence per citizen", + "effect": "More defence per townsperson", }, "smithy": { "name": "Smithy", "icon": "⚒️", "max": 5, "gold": 250, @@ -663,42 +663,42 @@ BUILDINGS = { "advanced": {"iron_bar": 180, "leather": 70}, "late": {"steel_bar": 100, "oak_plank": 25, "hide_glue": 40}, "final": {"mithril_bar": 70, "maple_plank": 20, "bear_leather": 35}, - "effect": "Smithing 8% faster per level; each level unlocks a metal: iron, steel, mithril, adamant, rune", + "effect": "Smithing 8% faster and one more metal per level", }, "temple": { "name": "Temple", "icon": "⛪", "max": 3, "gold": 300, "base": {"stone": 700, "bricks": 200, "cut_stone": 60, "provisions": 60}, "advanced": {"beam": 15, "rope": 40, "iron_nails": 150}, "late": {"willow_logs": 80, "oak_plank": 35, "hide_glue": 40, "steel_hinges": 20}, - "effect": "Offerings ward the city in raids; +1 prayer point per bone buried and +25% devotion per level", + "effect": "Per level: +1 prayer point per bone, +25% devotion", }, "granary": { "name": "Granary", "icon": "🌾", "max": 3, "gold": 150, "base": {"logs": 200, "bricks": 100, "plank": 40, "thatch": 300, "provisions": 50, "copper_rivets": 40}, "advanced": {"cut_stone": 40, "rope": 40, "iron_nails": 100}, "late": {"oak_plank": 35, "hide_glue": 35, "steel_hinges": 15}, - "effect": "Protects a quarter of the food from locusts and rats per level", + "effect": "Guards 25% of food from pests per level", }, "lumber_shed": { "name": "Lumber shed", "icon": "🪵", "max": 3, "gold": 120, "base": {"logs": 200, "stone": 250, "plank": 50, "thatch": 180, "provisions": 40, "copper_rivets": 40}, "advanced": {"oak_plank": 40, "rope": 40, "iron_nails": 100}, "late": {"willow_plank": 25, "hide_glue": 35}, - "effect": "Keeps a quarter of the stored wood safe from rot per level", + "effect": "Guards 25% of wood from rot per level", }, "well": { "name": "Well", "icon": "🪣", "max": 3, "gold": 120, "base": {"stone": 500, "bricks": 80, "rope": 50, "provisions": 40}, "advanced": {"cut_stone": 40, "leather": 60, "iron_nails": 125}, "late": {"oak_plank": 25, "hide_glue": 35, "steel_hinges": 15}, - "effect": "Each level cuts fire losses by a quarter and may save the building", + "effect": "Fire damage -25% per level", }, "spymaster": { "name": "Spymaster's guild", "icon": "🕵️", "max": 3, "gold": 400, "base": {"logs": 200, "bricks": 150, "hide": 120, "feathers": 300, "provisions": 60}, "advanced": {"cut_stone": 80, "hard_leather": 40, "iron_nails": 100}, "late": {"bear_leather": 50, "oak_plank": 25, "steel_hinges": 15}, - "effect": "+25% reconnaissance per level, and its agents gather some every hour on their own", + "effect": "+25% recon per level, plus some each hour", }, "storehouse": { "name": "Storehouse", "icon": "📦", "max": 5, "gold": 200, @@ -706,7 +706,7 @@ BUILDINGS = { "advanced": {"oak_logs": 150, "cut_stone": 100, "rope": 50, "iron_nails": 150}, "late": {"willow_logs": 120, "oak_plank": 45, "hide_glue": 45, "steel_hinges": 25}, "final": {"yew_logs": 40, "yew_plank": 20, "bear_leather": 40, "rune_lock": 6}, - "effect": "+3 bank slots and +250 per stack for everyone, per level", + "effect": "Per level: +3 bank slots, +250 stack size", }, } # Buildings rise by labour: players working the site, and a slow trickle from @@ -1285,9 +1285,9 @@ def describe_prayer(prayer: dict) -> str: if prayer.get("power"): parts.append(f"+{prayer['power']} power") if prayer.get("regen"): - parts.append("HP recovers twice as fast while fighting") + parts.append("HP recovers twice as fast") if prayer.get("wind"): - parts.append("once per outing, a retreat becomes a second wind: back to half health") + parts.append("once per outing, revive at half HP") return ", ".join(parts) @@ -2269,7 +2269,7 @@ def event_view(world: dict, event: dict, now: float) -> dict: view["sabotage"] = round(event["sabotage"] * 100) view["requests"] = council_requests(world, event) elif event["kind"] == "merchant": - view["headline"] = "A merchant caravan is on its way. Everything sells for half again as much while it's here." + view["headline"] = "A merchant caravan is on its way. Sell prices +50% while it's here." else: view["headline"] = f"About {event['people']} refugees are walking towards the city, hoping for a roof." return view @@ -2481,15 +2481,15 @@ SEASON_DAYS = 7 # `speed` multiplies action time (below 1 is faster), `yield` multiplies output, # `food` multiplies what the townsfolk eat, `firewood` is logs per person per hour. WEATHER = { - "clear": {"name": "Clear skies", "icon": "🌤️", "text": "No effect on work or the city"}, + "clear": {"name": "Clear skies", "icon": "🌤️", "text": "No effects"}, "rain": {"name": "Rain", "icon": "🌧️", "speed": {"farming": 0.9}, "text": "Farming 10% faster"}, "heat": {"name": "Heat wave", "icon": "🥵", "speed": {"farming": 0.85}, "food": 1.15, - "text": "Farming 15% faster, but food spoils: the city eats 15% more"}, + "text": "Farming 15% faster, city eats 15% more"}, "storm": {"name": "Storm", "icon": "⛈️", "speed": {"fishing": 1.25}, "text": "Fishing 25% slower"}, "fog": {"name": "Fog", "icon": "🌫️", "speed": {"reconnaissance": 1.3, "hunting": 1.1}, "text": "Scouting 30% and hunting 10% slower"}, "snow": {"name": "Snow", "icon": "🌨️", "speed": {"hunting": 1.2}, "firewood": 0.05, - "text": "Hunting 20% slower, and the city burns firewood"}, + "text": "Hunting 20% slower, burns firewood"}, } SEASON_WEATHER = [ {"clear": 35, "rain": 40, "storm": 10, "fog": 15}, @@ -2504,23 +2504,23 @@ WOOD = FIREWOOD + tuple(plank for plank, _name in PLANKS) # either by working a special action (`work` completions) or by donating (`need`). PROBLEMS = { "collapse": {"name": "Mine collapse", "icon": "⛰️", "need": {"plank": 30}, - "block": {"mining": "The mine has collapsed: shore it up with planks"}, "allow": ["mine_stone"], - "text": "A shaft caved in. Ore can't be mined until the mine is shored up with planks."}, + "block": {"mining": "Mine collapsed"}, "allow": ["mine_stone"], + "text": "No ore until it's shored up with planks."}, "wolves": {"name": "Wolves at the farms", "icon": "🐺", "yield": {"farming": 0.5}, "work": 10, "action": "clear_wolves", - "text": "A pack is harrying the fields. Farming yields are halved until fighters drive them off."}, + "text": "Farming yield halved until fighters drive them off."}, "bear": {"name": "Bear in the woods", "icon": "🐻", "speed": {"woodcutting": 1.5}, "work": 8, "action": "drive_bear", - "text": "Woodcutters won't go near it. Woodcutting is much slower until hunters drive it off."}, + "text": "Woodcutting much slower until hunters drive it off."}, "goblins": {"name": "Goblin thieves", "icon": "👺", "steal": 0.01, "work": 10, "action": "catch_goblins", - "text": "Goblins raid the stockpile every hour until fighters catch them."}, + "text": "Steal from the stockpile hourly until fighters catch them."}, "rockslide": {"name": "Rockslide on the quarry road", "icon": "🪨", "work": 25, "action": "clear_rockslide", - "block": {"mining": "The quarry road is blocked by a rockslide"}, "only": ["mine_stone"], - "text": "No stone can be quarried until builders clear the road."}, + "block": {"mining": "Quarry road blocked"}, "only": ["mine_stone"], + "text": "No stone until builders clear it."}, "flood": {"name": "Flooded fishing grounds", "icon": "🌊", "speed": {"fishing": 1.5}, "need": {"plank": 20}, "lasts": 36, - "text": "The river burst its banks. Fishing is slow until it drops in a day and a half, or a dam of planks holds it back."}, + "text": "Fishing slow for 36h, or until dammed with planks."}, "boars": {"name": "Boars trampling the crops", "icon": "🐗", "yield": {"farming": 0.6}, "work": 10, "action": "cull_boars", - "text": "A herd is trampling the fields. Farming yields are down until hunters cull them."}, + "text": "Farming yield down until hunters cull them."}, "rats": {"name": "Rats in the granary", "icon": "🐀", "steal_food": 0.03, "work": 12, "action": "catch_rats", - "text": "Rats eat into the city's food every hour until someone catches them."}, + "text": "Eat city food hourly until hunters catch them."}, } MINOR_THREATS = ("collapse", "wolves", "bear", "goblins", "rockslide", "flood", "boars") PROBLEM_ACTIONS = [ @@ -2547,15 +2547,15 @@ ACTION_BY_ID[_barrel["id"]] = _barrel # id, name, building, building level, construction level, inputs, effect, glyph, tint FITTINGS = [ ("grain_bins", "Grain bins", "granary", 1, 15, {"oak_plank": 20, "iron_nails": 25}, - "The city eats 10% less food", "grain-bundle", "#e3c060"), + "City eats 10% less", "grain-bundle", "#e3c060"), ("saw_benches", "Saw benches", "lumber_shed", 1, 15, {"oak_plank": 20, "iron_nails": 40}, - "Sawing planks is 20% faster", "hand-saw", "#8f5e30"), + "Planks 20% faster", "hand-saw", "#8f5e30"), ("shelving", "Shelving", "storehouse", 2, 20, {"oak_plank": 40, "iron_nails": 40}, - "+1 bank slot for everyone", "bookshelf", "#8f5e30"), + "+1 bank slot", "bookshelf", "#8f5e30"), ("map_table", "Map table", "spymaster", 1, 30, {"willow_plank": 25, "hard_leather": 10, "steel_hinges": 4}, - "Scouting gathers one more point of reconnaissance", "treasure-map", "#a3b86c"), + "+1 recon per scout", "treasure-map", "#a3b86c"), ("well_pump", "Well pump", "well", 2, 30, {"willow_plank": 25, "steel_hinges": 10}, - "Half of all fires are put out before they spread", "water-tank", "#5aa0d8"), + "Stops half of all fires", "water-tank", "#5aa0d8"), ("furniture", "Furniture", "houses", 2, 35, {"willow_plank": 40, "leather": 30, "steel_hinges": 10}, "Population grows 25% faster", "sofa", "#a3b86c"), ("weapon_racks", "Weapon racks", "barracks", 2, 40, {"willow_plank": 30, "steel_hinges": 16}, @@ -2563,13 +2563,13 @@ FITTINGS = [ ("hoardings", "Hoardings", "walls", 3, 45, {"maple_plank": 40, "mithril_brackets": 20}, "+25 defence", "palisade", "#d0663a"), ("bellows", "Great bellows", "smithy", 3, 50, {"maple_plank": 30, "bear_leather": 15, "mithril_brackets": 6}, - "Smithing another 10% faster", "bellows", "#d0663a"), + "Smithing 10% faster", "bellows", "#d0663a"), ("carved_altar", "Carved altar", "temple", 2, 60, {"yew_plank": 30, "cut_stone": 60, "adamant_chain": 4}, - "+1 prayer point per bone buried, and offerings can ward 15 more", "star-altar", "#6f9a4a"), + "+1 prayer point per bone, ward +15", "star-altar", "#6f9a4a"), ("enchanted_chests", "Enchanted chests", "storehouse", 4, 75, {"magic_plank": 40, "rune_lock": 6}, - "+500 per bank stack for everyone", "open-treasure-chest", "#a77be8"), + "+500 stack size", "open-treasure-chest", "#a77be8"), ("enchanted_hearths", "Enchanted hearths", "houses", 4, 80, {"magic_plank": 50, "red_dragon_leather": 5, "adamant_chain": 6}, - "Everyone recovers HP twice as fast", "fireplace", "#a77be8"), + "HP recovers twice as fast", "fireplace", "#a77be8"), ] # Builders' shields, homesteaders' staves and rangers' bows, after each skill's other tabs. for _weapon in _weapon_actions(): @@ -2748,7 +2748,7 @@ def _hardship(world: dict, now: float, rng: random.Random, kind: str | None = No if kind == "sickness": hours = 72 * (1 - 0.2 * building_level(world, "temple")) _add_effect(world, now, hours, kind="sickness", name="Sickness", icon="🤒", food=1.5, - text="The sick need more food: the city eats half again as much") + text="City eats 50% more") _record(world, now, "hardship", kind, "🤒", "Sickness is spreading through the city", [f"For about {round(hours)} hours the townsfolk eat half again as much.", "A bigger temple shortens it."]) @@ -2795,7 +2795,7 @@ def _hardship(world: dict, now: float, rng: random.Random, kind: str | None = No _record(world, now, "hardship", kind, "🔥", "Fire swept through part of the city", lines) elif kind == "winter": _add_effect(world, now, 48, kind="winter", name="Harsh winter", icon="🥶", firewood=0.1, - text="The city burns firewood from the stockpile every hour; without it, people go cold and hungry") + text="Burns stockpile logs hourly") _record(world, now, "hardship", kind, "🥶", "A harsh cold snap has set in", ["For two days the city burns logs from the stockpile to keep warm.", "Without firewood, the cold makes everyone eat half again as much."]) @@ -2828,27 +2828,27 @@ def _fortune(world: dict, now: float, rng: random.Random, kind: str | None = Non skill = rng.choice([s for s in SKILL_IDS if s != "reconnaissance"]) name = next(s["name"] for s in SKILLS if s["id"] == skill) _add_effect(world, now, 12, kind="master", name=f"A master of {name.lower()}", icon="🎓", - xp={skill: 1.5}, text=f"{name} earns 50% more XP") + xp={skill: 1.5}, text=f"{name} +50% XP") _record(world, now, "fortune", kind, "🎓", f"A travelling master of {name.lower()} is visiting", [f"For 12 hours, {name.lower()} earns 50% more XP. Learn while you can."]) elif kind == "harvest": _add_effect(world, now, 12, kind="harvest", name="Bountiful harvest", icon="🌾", **{"yield": {"farming": 2}}, - text="Farming yields doubled") + text="Farming yield doubled") _record(world, now, "fortune", kind, "🌾", "The fields are bursting: a bountiful harvest", ["Farming yields are doubled for 12 hours."]) elif kind == "rich_vein": _add_effect(world, now, 12, kind="rich_vein", name="Rich vein", icon="💎", **{"yield": {"mining": 1.5}}, - text="Mining yields 50% more") + text="Mining yield +50%") _record(world, now, "fortune", kind, "💎", "Miners struck a rich vein", ["Mining yields 50% more for 12 hours."]) elif kind == "salmon": _add_effect(world, now, 12, kind="salmon", name="Salmon run", icon="🐟", **{"yield": {"fishing": 2}}, - text="Fishing yields doubled") + text="Fishing yield doubled") _record(world, now, "fortune", kind, "🐟", "The salmon are running", ["Fishing yields are doubled for 12 hours."]) elif kind == "festival": _add_effect(world, now, 24, kind="festival", name="Festival", icon="🎉", growth=2, favour=1.5, - text="The population grows twice as fast, and giving to the city earns 50% more favour") + text="Growth doubled, +50% favour from donations") _record(world, now, "fortune", kind, "🎉", "The city is holding a festival", ["For a day, the population grows twice as fast and donations earn 50% more favour."]) elif kind == "noble": @@ -2870,7 +2870,7 @@ def _fortune(world: dict, now: float, rng: random.Random, kind: str | None = Non elif kind == "mercenaries": cost = 150 + 30 * season(world, now)["index"] + int(city["population"]) * 5 _add_effect(world, now, 24, kind="mercenaries", name="Mercenaries for hire", icon="🗡️", offer=True, - cost=cost, defence=25, text=f"A sellsword company offers +25 defence for the coming threat for {cost:,} gold from the treasury") + cost=cost, defence=25, text="+25 defence for the coming threat") _record(world, now, "fortune", kind, "🗡️", "A sellsword company is camped outside the walls", [f"For a day they'll join the next defence for {cost:,} gold from the treasury."]) else: diff --git a/static/app.js b/static/app.js index efe6c37..9b73cd8 100644 --- a/static/app.js +++ b/static/app.js @@ -150,8 +150,8 @@ function showSummary(sum) { if (sum.stopped) toast(`Stopped: ${sum.stopped}`, "bad"); for (const id of sum.potion_out || []) toast(`🧪 Your ${item(id).name.toLowerCase()} ran out`, "bad"); for (const id of Object.keys(sum.potions_drunk || {})) toast(`🧪 Drank another ${item(id).name.toLowerCase()}`); - if (sum.prayer_out) toast("🙏 Out of prayer points: your prayers have gone quiet", "bad"); - if (sum.second_wind) toast("🌬️ Second Wind! Back on your feet at half health", "level"); + if (sum.prayer_out) toast("🙏 Out of prayer points", "bad"); + if (sum.second_wind) toast("🌬️ Second Wind! Back to half HP", "level"); if (sum.resumed) toast(`Back to ${C.actions.find((x) => x.id === sum.resumed).name.toLowerCase()}`); } @@ -169,8 +169,8 @@ function awayModal(sum) { ${sum.gold ? `

+${fmt(sum.gold)} gold

` : ""} ${xp ? `` : ""} ${levels ? `` : ""} - ${sold ? `

Your bank was full, so this was sold for ${fmt(sum.sold_gold)} gold:

` : ""} - ${spilled ? `

Your bank was full, so this went to the city stockpile (for half the favour of giving it yourself):

` : ""} + ${sold ? `

Bank full, sold for ${fmt(sum.sold_gold)} gold:

` : ""} + ${spilled ? `

Bank full, sent to the stockpile:

` : ""} ${sum.stopped ? `

Stopped: ${esc(sum.stopped)}

` : ""}

`); } @@ -230,7 +230,7 @@ function sidebar() {
${esc(me.name)}
${cls.icon} ${cls.name} · ${HEART} ${fmt(me.hp)}/${me.max_hp}
-
${COIN} ${fmt(me.gold)}${FAVOUR} ${fmt(me.favour)}
+
${COIN} ${fmt(me.gold)}${FAVOUR} ${fmt(me.favour)}
${nav("town", "🏘️", "Town square", `${S.city.population}`, "", `Population ${S.city.population}`)} @@ -342,11 +342,11 @@ function skillPage(id) { if (id === "combat") html += fightPanel() + combatPanel(); if (id === "prayer") html += prayerPanel(); if (id === "construction") html += S.city.construction ? siteBanner() - : `
Nothing is being built right now. Fund a building on the Town square and its site opens here.
`; + : `
Nothing under construction.
`; if (id === "farming" && me.cls === "homesteader") html += plotsPanel(); html += potionPanel(id); if (`tool_${id}` in me.equip) html += toolPanel(id); - if (id === "reconnaissance") html += `
Reconnaissance on a coming threat raises its intel. Each level takes more than the last, a bigger band takes longer to read, and the sharpest news (the exact day and their camp) only comes in the last hours before they strike. The spymaster's guild adds to every point and gathers some on its own. The council & threats page shows the progress.
`; + if (id === "reconnaissance") html += `
Raises intel on the coming threat. Higher levels open as it nears.
`; html += skillHappenings(id); const live = new Set(S.happenings.problems.map((p) => p.kind)); const listed = C.actions.filter((a) => a.skill === id && (a.special !== "problem" || live.has(a.problem))); @@ -384,9 +384,9 @@ function isLocked(a) { function plotsPanel() { const a = S.me.action, farming = a && C.actions.find((x) => x.id === a.id)?.skill === "farming"; const name = (id) => esc(C.actions.find((x) => x.id === id).name); - const status = !farming ? `Start a crop to plant your first plot.` - : a.plot2 ? `Plot 1: ${name(a.id)} · Plot 2: ${name(a.plot2)} · harvests alternate, together 20% faster than one crop` - : `Plot 1: ${name(a.id)} · Plot 2: empty, so you're focused: 30% faster. Use "Plot 2" on another crop to grow both.`; + const status = !farming ? `Start a crop to plant plot 1.` + : a.plot2 ? `Plot 1: ${name(a.id)} · Plot 2: ${name(a.plot2)} · alternating, 20% faster` + : `Plot 1: ${name(a.id)} · Plot 2: empty · focused, 30% faster`; return `
🌱 ${status}
`; } @@ -402,7 +402,7 @@ function potionPanel(skill) { ? `${item(active.item).icon} ${esc(item(active.item).name)} ${esc(item(active.item).potion.text)} · ${fmt(active.charges)} uses left${me.bank[active.item] ? `, ${fmt(me.bank[active.item])} more in the bank` : ""}` : `No potion.`} ${!active && drink ? `Drink: ${drink}` : ""} - ${!active && !drink ? `Herblore can brew ${kinds.length === 1 ? "one" : "some"} for ${skill === "combat" ? "fighting" : esc(skillOf(skill).name.toLowerCase())}.` : ""} + ${!active && !drink ? `Brewed in Herblore.` : ""} `; } @@ -413,7 +413,7 @@ function prayerPanel() { const on = me.prayers.includes(p.id), locked = level < p.level; const full = !on && me.prayers.length >= C.max_prayers; return `` - : `No tool. A smith can make you one that speeds this up.`}`; + : `No tool.`}`; } // What a monster is weak to and shrugs off, and how your weapon fares against it. @@ -525,13 +525,13 @@ function animateFight(now) { function styleNote(me) { const weapon = me.equip.weapon, styleId = (weapon && item(weapon).style) || "sword", st = C.styles[styleId]; const own = C.class_style[me.cls], traits = { - sword: `a swing every ${st.interval}s${me.equip.shield ? `, and your shield blocks ${Math.round(C.block_chance * 100)}% of hits` : ""}`, - hammer: `slow (${st.interval}s) heavy blows, twice the crits`, - staff: `a cast every ${st.interval}s that rarely misses and goes through armour`, - bow: `fast (${st.interval}s), and you shoot first`, + sword: `${st.interval}s${me.equip.shield ? `, shield blocks ${Math.round(C.block_chance * 100)}%` : ""}`, + hammer: `${st.interval}s, double crits`, + staff: `${st.interval}s, accurate, ignores armour`, + bow: `${st.interval}s, shoots first`, }; - return `

Fighting with ${weapon ? `a ${esc(st.name)}` : "your fists"}: ${traits[styleId]}. - ${own === styleId ? `Your trade's weapon: +15% damage.` : `As a ${esc(C.classes[me.cls].name.toLowerCase())} you hit 15% harder with a ${esc(C.styles[own].name)}.`}

`; + return `

${weapon ? st.name[0].toUpperCase() + st.name.slice(1) : "Fists"}: ${traits[styleId]}. + ${own === styleId ? `Trade weapon: +15% damage.` : `Trade weapon: ${esc(C.styles[own].name)} (+15%).`}

`; } function combatPanel() { @@ -551,15 +551,14 @@ function combatPanel() {
-

${skillOf("prayer").icon} ${me.prayers.length ? `${me.prayers.map((id) => esc(C.prayers.find((p) => p.id === id).name)).join(" + ")} · ${fmt(me.pp)} points${me.blessing.cost ? "" : ` (not enough points: prayers are off)`}` : `No prayers active.`} +

${skillOf("prayer").icon} ${me.prayers.length ? `${me.prayers.map((id) => esc(C.prayers.find((p) => p.id === id).name)).join(" + ")} · ${fmt(me.pp)} points${me.blessing.cost ? "" : ` (out of points)`}` : `No prayers active.`}

-

Below half health you eat from your bank automatically. - ${me.edible.length ? "" : `You have no food in your bank.`} - Out of food and badly hurt, you retreat: the fight is lost, but nothing else is.

+

Eats below half HP, retreats when out of food. + ${me.edible.length ? "" : `No food in your bank.`}

Equipment

${slot("weapon", "weapon")}${slot("shield", "shield")}${slot("helmet", "helmet")}${slot("body", "body armour")}${slot("legs", "leg armour")}
-

Power ${me.power} (level + weapon + shield + half your armour) · armour ${me.armour} takes ${Math.round(me.reduction * 100)}% off the damage you take. Equip gear from the bank.

+

Power ${me.power} · armour ${me.armour} (-${Math.round(me.reduction * 100)}% damage)

${styleNote(me)}
`; } @@ -569,20 +568,20 @@ function itemStats(id) { const it = item(id); const parts = []; if (it.heal) parts.push(`❤️ Heals ${it.heal}`); - if (it.food) parts.push(`feeds ${it.food} ${it.food === 1 ? "person" : "people"} for two days`); + if (it.food) parts.push(`feeds ${it.food} for 2 days`); if (it.power) parts.push(`+${it.power} power`); if (it.style) { const st = C.styles[it.style], trade = Object.entries(C.class_style).find(([, s]) => s === it.style)?.[0]; - parts.push(`${it.two_handed ? "two-handed " : ""}${st.name}, a swing every ${st.interval}s${trade ? ` (the ${C.classes[trade].name.toLowerCase()}'s weapon)` : ""}`); + parts.push(`${it.two_handed ? "two-handed " : ""}${st.name}, ${st.interval}s${trade ? ` (${C.classes[trade].name.toLowerCase()})` : ""}`); } if (it.armour) parts.push(`+${it.armour} armour`); if (it.tool) parts.push(`${Math.round(it.speed * 100)}% faster ${skillOf(it.tool).name.toLowerCase()}`); if (it.req && it.req[1] > 1) parts.push(`needs ${skillOf(it.req[0]).name.toLowerCase()} ${it.req[1]}`); if (it.potion) parts.push(`${it.potion.text}, ${C.potion_charges} uses`); - if (it.trophy) parts.push(`An expedition trophy: stronger weapons will need these`); + if (it.trophy) parts.push(`Trophy for stronger weapons`); if (it.fitting) { const f = C.fittings.find((x) => x.id === id), done = S.city.buildings[f.building].fittings.includes(id); - parts.push(`${C.buildings[f.building].icon} For the ${C.buildings[f.building].name.toLowerCase()} (Lv ${f.level}): ${f.effect}${done ? " · already fitted" : ""}`); + parts.push(`${C.buildings[f.building].icon} ${C.buildings[f.building].name} Lv ${f.level}: ${f.effect}${done ? " · fitted" : ""}`); } return parts.join(", "); } @@ -609,23 +608,22 @@ function actionCard(a) { if (a.enemy) { const o = me.fight_odds[a.id], e = a.enemy, pct = (x) => `${Math.round(x * 100)}%`; extra = `
${HEART} ${fmt(e.hp)} HP · hits up to ${o.their_max} every ${e.interval}s${e.boss ? ` · Boss` : ""}
-
You land ${pct(o.hit)} of swings (up to ${o.max_hit}) · it lands ${pct(o.their_hit)}
-
About ${dur(o.kill_time)} a kill, ${Math.round(o.damage)} damage taken${a.gold ? ` · ${a.gold} gold` : ""}
+
You hit ${pct(o.hit)} (max ${o.max_hit}) · it hits ${pct(o.their_hit)}
+
~${dur(o.kill_time)} a kill · ${Math.round(o.damage)} damage taken${a.gold ? ` · ${a.gold} gold` : ""}
${weaknessLine(e.kind, o)}`; if (a.drops) io += a.drops.map(([id, q, odds]) => `${item(id).icon} ${q} ${Math.round(odds * 100)}%`).join(""); - if (a.special === "sabotage") extra += `
Each win weakens the coming raid by 3%, up to 30%.
`; + if (a.special === "sabotage") extra += `
-3% raid strength per win (max 30%)
`; } if (a.special === "drill") { const s = S.favours.sergeant, boost = s.boost; - extra = !s.needed ? `
Fighters drill the townsfolk while a raid is coming. A fully drilled militia adds one defence per townsperson.
` - : `
${skillOf("combat").icon} +${+me.drill_per_action.toFixed(2)} drill · ${fmt(s.points)} / ${fmt(s.needed)} points, worth ${fmt(s.defence)} of ${fmt(s.cap)} defence${boost ? ` ×2 drill and XP for the next ${fmt(boost)}` : ""}
-
A bigger band needs more drilling. A fully drilled militia adds one defence per townsperson.
`; + extra = !s.needed ? `
Only while a raid is coming.
` + : `
${skillOf("combat").icon} +${+me.drill_per_action.toFixed(2)} drill · ${fmt(s.points)} / ${fmt(s.needed)} points, worth ${fmt(s.defence)} of ${fmt(s.cap)} defence${boost ? ` ×2 drill and XP for the next ${fmt(boost)}` : ""}
`; } if (a.special === "recon") { const boost = S.favours.informant.boost; const ev = S.upcoming, full = ev && ev.recon >= ev.recon_full; - extra = full ? `
The coming threat is fully scouted: this only trains the skill for now.
` - : `
${skillOf("reconnaissance").icon} +${+me.recon_gain[a.id].toFixed(2)} recon on the coming threat${boost ? ` ×2 recon and XP for the next ${fmt(boost)}` : ""}
`; + extra = full ? `
Fully scouted: XP only.
` + : `
${skillOf("reconnaissance").icon} +${+me.recon_gain[a.id].toFixed(2)} recon${boost ? ` ×2 recon and XP for the next ${fmt(boost)}` : ""}
`; } const temple = S.city.buildings.temple?.level || 0; if (a.special === "labour") { @@ -633,7 +631,7 @@ function actionCard(a) { extra = `
${skillOf("construction").icon} +${me.labour_per_action} labour per action${boost ? ` ×2 labour and XP for the next ${fmt(boost)}` : ""}
`; } if (a.special === "bury") extra = `
${skillOf("prayer").icon} +${a.pp + temple} prayer points
`; - if (a.special === "offer") extra = `
${C.buildings.temple.icon} +${+(a.devotion * (1 + 0.25 * temple)).toFixed(1)} devotion for the city's ward
`; + if (a.special === "offer") extra = `
${C.buildings.temple.icon} +${+(a.devotion * (1 + 0.25 * temple)).toFixed(1)} devotion
`; const made = Object.keys(a.outputs).map(itemStats).filter(Boolean); if (made.length) extra += `
${made.join(" · ")}
`; return ` @@ -652,8 +650,8 @@ function actionCard(a) { ${!active && !second && why ? esc(why) : ""} ${second ? `` : active ? `` - : ` - ${canPlot ? `` : ""} + : ` + ${canPlot ? `` : ""} `} `; @@ -682,11 +680,10 @@ function bank() {

Bank · ${me.slots_used} / ${me.slots} slots · ${fmt(me.stack)} per stack

${slots}
-

When an item doesn't fit: +

Bank full: -
Raids never touch your bank. Overflow sent to the city earns half the favour of giving it yourself; - anything the city has no use for is sold instead.

+
Overflow earns half favour; what the city can't use is sold.

${favourPanel()} @@ -711,11 +708,11 @@ function itemPanel(id) { ${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} in fights.
${S.me.food === id ? `Your fighting food` : ``}
` : ""} - ${it.food ? `
Feeds ${it.food} townsfolk for two days in the city granary.
` : ""} + ${it.food ? `
Feeds ${it.food} townsfolk for 2 days.
` : ""} ${C.city_uses[id].length ? `
Donate to the city · +${it.value} favour each
${qtyBtns("data-donate")}
Used for ${esc(listing(C.city_uses[id]))}.
` - : `
The city has no use for this. Sell it, or give it to someone who can use it.
`} + : `
The city has no use for this.
`} ${others.length ? `
Give to
${qtyBtns("data-give")}
` : ""}
Sell
${qtyBtns("data-sell")}
@@ -743,7 +740,7 @@ function goldPanel() { return `

Gold

${COIN} ${fmt(S.me.gold)}

-

The treasury pays the gold part of new buildings. 1 favour per gold.

`; +

1 favour per gold.

`; } function favourPanel() { @@ -751,7 +748,7 @@ function favourPanel() { return `

Favour with the council

${FAVOUR} ${fmt(me.favour)} favour · ${fmt(me.donated)} given in total

-

Favour can also hire master builders, informants and drill sergeants: see .

`; +

More to spend on in .

`; } // --- town ----------------------------------------------------------------- @@ -761,8 +758,8 @@ function town() { const growth = city.starving ? `Hungry: ${city.growth} a day` : city.growth > 0 ? `+${city.growth} a day` : city.growth < 0 ? `Overcrowded: ${city.growth} a day` - : `Full. Build more houses to grow`; - const next = city.next_tier ? `${city.next_tier.name} at ${city.next_tier.pop} people` : "The largest a settlement can be"; + : `Full: build houses`; + const next = city.next_tier ? `${city.next_tier.name} at ${city.next_tier.pop} people` : "Largest tier"; let html = `
Settlement
${city.tier_name}
${next}
Population
${city.population} / ${city.housing} homes
${growth}
@@ -772,16 +769,16 @@ function town() { ${weatherCard()}
`; html += happeningsPanel(); - if (city.starving) html += ``; - if (city.merchant_until > serverNow()) html += ``; + if (city.starving) html += ``; + if (city.merchant_until > serverNow()) html += ``; if (city.construction) html += siteBanner(); html += `
Buildings
-

Each building collects its own fund. Materials in a fund are safe from raiders, and building starts on its own once the fund is complete.

+

Funds are safe from raiders. Building starts once a fund is full.

${Object.entries(city.buildings).map(([key, b]) => buildingCard(key, b)).join("")}
`; html += `
City stockpile
`; const stock = Object.entries(city.stockpile); html += stock.length ? `
${stock.map(([id, q]) => itemChip(id, q)).join("")}
` : `

Empty.

`; - html += `

Food, weapons and anything not yet in a fund. The granary and armoury draw from here, and so do raiders.

`; + html += `

Feeds the granary and armoury. Raiders can loot it.

`; return html; } @@ -798,10 +795,10 @@ function siteBanner() { ${fmt(work.labour)} / ${fmt(work.needed)} labour ${working ? `Working` : ``} - ${hire.why ? "" : ``} + ${hire.why ? "" : ``}
-
${builders.length ? `On site: ${builders.join(", ")}. ` : ""}${hire.boost ? `Master builder on site: the next ${fmt(hire.boost)} labour counts double, with double XP. ` : ""}The townsfolk add about ${fmt(city.townsfolk_labour)} labour an hour.${queued ? ` Next: ${queued}.` : ""}
+
${builders.length ? `On site: ${builders.join(", ")}. ` : ""}${hire.boost ? `Master builder: ×2 for the next ${fmt(hire.boost)}. ` : ""}Townsfolk +${fmt(city.townsfolk_labour)}/h.${queued ? ` Next: ${queued}.` : ""}
`; } @@ -812,7 +809,7 @@ function weatherTip(w) { function weatherCard() { const h = S.happenings, w = h.weather; - return `
${h.season.icon} ${h.season.name}, day ${h.season.day}
+ return `
${h.season.icon} ${h.season.name}, day ${h.season.day}
${w.icon} ${esc(w.name)}
${w.next_icon} ${esc(w.next_name)} in ${countdown(w.until)}
`; } @@ -822,7 +819,7 @@ function happeningsPanel() { const h = S.happenings, me = S.me, city = S.city; const rows = []; if (h.weather.kind !== "clear") rows.push(`
${h.weather.icon}${esc(h.weather.name)}${esc(h.weather.text)}
`); - if (h.cold) rows.push(`
🥶Out of firewoodPeople are cold and eating half again as much. Donate logs.
`); + if (h.cold) rows.push(`
🥶Out of firewoodCity eats 50% more. Donate logs.
`); for (const e of h.effects) { const hire = e.offer ? `` : ""; rows.push(`
${e.icon}${esc(e.name)} @@ -860,7 +857,7 @@ function skillHappenings(skill) { for (const p of h.problems) { if (!C.problems[p.kind] || !touches(C.problems[p.kind])) continue; const fix = p.action && C.actions.find((x) => x.id === p.action); - note(p.icon, p.name, !fix ? p.text : fix.skill === skill ? "Help clear it below." : `${skillOf(fix.skill).name} can clear it: see the Town square.`); + note(p.icon, p.name, !fix ? p.text : fix.skill === skill ? "Clear it below." : `${skillOf(fix.skill).name} clears it (Town square).`); } return lines.length ? `
${lines.join("")}
` : ""; } @@ -869,9 +866,9 @@ function buildingCard(key, b) { const spec = C.buildings[key], city = S.city, me = S.me; const tags = { building: `Under construction`, - queued: `Funded · waiting for builders`, + queued: `Funded · queued`, max: `Fully built`, - capped: `Next level when the settlement grows`, + capped: `Needs a bigger settlement`, }; let body = tags[b.status] ? `
${tags[b.status]}
` : ""; if (b.status === "funding" || b.status === "queued") { @@ -888,10 +885,10 @@ function buildingCard(key, b) { }).join("")}
`; } body += fittingRows(key, b); - const levelText = b.cap < spec.max ? `Lv ${b.level} · max ${b.cap} for a ${city.tier_name.toLowerCase()}` : `Lv ${b.level} / ${spec.max}`; + const levelText = b.cap < spec.max ? `Lv ${b.level} / ${b.cap} (${city.tier_name.toLowerCase()} cap)` : `Lv ${b.level} / ${spec.max}`; return `
${spec.icon}
-
${esc(spec.name)} ${levelText} ${b.damaged ? `Damaged: repairs needed` : ""}
+
${esc(spec.name)} ${levelText} ${b.damaged ? `Damaged` : ""}
${esc(spec.effect)}
${body}
`; } @@ -927,17 +924,17 @@ function pips(level) { function reconText(ev) { const points = `${fmt(ev.recon)} / ${fmt(ev.recon_full)} points`; - if (ev.intel >= 3) return `${points} · nothing more to learn`; - const boost = S.favours.informant.boost ? ` · informant's leads: ×2 for the next ${fmt(S.favours.informant.boost)}` : ""; - if (ev.recon_next <= 0) return `${points} · enough for the next level, but they're too far off to read yet${boost}`; - return `${points} · ${fmt(ev.recon_next)} more for the next intel level${boost}`; + if (ev.intel >= 3) return `${points} · complete`; + const boost = S.favours.informant.boost ? ` · informant: ×2 for the next ${fmt(S.favours.informant.boost)}` : ""; + if (ev.recon_next <= 0) return `${points} · next level opens closer to the threat${boost}`; + return `${points} · ${fmt(ev.recon_next)} to the next level${boost}`; } function council() { const ev = S.upcoming, city = S.city, me = S.me; let html = ""; if (!ev) { - html += `

All quiet

No news on the roads. The spymasters will report as soon as something stirs.

`; + html += `

All quiet

No news on the roads.

`; } else { const threat = ["raid", "dragon"].includes(ev.kind); html += `
@@ -947,10 +944,9 @@ function council() { Expected${eventWhen(ev)} ${ev.range ? `Strength${ev.strength ?? `${ev.range[0]}–${ev.range[1]}`}` : ""} ${ev.trait ? `Scouts say${esc(ev.trait)}` : ""} - ${ev.camp ? `Their campLocated. Fighters can sabotage it from the combat page${ev.sabotage ? ` (weakened ${ev.sabotage}% so far)` : ""}.` : ""} + ${ev.camp ? `Their campFound · sabotage it in Combat${ev.sabotage ? ` (-${ev.sabotage}% so far)` : ""}` : ""} Reconnaissance${reconText(ev)} -
- ${ev.intel < 3 ? `

Better intel comes from the spymaster's guild and anyone's scouting (rangers are best at it), and the sharpest only as the threat draws near.

` : ""}`; +
`; if (threat) { const strength = ev.strength ?? (ev.range ? `${ev.range[0]}–${ev.range[1]}` : "?"); html += `
Our defence${fmt(city.defence.total)}
vs
Raiders${strength}
`; @@ -988,9 +984,9 @@ function favourSpends() { `; }; return `

Spend favour on the city · you have ${FAVOUR} ${fmt(me.favour)}

- ${option("builders", "🏗️", "Hire a master builder", f.builders.why ? esc(f.builders.why) + "." : `The next ${f.builders.labour} labour anyone puts into the site counts double, with double Construction XP${f.builders.boost ? ` (${fmt(f.builders.boost)} still to use)` : ""}. Costs more each time on the same site.`)} - ${option("informant", "🕵️", "Pay an informant", f.informant.why ? esc(f.informant.why) + "." : `Leads to follow up: the next ${f.informant.recon} reconnaissance on the coming threat counts double, with double XP${f.informant.boost ? ` (${fmt(f.informant.boost)} still to use)` : ""}. Costs more each time for the same threat.`)} - ${option("sergeant", "📯", "Hire a drill sergeant", f.sergeant.why ? esc(f.sergeant.why) + "." : `The next ${f.sergeant.drill} drill points fighters put into the militia count double, with double combat XP${f.sergeant.boost ? ` (${fmt(f.sergeant.boost)} still to use)` : ""}. Costs more each time for the same threat.`)} + ${option("builders", "🏗️", "Hire a master builder", f.builders.why ? esc(f.builders.why) + "." : `×2 labour and XP for the next ${f.builders.labour}${f.builders.boost ? ` (${fmt(f.builders.boost)} left)` : ""}. Price rises per hire.`)} + ${option("informant", "🕵️", "Pay an informant", f.informant.why ? esc(f.informant.why) + "." : `×2 recon and XP for the next ${f.informant.recon}${f.informant.boost ? ` (${fmt(f.informant.boost)} left)` : ""}. Price rises per hire.`)} + ${option("sergeant", "📯", "Hire a drill sergeant", f.sergeant.why ? esc(f.sergeant.why) + "." : `×2 drill and XP for the next ${f.sergeant.drill}${f.sergeant.boost ? ` (${fmt(f.sergeant.boost)} left)` : ""}. Price rises per hire.`)}
`; } @@ -1005,15 +1001,15 @@ function raidPrep(ev) { }).join(""); return `

Council requests

${reqs} -

Arrows, swords and shields in the stockpile arm the defenders: one sword and shield each, best first. Every fight uses up arrows and breaks some of the gear, more when it's close. Food keeps them fighting.

+

Stockpile gear arms the defenders, best first. Fights use up arrows and break some gear.

Defence

${Object.entries(d.parts).filter(([k, v]) => k !== "rallied" || v).map(([k, v]) => ``).join("")} ${d.starving ? `` : ""}
${names[k]}${fmt(v)}
Hungry (×0.8)

Garrison

-

${d.garrison.length ? d.garrison.map((g) => `${esc(g.name)} (${g.power})`).join(", ") : `Nobody has signed up yet.`}

+

${d.garrison.length ? d.garrison.map((g) => `${esc(g.name)} (${g.power})`).join(", ") : `Nobody yet.`}

${me.garrison ? `` : ``} -

You can keep working while you're signed up. Victory earns the garrison favour and combat XP.

+

Keep working while on guard. Victory earns favour and combat XP.

`; } @@ -1026,7 +1022,7 @@ function board() { const ledger = [...S.ledger].reverse().map((l) => `
  • ${esc(l.text)}${ago(l.at)}
  • `).join(""); return `

    Town board

    -
    ${msgs || `

    No messages yet. Say hello, or ask for that iron you need.

    `}
    +
    ${msgs || `

    No messages yet.

    `}

    Town ledger

      ${ledger || `
    • Nothing recorded yet.
    • `}
    @@ -1117,7 +1113,7 @@ function characterSheet(uid) {
    ${Object.entries(SLOT_NAMES).map(([slot, label]) => gearSlot(slot, label, "Nothing")).join("")}

    Tools

    ${C.skills.filter((s) => `tool_${s.id}` in p.equip).map((s) => gearSlot(`tool_${s.id}`, s.name, "No tool")).join("")}
    - ${mine ? `

    Equip gear and tools from the .

    ` : ""} + ${mine ? `

    Equip from the .

    ` : ""}

    Skills

    ${skillRows}
    @@ -1137,12 +1133,10 @@ function classCards() { function repickPage() { return `

    Pick your trade

    -

    Classes are now whole trades, from gathering through to the finished goods, so you can play - the quiet gathering side and the busier crafting side yourself. You keep every level and item. This choice - is free, once.

    +

    Classes are now trades, gathering through crafting. You keep every level and item. Free, once.

    ${classCards()}
    -

    Every class can still train every skill, scout and drill. A trade makes its skills faster and better.

    +

    Any class can train any skill.

    `; } @@ -1150,15 +1144,14 @@ function register() { const cards = classCards(); return `

    A new city on the frontier

    -

    You're one of a handful of adventurers settling a young city at the edge of the kingdom. - Train your skills, supply the council, and keep the place standing when the bandits come. - Your adventurer keeps working while you're away, for up to a day.

    +

    Settle a frontier city together. Train, build, and hold off the bandits. + You keep working while away, for up to a day.

    ${cards}
    -

    Every class can train every skill. Your class is a trade: its skills are faster, its crafts sometimes make double, and it has one bonus of its own.

    +

    Any class can train any skill.

    `; } @@ -1236,7 +1229,7 @@ document.addEventListener("click", (e) => { if (d.devHappen) return act("/api/dev/happen", { track: d.devHappen }); if (d.pray) { const on = S.me.prayers.includes(d.pray); - if (!on && S.me.prayers.length >= C.max_prayers) return toast(`Only ${C.max_prayers} prayers at once: turn one off first`, "bad"); + if (!on && S.me.prayers.length >= C.max_prayers) return toast(`Max ${C.max_prayers} prayers active`, "bad"); return act("/api/prayers", { ids: on ? S.me.prayers.filter((x) => x !== d.pray) : [...S.me.prayers, d.pray] }); } if (d.overflow) return act("/api/settings", { overflow: d.overflow });