Six tiers of ore, trees, hides and gear; dragons; tier tabs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
a18e8e5bb7
commit
ba68a62080
6 changed files with 319 additions and 103 deletions
313
game.py
313
game.py
|
|
@ -49,21 +49,11 @@ def level_for(xp: float) -> int:
|
|||
|
||||
ITEMS = {
|
||||
"stone": {"name": "Stone", "icon": "🪨", "value": 1},
|
||||
"copper_ore": {"name": "Copper ore", "icon": "🟤", "value": 2},
|
||||
"iron_ore": {"name": "Iron ore", "icon": "⚫", "value": 4},
|
||||
"logs": {"name": "Logs", "icon": "🪵", "value": 1},
|
||||
"oak_logs": {"name": "Oak logs", "icon": "🌳", "value": 4},
|
||||
"wheat": {"name": "Wheat", "icon": "🌾", "value": 1},
|
||||
"vegetables": {"name": "Vegetables", "icon": "🥕", "value": 2},
|
||||
"feathers": {"name": "Feathers", "icon": "🪶", "value": 1},
|
||||
"raw_meat": {"name": "Raw meat", "icon": "🥩", "value": 2},
|
||||
"hide": {"name": "Hide", "icon": "🟫", "value": 3},
|
||||
"copper_bar": {"name": "Copper bar", "icon": "🟧", "value": 5},
|
||||
"iron_bar": {"name": "Iron bar", "icon": "⬛", "value": 10},
|
||||
"arrows": {"name": "Arrows", "icon": "🏹", "value": 1},
|
||||
"copper_sword": {"name": "Copper sword", "plural": "Copper swords", "icon": "🗡️", "value": 14, "slot": "weapon", "power": 4},
|
||||
"iron_sword": {"name": "Iron sword", "plural": "Iron swords", "icon": "⚔️", "value": 30, "slot": "weapon", "power": 9},
|
||||
"iron_shield": {"name": "Iron shield", "plural": "Iron shields", "icon": "🛡️", "value": 40, "slot": "shield", "power": 6},
|
||||
"bricks": {"name": "Bricks", "icon": "🧱", "value": 3},
|
||||
"cut_stone": {"name": "Cut stone", "icon": "🔷", "value": 5},
|
||||
"beam": {"name": "Reinforced beam", "plural": "Reinforced beams", "icon": "🪜", "value": 25},
|
||||
|
|
@ -71,39 +61,120 @@ ITEMS = {
|
|||
"bread": {"name": "Bread", "icon": "🍞", "value": 4, "food": 1, "heal": 8},
|
||||
"cooked_meat": {"name": "Cooked meat", "icon": "🍖", "value": 5, "food": 1, "heal": 12},
|
||||
"stew": {"name": "Stew", "icon": "🍲", "value": 10, "food": 2, "heal": 20},
|
||||
"raw_shrimp": {"name": "Raw shrimp", "icon": "🦐", "value": 1},
|
||||
"raw_trout": {"name": "Raw trout", "icon": "🐟", "value": 3},
|
||||
"raw_salmon": {"name": "Raw salmon", "icon": "🐠", "value": 6},
|
||||
"raw_eel": {"name": "Raw eel", "icon": "🐍", "value": 10},
|
||||
"cooked_shrimp": {"name": "Cooked shrimp", "icon": "🍤", "value": 4, "food": 1, "heal": 10},
|
||||
"cooked_trout": {"name": "Grilled trout", "icon": "🍢", "value": 8, "food": 1, "heal": 18},
|
||||
"cooked_salmon": {"name": "Baked salmon", "icon": "🍣", "value": 14, "food": 2, "heal": 28},
|
||||
"smoked_eel": {"name": "Smoked eel", "icon": "🥓", "value": 22, "food": 2, "heal": 40},
|
||||
"leather": {"name": "Leather", "icon": "🪢", "value": 5},
|
||||
# `armour` lowers the damage a fight deals; `power` adds to fighting strength.
|
||||
"leather_cap": {"name": "Leather cap", "icon": "🧢", "value": 12, "slot": "helmet", "armour": 1},
|
||||
"leather_legs": {"name": "Leather trousers", "plural": "Leather trousers", "icon": "👖", "value": 18, "slot": "legs", "armour": 2},
|
||||
"leather_body": {"name": "Leather jerkin", "icon": "🦺", "value": 24, "slot": "body", "armour": 3},
|
||||
"studded_body": {"name": "Studded jerkin", "icon": "🧥", "value": 40, "slot": "body", "armour": 6},
|
||||
"copper_helmet": {"name": "Copper helmet", "icon": "⛑️", "value": 12, "slot": "helmet", "armour": 2},
|
||||
"copper_legs": {"name": "Copper greaves", "plural": "Copper greaves", "icon": "🦵", "value": 22, "slot": "legs", "armour": 3},
|
||||
"copper_body": {"name": "Copper cuirass", "icon": "🥋", "value": 28, "slot": "body", "armour": 4},
|
||||
"iron_helmet": {"name": "Iron helmet", "icon": "🪖", "value": 25, "slot": "helmet", "armour": 4},
|
||||
"iron_legs": {"name": "Iron greaves", "plural": "Iron greaves", "icon": "🦿", "value": 45, "slot": "legs", "armour": 6},
|
||||
"iron_body": {"name": "Iron cuirass", "icon": "🦾", "value": 55, "slot": "body", "armour": 8},
|
||||
"studded_body": {"name": "Studded jerkin", "icon": "🧥", "value": 40, "slot": "body", "armour": 6,
|
||||
"req": ["combat", 10]},
|
||||
}
|
||||
|
||||
# --- tier ladders -----------------------------------------------------------
|
||||
# Six tiers, RuneScape's metals with Melvor-ish level spacing. Each row of a
|
||||
# ladder generates its items and recipes below; the copper and iron ids
|
||||
# predate the ladders and are kept as they were.
|
||||
|
||||
TIER_LEVELS = [1, 15, 30, 45, 60, 75] # gathering / smelting level of each tier
|
||||
EQUIP_LEVELS = [1, 10, 20, 30, 40, 50] # combat level to wear, skill level to use a tool
|
||||
|
||||
# key, name, ore (item id, name, icon, value), coal per bar, bar icon, bar value, smithy level, handle wood
|
||||
METALS = [
|
||||
("copper", "Copper", ("copper_ore", "Copper ore", "🟤", 2), 0, "🟧", 5, 0, "logs"),
|
||||
("iron", "Iron", ("iron_ore", "Iron ore", "⚫", 4), 0, "⬛", 10, 1, "oak_logs"),
|
||||
("steel", "Steel", None, 2, "🔲", 20, 2, "willow_logs"),
|
||||
("mithril", "Mithril", ("mithril_ore", "Mithril ore", "🔵", 15), 3, "🟦", 40, 3, "maple_logs"),
|
||||
("adamant", "Adamant", ("adamantite_ore", "Adamantite ore", "🟢", 30), 4, "🟩", 70, 4, "yew_logs"),
|
||||
("rune", "Rune", ("runite_ore", "Runite ore", "💠", 55), 5, "🟪", 120, 5, "magic_logs"),
|
||||
]
|
||||
# piece, slot, display noun, bars, smithing level above the tier, power by tier, armour by tier
|
||||
PIECES = [
|
||||
("sword", "weapon", "sword", 2, 7, [4, 9, 14, 20, 27, 35], None),
|
||||
("shield", "shield", "shield", 3, 10, [3, 6, 8, 11, 14, 18], [2, 4, 6, 9, 12, 15]),
|
||||
("helmet", "helmet", "helmet", 2, 5, None, [2, 4, 6, 8, 11, 14]),
|
||||
("legs", "legs", "greaves", 4, 9, None, [3, 6, 9, 12, 16, 21]),
|
||||
("body", "body", "cuirass", 5, 11, None, [4, 8, 12, 16, 22, 28]),
|
||||
]
|
||||
PLURALS = {"sword": "swords", "shield": "shields", "helmet": "helmets", "greaves": "greaves", "cuirass": "cuirasses"}
|
||||
PIECE_ICONS = {"sword": "⚔️", "shield": "🛡️", "helmet": "🪖", "legs": "🦿", "body": "🦾"}
|
||||
COPPER_ICONS = {"sword": "🗡️", "helmet": "⛑️", "legs": "🦵", "body": "🥋"}
|
||||
# Gathering tools: one slot per skill, `speed` cuts the action time.
|
||||
TOOLS = {"mining": ("pickaxe", "⛏️"), "woodcutting": ("axe", "🪓"), "farming": ("hoe", ""),
|
||||
"fishing": ("fishing rod", "🎣"), "hunting": ("hunting knife", "🔪")}
|
||||
for _skill, (_tool, _icon) in TOOLS.items():
|
||||
for _metal, _value, _speed in (("copper", 14, 0.15), ("iron", 30, 0.3)):
|
||||
ITEMS[f"{_metal}_{_tool.replace(' ', '_')}"] = {
|
||||
"name": f"{_metal.title()} {_tool}", "icon": _icon, "value": _value,
|
||||
"slot": f"tool_{_skill}", "tool": _skill, "speed": _speed,
|
||||
TOOL_SPEED = [0.15, 0.30, 0.38, 0.45, 0.52, 0.60]
|
||||
|
||||
# item id, name, icon, value, action id, verb
|
||||
TREES = [
|
||||
("logs", "Logs", "🪵", 1, "cut_logs", "Cut trees"),
|
||||
("oak_logs", "Oak logs", "🌳", 4, "cut_oak", "Cut oaks"),
|
||||
("willow_logs", "Willow logs", "🌿", 8, "cut_willow", "Cut willows"),
|
||||
("maple_logs", "Maple logs", "🍁", 15, "cut_maple", "Cut maples"),
|
||||
("yew_logs", "Yew logs", "🌲", 30, "cut_yew", "Cut yews"),
|
||||
("magic_logs", "Magic logs", "🔮", 55, "cut_magic", "Cut magic trees"),
|
||||
]
|
||||
|
||||
# raw hide (id, name, icon, value), leather (id, name, value), armour id prefix, armour name prefix,
|
||||
# leatherworking level, armour by piece
|
||||
LEATHERS = [
|
||||
(("hide", "Hide", "🟫", 3), ("leather", "Leather", 5), "leather", "Leather", 1, (1, 2, 3)),
|
||||
(("boar_hide", "Boar hide", "🐗", 8), ("hard_leather", "Hard leather", 12), "hard_leather", "Hard leather", 20, (2, 4, 6)),
|
||||
(("bear_pelt", "Bear pelt", "🐻", 16), ("bear_leather", "Bear leather", 24), "bear_leather", "Bear leather", 35, (4, 6, 9)),
|
||||
(("green_dragonhide", "Green dragonhide", "🐉", 35), ("green_dragon_leather", "Green dragon leather", 50),
|
||||
"green_dhide", "Green dragonhide", 50, (6, 9, 13)),
|
||||
(("blue_dragonhide", "Blue dragonhide", "🐲", 60), ("blue_dragon_leather", "Blue dragon leather", 85),
|
||||
"blue_dhide", "Blue dragonhide", 65, (8, 12, 17)),
|
||||
(("red_dragonhide", "Red dragonhide", "🔥", 100), ("red_dragon_leather", "Red dragon leather", 140),
|
||||
"red_dhide", "Red dragonhide", 80, (10, 15, 21)),
|
||||
]
|
||||
# piece suffix, slot, noun, leather used, level above the tier, icon
|
||||
LEATHER_PIECES = [("cap", "helmet", "cap", 2, 2, "🧢"), ("legs", "legs", "trousers", 3, 7, "👖"),
|
||||
("body", "body", "jerkin", 4, 11, "🦺")]
|
||||
|
||||
# raw (id, name, icon, value), cooked (id, name, icon, value, heal, food), fishing level, fish action, verb
|
||||
FISH = [
|
||||
(("raw_shrimp", "Raw shrimp", "🦐", 1), ("cooked_shrimp", "Cooked shrimp", "🍤", 4, 10, 1), 1, "fish_shrimp", "Net shrimp"),
|
||||
(("raw_trout", "Raw trout", "🐟", 3), ("cooked_trout", "Grilled trout", "🍢", 8, 18, 1), 15, "fish_trout", "Fly-fish trout"),
|
||||
(("raw_salmon", "Raw salmon", "🐠", 6), ("cooked_salmon", "Baked salmon", "🍣", 14, 28, 2), 30, "fish_salmon", "Catch salmon"),
|
||||
(("raw_eel", "Raw eel", "🐍", 10), ("smoked_eel", "Smoked eel", "🥓", 22, 40, 2), 45, "fish_eel", "Trap eels"),
|
||||
(("raw_lobster", "Raw lobster", "🦞", 18), ("cooked_lobster", "Cooked lobster", "🍜", 35, 55, 3), 60, "fish_lobster", "Pot lobsters"),
|
||||
(("raw_swordfish", "Raw swordfish", "🐬", 30), ("cooked_swordfish", "Grilled swordfish", "🍱", 55, 70, 3), 75, "fish_swordfish", "Harpoon swordfish"),
|
||||
]
|
||||
COOK_IDS = {"raw_shrimp": "cook_shrimp", "raw_trout": "cook_trout", "raw_salmon": "cook_salmon", "raw_eel": "cook_eel",
|
||||
"raw_lobster": "cook_lobster", "raw_swordfish": "cook_swordfish"}
|
||||
|
||||
ITEMS["coal"] = {"name": "Coal", "icon": "🌑", "value": 6}
|
||||
for _tier, (_key, _name, _ore, _coal, _bar_icon, _bar_value, _smithy, _wood) in enumerate(METALS):
|
||||
if _ore:
|
||||
ITEMS[_ore[0]] = {"name": _ore[1], "icon": _ore[2], "value": _ore[3]}
|
||||
ITEMS[f"{_key}_bar"] = {"name": f"{_name} bar", "icon": _bar_icon, "value": _bar_value, "tier": _tier}
|
||||
for _piece, _slot, _noun, _bars, _offset, _power, _armour in PIECES:
|
||||
_item = {"name": f"{_name} {_noun}", "plural": f"{_name} {PLURALS[_noun]}",
|
||||
"icon": COPPER_ICONS.get(_piece, PIECE_ICONS[_piece]) if _key == "copper" else PIECE_ICONS[_piece],
|
||||
"value": round(_bars * _bar_value * 1.2), "slot": _slot, "tier": _tier,
|
||||
"req": ["combat", EQUIP_LEVELS[_tier]]}
|
||||
if _power:
|
||||
_item["power"] = _power[_tier]
|
||||
if _armour:
|
||||
_item["armour"] = _armour[_tier]
|
||||
ITEMS[f"{_key}_{_piece}"] = {k: v for k, v in _item.items() if v is not None}
|
||||
for _skill, (_tool, _icon) in TOOLS.items():
|
||||
ITEMS[f"{_key}_{_tool.replace(' ', '_')}"] = {
|
||||
"name": f"{_name} {_tool}", "icon": _icon, "value": round(2 * _bar_value * 1.4), "tier": _tier,
|
||||
"slot": f"tool_{_skill}", "tool": _skill, "speed": TOOL_SPEED[_tier], "req": [_skill, EQUIP_LEVELS[_tier]],
|
||||
}
|
||||
ITEMS["iron_shield"]["armour"] = 4
|
||||
for _log, _name, _icon, _value, _action_id, _verb in TREES:
|
||||
ITEMS[_log] = {"name": _name, "icon": _icon, "value": _value}
|
||||
for _tier, (_raw, _leather, _prefix, _label, _level, _armour) in enumerate(LEATHERS):
|
||||
ITEMS[_raw[0]] = {"name": _raw[1], "icon": _raw[2], "value": _raw[3]}
|
||||
ITEMS[_leather[0]] = {"name": _leather[1], "icon": "🪢", "value": _leather[2]}
|
||||
for (_suffix, _slot, _noun, _used, _offset, _icon), _points in zip(LEATHER_PIECES, _armour):
|
||||
ITEMS[f"{_prefix}_{_suffix}"] = {
|
||||
"name": f"{_label} {_noun}", "icon": _icon, "value": round(_used * _leather[2] * 1.3),
|
||||
"slot": _slot, "armour": _points, "tier": _tier, "req": ["combat", EQUIP_LEVELS[_tier]],
|
||||
}
|
||||
if _noun == "trousers":
|
||||
ITEMS[f"{_prefix}_{_suffix}"]["plural"] = ITEMS[f"{_prefix}_{_suffix}"]["name"]
|
||||
for _raw, _cooked, _level, _action_id, _verb in FISH:
|
||||
ITEMS[_raw[0]] = {"name": _raw[1], "icon": _raw[2], "value": _raw[3]}
|
||||
ITEMS[_cooked[0]] = {"name": _cooked[1], "icon": _cooked[2], "value": _cooked[3], "heal": _cooked[4], "food": _cooked[5]}
|
||||
|
||||
# What the city eats first: plain food before the good stuff.
|
||||
FOOD_ORDER = ("bread", "cooked_meat", "cooked_shrimp", "cooked_trout", "stew", "cooked_salmon", "smoked_eel")
|
||||
FOOD_ORDER = tuple(sorted((item for item, spec in ITEMS.items() if "food" in spec), key=lambda i: ITEMS[i]["heal"]))
|
||||
EQUIP_SLOTS = ["weapon", "shield", "helmet", "body", "legs"] + [f"tool_{skill}" for skill in TOOLS]
|
||||
|
||||
SKILLS = [
|
||||
|
|
@ -153,55 +224,68 @@ def _action(id, skill, name, level, time, xp, inputs=None, outputs=None, **extra
|
|||
"inputs": inputs or {}, "outputs": outputs or {}, **extra}
|
||||
|
||||
|
||||
def _tiered_actions() -> list[dict]:
|
||||
"""Mining, woodcutting, smelting, smithing, tanning and leatherworking for every tier."""
|
||||
actions = []
|
||||
ore_xp = [7, 15, 30, 40, 55, 75]
|
||||
ore_time = [25, 35, 50, 55, 65, 80]
|
||||
bar_xp = [6, 14, 22, 32, 44, 58]
|
||||
piece_xp = [6, 13, 20, 28, 37, 48] # per bar used
|
||||
for tier, (key, name, ore, coal, _icon, _value, smithy, wood) in enumerate(METALS):
|
||||
if ore:
|
||||
actions.append(_action(f"mine_{key}" if key in ("copper", "iron") else f"mine_{ore[0].removesuffix('_ore')}",
|
||||
"mining", f"Mine {ore[1].removesuffix(' ore').lower()}", TIER_LEVELS[tier],
|
||||
ore_time[tier], ore_xp[tier], outputs={ore[0]: 1}))
|
||||
if tier == 2:
|
||||
actions.append(_action("mine_coal", "mining", "Mine coal", 30, 40, 22, outputs={"coal": 1}))
|
||||
city = {"smithy": smithy} if smithy else {}
|
||||
inputs = {"iron_ore" if key == "steel" else ore[0]: 1}
|
||||
inputs.update({"coal": coal} if coal else {"logs": 1})
|
||||
actions.append(_action(f"smith_{key}_bar", "smithing", f"{name} bar", TIER_LEVELS[tier], 20 + 4 * tier,
|
||||
bar_xp[tier], inputs, {f"{key}_bar": 1}, group="Bars",
|
||||
city={"smithy": smithy} if tier >= 2 else {}))
|
||||
for piece, _slot, noun, bars, offset, _power, _armour in PIECES:
|
||||
actions.append(_action(f"smith_{key}_{piece}", "smithing", f"{name} {noun}", TIER_LEVELS[tier] + offset,
|
||||
25 + 5 * bars + 2 * tier, bars * piece_xp[tier], {f"{key}_bar": bars},
|
||||
{f"{key}_{piece}": 1}, group=name, city=city))
|
||||
for tool, _icon in TOOLS.values():
|
||||
tool_id = f"{key}_{tool.replace(' ', '_')}"
|
||||
actions.append(_action(f"smith_{tool_id}", "smithing", f"{name} {tool}", TIER_LEVELS[tier] + 3,
|
||||
30 + 2 * tier, 2 * piece_xp[tier], {f"{key}_bar": 2, wood: 1}, {tool_id: 1},
|
||||
group=name, city=city))
|
||||
for tier, (log, _name, _icon, _value, action_id, verb) in enumerate(TREES):
|
||||
actions.append(_action(action_id, "woodcutting", verb, TIER_LEVELS[tier], [20, 35, 40, 50, 60, 75][tier],
|
||||
[5, 15, 24, 36, 52, 75][tier], outputs={log: 1}))
|
||||
for tier, (raw, leather, prefix, label, level, _armour) in enumerate(LEATHERS):
|
||||
actions.append(_action("tan_leather" if tier == 0 else f"tan_{leather[0]}", "leatherworking",
|
||||
f"Tan {leather[1].lower()}", level, 25 + 3 * tier, bar_xp[tier],
|
||||
{raw[0]: 1}, {leather[0]: 1}, group="Tanning"))
|
||||
for suffix, _slot, noun, used, offset, _icon in LEATHER_PIECES:
|
||||
actions.append(_action(f"lw_{suffix}" if tier == 0 else f"lw_{prefix}_{suffix}", "leatherworking",
|
||||
f"{label} {noun}", level + offset, 25 + 5 * used + 2 * tier, used * piece_xp[tier],
|
||||
{leather[0]: used}, {f"{prefix}_{suffix}": 1}, group=label))
|
||||
for raw, cooked, level, fish_id, verb in FISH:
|
||||
tier = TIER_LEVELS.index(level) if level in TIER_LEVELS else 3
|
||||
actions.append(_action(fish_id, "fishing", verb, level, [25, 35, 45, 55, 65, 80][tier],
|
||||
[6, 15, 28, 45, 62, 85][tier], outputs={raw[0]: 1}))
|
||||
actions.append(_action(COOK_IDS[raw[0]], "cooking", cooked[1], level, [20, 30, 35, 40, 45, 50][tier],
|
||||
[6, 15, 27, 42, 58, 80][tier], {raw[0]: 1, "logs": 1}, {cooked[0]: 1}))
|
||||
return actions
|
||||
|
||||
|
||||
ACTIONS = [
|
||||
_action("mine_stone", "mining", "Quarry stone", 1, 20, 5, outputs={"stone": 1}),
|
||||
_action("mine_copper", "mining", "Mine copper", 1, 25, 7, outputs={"copper_ore": 1}),
|
||||
_action("mine_iron", "mining", "Mine iron", 15, 35, 15, outputs={"iron_ore": 1}),
|
||||
_action("cut_logs", "woodcutting", "Cut trees", 1, 20, 5, outputs={"logs": 1}),
|
||||
_action("cut_oak", "woodcutting", "Cut oaks", 15, 35, 15, outputs={"oak_logs": 1}),
|
||||
_action("farm_wheat", "farming", "Grow wheat", 1, 25, 6, outputs={"wheat": 2}),
|
||||
_action("farm_veg", "farming", "Grow vegetables", 12, 35, 13, outputs={"vegetables": 2}),
|
||||
_action("hunt_birds", "hunting", "Snare birds", 1, 25, 6, outputs={"feathers": 3}),
|
||||
_action("hunt_deer", "hunting", "Hunt deer", 8, 40, 13, outputs={"raw_meat": 2, "hide": 1}),
|
||||
_action("fish_shrimp", "fishing", "Net shrimp", 1, 25, 6, outputs={"raw_shrimp": 1}),
|
||||
_action("fish_trout", "fishing", "Fly-fish trout", 15, 35, 15, outputs={"raw_trout": 1}),
|
||||
_action("fish_salmon", "fishing", "Catch salmon", 30, 45, 28, outputs={"raw_salmon": 1}),
|
||||
_action("fish_eel", "fishing", "Trap eels", 45, 55, 45, outputs={"raw_eel": 1}),
|
||||
_action("smith_copper_bar", "smithing", "Copper bar", 1, 20, 6,
|
||||
{"copper_ore": 1, "logs": 1}, {"copper_bar": 1}, group="Bars"),
|
||||
_action("smith_iron_bar", "smithing", "Iron bar", 15, 30, 14,
|
||||
{"iron_ore": 1, "logs": 1}, {"iron_bar": 1}, group="Bars"),
|
||||
_action("smith_copper_sword", "smithing", "Copper sword", 8, 40, 18,
|
||||
{"copper_bar": 3}, {"copper_sword": 1}, group="Weapons"),
|
||||
_action("smith_iron_sword", "smithing", "Iron sword", 20, 45, 32,
|
||||
{"iron_bar": 2}, {"iron_sword": 1}, city={"smithy": 1}, group="Weapons"),
|
||||
_action("smith_iron_shield", "smithing", "Iron shield", 25, 50, 40,
|
||||
{"iron_bar": 3}, {"iron_shield": 1}, city={"smithy": 2}, group="Weapons"),
|
||||
_action("smith_copper_helmet", "smithing", "Copper helmet", 6, 35, 14,
|
||||
{"copper_bar": 2}, {"copper_helmet": 1}, group="Armour"),
|
||||
_action("smith_copper_legs", "smithing", "Copper greaves", 10, 40, 20,
|
||||
{"copper_bar": 4}, {"copper_legs": 1}, group="Armour"),
|
||||
_action("smith_copper_body", "smithing", "Copper cuirass", 12, 45, 26,
|
||||
{"copper_bar": 5}, {"copper_body": 1}, group="Armour"),
|
||||
_action("smith_iron_helmet", "smithing", "Iron helmet", 22, 40, 34,
|
||||
{"iron_bar": 2}, {"iron_helmet": 1}, city={"smithy": 1}, group="Armour"),
|
||||
_action("smith_iron_legs", "smithing", "Iron greaves", 27, 45, 46,
|
||||
{"iron_bar": 4}, {"iron_legs": 1}, city={"smithy": 1}, group="Armour"),
|
||||
_action("smith_iron_body", "smithing", "Iron cuirass", 30, 50, 56,
|
||||
{"iron_bar": 5}, {"iron_body": 1}, city={"smithy": 2}, group="Armour"),
|
||||
*[_action(f"smith_{metal}_{tool.replace(' ', '_')}", "smithing", f"{metal.title()} {tool}", level, time, xp,
|
||||
{f"{metal}_bar": 2, wood: 1}, {f"{metal}_{tool.replace(' ', '_')}": 1}, group="Tools",
|
||||
**({"city": {"smithy": 1}} if metal == "iron" else {}))
|
||||
for metal, level, time, xp, wood in (("copper", 4, 30, 12, "logs"), ("iron", 20, 40, 30, "oak_logs"))
|
||||
for tool, _icon in TOOLS.values()],
|
||||
_action("hunt_boar", "hunting", "Hunt boar", 25, 50, 30, outputs={"raw_meat": 2, "boar_hide": 1}),
|
||||
_action("hunt_bear", "hunting", "Hunt bears", 40, 60, 48, outputs={"raw_meat": 3, "bear_pelt": 1}),
|
||||
*_tiered_actions(),
|
||||
_action("smith_arrows", "smithing", "Arrows", 5, 30, 10,
|
||||
{"copper_bar": 1, "logs": 1, "feathers": 2}, {"arrows": 15}, group="Supplies"),
|
||||
_action("tan_leather", "leatherworking", "Tan leather", 1, 25, 6, {"hide": 1}, {"leather": 1}),
|
||||
_action("lw_cap", "leatherworking", "Leather cap", 3, 30, 10, {"leather": 2}, {"leather_cap": 1}),
|
||||
_action("lw_legs", "leatherworking", "Leather trousers", 8, 35, 16, {"leather": 3}, {"leather_legs": 1}),
|
||||
_action("lw_body", "leatherworking", "Leather jerkin", 12, 40, 22, {"leather": 4}, {"leather_body": 1}),
|
||||
{"copper_bar": 1, "logs": 1, "feathers": 2}, {"arrows": 15}, group="Bars"),
|
||||
_action("lw_studded", "leatherworking", "Studded jerkin", 25, 50, 40,
|
||||
{"leather": 4, "iron_bar": 1}, {"studded_body": 1}),
|
||||
{"leather": 4, "iron_bar": 1}, {"studded_body": 1}, group="Leather"),
|
||||
_action("mason_bricks", "masonry", "Bricks", 1, 25, 6, {"stone": 2, "logs": 1}, {"bricks": 2}),
|
||||
_action("mason_cut_stone", "masonry", "Cut stone", 10, 30, 12, {"stone": 3}, {"cut_stone": 1}),
|
||||
_action("mason_beam", "masonry", "Reinforced beam", 20, 45, 30,
|
||||
|
|
@ -210,10 +294,6 @@ ACTIONS = [
|
|||
_action("cook_meat", "cooking", "Cooked meat", 5, 20, 7, {"raw_meat": 1, "logs": 1}, {"cooked_meat": 1}),
|
||||
_action("cook_stew", "cooking", "Stew", 15, 35, 16,
|
||||
{"raw_meat": 1, "vegetables": 2, "logs": 1}, {"stew": 2}),
|
||||
_action("cook_shrimp", "cooking", "Cooked shrimp", 1, 20, 6, {"raw_shrimp": 1, "logs": 1}, {"cooked_shrimp": 1}),
|
||||
_action("cook_trout", "cooking", "Grilled trout", 15, 30, 15, {"raw_trout": 1, "logs": 1}, {"cooked_trout": 1}),
|
||||
_action("cook_salmon", "cooking", "Baked salmon", 30, 35, 27, {"raw_salmon": 1, "logs": 1}, {"cooked_salmon": 1}),
|
||||
_action("cook_eel", "cooking", "Smoked eel", 45, 40, 42, {"raw_eel": 1, "logs": 1}, {"smoked_eel": 1}),
|
||||
_action("fight_wolves", "combat", "Wolves in the woods", 1, 60, 14, difficulty=2, damage=2, gold=2,
|
||||
drops=[["raw_meat", 1, 0.6], ["hide", 1, 0.5]]),
|
||||
_action("fight_scouts", "combat", "Bandit scouts", 10, 75, 30, difficulty=14, damage=6, gold=10,
|
||||
|
|
@ -221,7 +301,15 @@ ACTIONS = [
|
|||
_action("fight_ruins", "combat", "Watchtower ruins", 25, 90, 60, difficulty=30, damage=14, gold=25,
|
||||
drops=[["iron_bar", 1, 0.4], ["iron_sword", 1, 0.03]]),
|
||||
_action("fight_stronghold", "combat", "Bandit stronghold", 40, 105, 95, difficulty=50, damage=25, gold=60,
|
||||
drops=[["iron_bar", 2, 0.5], ["iron_shield", 1, 0.04]]),
|
||||
drops=[["steel_bar", 1, 0.4], ["coal", 3, 0.5], ["iron_shield", 1, 0.04]]),
|
||||
_action("fight_green_dragon", "combat", "Green dragon", 50, 120, 135, difficulty=90, damage=30, gold=80,
|
||||
drops=[["green_dragonhide", 1, 0.9], ["mithril_ore", 2, 0.3]]),
|
||||
_action("fight_blue_dragon", "combat", "Blue dragon", 62, 130, 175, difficulty=115, damage=40, gold=110,
|
||||
drops=[["blue_dragonhide", 1, 0.9], ["adamantite_ore", 2, 0.3]]),
|
||||
_action("fight_red_dragon", "combat", "Red dragon", 75, 140, 220, difficulty=140, damage=52, gold=150,
|
||||
drops=[["red_dragonhide", 1, 0.9], ["runite_ore", 1, 0.25]]),
|
||||
_action("fight_black_dragon", "combat", "Black dragon", 85, 150, 280, difficulty=165, damage=65, gold=220,
|
||||
drops=[["red_dragonhide", 2, 0.9], ["rune_bar", 1, 0.1]]),
|
||||
_action("sabotage", "combat", "Sabotage the bandit camp", 10, 120, 35, difficulty=12, damage=10,
|
||||
special="sabotage"),
|
||||
_action("recon_roads", "reconnaissance", "Scout the roads", 1, 60, 12, special="recon", recon=1),
|
||||
|
|
@ -233,31 +321,37 @@ BUILDINGS = {
|
|||
"houses": {
|
||||
"name": "Houses", "icon": "🏠", "max": 8, "gold": 200,
|
||||
"base": {"logs": 600, "bricks": 300}, "advanced": {"oak_logs": 200},
|
||||
"late": {"willow_logs": 150}, "final": {"maple_logs": 100},
|
||||
"effect": "Room for 20 more people per level",
|
||||
},
|
||||
"walls": {
|
||||
"name": "Walls", "icon": "🏰", "max": 5, "gold": 300,
|
||||
"base": {"stone": 1200, "bricks": 500}, "advanced": {"cut_stone": 300, "beam": 40},
|
||||
"late": {"steel_bar": 60}, "final": {"mithril_bar": 40},
|
||||
"effect": "+20 defence per level, and raiders carry off less",
|
||||
},
|
||||
"barracks": {
|
||||
"name": "Barracks", "icon": "⛺", "max": 5, "gold": 300,
|
||||
"base": {"logs": 600, "bricks": 400, "copper_sword": 20}, "advanced": {"iron_sword": 15},
|
||||
"late": {"steel_sword": 10}, "final": {"mithril_sword": 6},
|
||||
"effect": "Trains townsfolk as militia: more defence per citizen",
|
||||
},
|
||||
"smithy": {
|
||||
"name": "Smithy", "icon": "⚒️", "max": 3, "gold": 250,
|
||||
"name": "Smithy", "icon": "⚒️", "max": 5, "gold": 250,
|
||||
"base": {"stone": 500, "logs": 600, "copper_bar": 150}, "advanced": {"iron_bar": 150},
|
||||
"effect": "Smithing 8% faster per level; unlocks iron swords (1) and shields (2)",
|
||||
"late": {"steel_bar": 80}, "final": {"mithril_bar": 60},
|
||||
"effect": "Smithing 8% faster per level; each level unlocks a metal: iron, steel, mithril, adamant, rune",
|
||||
},
|
||||
"spymaster": {
|
||||
"name": "Spymaster's guild", "icon": "🕵️", "max": 3, "gold": 400,
|
||||
"base": {"logs": 500, "bricks": 300, "hide": 100, "feathers": 300}, "advanced": {"cut_stone": 150},
|
||||
"late": {"bear_leather": 40},
|
||||
"effect": "One more level of intel on every event per level",
|
||||
},
|
||||
"storehouse": {
|
||||
"name": "Storehouse", "icon": "📦", "max": 5, "gold": 200,
|
||||
"base": {"logs": 800, "stone": 800}, "advanced": {"oak_logs": 300, "cut_stone": 200},
|
||||
"late": {"willow_logs": 200}, "final": {"yew_logs": 80},
|
||||
"effect": "+3 bank slots and +250 per stack for everyone, per level",
|
||||
},
|
||||
}
|
||||
|
|
@ -367,9 +461,11 @@ def building_level(world: dict, key: str) -> int:
|
|||
def build_cost(key: str, level: int) -> dict:
|
||||
spec = BUILDINGS[key]
|
||||
cost = {item: qty * level for item, qty in spec["base"].items()}
|
||||
for item, qty in spec["advanced"].items():
|
||||
if level > 1:
|
||||
cost[item] = cost.get(item, 0) + qty * (level - 1)
|
||||
# Later levels add materials from higher tiers: advanced from level 2, late from 3, final from 5.
|
||||
for key, first in (("advanced", 2), ("late", 3), ("final", 5)):
|
||||
for item, qty in spec.get(key, {}).items():
|
||||
if level >= first:
|
||||
cost[item] = cost.get(item, 0) + qty * (level - first + 1)
|
||||
cost["gold"] = spec["gold"] * level
|
||||
return cost
|
||||
|
||||
|
|
@ -889,20 +985,43 @@ def population_view(world: dict) -> dict:
|
|||
}
|
||||
|
||||
|
||||
def best_kit(stock: dict, slot: str, stat: str, people: int) -> float:
|
||||
"""Total `stat` when the best `slot` items in the stockpile go to `people` defenders."""
|
||||
total = 0
|
||||
for item in sorted((i for i in stock if ITEMS[i].get("slot") == slot), key=lambda i: -ITEMS[i].get(stat, 0)):
|
||||
take = min(people, stock[item])
|
||||
total += take * ITEMS[item].get(stat, 0)
|
||||
people -= take
|
||||
if not people:
|
||||
break
|
||||
return total
|
||||
|
||||
|
||||
def kit_count(stock: dict, slot: str) -> int:
|
||||
return sum(qty for item, qty in stock.items() if ITEMS[item].get("slot") == slot)
|
||||
|
||||
|
||||
def equip_problem(player: dict, item: str) -> str | None:
|
||||
"""Why the player can't wear or use this yet, or None."""
|
||||
req = ITEMS[item].get("req")
|
||||
if req and skill_level(player, req[0]) < req[1]:
|
||||
name = next(s["name"] for s in SKILLS if s["id"] == req[0])
|
||||
return f"Needs {name.lower()} level {req[1]}"
|
||||
return None
|
||||
|
||||
|
||||
def defence(world: dict) -> dict:
|
||||
"""The city's defence right now, part by part."""
|
||||
city = world["city"]
|
||||
pop = city["population"]
|
||||
stock = city["stockpile"]
|
||||
armed = int(pop // 2)
|
||||
swords = min(armed, stock.get("copper_sword", 0) + stock.get("iron_sword", 0))
|
||||
iron = min(swords, stock.get("iron_sword", 0))
|
||||
parts = {
|
||||
"townsfolk": 10,
|
||||
"walls": 20 * building_level(world, "walls"),
|
||||
"militia": round(0.3 * building_level(world, "barracks") * pop, 1),
|
||||
"weapons": round(3 * iron + 1.5 * (swords - iron), 1),
|
||||
"shields": 2 * min(armed, stock.get("iron_shield", 0)),
|
||||
"weapons": round(0.35 * best_kit(stock, "weapon", "power", armed), 1),
|
||||
"shields": round(0.5 * best_kit(stock, "shield", "armour", armed), 1),
|
||||
"arrows": min(100, stock.get("arrows", 0) // 10),
|
||||
"rallied": city.get("rally", 0),
|
||||
"garrison": 0,
|
||||
|
|
@ -1093,16 +1212,18 @@ def council_requests(world: dict, event: dict) -> list[dict]:
|
|||
("copper_sword", min(armed, max(5, math.ceil(strength / 6)))),
|
||||
("bread", math.ceil(city["population"] * 1.5)),
|
||||
]
|
||||
if building_level(world, "smithy") >= 2:
|
||||
if building_level(world, "smithy") >= 1:
|
||||
wants.append(("iron_shield", min(armed, math.ceil(strength / 10))))
|
||||
requests = []
|
||||
for item, qty in wants:
|
||||
have = stock.get(item, 0)
|
||||
if item == "copper_sword":
|
||||
have += stock.get("iron_sword", 0)
|
||||
if item in ("copper_sword", "iron_shield"):
|
||||
have = kit_count(stock, ITEMS[item]["slot"])
|
||||
if item == "bread":
|
||||
have = sum(stock.get(food, 0) * ITEMS[food]["food"] for food in FOOD_ORDER)
|
||||
requests.append({"item": item, "want": qty, "have": have})
|
||||
label = {"copper_sword": "Swords (any)", "iron_shield": "Shields (any)", "bread": "Food (any)"}[item] \
|
||||
if item in ("copper_sword", "iron_shield", "bread") else ITEMS[item]["name"]
|
||||
requests.append({"item": item, "label": label, "want": qty, "have": have})
|
||||
return requests
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue