#!/usr/bin/env python3
"""Writes BESTIARY.md from the game's own data: the bestiary texts in lang/en.lang, the unit
stats in src/game.rs and the personalities in src/personality.rs. Pictures come from
docs/img (refresh them with `cargo test --release export_doc_images -- --ignored`).
usage: python3 tools/make_bestiary.py
"""
import pathlib
import re
root = pathlib.Path(__file__).resolve().parent.parent
lang = {}
for line in (root / "lang/en.lang").read_text().splitlines():
if " = " in line and not line.startswith("#"):
k, v = line.split(" = ", 1)
lang[k.strip()] = v.strip()
game = (root / "src/game.rs").read_text()
stats = {}
for m in re.finditer(r'EK::(\w+) => s\("unit\.(\w+)", ([\d.]+), ([\d.]+), ([\d.]+), ([\d.]+), ([\d.]+), (\d+), (\d+)\)', game):
kind, uid, hp, atk, rng, spd, itv, wage, bounty = m.groups()
stats[kind] = dict(id=uid, hp=float(hp), atk=float(atk), range=float(rng), speed=float(spd), interval=float(itv), wage=int(wage), bounty=int(bounty))
pers = {}
src = (root / "src/personality.rs").read_text()
for m in re.finditer(r'EK::(\w+) => p\(Kind::(\w+), &\[([^\]]*)\], (?:Some\(\((\d+), "(\w+)"\)\)|None)\)', src):
kind, likes, hates, lv, ab = m.groups()
pers[kind] = dict(likes=likes, hates=[h.replace("EK::", "").strip() for h in hates.split(",") if h.strip()], lv=lv, ab=ab)
room_key = {"Treasury": "treasury", "Lair": "lair", "Farm": "farm", "Training": "training", "Library": "library", "Torture": "torture", "Temple": "temple", "Aerie": "aerie", "Forge": "forge", "Prison": "prison", "Graveyard": "graveyard", "GuardPost": "guardpost", "Arena": "arena", "Altar": "altar"}
def para(text):
return "\n\n".join(p.strip() for p in text.split(" | "))
def unit(kind, human=False):
s = stats[kind]
uid = s["id"]
name = lang.get(f"unit.{uid}", uid)
out = [f"### {name}", ""]
out.append(f'
'
f'
'
f'
')
out.append("")
out.append("*Level 1, 5 and 10 - the gear grows with the rank.*")
out.append("")
dps = s["atk"] / s["interval"]
reach = "close combat" if s["range"] <= 1.5 else f"{s['range']:.1f} tiles"
row = f"| {s['hp']:.0f} | {s['atk']:.0f} every {s['interval']:.1f} s ({dps:.0f}/s) | {reach} | {s['speed']:.1f} tiles/s |"
if human:
out += ["| Health | Attack | Range | Speed | Bounty |", "|---|---|---|---|---|", row + f" {s['bounty']} gold |"]
else:
out += ["| Health | Attack | Range | Speed | Wage |", "|---|---|---|---|---|", row + f" {s['wage']} gold |"]
out.append("")
if not human:
lure = lang.get(f"best.{uid}.lure")
if lure:
out.append(f"**Comes for:** {lure} ")
p = pers.get(kind)
if p:
out.append(f"**Loves:** {lang.get('room.' + room_key[p['likes']], p['likes'])} ")
if p["hates"]:
out.append(f"**Cannot stand:** {', '.join(lang.get('unit.' + stats[h]['id'], h) for h in p['hates'] if h in stats)} ")
if p["ab"]:
out.append(f"**From level {p['lv']}:** {lang.get('ability.' + p['ab'] + '.name')} - {lang.get('ability.' + p['ab'] + '.desc')}")
out.append("")
out.append(para(lang.get(f"best.{uid}.text", "")))
out.append("")
return out
def building(key, icon, title=None):
name = title or lang.get(f"room.{key}") or lang.get(f"tool.{key}.name") or lang.get(f"trap.{key}") or key
text = lang.get(f"best.b.{key}.text", "")
desc = lang.get(f"tool.{key}.desc", "")
return [f'|
| **{name}** | {desc} *{text}* |']
creatures = ["Worker", "Kobold", "Spider", "Mage", "Ogre", "Jailer", "Priestess", "Chainbreaker", "Cogling", "Revenant", "Bat", "Gargoyle", "Owl", "Wyrm"]
humans = ["Knight", "Archer", "Priest", "Dwarf", "Fairy", "BattleMage", "Giant", "Paladin"]
md = []
md += [
"# Crookfinger's Bestiary",
"",
'

', "", para(lang["best.cavedwarf.text"]), ""]
md += ["## Rooms of the dungeon", "", "| | Room | |", "|---|---|---|"]
for key in ["treasury", "lair", "farm", "training", "library", "torture", "prison", "temple", "aerie", "forge", "graveyard", "guardpost", "arena", "altar"]:
md += building(key, key)
md += ["", "## Towers, traps and doors", "", "| | | |", "|---|---|---|"]
for key, icon in [("arrow", "arrow"), ("cannon", "cannon"), ("magic", "magic"), ("rune", "rune")]:
md += [f'|