"""Computes modifiers, derived stats, skill totals and carried weight (mirror of frontend/src/calc.js).""" def kit_items(ch: dict, rules: dict) -> list: """Returns [(label, item, qty)] for the starting kit and extra items.""" items = {e["id"]: e for e in rules.get("equipment", [])} kit = ch.get("kit") or {} out = [] for label, key in (("Weapon", "weapon"), ("Armor", "armor"), ("Tool", "tool")): if kit.get(key) in items: out.append((label, items[kit[key]], 1)) for rid in kit.get("resources", []): if rid in items: out.append(("Resource", items[rid], 1)) for iid, qty in (kit.get("extras") or {}).items(): if iid in items and int(qty) > 0: out.append(("Extra", items[iid], int(qty))) return out 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 > 0 # advanced skills must be unlocked best = max(attrs[a] for a in s["attrs"]) # with several attributes the higher one counts skills.append({**s, "points": pts, "prof": prof, "pips": pips, "usable": usable, "unlocked": unlocked, "total": best + 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)}