Files

56 lines
3.1 KiB
Python

"""Computes modifiers, derived stats, skill totals and carried weight (mirror of frontend/src/calc.js)."""
def kit_items(ch: dict, rules: dict) -> list:
"""Returns [(category, item, qty)] for the carried items. Old starting-kit slots count as one item each."""
items = {e["id"]: e for e in rules.get("equipment", [])}
cats = {c["id"]: c["name"] for c in rules.get("equipment_categories", [])}
kit = ch.get("kit") or {}
qty: dict = {}
for iid in [kit.get("weapon"), kit.get("armor"), kit.get("tool"), *(kit.get("resources") or [])]:
if iid:
qty[iid] = qty.get(iid, 0) + 1
for iid, n in (kit.get("extras") or {}).items():
qty[iid] = qty.get(iid, 0) + int(n)
return [(cats.get(items[i]["cat"], ""), items[i], n) for i, n in qty.items() if i in items and n > 0]
def compute(ch: dict, rules: dict) -> dict:
cr = {**rules["creation"], **(ch.get("creation") or {})} # per-character rules preset overrides
prof_id = ch.get("profession")
bonus_attr = ch.get("profession_bonus_attr") if prof_id else None
attrs = {}
for a in rules["attributes"]:
base = int((ch.get("attributes") or {}).get(a["id"], cr["attr_start"]))
attrs[a["id"]] = base + (cr["profession_attr_bonus"] if a["id"] == bonus_attr else 0)
mods = {k: v // 10 for k, v in attrs.items()}
derived = {d["id"]: sum(mods[x] for x in d["formula"]) for d in rules["derived"]}
skills = []
for s in rules["skills"]:
pts = int((ch.get("skill_points") or {}).get(s["id"], 0))
# Proficiency "pips": 1 from the profession plus free picks, at most 2 (2 = Expertise)
prof_pip = 1 if (ch.get("proficiencies") or {}).get(s["id"]) else 0
free_pips = min(int((ch.get("proficiency_picks") or {}).get(s["id"], 0)), cr.get("max_proficiency_pips", 2) - prof_pip)
pips = prof_pip + max(0, free_pips)
prof = [0, cr["proficiency_bonus"], cr["expertise_bonus"]][pips]
unlocked = s["id"] in (ch.get("unlocked") or [])
usable = s["type"] == "basic" or unlocked or prof_pip > 0 # advanced skills must be unlocked (a profession proficiency does it for free)
best = max(attrs[a] for a in s["attrs"]) # with several attributes the higher one counts
base = best * cr.get("advanced_attr_percent", 100) // 100 if s["type"] == "advanced" else best # advanced skills only use part of the attribute
skills.append({**s, "points": pts, "prof": prof, "pips": pips, "usable": usable, "unlocked": unlocked,
"base": base, "total": base + pts + prof})
aug_by_id = {a["id"]: a for a in rules["augments"]}
chosen = [aug_by_id[i] for i in ch.get("augments", []) if i in aug_by_id]
used = sum(a["cost"] for a in chosen)
weight = round(sum(item["kg"] * qty for _, item, qty in kit_items(ch, rules)), 1)
return {"attrs": attrs, "mods": mods, "derived": derived, "skills": skills,
"capacity_used": used, "capacity_max": derived.get("augmentation", 0),
"augments": chosen, "weight": weight,
"carry_max": max(0, mods["strength"]) * cr.get("carry_per_strength_mod", 10)}