#!/usr/bin/env python3 """Collect what each machine weighs and costs, for every lobby view to read. scripts/unit-stats.py --out web/src/content/unit-stats.json scripts/unit-stats.py --out web/src/content/unit-stats.json --check A force card wants to say what each machine weighs and what the force adds up to. None of that is in a unit file in any form this repo can read, so this reads what real MegaMek already reported: arena's `ScenarioReport` loads a scenario for real, and its output for the daily challenges is committed beside the scenarios. Nothing here computes anything. Weight, price and introduction year only. Battle value is deliberately not in this file, and the reason is the whole shape of it: BV 2.0 multiplies by the crew's gunnery and piloting, so the same machine is worth 359 with one pilot in it and 323 with another - Stinger STG-3R is exactly this, across two dailies. A table keyed by the machine cannot hold a number that belongs to the machine *and its pilot*, so per-unit BV rides on the scenario instead, in daily.json. What is here is true of the design wherever it turns up. Keyed by the full designation - "Clint CLNT-5U", chassis and model - because that is what a scenario file names and what the catalog names. Both catalogs are reported now: the dailies, and the vendored scenario files beside them. A machine still missing from both gets no entry, `statsFor` returns null and the card shows a dash - nothing is invented to fill a hole. Refreshing either report is an arena run, not something this script can do: java -cp arena.ScenarioReport > report.json """ import argparse import json import pathlib import sys REPORTS = [ "services/api/src/matches/daily/report.json", "services/api/src/matches/scenarios/report.json", ] def collect(root: pathlib.Path) -> dict[str, dict[str, int]]: """Every unit any report names, with what that report said it was worth.""" units: dict[str, dict[str, int]] = {} for name in REPORTS: path = root / name if not path.exists(): continue for scenario in json.loads(path.read_text()): for faction in scenario.get("factions", []): for unit in faction.get("units", []): designation = unit.get("name") if not designation: continue entry = { "tons": unit["tons"], "cost": unit["cost"], "year": unit["year"], } # A machine fielded by two scenarios is reported twice. # These three are the design's own, so the two agree - and # if they ever do not, the disagreement is a bug worth # failing on rather than a value worth picking between. # (BV would not agree, which is why it is not here.) if designation in units and units[designation] != entry: raise SystemExit( f"{designation}: two reports disagree, " f"{units[designation]} vs {entry}" ) units[designation] = entry return units def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--out", required=True, type=pathlib.Path) parser.add_argument( "--check", action="store_true", help="write nothing; fail if the committed file is not what this run " "would produce", ) args = parser.parse_args() root = pathlib.Path(__file__).resolve().parent.parent units = collect(root) text = json.dumps({"units": dict(sorted(units.items()))}, indent=1) + "\n" if args.check: if not args.out.exists(): print(f"{args.out}: missing; run scripts/unit-stats.py", file=sys.stderr) return 1 if args.out.read_text() != text: print( f"{args.out}: stale; re-run scripts/unit-stats.py", file=sys.stderr, ) return 1 return 0 args.out.write_text(text) print(f"{args.out}: {len(units)} machines.") return 0 if __name__ == "__main__": sys.exit(main())