Crafting stations, strongboxes and bags
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
636123e454
commit
722cf0b382
6 changed files with 182 additions and 14 deletions
94
game.py
94
game.py
|
|
@ -124,6 +124,20 @@ COPPER_ICONS = {"sword": "🗡️", "helmet": "⛑️", "legs": "🦵", "body":
|
|||
TOOLS = {"mining": ("pickaxe", "⛏️"), "woodcutting": ("axe", "🪓"), "farming": ("hoe", ""),
|
||||
"fishing": ("fishing rod", "🎣"), "hunting": ("hunting knife", "🔪"), "construction": ("hammer", "🔨")}
|
||||
TOOL_SPEED = [0.15, 0.30, 0.38, 0.45, 0.52, 0.60]
|
||||
# Crafting stations: the builder's tools for every crafting skill, as fast as the smith's
|
||||
# tools for gathering. Each takes the tier's planks and hardware and a material from
|
||||
# another trade. skill: (id suffix, noun, glyph, other material)
|
||||
STATIONS = {
|
||||
"smithing": ("anvil_stand", "anvil stand", "anvil-impact", "leather"),
|
||||
"jewelcrafting": ("jewellers_bench", "jeweller's bench", "gem-chain", "cloth"),
|
||||
"cooking": ("hearth", "hearth", "campfire", "bar"),
|
||||
"herblore": ("alembic", "alembic", "erlenmeyer", "bar"),
|
||||
"leatherworking": ("tanning_rack", "tanning rack", "animal-hide", "cloth"),
|
||||
"tailoring": ("loom", "loom", "spinning-wheel", "leather"),
|
||||
}
|
||||
# Storage: a builder's strongbox adds bank slots, a homesteader's bag a bigger stack.
|
||||
STRONGBOX_SLOTS = [2, 4, 6, 8, 11, 14]
|
||||
BAG_STACK = [250, 500, 750, 1000, 1500, 2000]
|
||||
|
||||
# item id, name, icon, value, action id, verb
|
||||
TREES = [
|
||||
|
|
@ -290,6 +304,24 @@ for _tier, _metal in enumerate(METALS):
|
|||
ITEMS[f"{WOODS[_tier]}_staff"] = {"name": STAVES[_tier], "plural": STAVES[_tier].replace("staff", "staves"),
|
||||
"icon": "🪄", "style": "staff", **_weapon}
|
||||
ITEMS[f"{WOODS[_tier]}_bow"] = {"name": BOWS[_tier], "icon": "🏹", "style": "bow", **_weapon}
|
||||
WOOD_NAMES = ["Wooden", "Oak", "Willow", "Maple", "Yew", "Enchanted"]
|
||||
|
||||
|
||||
def station_material(kind: str, tier: int) -> str:
|
||||
"""The other trade's part of a station: the tier's bar, leather or cloth."""
|
||||
return {"bar": f"{METALS[tier][0]}_bar", "leather": LEATHERS[tier][1][0], "cloth": CLOTHS[tier][4][0]}[kind]
|
||||
|
||||
|
||||
for _tier, _wood in enumerate(WOODS):
|
||||
for _skill, (_suffix, _noun, _glyph, _kind) in STATIONS.items():
|
||||
ITEMS[f"{_wood}_{_suffix}"] = {
|
||||
"name": f"{WOOD_NAMES[_tier]} {_noun}", "icon": "🪚", "tier": _tier, "slot": f"tool_{_skill}", "tool": _skill,
|
||||
"speed": TOOL_SPEED[_tier], "req": [_skill, EQUIP_LEVELS[_tier]], "value": 0}
|
||||
ITEMS[f"{_wood}_strongbox"] = {"name": f"{WOOD_NAMES[_tier]} strongbox", "plural": f"{WOOD_NAMES[_tier]} strongboxes",
|
||||
"icon": "🧰", "tier": _tier, "slot": "storage", "slots": STRONGBOX_SLOTS[_tier], "value": 0}
|
||||
for _tier, (_fibre, _level, _time, _xp, _cloth, _prefix, _label) in enumerate(CLOTHS):
|
||||
ITEMS[f"{_prefix}_bag"] = {"name": f"{_label} bag", "icon": "👜", "tier": _tier, "slot": "bag",
|
||||
"stack": BAG_STACK[_tier], "value": 0}
|
||||
for _tier, (_raw, _leather, _prefix, _label, _level) 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]}
|
||||
|
|
@ -400,7 +432,8 @@ for _id, _name, _level, _herb, _second, _skill, _effect in POTIONS:
|
|||
|
||||
# What the city eats first: plain food before the good stuff.
|
||||
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", "ring", "amulet", "trinket"] + [f"tool_{skill}" for skill in TOOLS]
|
||||
EQUIP_SLOTS = (["weapon", "shield", "helmet", "body", "legs", "ring", "amulet", "trinket", "storage", "bag"]
|
||||
+ [f"tool_{skill}" for skill in [*TOOLS, *STATIONS]])
|
||||
|
||||
SKILLS = [
|
||||
{"id": "mining", "name": "Mining", "icon": "⛏️", "group": "Gathering"},
|
||||
|
|
@ -789,6 +822,30 @@ def _weapon_actions() -> list[dict]:
|
|||
return actions
|
||||
|
||||
|
||||
def _storage_actions() -> list[dict]:
|
||||
"""Crafting stations and strongboxes (Construction), and bags (Tailoring)."""
|
||||
actions = []
|
||||
for tier, wood in enumerate(WOODS):
|
||||
plank, (hardware, *_rest, made) = PLANKS[tier][0], HARDWARE[tier]
|
||||
for skill, (suffix, _noun, _glyph, kind) in STATIONS.items():
|
||||
inputs = {plank: 3, hardware: made, station_material(kind, tier): 2}
|
||||
actions.append(_action(f"make_{wood}_{suffix}", "construction", ITEMS[f"{wood}_{suffix}"]["name"],
|
||||
TIER_LEVELS[tier] + 4, 40 + 3 * tier, [14, 30, 48, 68, 90, 115][tier], inputs,
|
||||
{f"{wood}_{suffix}": 1}, group="Stations"))
|
||||
inputs = {plank: 4, hardware: 2 * made, LEATHERS[tier][1][0]: 2}
|
||||
actions.append(_action(f"make_{wood}_strongbox", "construction", ITEMS[f"{wood}_strongbox"]["name"],
|
||||
TIER_LEVELS[tier] + 8, 45 + 3 * tier, [18, 38, 60, 85, 112, 142][tier], inputs,
|
||||
{f"{wood}_strongbox": 1}, group="Storage"))
|
||||
for tier, (_fibre, _level, _time, _xp, cloth, prefix, label) in enumerate(CLOTHS):
|
||||
actions.append(_action(f"sew_{prefix}_bag", "tailoring", f"{label} bag", TIER_LEVELS[tier] + 8, 45 + 3 * tier,
|
||||
[18, 38, 60, 85, 112, 142][tier], {cloth[0]: 4, LEATHERS[tier][1][0]: 1},
|
||||
{f"{prefix}_bag": 1}, group=label))
|
||||
for action in actions:
|
||||
made_item = ITEMS[next(iter(action["outputs"]))]
|
||||
made_item["value"] = round(sum(ITEMS[item]["value"] * qty for item, qty in action["inputs"].items()) * 1.3)
|
||||
return actions
|
||||
|
||||
|
||||
def _jewel_actions() -> list[dict]:
|
||||
"""Silver and gold, prospecting ore for gems, and every tier's jewellery."""
|
||||
actions = [
|
||||
|
|
@ -1033,7 +1090,7 @@ RAID_TRAITS = ["lightly armoured, heavy on archers", "mounted and fast, few bows
|
|||
|
||||
# --- world ---------------------------------------------------------------
|
||||
|
||||
WORLD_VERSION = 20
|
||||
WORLD_VERSION = 21
|
||||
|
||||
|
||||
def new_world(now: float, speed: float = 1.0) -> dict:
|
||||
|
|
@ -1190,6 +1247,12 @@ def migrate(world: dict, now: float | None = None) -> dict:
|
|||
if world["version"] < 20:
|
||||
_precision_to_haste(world)
|
||||
world["version"] = 20
|
||||
if world["version"] < 21:
|
||||
# Crafting stations, strongboxes and bags: their slots.
|
||||
for player in world["players"].values():
|
||||
for slot in EQUIP_SLOTS:
|
||||
player["equip"].setdefault(slot, None)
|
||||
world["version"] = 21
|
||||
return world
|
||||
|
||||
|
||||
|
|
@ -1459,22 +1522,29 @@ def skill_level(player: dict, skill: str) -> int:
|
|||
|
||||
def bank_slots(world: dict, player: dict) -> int:
|
||||
return (BASE_SLOTS + 3 * building_level(world, "storehouse") + TIER_SLOTS * world["city"]["tier"]
|
||||
+ player["perks"]["bank_slot"] + fitted(world, "shelving"))
|
||||
+ player["perks"]["bank_slot"] + fitted(world, "shelving") + int(gear_bonus(player, "slots")))
|
||||
|
||||
|
||||
def stack_size(world: dict) -> int:
|
||||
return BASE_STACK + 250 * building_level(world, "storehouse") + 500 * fitted(world, "enchanted_chests")
|
||||
def stack_size(world: dict, player: dict | None = None) -> int:
|
||||
"""How many of one item fit in a bank slot: the storehouse's, plus your bag's."""
|
||||
bag = int(gear_bonus(player, "stack")) if player else 0
|
||||
return BASE_STACK + 250 * building_level(world, "storehouse") + 500 * fitted(world, "enchanted_chests") + bag
|
||||
|
||||
|
||||
def overfull(world: dict, player: dict) -> int:
|
||||
"""Bank slots in use past what the bank holds (after a smaller strongbox or bag, say)."""
|
||||
return slots_used(world, player) - bank_slots(world, player)
|
||||
|
||||
|
||||
def slots_used(world: dict, player: dict) -> int:
|
||||
"""Bank slots taken: an item spills into another slot every full stack."""
|
||||
stack = stack_size(world)
|
||||
stack = stack_size(world, player)
|
||||
return sum(-(-qty // stack) for qty in player["bank"].values())
|
||||
|
||||
|
||||
def bank_room(world: dict, player: dict, item: str) -> int:
|
||||
"""How many more of `item` fit: the rest of its last stack, plus any free slots."""
|
||||
stack = stack_size(world)
|
||||
stack = stack_size(world, player)
|
||||
have = player["bank"].get(item, 0)
|
||||
partial = -have % stack # room left in the item's last, part-filled stack
|
||||
free = max(0, bank_slots(world, player) - slots_used(world, player))
|
||||
|
|
@ -3320,7 +3390,7 @@ FITTINGS = [
|
|||
for _weapon in _weapon_actions():
|
||||
ACTIONS.append(_weapon)
|
||||
ACTION_BY_ID[_weapon["id"]] = _weapon
|
||||
for _jewel in _jewel_actions():
|
||||
for _jewel in [*_jewel_actions(), *_storage_actions()]:
|
||||
ACTIONS.append(_jewel)
|
||||
ACTION_BY_ID[_jewel["id"]] = _jewel
|
||||
FITTING_BY_ID = {}
|
||||
|
|
@ -4026,6 +4096,12 @@ def _assign_glyphs() -> None:
|
|||
ITEMS[f"{prefix}_{suffix}"].update(glyph=glyph, tint=tint)
|
||||
for herb, tint in HERB_TINTS.items():
|
||||
ITEMS[herb].update(glyph="herbs-bundle", tint=tint)
|
||||
for tier, (wood, tint) in enumerate(zip(WOODS, WOOD_TINTS)):
|
||||
for suffix, _noun, glyph, _kind in STATIONS.values():
|
||||
ITEMS[f"{wood}_{suffix}"].update(glyph=glyph, tint=tint)
|
||||
ITEMS[f"{wood}_strongbox"].update(glyph="locked-chest", tint=tint)
|
||||
for cloth, tint in zip(CLOTHS, CLOTH_TINTS):
|
||||
ITEMS[f"{cloth[5]}_bag"].update(glyph="shoulder-bag", tint=tint)
|
||||
for gem, _name, _value, tint, glyph in GEMS:
|
||||
ITEMS[gem].update(glyph=glyph, tint=tint)
|
||||
for key, *_rest, piece_glyph in JEWELLERY:
|
||||
|
|
@ -4088,6 +4164,6 @@ def content() -> dict:
|
|||
"order_limit": ORDER_LIMIT, "order_max": ORDER_MAX, "problems": PROBLEMS, "fittings": list(FITTING_BY_ID.values()),
|
||||
"equip_slots": EQUIP_SLOTS, "eat_below": EAT_BELOW, "class_speed": CLASS_SPEED,
|
||||
"styles": STYLES, "damage_types": DAMAGE_TYPES, "weights": WEIGHTS, "vulnerable": VULNERABLE,
|
||||
"resistant": RESISTANT, "jewel_stats": JEWEL_STATS, "class_style": CLASS_STYLE, "block_chance": BLOCK_CHANCE,
|
||||
"resistant": RESISTANT, "jewel_stats": JEWEL_STATS, "stations": list(STATIONS), "class_style": CLASS_STYLE, "block_chance": BLOCK_CHANCE,
|
||||
"regen_per_hour": REGEN_PER_HOUR,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue