Shorter descriptions throughout
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
5898e2006f
commit
61d8b2fce3
2 changed files with 146 additions and 153 deletions
124
game.py
124
game.py
|
|
@ -281,14 +281,14 @@ def describe_potion(skill: str, effect: dict) -> str:
|
||||||
if "speed" in effect:
|
if "speed" in effect:
|
||||||
return f"{name.title()} {round(effect['speed'] * 100)}% faster"
|
return f"{name.title()} {round(effect['speed'] * 100)}% faster"
|
||||||
if "double" in effect:
|
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:
|
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:
|
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:
|
if "power" in effect:
|
||||||
return f"+{effect['power']} combat power"
|
return f"+{effect['power']} power"
|
||||||
return f"-{round(effect['reduce'] * 100)}% damage taken in fights"
|
return f"-{round(effect['reduce'] * 100)}% damage taken"
|
||||||
|
|
||||||
|
|
||||||
for _id, _name, _icon, _level, _time, _xp, _value in HERBS:
|
for _id, _name, _icon, _level, _time, _xp, _value in HERBS:
|
||||||
|
|
@ -328,31 +328,31 @@ CLASSES = {
|
||||||
"miner": {
|
"miner": {
|
||||||
"name": "Miner", "icon": "⛏️", "fast": ["mining", "smithing"], "craft": ["smithing"],
|
"name": "Miner", "icon": "⛏️", "fast": ["mining", "smithing"], "craft": ["smithing"],
|
||||||
"bonus": "Mining and smithing 20% faster",
|
"bonus": "Mining and smithing 20% faster",
|
||||||
"masterwork": "Masterwork: 1 in 10 smithing crafts make double",
|
"masterwork": "Masterwork: 10% double smithing crafts",
|
||||||
"signature": "Prospector: 1 in 10 mining trips bring back double",
|
"signature": "Prospector: 10% double ore",
|
||||||
"combat": "Warhammer: +15% damage with one. Slow, heavy, crushes golems",
|
"combat": "Warhammer: +15% damage",
|
||||||
},
|
},
|
||||||
"builder": {
|
"builder": {
|
||||||
"name": "Builder", "icon": "🏗️", "fast": ["woodcutting", "construction"], "craft": ["construction"],
|
"name": "Builder", "icon": "🏗️", "fast": ["woodcutting", "construction"], "craft": ["construction"],
|
||||||
"bonus": "Woodcutting and construction (masonry included) 20% faster",
|
"bonus": "Woodcutting and construction 20% faster",
|
||||||
"masterwork": "Masterwork: 1 in 10 construction crafts make double",
|
"masterwork": "Masterwork: 10% double construction crafts",
|
||||||
"signature": "Foreman: +1 labour every time you work a building site",
|
"signature": "Foreman: +1 labour per site action",
|
||||||
"combat": "Sword and shield: +15% damage with a sword. The shield blocks, and trolls fall to blades",
|
"combat": "Sword: +15% damage",
|
||||||
},
|
},
|
||||||
"homesteader": {
|
"homesteader": {
|
||||||
"name": "Homesteader", "icon": "🌾", "fast": ["cooking", "herblore"], "craft": ["cooking", "herblore"],
|
"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",
|
"bonus": "Cooking and herblore 20% faster, two farm plots",
|
||||||
"masterwork": "Masterwork: 1 in 10 cooking and herblore crafts make double",
|
"masterwork": "Masterwork: 10% double cooking and herblore crafts",
|
||||||
"signature": "Steady hand: potions you drink last 25% longer",
|
"signature": "Steady hand: potions last 25% longer",
|
||||||
"combat": "Staff: +15% damage with one. Rarely misses, ignores armour, and spirits fear it",
|
"combat": "Staff: +15% damage",
|
||||||
},
|
},
|
||||||
"ranger": {
|
"ranger": {
|
||||||
"name": "Ranger", "icon": "🏹", "fast": ["hunting", "fishing", "leatherworking", "cooking"],
|
"name": "Ranger", "icon": "🏹", "fast": ["hunting", "fishing", "leatherworking", "cooking"],
|
||||||
"craft": ["leatherworking", "cooking"],
|
"craft": ["leatherworking", "cooking"],
|
||||||
"bonus": "Hunting, fishing, leatherworking and cooking 20% faster",
|
"bonus": "Hunting, fishing, leatherworking and cooking 20% faster",
|
||||||
"masterwork": "Masterwork: 1 in 10 leatherworking and cooking crafts make double",
|
"masterwork": "Masterwork: 10% double leatherworking and cooking crafts",
|
||||||
"signature": "Pathfinder: your scouting gathers 50% more reconnaissance",
|
"signature": "Pathfinder: +50% recon",
|
||||||
"combat": "Bow: +15% damage with one. Fast, strikes first, and brings down flyers",
|
"combat": "Bow: +15% damage",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
# Classes from before the trades, as a starting point until their player re-picks.
|
# 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},
|
"advanced": {"oak_logs": 100, "leather": 80, "rope": 40, "iron_nails": 150},
|
||||||
"late": {"willow_logs": 80, "oak_plank": 35, "hide_glue": 50, "steel_hinges": 20},
|
"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},
|
"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": {
|
"walls": {
|
||||||
"name": "Walls", "icon": "🏰", "max": 5, "gold": 300,
|
"name": "Walls", "icon": "🏰", "max": 5, "gold": 300,
|
||||||
|
|
@ -647,7 +647,7 @@ BUILDINGS = {
|
||||||
"advanced": {"cut_stone": 150, "beam": 20, "rope": 50, "iron_nails": 200},
|
"advanced": {"cut_stone": 150, "beam": 20, "rope": 50, "iron_nails": 200},
|
||||||
"late": {"steel_bar": 60, "oak_plank": 25, "hide_glue": 50, "steel_hinges": 30},
|
"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},
|
"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": {
|
"barracks": {
|
||||||
"name": "Barracks", "icon": "⛺", "max": 5, "gold": 300,
|
"name": "Barracks", "icon": "⛺", "max": 5, "gold": 300,
|
||||||
|
|
@ -655,7 +655,7 @@ BUILDINGS = {
|
||||||
"advanced": {"iron_bar": 60, "hard_leather": 40},
|
"advanced": {"iron_bar": 60, "hard_leather": 40},
|
||||||
"late": {"steel_bar": 40, "oak_plank": 30, "hide_glue": 40},
|
"late": {"steel_bar": 40, "oak_plank": 30, "hide_glue": 40},
|
||||||
"final": {"mithril_bar": 25, "willow_plank": 25, "bear_leather": 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": {
|
"smithy": {
|
||||||
"name": "Smithy", "icon": "⚒️", "max": 5, "gold": 250,
|
"name": "Smithy", "icon": "⚒️", "max": 5, "gold": 250,
|
||||||
|
|
@ -663,42 +663,42 @@ BUILDINGS = {
|
||||||
"advanced": {"iron_bar": 180, "leather": 70},
|
"advanced": {"iron_bar": 180, "leather": 70},
|
||||||
"late": {"steel_bar": 100, "oak_plank": 25, "hide_glue": 40},
|
"late": {"steel_bar": 100, "oak_plank": 25, "hide_glue": 40},
|
||||||
"final": {"mithril_bar": 70, "maple_plank": 20, "bear_leather": 35},
|
"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": {
|
"temple": {
|
||||||
"name": "Temple", "icon": "⛪", "max": 3, "gold": 300,
|
"name": "Temple", "icon": "⛪", "max": 3, "gold": 300,
|
||||||
"base": {"stone": 700, "bricks": 200, "cut_stone": 60, "provisions": 60},
|
"base": {"stone": 700, "bricks": 200, "cut_stone": 60, "provisions": 60},
|
||||||
"advanced": {"beam": 15, "rope": 40, "iron_nails": 150},
|
"advanced": {"beam": 15, "rope": 40, "iron_nails": 150},
|
||||||
"late": {"willow_logs": 80, "oak_plank": 35, "hide_glue": 40, "steel_hinges": 20},
|
"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": {
|
"granary": {
|
||||||
"name": "Granary", "icon": "🌾", "max": 3, "gold": 150,
|
"name": "Granary", "icon": "🌾", "max": 3, "gold": 150,
|
||||||
"base": {"logs": 200, "bricks": 100, "plank": 40, "thatch": 300, "provisions": 50, "copper_rivets": 40},
|
"base": {"logs": 200, "bricks": 100, "plank": 40, "thatch": 300, "provisions": 50, "copper_rivets": 40},
|
||||||
"advanced": {"cut_stone": 40, "rope": 40, "iron_nails": 100},
|
"advanced": {"cut_stone": 40, "rope": 40, "iron_nails": 100},
|
||||||
"late": {"oak_plank": 35, "hide_glue": 35, "steel_hinges": 15},
|
"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": {
|
"lumber_shed": {
|
||||||
"name": "Lumber shed", "icon": "🪵", "max": 3, "gold": 120,
|
"name": "Lumber shed", "icon": "🪵", "max": 3, "gold": 120,
|
||||||
"base": {"logs": 200, "stone": 250, "plank": 50, "thatch": 180, "provisions": 40, "copper_rivets": 40},
|
"base": {"logs": 200, "stone": 250, "plank": 50, "thatch": 180, "provisions": 40, "copper_rivets": 40},
|
||||||
"advanced": {"oak_plank": 40, "rope": 40, "iron_nails": 100},
|
"advanced": {"oak_plank": 40, "rope": 40, "iron_nails": 100},
|
||||||
"late": {"willow_plank": 25, "hide_glue": 35},
|
"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": {
|
"well": {
|
||||||
"name": "Well", "icon": "🪣", "max": 3, "gold": 120,
|
"name": "Well", "icon": "🪣", "max": 3, "gold": 120,
|
||||||
"base": {"stone": 500, "bricks": 80, "rope": 50, "provisions": 40},
|
"base": {"stone": 500, "bricks": 80, "rope": 50, "provisions": 40},
|
||||||
"advanced": {"cut_stone": 40, "leather": 60, "iron_nails": 125},
|
"advanced": {"cut_stone": 40, "leather": 60, "iron_nails": 125},
|
||||||
"late": {"oak_plank": 25, "hide_glue": 35, "steel_hinges": 15},
|
"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": {
|
"spymaster": {
|
||||||
"name": "Spymaster's guild", "icon": "🕵️", "max": 3, "gold": 400,
|
"name": "Spymaster's guild", "icon": "🕵️", "max": 3, "gold": 400,
|
||||||
"base": {"logs": 200, "bricks": 150, "hide": 120, "feathers": 300, "provisions": 60},
|
"base": {"logs": 200, "bricks": 150, "hide": 120, "feathers": 300, "provisions": 60},
|
||||||
"advanced": {"cut_stone": 80, "hard_leather": 40, "iron_nails": 100},
|
"advanced": {"cut_stone": 80, "hard_leather": 40, "iron_nails": 100},
|
||||||
"late": {"bear_leather": 50, "oak_plank": 25, "steel_hinges": 15},
|
"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": {
|
"storehouse": {
|
||||||
"name": "Storehouse", "icon": "📦", "max": 5, "gold": 200,
|
"name": "Storehouse", "icon": "📦", "max": 5, "gold": 200,
|
||||||
|
|
@ -706,7 +706,7 @@ BUILDINGS = {
|
||||||
"advanced": {"oak_logs": 150, "cut_stone": 100, "rope": 50, "iron_nails": 150},
|
"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},
|
"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},
|
"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
|
# 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"):
|
if prayer.get("power"):
|
||||||
parts.append(f"+{prayer['power']} power")
|
parts.append(f"+{prayer['power']} power")
|
||||||
if prayer.get("regen"):
|
if prayer.get("regen"):
|
||||||
parts.append("HP recovers twice as fast while fighting")
|
parts.append("HP recovers twice as fast")
|
||||||
if prayer.get("wind"):
|
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)
|
return ", ".join(parts)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2269,7 +2269,7 @@ def event_view(world: dict, event: dict, now: float) -> dict:
|
||||||
view["sabotage"] = round(event["sabotage"] * 100)
|
view["sabotage"] = round(event["sabotage"] * 100)
|
||||||
view["requests"] = council_requests(world, event)
|
view["requests"] = council_requests(world, event)
|
||||||
elif event["kind"] == "merchant":
|
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:
|
else:
|
||||||
view["headline"] = f"About {event['people']} refugees are walking towards the city, hoping for a roof."
|
view["headline"] = f"About {event['people']} refugees are walking towards the city, hoping for a roof."
|
||||||
return view
|
return view
|
||||||
|
|
@ -2481,15 +2481,15 @@ SEASON_DAYS = 7
|
||||||
# `speed` multiplies action time (below 1 is faster), `yield` multiplies output,
|
# `speed` multiplies action time (below 1 is faster), `yield` multiplies output,
|
||||||
# `food` multiplies what the townsfolk eat, `firewood` is logs per person per hour.
|
# `food` multiplies what the townsfolk eat, `firewood` is logs per person per hour.
|
||||||
WEATHER = {
|
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"},
|
"rain": {"name": "Rain", "icon": "🌧️", "speed": {"farming": 0.9}, "text": "Farming 10% faster"},
|
||||||
"heat": {"name": "Heat wave", "icon": "🥵", "speed": {"farming": 0.85}, "food": 1.15,
|
"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"},
|
"storm": {"name": "Storm", "icon": "⛈️", "speed": {"fishing": 1.25}, "text": "Fishing 25% slower"},
|
||||||
"fog": {"name": "Fog", "icon": "🌫️", "speed": {"reconnaissance": 1.3, "hunting": 1.1},
|
"fog": {"name": "Fog", "icon": "🌫️", "speed": {"reconnaissance": 1.3, "hunting": 1.1},
|
||||||
"text": "Scouting 30% and hunting 10% slower"},
|
"text": "Scouting 30% and hunting 10% slower"},
|
||||||
"snow": {"name": "Snow", "icon": "🌨️", "speed": {"hunting": 1.2}, "firewood": 0.05,
|
"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 = [
|
SEASON_WEATHER = [
|
||||||
{"clear": 35, "rain": 40, "storm": 10, "fog": 15},
|
{"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`).
|
# either by working a special action (`work` completions) or by donating (`need`).
|
||||||
PROBLEMS = {
|
PROBLEMS = {
|
||||||
"collapse": {"name": "Mine collapse", "icon": "⛰️", "need": {"plank": 30},
|
"collapse": {"name": "Mine collapse", "icon": "⛰️", "need": {"plank": 30},
|
||||||
"block": {"mining": "The mine has collapsed: shore it up with planks"}, "allow": ["mine_stone"],
|
"block": {"mining": "Mine collapsed"}, "allow": ["mine_stone"],
|
||||||
"text": "A shaft caved in. Ore can't be mined until the mine is shored up with planks."},
|
"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",
|
"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",
|
"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",
|
"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",
|
"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"],
|
"block": {"mining": "Quarry road blocked"}, "only": ["mine_stone"],
|
||||||
"text": "No stone can be quarried until builders clear the road."},
|
"text": "No stone until builders clear it."},
|
||||||
"flood": {"name": "Flooded fishing grounds", "icon": "🌊", "speed": {"fishing": 1.5}, "need": {"plank": 20}, "lasts": 36,
|
"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",
|
"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",
|
"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")
|
MINOR_THREATS = ("collapse", "wolves", "bear", "goblins", "rockslide", "flood", "boars")
|
||||||
PROBLEM_ACTIONS = [
|
PROBLEM_ACTIONS = [
|
||||||
|
|
@ -2547,15 +2547,15 @@ ACTION_BY_ID[_barrel["id"]] = _barrel
|
||||||
# id, name, building, building level, construction level, inputs, effect, glyph, tint
|
# id, name, building, building level, construction level, inputs, effect, glyph, tint
|
||||||
FITTINGS = [
|
FITTINGS = [
|
||||||
("grain_bins", "Grain bins", "granary", 1, 15, {"oak_plank": 20, "iron_nails": 25},
|
("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},
|
("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},
|
("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},
|
("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},
|
("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},
|
("furniture", "Furniture", "houses", 2, 35, {"willow_plank": 40, "leather": 30, "steel_hinges": 10},
|
||||||
"Population grows 25% faster", "sofa", "#a3b86c"),
|
"Population grows 25% faster", "sofa", "#a3b86c"),
|
||||||
("weapon_racks", "Weapon racks", "barracks", 2, 40, {"willow_plank": 30, "steel_hinges": 16},
|
("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},
|
("hoardings", "Hoardings", "walls", 3, 45, {"maple_plank": 40, "mithril_brackets": 20},
|
||||||
"+25 defence", "palisade", "#d0663a"),
|
"+25 defence", "palisade", "#d0663a"),
|
||||||
("bellows", "Great bellows", "smithy", 3, 50, {"maple_plank": 30, "bear_leather": 15, "mithril_brackets": 6},
|
("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},
|
("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},
|
("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},
|
("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.
|
# Builders' shields, homesteaders' staves and rangers' bows, after each skill's other tabs.
|
||||||
for _weapon in _weapon_actions():
|
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":
|
if kind == "sickness":
|
||||||
hours = 72 * (1 - 0.2 * building_level(world, "temple"))
|
hours = 72 * (1 - 0.2 * building_level(world, "temple"))
|
||||||
_add_effect(world, now, hours, kind="sickness", name="Sickness", icon="🤒", food=1.5,
|
_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",
|
_record(world, now, "hardship", kind, "🤒", "Sickness is spreading through the city",
|
||||||
[f"For about {round(hours)} hours the townsfolk eat half again as much.",
|
[f"For about {round(hours)} hours the townsfolk eat half again as much.",
|
||||||
"A bigger temple shortens it."])
|
"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)
|
_record(world, now, "hardship", kind, "🔥", "Fire swept through part of the city", lines)
|
||||||
elif kind == "winter":
|
elif kind == "winter":
|
||||||
_add_effect(world, now, 48, kind="winter", name="Harsh winter", icon="🥶", firewood=0.1,
|
_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",
|
_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.",
|
["For two days the city burns logs from the stockpile to keep warm.",
|
||||||
"Without firewood, the cold makes everyone eat half again as much."])
|
"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"])
|
skill = rng.choice([s for s in SKILL_IDS if s != "reconnaissance"])
|
||||||
name = next(s["name"] for s in SKILLS if s["id"] == skill)
|
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="🎓",
|
_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",
|
_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."])
|
[f"For 12 hours, {name.lower()} earns 50% more XP. Learn while you can."])
|
||||||
elif kind == "harvest":
|
elif kind == "harvest":
|
||||||
_add_effect(world, now, 12, kind="harvest", name="Bountiful harvest", icon="🌾", **{"yield": {"farming": 2}},
|
_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",
|
_record(world, now, "fortune", kind, "🌾", "The fields are bursting: a bountiful harvest",
|
||||||
["Farming yields are doubled for 12 hours."])
|
["Farming yields are doubled for 12 hours."])
|
||||||
elif kind == "rich_vein":
|
elif kind == "rich_vein":
|
||||||
_add_effect(world, now, 12, kind="rich_vein", name="Rich vein", icon="💎", **{"yield": {"mining": 1.5}},
|
_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",
|
_record(world, now, "fortune", kind, "💎", "Miners struck a rich vein",
|
||||||
["Mining yields 50% more for 12 hours."])
|
["Mining yields 50% more for 12 hours."])
|
||||||
elif kind == "salmon":
|
elif kind == "salmon":
|
||||||
_add_effect(world, now, 12, kind="salmon", name="Salmon run", icon="🐟", **{"yield": {"fishing": 2}},
|
_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",
|
_record(world, now, "fortune", kind, "🐟", "The salmon are running",
|
||||||
["Fishing yields are doubled for 12 hours."])
|
["Fishing yields are doubled for 12 hours."])
|
||||||
elif kind == "festival":
|
elif kind == "festival":
|
||||||
_add_effect(world, now, 24, kind="festival", name="Festival", icon="🎉", growth=2, favour=1.5,
|
_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",
|
_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."])
|
["For a day, the population grows twice as fast and donations earn 50% more favour."])
|
||||||
elif kind == "noble":
|
elif kind == "noble":
|
||||||
|
|
@ -2870,7 +2870,7 @@ def _fortune(world: dict, now: float, rng: random.Random, kind: str | None = Non
|
||||||
elif kind == "mercenaries":
|
elif kind == "mercenaries":
|
||||||
cost = 150 + 30 * season(world, now)["index"] + int(city["population"]) * 5
|
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,
|
_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",
|
_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."])
|
[f"For a day they'll join the next defence for {cost:,} gold from the treasury."])
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
175
static/app.js
175
static/app.js
|
|
@ -150,8 +150,8 @@ function showSummary(sum) {
|
||||||
if (sum.stopped) toast(`Stopped: ${sum.stopped}`, "bad");
|
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 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()}`);
|
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.prayer_out) toast("🙏 Out of prayer points", "bad");
|
||||||
if (sum.second_wind) toast("🌬️ Second Wind! Back on your feet at half health", "level");
|
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()}`);
|
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 ? `<p class="gold">+${fmt(sum.gold)} gold</p>` : ""}
|
${sum.gold ? `<p class="gold">+${fmt(sum.gold)} gold</p>` : ""}
|
||||||
${xp ? `<ul>${xp}</ul>` : ""}
|
${xp ? `<ul>${xp}</ul>` : ""}
|
||||||
${levels ? `<ul>${levels}</ul>` : ""}
|
${levels ? `<ul>${levels}</ul>` : ""}
|
||||||
${sold ? `<p class="muted small">Your bank was full, so this was sold for ${fmt(sum.sold_gold)} gold:</p><ul>${sold}</ul>` : ""}
|
${sold ? `<p class="muted small">Bank full, sold for ${fmt(sum.sold_gold)} gold:</p><ul>${sold}</ul>` : ""}
|
||||||
${spilled ? `<p class="muted small">Your bank was full, so this went to the city stockpile (for half the favour of giving it yourself):</p><ul>${spilled}</ul>` : ""}
|
${spilled ? `<p class="muted small">Bank full, sent to the stockpile:</p><ul>${spilled}</ul>` : ""}
|
||||||
${sum.stopped ? `<p class="tag bad">Stopped: ${esc(sum.stopped)}</p>` : ""}
|
${sum.stopped ? `<p class="tag bad">Stopped: ${esc(sum.stopped)}</p>` : ""}
|
||||||
<p><button class="btn primary" data-close>Back to work</button></p>`);
|
<p><button class="btn primary" data-close>Back to work</button></p>`);
|
||||||
}
|
}
|
||||||
|
|
@ -230,7 +230,7 @@ function sidebar() {
|
||||||
<div class="me">
|
<div class="me">
|
||||||
<div class="who">${esc(me.name)}</div>
|
<div class="who">${esc(me.name)}</div>
|
||||||
<div class="cls">${cls.icon} ${cls.name} · ${HEART} <span data-live-hp>${fmt(me.hp)}</span>/${me.max_hp}</div>
|
<div class="cls">${cls.icon} ${cls.name} · ${HEART} <span data-live-hp>${fmt(me.hp)}</span>/${me.max_hp}</div>
|
||||||
<div class="purse"><span class="gold" title="Gold: from fights and selling. Fund buildings with it or give it to the treasury">${COIN} ${fmt(me.gold)}</span><span class="favour" title="Favour: earned by giving to the city. Spend it on the Council page, or on extra bank slots">${FAVOUR} ${fmt(me.favour)}</span></div>
|
<div class="purse"><span class="gold" title="Gold">${COIN} ${fmt(me.gold)}</span><span class="favour" title="Favour: earned by donating">${FAVOUR} ${fmt(me.favour)}</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="navhead">City</div>
|
<div class="navhead">City</div>
|
||||||
${nav("town", "🏘️", "Town square", `${S.city.population}`, "", `Population ${S.city.population}`)}
|
${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 === "combat") html += fightPanel() + combatPanel();
|
||||||
if (id === "prayer") html += prayerPanel();
|
if (id === "prayer") html += prayerPanel();
|
||||||
if (id === "construction") html += S.city.construction ? siteBanner()
|
if (id === "construction") html += S.city.construction ? siteBanner()
|
||||||
: `<div class="panel muted">Nothing is being built right now. Fund a building on the Town square and its site opens here.</div>`;
|
: `<div class="panel muted">Nothing under construction.</div>`;
|
||||||
if (id === "farming" && me.cls === "homesteader") html += plotsPanel();
|
if (id === "farming" && me.cls === "homesteader") html += plotsPanel();
|
||||||
html += potionPanel(id);
|
html += potionPanel(id);
|
||||||
if (`tool_${id}` in me.equip) html += toolPanel(id);
|
if (`tool_${id}` in me.equip) html += toolPanel(id);
|
||||||
if (id === "reconnaissance") html += `<div class="panel small muted">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.</div>`;
|
if (id === "reconnaissance") html += `<div class="panel small muted">Raises intel on the coming threat. Higher levels open as it nears.</div>`;
|
||||||
html += skillHappenings(id);
|
html += skillHappenings(id);
|
||||||
const live = new Set(S.happenings.problems.map((p) => p.kind));
|
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)));
|
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() {
|
function plotsPanel() {
|
||||||
const a = S.me.action, farming = a && C.actions.find((x) => x.id === a.id)?.skill === "farming";
|
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 name = (id) => esc(C.actions.find((x) => x.id === id).name);
|
||||||
const status = !farming ? `<span class="muted">Start a crop to plant your first plot.</span>`
|
const status = !farming ? `<span class="muted">Start a crop to plant plot 1.</span>`
|
||||||
: a.plot2 ? `Plot 1: <b>${name(a.id)}</b> · Plot 2: <b>${name(a.plot2)}</b> <span class="muted">· harvests alternate, together 20% faster than one crop</span>`
|
: a.plot2 ? `Plot 1: <b>${name(a.id)}</b> · Plot 2: <b>${name(a.plot2)}</b> <span class="muted">· alternating, 20% faster</span>`
|
||||||
: `Plot 1: <b>${name(a.id)}</b> · Plot 2: <span class="muted">empty, so you're focused: 30% faster. Use "Plot 2" on another crop to grow both.</span>`;
|
: `Plot 1: <b>${name(a.id)}</b> · Plot 2: <span class="muted">empty · focused, 30% faster</span>`;
|
||||||
return `<div class="panel tool-panel"><span>🌱 ${status}</span></div>`;
|
return `<div class="panel tool-panel"><span>🌱 ${status}</span></div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -402,7 +402,7 @@ function potionPanel(skill) {
|
||||||
? `${item(active.item).icon} <b>${esc(item(active.item).name)}</b> <span class="muted">${esc(item(active.item).potion.text)} · ${fmt(active.charges)} uses left${me.bank[active.item] ? `, ${fmt(me.bank[active.item])} more in the bank` : ""}</span>`
|
? `${item(active.item).icon} <b>${esc(item(active.item).name)}</b> <span class="muted">${esc(item(active.item).potion.text)} · ${fmt(active.charges)} uses left${me.bank[active.item] ? `, ${fmt(me.bank[active.item])} more in the bank` : ""}</span>`
|
||||||
: `<span class="muted">No potion.</span>`}
|
: `<span class="muted">No potion.</span>`}
|
||||||
${!active && drink ? `<span class="btns">Drink: ${drink}</span>` : ""}
|
${!active && drink ? `<span class="btns">Drink: ${drink}</span>` : ""}
|
||||||
${!active && !drink ? `<span class="small muted">Herblore can brew ${kinds.length === 1 ? "one" : "some"} for ${skill === "combat" ? "fighting" : esc(skillOf(skill).name.toLowerCase())}.</span>` : ""}
|
${!active && !drink ? `<span class="small muted">Brewed in Herblore.</span>` : ""}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -413,7 +413,7 @@ function prayerPanel() {
|
||||||
const on = me.prayers.includes(p.id), locked = level < p.level;
|
const on = me.prayers.includes(p.id), locked = level < p.level;
|
||||||
const full = !on && me.prayers.length >= C.max_prayers;
|
const full = !on && me.prayers.length >= C.max_prayers;
|
||||||
return `<button class="prayer ${on ? "on" : ""} ${locked ? "locked" : ""}" data-pray="${p.id}" ${locked ? "disabled" : ""}
|
return `<button class="prayer ${on ? "on" : ""} ${locked ? "locked" : ""}" data-pray="${p.id}" ${locked ? "disabled" : ""}
|
||||||
title="${locked ? `Needs prayer level ${p.level}` : full ? `Only ${C.max_prayers} at once: turn one off first` : ""}">
|
title="${locked ? `Needs prayer level ${p.level}` : full ? `Max ${C.max_prayers} active` : ""}">
|
||||||
<span class="ico">${p.icon}</span>
|
<span class="ico">${p.icon}</span>
|
||||||
<span class="grow"><b>${esc(p.name)}</b><span class="small muted">${esc(p.text)}</span></span>
|
<span class="grow"><b>${esc(p.name)}</b><span class="small muted">${esc(p.text)}</span></span>
|
||||||
<span class="small ${locked ? "why" : "muted"}">${locked ? `Lv ${p.level}` : `${p.cost} pt${p.cost > 1 ? "s" : ""}/fight`}</span>
|
<span class="small ${locked ? "why" : "muted"}">${locked ? `Lv ${p.level}` : `${p.cost} pt${p.cost > 1 ? "s" : ""}/fight`}</span>
|
||||||
|
|
@ -422,14 +422,14 @@ function prayerPanel() {
|
||||||
const temple = city.buildings.temple.level;
|
const temple = city.buildings.temple.level;
|
||||||
return `<div class="cols">
|
return `<div class="cols">
|
||||||
<div class="panel"><h2>Prayers · ${me.prayers.length} / ${C.max_prayers} active</h2>
|
<div class="panel"><h2>Prayers · ${me.prayers.length} / ${C.max_prayers} active</h2>
|
||||||
<div class="prayer-points">${skillOf("prayer").icon} <b>${fmt(me.pp)}</b> prayer points${cost ? ` · ${cost} per fight, enough for about ${fmt(me.pp / cost)} fights` : ""}</div>
|
<div class="prayer-points">${skillOf("prayer").icon} <b>${fmt(me.pp)}</b> prayer points${cost ? ` · ${cost} per fight (~${fmt(me.pp / cost)} fights)` : ""}</div>
|
||||||
<div class="prayers">${cards}</div>
|
<div class="prayers">${cards}</div>
|
||||||
<p class="small muted">Active prayers work in every fight while you have the points to pay for them, and each point spent trains Prayer. Bury bones for more points.</p>
|
<p class="small muted">Prayers cost points each fight. Bury bones for more.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel"><h2>Temple</h2>
|
<div class="panel"><h2>Temple</h2>
|
||||||
${temple ? `<p>Devotion <b>${fmt(city.devotion)}</b> · divine ward <b>+${city.ward}</b> defence for the next raid (up to +${15 * temple} at temple level ${temple}).</p>
|
${temple ? `<p>Devotion <b>${fmt(city.devotion)}</b> · ward <b>+${city.ward}</b> defence (max +${15 * temple})</p>
|
||||||
<p class="small muted">Offering bones builds devotion; it wards the city through the next raid, then starts again. The temple also adds ${temple} prayer point${temple > 1 ? "s" : ""} to every bone you bury.</p>`
|
<p class="small muted">Offerings ward the next raid. +${temple} prayer point${temple > 1 ? "s" : ""} per bone buried.</p>`
|
||||||
: `<p class="muted">No temple yet. Build one on the Town square to offer bones for a divine ward in raids, and to get more prayer points from bones.</p>`}
|
: `<p class="muted">No temple yet.</p>`}
|
||||||
</div></div>`;
|
</div></div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -438,7 +438,7 @@ function toolPanel(skill) {
|
||||||
return `<div class="panel tool-panel">${tool
|
return `<div class="panel tool-panel">${tool
|
||||||
? `${item(tool).icon} <b>${esc(item(tool).name)}</b> <span class="muted">${Math.round(item(tool).speed * 100)}% faster</span>
|
? `${item(tool).icon} <b>${esc(item(tool).name)}</b> <span class="muted">${Math.round(item(tool).speed * 100)}% faster</span>
|
||||||
<button class="btn sm" data-unequip="tool_${skill}">Unequip</button>`
|
<button class="btn sm" data-unequip="tool_${skill}">Unequip</button>`
|
||||||
: `<span class="muted">No tool. A smith can make you one that speeds this up.</span>`}</div>`;
|
: `<span class="muted">No tool.</span>`}</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// What a monster is weak to and shrugs off, and how your weapon fares against it.
|
// 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) {
|
function styleNote(me) {
|
||||||
const weapon = me.equip.weapon, styleId = (weapon && item(weapon).style) || "sword", st = C.styles[styleId];
|
const weapon = me.equip.weapon, styleId = (weapon && item(weapon).style) || "sword", st = C.styles[styleId];
|
||||||
const own = C.class_style[me.cls], traits = {
|
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` : ""}`,
|
sword: `${st.interval}s${me.equip.shield ? `, shield blocks ${Math.round(C.block_chance * 100)}%` : ""}`,
|
||||||
hammer: `slow (${st.interval}s) heavy blows, twice the crits`,
|
hammer: `${st.interval}s, double crits`,
|
||||||
staff: `a cast every ${st.interval}s that rarely misses and goes through armour`,
|
staff: `${st.interval}s, accurate, ignores armour`,
|
||||||
bow: `fast (${st.interval}s), and you shoot first`,
|
bow: `${st.interval}s, shoots first`,
|
||||||
};
|
};
|
||||||
return `<p class="small">Fighting with ${weapon ? `a ${esc(st.name)}` : "your fists"}: ${traits[styleId]}.
|
return `<p class="small">${weapon ? st.name[0].toUpperCase() + st.name.slice(1) : "Fists"}: ${traits[styleId]}.
|
||||||
${own === styleId ? `<b class="good">Your trade's weapon: +15% damage.</b>` : `As a ${esc(C.classes[me.cls].name.toLowerCase())} you hit 15% harder with a ${esc(C.styles[own].name)}.`}</p>`;
|
${own === styleId ? `<b class="good">Trade weapon: +15% damage.</b>` : `Trade weapon: ${esc(C.styles[own].name)} (+15%).`}</p>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function combatPanel() {
|
function combatPanel() {
|
||||||
|
|
@ -551,15 +551,14 @@ function combatPanel() {
|
||||||
<div class="bar thick hp"><span data-live-hpbar style="width:${hpPct}%"></span></div>
|
<div class="bar thick hp"><span data-live-hpbar style="width:${hpPct}%"></span></div>
|
||||||
<div class="row" style="margin-top:12px"><label class="small muted">Food in fights
|
<div class="row" style="margin-top:12px"><label class="small muted">Food in fights
|
||||||
<select data-food-pick>${`<option value="">Cheapest first</option>` + foods}</select></label></div>
|
<select data-food-pick>${`<option value="">Cheapest first</option>` + foods}</select></label></div>
|
||||||
<p class="small">${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 ? "" : ` <b class="warn">(not enough points: prayers are off)</b>`}` : `<span class="muted">No prayers active.</span>`}
|
<p class="small">${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 ? "" : ` <b class="warn">(out of points)</b>`}` : `<span class="muted">No prayers active.</span>`}
|
||||||
<button class="linkish" data-nav="skill:prayer">Prayers</button></p>
|
<button class="linkish" data-nav="skill:prayer">Prayers</button></p>
|
||||||
<p class="small muted">Below half health you eat from your bank automatically.
|
<p class="small muted">Eats below half HP, retreats when out of food.
|
||||||
${me.edible.length ? "" : `<b class="warn">You have no food in your bank.</b>`}
|
${me.edible.length ? "" : `<b class="warn">No food in your bank.</b>`}</p>
|
||||||
Out of food and badly hurt, you retreat: the fight is lost, but nothing else is.</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="panel"><h2>Equipment</h2>
|
<div class="panel"><h2>Equipment</h2>
|
||||||
<div class="gear">${slot("weapon", "weapon")}${slot("shield", "shield")}${slot("helmet", "helmet")}${slot("body", "body armour")}${slot("legs", "leg armour")}</div>
|
<div class="gear">${slot("weapon", "weapon")}${slot("shield", "shield")}${slot("helmet", "helmet")}${slot("body", "body armour")}${slot("legs", "leg armour")}</div>
|
||||||
<p class="small muted">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.</p>
|
<p class="small muted">Power ${me.power} · armour ${me.armour} (-${Math.round(me.reduction * 100)}% damage)</p>
|
||||||
${styleNote(me)}
|
${styleNote(me)}
|
||||||
</div></div>`;
|
</div></div>`;
|
||||||
}
|
}
|
||||||
|
|
@ -569,20 +568,20 @@ function itemStats(id) {
|
||||||
const it = item(id);
|
const it = item(id);
|
||||||
const parts = [];
|
const parts = [];
|
||||||
if (it.heal) parts.push(`❤️ Heals ${it.heal}`);
|
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.power) parts.push(`+${it.power} power`);
|
||||||
if (it.style) {
|
if (it.style) {
|
||||||
const st = C.styles[it.style], trade = Object.entries(C.class_style).find(([, s]) => s === it.style)?.[0];
|
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.armour) parts.push(`+${it.armour} armour`);
|
||||||
if (it.tool) parts.push(`${Math.round(it.speed * 100)}% faster ${skillOf(it.tool).name.toLowerCase()}`);
|
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.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.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) {
|
if (it.fitting) {
|
||||||
const f = C.fittings.find((x) => x.id === id), done = S.city.buildings[f.building].fittings.includes(id);
|
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(", ");
|
return parts.join(", ");
|
||||||
}
|
}
|
||||||
|
|
@ -609,23 +608,22 @@ function actionCard(a) {
|
||||||
if (a.enemy) {
|
if (a.enemy) {
|
||||||
const o = me.fight_odds[a.id], e = a.enemy, pct = (x) => `${Math.round(x * 100)}%`;
|
const o = me.fight_odds[a.id], e = a.enemy, pct = (x) => `${Math.round(x * 100)}%`;
|
||||||
extra = `<div class="small">${HEART} ${fmt(e.hp)} HP · hits up to <b>${o.their_max}</b> every ${e.interval}s${e.boss ? ` · <span class="tag warn">Boss</span>` : ""}</div>
|
extra = `<div class="small">${HEART} ${fmt(e.hp)} HP · hits up to <b>${o.their_max}</b> every ${e.interval}s${e.boss ? ` · <span class="tag warn">Boss</span>` : ""}</div>
|
||||||
<div class="small">You land <b>${pct(o.hit)}</b> of swings (up to ${o.max_hit}) · it lands <b>${pct(o.their_hit)}</b></div>
|
<div class="small">You hit <b>${pct(o.hit)}</b> (max ${o.max_hit}) · it hits <b>${pct(o.their_hit)}</b></div>
|
||||||
<div class="small">About <b>${dur(o.kill_time)}</b> a kill, <b>${Math.round(o.damage)}</b> damage taken${a.gold ? ` · ${a.gold} gold` : ""}</div>
|
<div class="small">~<b>${dur(o.kill_time)}</b> a kill · <b>${Math.round(o.damage)}</b> damage taken${a.gold ? ` · ${a.gold} gold` : ""}</div>
|
||||||
${weaknessLine(e.kind, o)}`;
|
${weaknessLine(e.kind, o)}`;
|
||||||
if (a.drops) io += a.drops.map(([id, q, odds]) => `<span class="item-chip" title="${esc(item(id).name)}: ${Math.round(odds * 100)}% chance">${item(id).icon} ${q} <span class="have">${Math.round(odds * 100)}%</span></span>`).join("");
|
if (a.drops) io += a.drops.map(([id, q, odds]) => `<span class="item-chip" title="${esc(item(id).name)}: ${Math.round(odds * 100)}% chance">${item(id).icon} ${q} <span class="have">${Math.round(odds * 100)}%</span></span>`).join("");
|
||||||
if (a.special === "sabotage") extra += `<div class="small muted">Each win weakens the coming raid by 3%, up to 30%.</div>`;
|
if (a.special === "sabotage") extra += `<div class="small muted">-3% raid strength per win (max 30%)</div>`;
|
||||||
}
|
}
|
||||||
if (a.special === "drill") {
|
if (a.special === "drill") {
|
||||||
const s = S.favours.sergeant, boost = s.boost;
|
const s = S.favours.sergeant, boost = s.boost;
|
||||||
extra = !s.needed ? `<div class="small muted">Fighters drill the townsfolk while a raid is coming. A fully drilled militia adds one defence per townsperson.</div>`
|
extra = !s.needed ? `<div class="small muted">Only while a raid is coming.</div>`
|
||||||
: `<div class="small makes">${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 ? ` <b class="boosted">×2 drill and XP for the next ${fmt(boost)}</b>` : ""}</div>
|
: `<div class="small makes">${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 ? ` <b class="boosted">×2 drill and XP for the next ${fmt(boost)}</b>` : ""}</div>`;
|
||||||
<div class="small muted">A bigger band needs more drilling. A fully drilled militia adds one defence per townsperson.</div>`;
|
|
||||||
}
|
}
|
||||||
if (a.special === "recon") {
|
if (a.special === "recon") {
|
||||||
const boost = S.favours.informant.boost;
|
const boost = S.favours.informant.boost;
|
||||||
const ev = S.upcoming, full = ev && ev.recon >= ev.recon_full;
|
const ev = S.upcoming, full = ev && ev.recon >= ev.recon_full;
|
||||||
extra = full ? `<div class="small muted">The coming threat is fully scouted: this only trains the skill for now.</div>`
|
extra = full ? `<div class="small muted">Fully scouted: XP only.</div>`
|
||||||
: `<div class="small makes">${skillOf("reconnaissance").icon} +${+me.recon_gain[a.id].toFixed(2)} recon on the coming threat${boost ? ` <b class="boosted">×2 recon and XP for the next ${fmt(boost)}</b>` : ""}</div>`;
|
: `<div class="small makes">${skillOf("reconnaissance").icon} +${+me.recon_gain[a.id].toFixed(2)} recon${boost ? ` <b class="boosted">×2 recon and XP for the next ${fmt(boost)}</b>` : ""}</div>`;
|
||||||
}
|
}
|
||||||
const temple = S.city.buildings.temple?.level || 0;
|
const temple = S.city.buildings.temple?.level || 0;
|
||||||
if (a.special === "labour") {
|
if (a.special === "labour") {
|
||||||
|
|
@ -633,7 +631,7 @@ function actionCard(a) {
|
||||||
extra = `<div class="small makes">${skillOf("construction").icon} +${me.labour_per_action} labour per action${boost ? ` <b class="boosted">×2 labour and XP for the next ${fmt(boost)}</b>` : ""}</div>`;
|
extra = `<div class="small makes">${skillOf("construction").icon} +${me.labour_per_action} labour per action${boost ? ` <b class="boosted">×2 labour and XP for the next ${fmt(boost)}</b>` : ""}</div>`;
|
||||||
}
|
}
|
||||||
if (a.special === "bury") extra = `<div class="small makes">${skillOf("prayer").icon} +${a.pp + temple} prayer points</div>`;
|
if (a.special === "bury") extra = `<div class="small makes">${skillOf("prayer").icon} +${a.pp + temple} prayer points</div>`;
|
||||||
if (a.special === "offer") extra = `<div class="small makes">${C.buildings.temple.icon} +${+(a.devotion * (1 + 0.25 * temple)).toFixed(1)} devotion for the city's ward</div>`;
|
if (a.special === "offer") extra = `<div class="small makes">${C.buildings.temple.icon} +${+(a.devotion * (1 + 0.25 * temple)).toFixed(1)} devotion</div>`;
|
||||||
const made = Object.keys(a.outputs).map(itemStats).filter(Boolean);
|
const made = Object.keys(a.outputs).map(itemStats).filter(Boolean);
|
||||||
if (made.length) extra += `<div class="small makes">${made.join(" · ")}</div>`;
|
if (made.length) extra += `<div class="small makes">${made.join(" · ")}</div>`;
|
||||||
return `
|
return `
|
||||||
|
|
@ -652,8 +650,8 @@ function actionCard(a) {
|
||||||
<span class="why">${!active && !second && why ? esc(why) : ""}</span>
|
<span class="why">${!active && !second && why ? esc(why) : ""}</span>
|
||||||
${second ? `<button class="btn" data-plot="">Clear plot 2</button>`
|
${second ? `<button class="btn" data-plot="">Clear plot 2</button>`
|
||||||
: active ? `<button class="btn danger" data-stop>Stop</button>`
|
: active ? `<button class="btn danger" data-stop>Stop</button>`
|
||||||
: `<span class="btns"><button class="btn" data-once="${a.id}" ${why ? "disabled" : ""} title="Do this once, then go back to what you were doing">${a.enemy ? "Fight once" : Object.keys(a.outputs).length ? "Make one" : "Once"}</button>
|
: `<span class="btns"><button class="btn" data-once="${a.id}" ${why ? "disabled" : ""} title="Once, then resume">${a.enemy ? "Fight once" : Object.keys(a.outputs).length ? "Make one" : "Once"}</button>
|
||||||
${canPlot ? `<button class="btn" data-plot="${a.id}" ${why ? "disabled" : ""} title="Grow this in your second plot as well">Plot 2</button>` : ""}
|
${canPlot ? `<button class="btn" data-plot="${a.id}" ${why ? "disabled" : ""} title="Grow in plot 2">Plot 2</button>` : ""}
|
||||||
<button class="btn primary" data-start="${a.id}" ${why ? "disabled" : ""}>Start</button></span>`}
|
<button class="btn primary" data-start="${a.id}" ${why ? "disabled" : ""}>Start</button></span>`}
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
@ -682,11 +680,10 @@ function bank() {
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<h2>Bank · ${me.slots_used} / ${me.slots} slots · ${fmt(me.stack)} per stack</h2>
|
<h2>Bank · ${me.slots_used} / ${me.slots} slots · ${fmt(me.stack)} per stack</h2>
|
||||||
<div class="slots">${slots}</div>
|
<div class="slots">${slots}</div>
|
||||||
<p class="small muted">When an item doesn't fit:
|
<p class="small muted">Bank full:
|
||||||
<button class="btn sm ${me.overflow === "city" ? "gold" : ""}" data-overflow="city">Send it to the city</button>
|
<button class="btn sm ${me.overflow === "city" ? "gold" : ""}" data-overflow="city">Send it to the city</button>
|
||||||
<button class="btn sm ${me.overflow === "stop" ? "gold" : ""}" data-overflow="stop">Stop working</button>
|
<button class="btn sm ${me.overflow === "stop" ? "gold" : ""}" data-overflow="stop">Stop working</button>
|
||||||
<br>Raids never touch your bank. Overflow sent to the city earns half the favour of giving it yourself;
|
<br>Overflow earns half favour; what the city can't use is sold.</p>
|
||||||
anything the city has no use for is sold instead.</p>
|
|
||||||
</div>
|
</div>
|
||||||
${favourPanel()}
|
${favourPanel()}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -711,11 +708,11 @@ function itemPanel(id) {
|
||||||
${S.me.potions[it.potion.skill] && S.me.potions[it.potion.skill].item !== id ? `<div class="small why">Replaces your ${esc(item(S.me.potions[it.potion.skill].item).name.toLowerCase())} (${S.me.potions[it.potion.skill].charges} uses left)</div>` : ""}</div>` : ""}
|
${S.me.potions[it.potion.skill] && S.me.potions[it.potion.skill].item !== id ? `<div class="small why">Replaces your ${esc(item(S.me.potions[it.potion.skill].item).name.toLowerCase())} (${S.me.potions[it.potion.skill].charges} uses left)</div>` : ""}</div>` : ""}
|
||||||
${it.heal ? `<div class="row"><div class="small muted">Heals ${it.heal} in fights.</div>
|
${it.heal ? `<div class="row"><div class="small muted">Heals ${it.heal} in fights.</div>
|
||||||
${S.me.food === id ? `<span class="tag good">Your fighting food</span>` : `<button class="btn sm" data-food="${id}">Eat this first in fights</button>`}</div>` : ""}
|
${S.me.food === id ? `<span class="tag good">Your fighting food</span>` : `<button class="btn sm" data-food="${id}">Eat this first in fights</button>`}</div>` : ""}
|
||||||
${it.food ? `<div class="row small muted">Feeds ${it.food} townsfolk for two days in the city granary.</div>` : ""}
|
${it.food ? `<div class="row small muted">Feeds ${it.food} townsfolk for 2 days.</div>` : ""}
|
||||||
${C.city_uses[id].length
|
${C.city_uses[id].length
|
||||||
? `<div class="row"><div class="k">Donate to the city · +${it.value} favour each</div><div class="btns">${qtyBtns("data-donate")}</div>
|
? `<div class="row"><div class="k">Donate to the city · +${it.value} favour each</div><div class="btns">${qtyBtns("data-donate")}</div>
|
||||||
<div class="small muted">Used for ${esc(listing(C.city_uses[id]))}.</div></div>`
|
<div class="small muted">Used for ${esc(listing(C.city_uses[id]))}.</div></div>`
|
||||||
: `<div class="row small muted">The city has no use for this. Sell it, or give it to someone who can use it.</div>`}
|
: `<div class="row small muted">The city has no use for this.</div>`}
|
||||||
${others.length ? `<div class="row"><div class="k">Give to <select data-keep="give-to">${others.map((p) => `<option value="${esc(p.id)}">${esc(p.name)}</option>`).join("")}</select></div><div class="btns">${qtyBtns("data-give")}</div></div>` : ""}
|
${others.length ? `<div class="row"><div class="k">Give to <select data-keep="give-to">${others.map((p) => `<option value="${esc(p.id)}">${esc(p.name)}</option>`).join("")}</select></div><div class="btns">${qtyBtns("data-give")}</div></div>` : ""}
|
||||||
<div class="row"><div class="k">Sell</div><div class="btns">${qtyBtns("data-sell")}</div></div>
|
<div class="row"><div class="k">Sell</div><div class="btns">${qtyBtns("data-sell")}</div></div>
|
||||||
<div class="row"><button class="btn sm" data-select="">Close</button></div>
|
<div class="row"><button class="btn sm" data-select="">Close</button></div>
|
||||||
|
|
@ -743,7 +740,7 @@ function goldPanel() {
|
||||||
return `<div class="panel"><h2>Gold</h2>
|
return `<div class="panel"><h2>Gold</h2>
|
||||||
<p class="gold">${COIN} ${fmt(S.me.gold)}</p>
|
<p class="gold">${COIN} ${fmt(S.me.gold)}</p>
|
||||||
<form data-form="gold" class="btns"><input type="number" min="1" data-keep="gold" placeholder="Amount" style="width:120px"><button class="btn gold">Donate to treasury</button></form>
|
<form data-form="gold" class="btns"><input type="number" min="1" data-keep="gold" placeholder="Amount" style="width:120px"><button class="btn gold">Donate to treasury</button></form>
|
||||||
<p class="small muted">The treasury pays the gold part of new buildings. 1 favour per gold.</p></div>`;
|
<p class="small muted">1 favour per gold.</p></div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function favourPanel() {
|
function favourPanel() {
|
||||||
|
|
@ -751,7 +748,7 @@ function favourPanel() {
|
||||||
return `<div class="panel"><h2>Favour with the council</h2>
|
return `<div class="panel"><h2>Favour with the council</h2>
|
||||||
<p class="favour">${FAVOUR} ${fmt(me.favour)} favour · ${fmt(me.donated)} given in total</p>
|
<p class="favour">${FAVOUR} ${fmt(me.favour)} favour · ${fmt(me.donated)} given in total</p>
|
||||||
<div class="btns"><button class="btn" data-perk="bank_slot" ${me.favour < me.perk_costs.bank_slot ? "disabled" : ""}>+1 bank slot (${fmt(me.perk_costs.bank_slot)} favour)</button></div>
|
<div class="btns"><button class="btn" data-perk="bank_slot" ${me.favour < me.perk_costs.bank_slot ? "disabled" : ""}>+1 bank slot (${fmt(me.perk_costs.bank_slot)} favour)</button></div>
|
||||||
<p class="small muted">Favour can also hire master builders, informants and drill sergeants: see <button class="linkish" data-nav="council">Council & threats</button>.</p></div>`;
|
<p class="small muted">More to spend on in <button class="linkish" data-nav="council">Council & threats</button>.</p></div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- town -----------------------------------------------------------------
|
// --- town -----------------------------------------------------------------
|
||||||
|
|
@ -761,8 +758,8 @@ function town() {
|
||||||
const growth = city.starving ? `<span class="bad">Hungry: ${city.growth} a day</span>`
|
const growth = city.starving ? `<span class="bad">Hungry: ${city.growth} a day</span>`
|
||||||
: city.growth > 0 ? `+${city.growth} a day`
|
: city.growth > 0 ? `+${city.growth} a day`
|
||||||
: city.growth < 0 ? `<span class="bad">Overcrowded: ${city.growth} a day</span>`
|
: city.growth < 0 ? `<span class="bad">Overcrowded: ${city.growth} a day</span>`
|
||||||
: `<span class="warn">Full. Build more houses to grow</span>`;
|
: `<span class="warn">Full: build houses</span>`;
|
||||||
const next = city.next_tier ? `${city.next_tier.name} at ${city.next_tier.pop} people` : "The largest a settlement can be";
|
const next = city.next_tier ? `${city.next_tier.name} at ${city.next_tier.pop} people` : "Largest tier";
|
||||||
let html = `<div class="stats">
|
let html = `<div class="stats">
|
||||||
<div class="stat"><div class="k">Settlement</div><div class="v">${city.tier_name}</div><div class="note">${next}</div></div>
|
<div class="stat"><div class="k">Settlement</div><div class="v">${city.tier_name}</div><div class="note">${next}</div></div>
|
||||||
<div class="stat"><div class="k">Population</div><div class="v">${city.population} <small>/ ${city.housing} homes</small></div><div class="note">${growth}</div></div>
|
<div class="stat"><div class="k">Population</div><div class="v">${city.population} <small>/ ${city.housing} homes</small></div><div class="note">${growth}</div></div>
|
||||||
|
|
@ -772,16 +769,16 @@ function town() {
|
||||||
${weatherCard()}
|
${weatherCard()}
|
||||||
</div>`;
|
</div>`;
|
||||||
html += happeningsPanel();
|
html += happeningsPanel();
|
||||||
if (city.starving) html += `<div class="banner bad">🍞<div class="grow"><b>The granary is empty.</b> People are leaving and the city defends worse. Donate bread, cooked meat or stew.</div></div>`;
|
if (city.starving) html += `<div class="banner bad">🍞<div class="grow"><b>Granary empty.</b> People are leaving, defence is down. Donate food.</div></div>`;
|
||||||
if (city.merchant_until > serverNow()) html += `<div class="banner">🐪<div class="grow"><b>A merchant caravan is in town.</b> Everything sells for 50% more for another ${countdown(city.merchant_until)}.</div></div>`;
|
if (city.merchant_until > serverNow()) html += `<div class="banner">🐪<div class="grow"><b>Merchant in town.</b> Sell prices +50% for ${countdown(city.merchant_until)}.</div></div>`;
|
||||||
if (city.construction) html += siteBanner();
|
if (city.construction) html += siteBanner();
|
||||||
html += `<div class="section-title">Buildings</div>
|
html += `<div class="section-title">Buildings</div>
|
||||||
<p class="small muted">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.</p>
|
<p class="small muted">Funds are safe from raiders. Building starts once a fund is full.</p>
|
||||||
<div class="grid wide">${Object.entries(city.buildings).map(([key, b]) => buildingCard(key, b)).join("")}</div>`;
|
<div class="grid wide">${Object.entries(city.buildings).map(([key, b]) => buildingCard(key, b)).join("")}</div>`;
|
||||||
html += `<div class="section-title">City stockpile</div><div class="panel">`;
|
html += `<div class="section-title">City stockpile</div><div class="panel">`;
|
||||||
const stock = Object.entries(city.stockpile);
|
const stock = Object.entries(city.stockpile);
|
||||||
html += stock.length ? `<div class="stock">${stock.map(([id, q]) => itemChip(id, q)).join("")}</div>` : `<p class="muted">Empty.</p>`;
|
html += stock.length ? `<div class="stock">${stock.map(([id, q]) => itemChip(id, q)).join("")}</div>` : `<p class="muted">Empty.</p>`;
|
||||||
html += `<p class="small muted">Food, weapons and anything not yet in a fund. The granary and armoury draw from here, and so do raiders.</p></div>`;
|
html += `<p class="small muted">Feeds the granary and armoury. Raiders can loot it.</p></div>`;
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -798,10 +795,10 @@ function siteBanner() {
|
||||||
<span class="muted small">${fmt(work.labour)} / ${fmt(work.needed)} labour</span>
|
<span class="muted small">${fmt(work.labour)} / ${fmt(work.needed)} labour</span>
|
||||||
<span class="btns">
|
<span class="btns">
|
||||||
${working ? `<span class="btn sm working">Working</span>` : `<button class="btn sm primary" data-start="labour">Work on it</button>`}
|
${working ? `<span class="btn sm working">Working</span>` : `<button class="btn sm primary" data-start="labour">Work on it</button>`}
|
||||||
${hire.why ? "" : `<button class="btn sm gold" data-favour="builders" ${me.favour < hire.cost ? "disabled" : ""} title="The next ${hire.labour} labour on the site counts double, with double XP">Master builder ${FAVOUR} ${fmt(hire.cost)}</button>`}
|
${hire.why ? "" : `<button class="btn sm gold" data-favour="builders" ${me.favour < hire.cost ? "disabled" : ""} title="×2 labour and XP for the next ${hire.labour}">Master builder ${FAVOUR} ${fmt(hire.cost)}</button>`}
|
||||||
</span></div>
|
</span></div>
|
||||||
<div class="bar gold" style="margin-top:6px"><span style="width:${Math.min(100, work.labour / work.needed * 100)}%"></span></div>
|
<div class="bar gold" style="margin-top:6px"><span style="width:${Math.min(100, work.labour / work.needed * 100)}%"></span></div>
|
||||||
<div class="small muted" style="margin-top:6px">${builders.length ? `On site: ${builders.join(", ")}. ` : ""}${hire.boost ? `<b class="boosted">Master builder on site: the next ${fmt(hire.boost)} labour counts double, with double XP.</b> ` : ""}The townsfolk add about ${fmt(city.townsfolk_labour)} labour an hour.${queued ? ` Next: ${queued}.` : ""}</div>
|
<div class="small muted" style="margin-top:6px">${builders.length ? `On site: ${builders.join(", ")}. ` : ""}${hire.boost ? `<b class="boosted">Master builder: ×2 for the next ${fmt(hire.boost)}.</b> ` : ""}Townsfolk +${fmt(city.townsfolk_labour)}/h.${queued ? ` Next: ${queued}.` : ""}</div>
|
||||||
</div></div>`;
|
</div></div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -812,7 +809,7 @@ function weatherTip(w) {
|
||||||
|
|
||||||
function weatherCard() {
|
function weatherCard() {
|
||||||
const h = S.happenings, w = h.weather;
|
const h = S.happenings, w = h.weather;
|
||||||
return `<div class="stat" title="${esc(`${weatherTip(w)} Weather changes every few hours; each season lasts a week.`)}"><div class="k">${h.season.icon} ${h.season.name}, day ${h.season.day}</div>
|
return `<div class="stat" title="${esc(weatherTip(w))}"><div class="k">${h.season.icon} ${h.season.name}, day ${h.season.day}</div>
|
||||||
<div class="v">${w.icon} <small>${esc(w.name)}</small></div>
|
<div class="v">${w.icon} <small>${esc(w.name)}</small></div>
|
||||||
<div class="note">${w.next_icon} ${esc(w.next_name)} in ${countdown(w.until)}</div></div>`;
|
<div class="note">${w.next_icon} ${esc(w.next_name)} in ${countdown(w.until)}</div></div>`;
|
||||||
}
|
}
|
||||||
|
|
@ -822,7 +819,7 @@ function happeningsPanel() {
|
||||||
const h = S.happenings, me = S.me, city = S.city;
|
const h = S.happenings, me = S.me, city = S.city;
|
||||||
const rows = [];
|
const rows = [];
|
||||||
if (h.weather.kind !== "clear") rows.push(`<div class="happening"><span class="ico">${h.weather.icon}</span><span class="grow"><b>${esc(h.weather.name)}</b><span class="small muted">${esc(h.weather.text)}</span></span></div>`);
|
if (h.weather.kind !== "clear") rows.push(`<div class="happening"><span class="ico">${h.weather.icon}</span><span class="grow"><b>${esc(h.weather.name)}</b><span class="small muted">${esc(h.weather.text)}</span></span></div>`);
|
||||||
if (h.cold) rows.push(`<div class="happening bad"><span class="ico">🥶</span><span class="grow"><b>Out of firewood</b><span class="small muted">People are cold and eating half again as much. Donate logs.</span></span></div>`);
|
if (h.cold) rows.push(`<div class="happening bad"><span class="ico">🥶</span><span class="grow"><b>Out of firewood</b><span class="small muted">City eats 50% more. Donate logs.</span></span></div>`);
|
||||||
for (const e of h.effects) {
|
for (const e of h.effects) {
|
||||||
const hire = e.offer ? `<button class="btn sm gold" data-hire ${city.treasury < e.cost ? "disabled" : ""}>Hire for ${fmt(e.cost)} gold</button>` : "";
|
const hire = e.offer ? `<button class="btn sm gold" data-hire ${city.treasury < e.cost ? "disabled" : ""}>Hire for ${fmt(e.cost)} gold</button>` : "";
|
||||||
rows.push(`<div class="happening"><span class="ico">${e.icon}</span><span class="grow"><b>${esc(e.name)}</b>
|
rows.push(`<div class="happening"><span class="ico">${e.icon}</span><span class="grow"><b>${esc(e.name)}</b>
|
||||||
|
|
@ -860,7 +857,7 @@ function skillHappenings(skill) {
|
||||||
for (const p of h.problems) {
|
for (const p of h.problems) {
|
||||||
if (!C.problems[p.kind] || !touches(C.problems[p.kind])) continue;
|
if (!C.problems[p.kind] || !touches(C.problems[p.kind])) continue;
|
||||||
const fix = p.action && C.actions.find((x) => x.id === p.action);
|
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 ? `<div class="panel tool-panel skill-happenings">${lines.join("")}</div>` : "";
|
return lines.length ? `<div class="panel tool-panel skill-happenings">${lines.join("")}</div>` : "";
|
||||||
}
|
}
|
||||||
|
|
@ -869,9 +866,9 @@ function buildingCard(key, b) {
|
||||||
const spec = C.buildings[key], city = S.city, me = S.me;
|
const spec = C.buildings[key], city = S.city, me = S.me;
|
||||||
const tags = {
|
const tags = {
|
||||||
building: `<span class="tag gold">Under construction</span>`,
|
building: `<span class="tag gold">Under construction</span>`,
|
||||||
queued: `<span class="tag good">Funded · waiting for builders</span>`,
|
queued: `<span class="tag good">Funded · queued</span>`,
|
||||||
max: `<span class="tag good">Fully built</span>`,
|
max: `<span class="tag good">Fully built</span>`,
|
||||||
capped: `<span class="tag">Next level when the settlement grows</span>`,
|
capped: `<span class="tag">Needs a bigger settlement</span>`,
|
||||||
};
|
};
|
||||||
let body = tags[b.status] ? `<div>${tags[b.status]}</div>` : "";
|
let body = tags[b.status] ? `<div>${tags[b.status]}</div>` : "";
|
||||||
if (b.status === "funding" || b.status === "queued") {
|
if (b.status === "funding" || b.status === "queued") {
|
||||||
|
|
@ -888,10 +885,10 @@ function buildingCard(key, b) {
|
||||||
}).join("")}</div>`;
|
}).join("")}</div>`;
|
||||||
}
|
}
|
||||||
body += fittingRows(key, b);
|
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 `<div class="action building ${b.damaged ? "damaged" : ""}">
|
return `<div class="action building ${b.damaged ? "damaged" : ""}">
|
||||||
<div class="top"><div class="ico">${spec.icon}</div><div>
|
<div class="top"><div class="ico">${spec.icon}</div><div>
|
||||||
<div class="name">${esc(spec.name)} <span class="lv muted">${levelText}</span> ${b.damaged ? `<span class="tag bad">Damaged: repairs needed</span>` : ""}</div>
|
<div class="name">${esc(spec.name)} <span class="lv muted">${levelText}</span> ${b.damaged ? `<span class="tag bad">Damaged</span>` : ""}</div>
|
||||||
<div class="sub">${esc(spec.effect)}</div>
|
<div class="sub">${esc(spec.effect)}</div>
|
||||||
</div></div>${body}</div>`;
|
</div></div>${body}</div>`;
|
||||||
}
|
}
|
||||||
|
|
@ -927,17 +924,17 @@ function pips(level) {
|
||||||
|
|
||||||
function reconText(ev) {
|
function reconText(ev) {
|
||||||
const points = `${fmt(ev.recon)} / ${fmt(ev.recon_full)} points`;
|
const points = `${fmt(ev.recon)} / ${fmt(ev.recon_full)} points`;
|
||||||
if (ev.intel >= 3) return `${points} · nothing more to learn`;
|
if (ev.intel >= 3) return `${points} · complete`;
|
||||||
const boost = S.favours.informant.boost ? ` · <b class="boosted">informant's leads: ×2 for the next ${fmt(S.favours.informant.boost)}</b>` : "";
|
const boost = S.favours.informant.boost ? ` · <b class="boosted">informant: ×2 for the next ${fmt(S.favours.informant.boost)}</b>` : "";
|
||||||
if (ev.recon_next <= 0) return `${points} · enough for the next level, but they're too far off to read yet${boost}`;
|
if (ev.recon_next <= 0) return `${points} · next level opens closer to the threat${boost}`;
|
||||||
return `${points} · ${fmt(ev.recon_next)} more for the next intel level${boost}`;
|
return `${points} · ${fmt(ev.recon_next)} to the next level${boost}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function council() {
|
function council() {
|
||||||
const ev = S.upcoming, city = S.city, me = S.me;
|
const ev = S.upcoming, city = S.city, me = S.me;
|
||||||
let html = "";
|
let html = "";
|
||||||
if (!ev) {
|
if (!ev) {
|
||||||
html += `<div class="panel"><h2>All quiet</h2><p class="muted">No news on the roads. The spymasters will report as soon as something stirs.</p></div>`;
|
html += `<div class="panel"><h2>All quiet</h2><p class="muted">No news on the roads.</p></div>`;
|
||||||
} else {
|
} else {
|
||||||
const threat = ["raid", "dragon"].includes(ev.kind);
|
const threat = ["raid", "dragon"].includes(ev.kind);
|
||||||
html += `<div class="panel ${threat ? "threat-card" : ""}">
|
html += `<div class="panel ${threat ? "threat-card" : ""}">
|
||||||
|
|
@ -947,10 +944,9 @@ function council() {
|
||||||
<span class="k">Expected</span><span>${eventWhen(ev)}</span>
|
<span class="k">Expected</span><span>${eventWhen(ev)}</span>
|
||||||
${ev.range ? `<span class="k">Strength</span><span>${ev.strength ?? `${ev.range[0]}–${ev.range[1]}`}</span>` : ""}
|
${ev.range ? `<span class="k">Strength</span><span>${ev.strength ?? `${ev.range[0]}–${ev.range[1]}`}</span>` : ""}
|
||||||
${ev.trait ? `<span class="k">Scouts say</span><span>${esc(ev.trait)}</span>` : ""}
|
${ev.trait ? `<span class="k">Scouts say</span><span>${esc(ev.trait)}</span>` : ""}
|
||||||
${ev.camp ? `<span class="k">Their camp</span><span>Located. Fighters can sabotage it from the combat page${ev.sabotage ? ` (weakened ${ev.sabotage}% so far)` : ""}.</span>` : ""}
|
${ev.camp ? `<span class="k">Their camp</span><span>Found · sabotage it in Combat${ev.sabotage ? ` (-${ev.sabotage}% so far)` : ""}</span>` : ""}
|
||||||
<span class="k">Reconnaissance</span><span>${reconText(ev)}</span>
|
<span class="k">Reconnaissance</span><span>${reconText(ev)}</span>
|
||||||
</div>
|
</div>`;
|
||||||
${ev.intel < 3 ? `<p class="small muted">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.</p>` : ""}`;
|
|
||||||
if (threat) {
|
if (threat) {
|
||||||
const strength = ev.strength ?? (ev.range ? `${ev.range[0]}–${ev.range[1]}` : "?");
|
const strength = ev.strength ?? (ev.range ? `${ev.range[0]}–${ev.range[1]}` : "?");
|
||||||
html += `<div class="vs"><div class="side"><span class="muted small">Our defence</span><b>${fmt(city.defence.total)}</b></div><span class="muted">vs</span><div class="side"><span class="muted small">Raiders</span><b>${strength}</b></div></div>`;
|
html += `<div class="vs"><div class="side"><span class="muted small">Our defence</span><b>${fmt(city.defence.total)}</b></div><span class="muted">vs</span><div class="side"><span class="muted small">Raiders</span><b>${strength}</b></div></div>`;
|
||||||
|
|
@ -988,9 +984,9 @@ function favourSpends() {
|
||||||
</div>`;
|
</div>`;
|
||||||
};
|
};
|
||||||
return `<div class="panel"><h2>Spend favour on the city · you have ${FAVOUR} ${fmt(me.favour)}</h2>
|
return `<div class="panel"><h2>Spend favour on the city · you have ${FAVOUR} ${fmt(me.favour)}</h2>
|
||||||
${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("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) + "." : `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("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) + "." : `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("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.`)}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1005,15 +1001,15 @@ function raidPrep(ev) {
|
||||||
}).join("");
|
}).join("");
|
||||||
return `<div class="cols">
|
return `<div class="cols">
|
||||||
<div class="panel"><h2>Council requests</h2>${reqs}
|
<div class="panel"><h2>Council requests</h2>${reqs}
|
||||||
<p class="small muted">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.</p></div>
|
<p class="small muted">Stockpile gear arms the defenders, best first. Fights use up arrows and break some gear.</p></div>
|
||||||
<div class="panel"><h2>Defence</h2>
|
<div class="panel"><h2>Defence</h2>
|
||||||
<table class="parts">${Object.entries(d.parts).filter(([k, v]) => k !== "rallied" || v).map(([k, v]) => `<tr><td>${names[k]}</td><td>${fmt(v)}</td></tr>`).join("")}
|
<table class="parts">${Object.entries(d.parts).filter(([k, v]) => k !== "rallied" || v).map(([k, v]) => `<tr><td>${names[k]}</td><td>${fmt(v)}</td></tr>`).join("")}
|
||||||
${d.starving ? `<tr><td class="bad">Hungry (×0.8)</td><td></td></tr>` : ""}</table>
|
${d.starving ? `<tr><td class="bad">Hungry (×0.8)</td><td></td></tr>` : ""}</table>
|
||||||
<h2 style="margin-top:16px">Garrison</h2>
|
<h2 style="margin-top:16px">Garrison</h2>
|
||||||
<p class="small">${d.garrison.length ? d.garrison.map((g) => `${esc(g.name)} (${g.power})`).join(", ") : `<span class="muted">Nobody has signed up yet.</span>`}</p>
|
<p class="small">${d.garrison.length ? d.garrison.map((g) => `${esc(g.name)} (${g.power})`).join(", ") : `<span class="muted">Nobody yet.</span>`}</p>
|
||||||
${me.garrison ? `<button class="btn" data-garrison="0">Leave the garrison</button>`
|
${me.garrison ? `<button class="btn" data-garrison="0">Leave the garrison</button>`
|
||||||
: `<button class="btn primary" data-garrison="1">Stand guard (+${me.power})</button>`}
|
: `<button class="btn primary" data-garrison="1">Stand guard (+${me.power})</button>`}
|
||||||
<p class="small muted">You can keep working while you're signed up. Victory earns the garrison favour and combat XP.</p>
|
<p class="small muted">Keep working while on guard. Victory earns favour and combat XP.</p>
|
||||||
</div></div>`;
|
</div></div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1026,7 +1022,7 @@ function board() {
|
||||||
const ledger = [...S.ledger].reverse().map((l) => `<li>${esc(l.text)}<span class="when">${ago(l.at)}</span></li>`).join("");
|
const ledger = [...S.ledger].reverse().map((l) => `<li>${esc(l.text)}<span class="when">${ago(l.at)}</span></li>`).join("");
|
||||||
return `<div class="cols">
|
return `<div class="cols">
|
||||||
<div class="panel"><h2>Town board</h2>
|
<div class="panel"><h2>Town board</h2>
|
||||||
<div class="msgs">${msgs || `<p class="muted">No messages yet. Say hello, or ask for that iron you need.</p>`}</div>
|
<div class="msgs">${msgs || `<p class="muted">No messages yet.</p>`}</div>
|
||||||
<form data-form="board"><textarea data-keep="board" maxlength="500" placeholder="Write to the guild…"></textarea>
|
<form data-form="board"><textarea data-keep="board" maxlength="500" placeholder="Write to the guild…"></textarea>
|
||||||
<p><button class="btn primary">Post</button></p></form></div>
|
<p><button class="btn primary">Post</button></p></form></div>
|
||||||
<div class="panel"><h2>Town ledger</h2><ul class="ledger">${ledger || `<li class="muted">Nothing recorded yet.</li>`}</ul></div>
|
<div class="panel"><h2>Town ledger</h2><ul class="ledger">${ledger || `<li class="muted">Nothing recorded yet.</li>`}</ul></div>
|
||||||
|
|
@ -1117,7 +1113,7 @@ function characterSheet(uid) {
|
||||||
<div class="sheet-gear">${Object.entries(SLOT_NAMES).map(([slot, label]) => gearSlot(slot, label, "Nothing")).join("")}</div>
|
<div class="sheet-gear">${Object.entries(SLOT_NAMES).map(([slot, label]) => gearSlot(slot, label, "Nothing")).join("")}</div>
|
||||||
<h2 style="margin-top:16px">Tools</h2>
|
<h2 style="margin-top:16px">Tools</h2>
|
||||||
<div class="sheet-gear">${C.skills.filter((s) => `tool_${s.id}` in p.equip).map((s) => gearSlot(`tool_${s.id}`, s.name, "No tool")).join("")}</div>
|
<div class="sheet-gear">${C.skills.filter((s) => `tool_${s.id}` in p.equip).map((s) => gearSlot(`tool_${s.id}`, s.name, "No tool")).join("")}</div>
|
||||||
${mine ? `<p class="small muted">Equip gear and tools from the <button class="linkish" data-nav="bank">bank</button>.</p>` : ""}
|
${mine ? `<p class="small muted">Equip from the <button class="linkish" data-nav="bank">bank</button>.</p>` : ""}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel"><h2>Skills</h2><div class="sheet-skills">${skillRows}</div></div>
|
<div class="panel"><h2>Skills</h2><div class="sheet-skills">${skillRows}</div></div>
|
||||||
|
|
@ -1137,12 +1133,10 @@ function classCards() {
|
||||||
function repickPage() {
|
function repickPage() {
|
||||||
return `<div class="welcome">
|
return `<div class="welcome">
|
||||||
<h2>Pick your trade</h2>
|
<h2>Pick your trade</h2>
|
||||||
<p class="muted">Classes are now whole trades, from gathering through to the finished goods, so you can play
|
<p class="muted">Classes are now trades, gathering through crafting. You keep every level and item. Free, once.</p>
|
||||||
the quiet gathering side and the busier crafting side yourself. You keep every level and item. This choice
|
|
||||||
is free, once.</p>
|
|
||||||
<div class="classes">${classCards()}</div>
|
<div class="classes">${classCards()}</div>
|
||||||
<div class="btns"><button class="btn primary" data-repick ${pickedClass ? "" : "disabled"}>Take up this trade</button></div>
|
<div class="btns"><button class="btn primary" data-repick ${pickedClass ? "" : "disabled"}>Take up this trade</button></div>
|
||||||
<p class="small muted">Every class can still train every skill, scout and drill. A trade makes its skills faster and better.</p>
|
<p class="small muted">Any class can train any skill.</p>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1150,15 +1144,14 @@ function register() {
|
||||||
const cards = classCards();
|
const cards = classCards();
|
||||||
return `<div class="welcome">
|
return `<div class="welcome">
|
||||||
<h2>A new city on the frontier</h2>
|
<h2>A new city on the frontier</h2>
|
||||||
<p class="muted">You're one of a handful of adventurers settling a young city at the edge of the kingdom.
|
<p class="muted">Settle a frontier city together. Train, build, and hold off the bandits.
|
||||||
Train your skills, supply the council, and keep the place standing when the bandits come.
|
You keep working while away, for up to a day.</p>
|
||||||
Your adventurer keeps working while you're away, for up to a day.</p>
|
|
||||||
<div class="classes">${cards}</div>
|
<div class="classes">${cards}</div>
|
||||||
<form data-form="register" class="btns">
|
<form data-form="register" class="btns">
|
||||||
<input type="text" data-keep="name" maxlength="20" placeholder="Your adventurer's name" style="flex:1;min-width:200px">
|
<input type="text" data-keep="name" maxlength="20" placeholder="Your adventurer's name" style="flex:1;min-width:200px">
|
||||||
<button class="btn primary" ${pickedClass ? "" : "disabled"}>Enter the city</button>
|
<button class="btn primary" ${pickedClass ? "" : "disabled"}>Enter the city</button>
|
||||||
</form>
|
</form>
|
||||||
<p class="small muted">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.</p>
|
<p class="small muted">Any class can train any skill.</p>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1236,7 +1229,7 @@ document.addEventListener("click", (e) => {
|
||||||
if (d.devHappen) return act("/api/dev/happen", { track: d.devHappen });
|
if (d.devHappen) return act("/api/dev/happen", { track: d.devHappen });
|
||||||
if (d.pray) {
|
if (d.pray) {
|
||||||
const on = S.me.prayers.includes(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] });
|
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 });
|
if (d.overflow) return act("/api/settings", { overflow: d.overflow });
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue