"""Import the revision-bound subset used by our CW adapter; no player files change.""" import argparse import hashlib import json import pathlib import subprocess def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--source", type=pathlib.Path, required=True) parser.add_argument("--root", type=pathlib.Path, default=pathlib.Path(__file__).resolve().parents[1]) args = parser.parse_args() source, root = args.source.resolve(), args.root.resolve() catalog = json.loads((root / "resources/CurrencyWarsCatalog.json").read_text(encoding="utf-8")) inputs = [ "resources/grid_fight/elite_rewards.json", "resources/grid_fight/rounds.raw.json", "resources/grid_fight/score.json", "resources/grid_fight/offering_levels.json", "resources/grid_fight/traits.json", "resources/grid_fight/trait_effects.json", "resources/grid_fight/augments.json", "resources/grid_fight/orb_rewards.json", "gameserver/src/services/grid_fight.zig", "gameserver/src/services/grid_fight_rewards.zig", "gameserver/src/services/grid_fight_strategy.zig", "gameserver/src/services/grid_fight_traits.zig", "gameserver/src/services/grid_fight_bonds.zig", "gameserver/src/services/grid_fight_talent.zig", ] hashes = {name: hashlib.sha256((source / name).read_bytes()).hexdigest() for name in inputs} elite = json.loads((source / inputs[0]).read_text(encoding="utf-8")) rounds = json.loads((source / inputs[1]).read_text(encoding="utf-8")) score_raw = json.loads((source / inputs[2]).read_text(encoding="utf-8")) offering_raw = json.loads((source / inputs[3]).read_text(encoding="utf-8")) traits_raw = json.loads((source / inputs[4]).read_text(encoding="utf-8")) effects_raw = json.loads((source / inputs[5]).read_text(encoding="utf-8")) augments_raw = json.loads((source / inputs[6]).read_text(encoding="utf-8")) orbs_raw = json.loads((source / inputs[7]).read_text(encoding="utf-8")) assembly = catalog["provenance"]["game_assembly_sha256"] if elite["source"]["game_assembly_sha256"] != assembly: raise ValueError("source Client assembly does not match installed CW catalog") commit = subprocess.check_output(["git", "-C", str(source), "rev-parse", "HEAD"], text=True).strip() active = {row["ID"] for row in catalog["active_stage_wrappers"]} divisions = [{key: row.get(key, 0) for key in ("DivisionID", "EnemyDifficultyLevel", "BinaryNodeDiffAddRule")} for row in rounds["GridFightDivisionStageRow"] if row["DivisionID"] in active] if {row["DivisionID"] for row in divisions} != active or len(divisions) != len(active): raise ValueError("source division table does not cover captured stages uniquely") for row in divisions: captured = next(stage for stage in catalog["active_stage_wrappers"] if stage["ID"] == row["DivisionID"]) if row["EnemyDifficultyLevel"] != captured["EnemyHardLevel"]: raise ValueError("source division difficulty differs from installed capture") output_cw = { "format_version": 1, "target_client_version": catalog["client_version"], "target_assembly_sha256": assembly, "provenance": { "repository": "https://gaytea.cod-algieba.ts.net/xaodev/Currency-War-SR", "commit": commit, "license": "AGPL-3.0-only", "inputs_sha256": hashes, "native_source": elite["source"], "adapted_functions": ["eliteOffers", "powerfulQuality", "encounter", "branchDropPreview", "sendEliteSelected", "roundExp"] }, "node_rules": elite["node_rules"], "diff_add": elite["diff_add"], "difficulty_levels": [{"chapter": row["chapter"], "level": row["level"]} for row in elite["difficulty"]], "divisions": sorted(divisions, key=lambda row: row["DivisionID"]), "generation_policy": { "source": "grid_fight.zig encounter fallback for EliteBranch nodes without a node_profile; server-authored, not official wave data", "monster_count": 3, "monster_tier": 2, "star_cap": 3, "normal_quality": 1, "powerful_qualities": [2, 3, 4], "seed_policy": "sha256 of run id, route, chapter and section; local deterministic sampling, not upstream Wyhash", "reward_policy": "preview_base_gold_already_paid_by_settlement", "extra_rewards_supported": False }, "gameplay_verified": False } destination = root / "resources/CurrencyWarsXaodev.json" destination.write_text(json.dumps(output_cw, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") # 1. Score & Offering Levels offering_s2 = [ {"level": row["Level"], "item_cost": row["ItemCost"], "reward_id": row["RewardID"]} for row in offering_raw if row.get("TypeID") == 16 ] offering_s2.sort(key=lambda x: x["level"]) score_output = { "format_version": 1, "target_client_version": catalog["client_version"], "target_assembly_sha256": assembly, "provenance": { "repository": "https://gaytea.cod-algieba.ts.net/xaodev/Currency-War-SR", "commit": commit, "license": "AGPL-3.0-only", "inputs_sha256": { "score.json": hashes["resources/grid_fight/score.json"], "offering_levels.json": hashes["resources/grid_fight/offering_levels.json"], } }, "weekly_cap": score_raw.get("weekly_cap", 18000), "weekly_ranks": score_raw.get("weekly_ranks", []), "divisions": score_raw.get("divisions", {}), "rules": score_raw.get("rules", {}), "offering_levels": offering_s2 } (root / "resources/CurrencyWarsScore.json").write_text( json.dumps(score_output, indent=2, ensure_ascii=False) + "\n", encoding="utf-8" ) # 2. Traits & Effects traits_list = [ { "id": t["id"], "name": t.get("name", ""), "thresholds": t.get("thresholds", []), "activation_type": t.get("raw", {}).get("base", {}).get("ActivationType", 1), "trait_type": t.get("raw", {}).get("base", {}).get("TraitType", 0), "season_id": t.get("raw", {}).get("base", {}).get("SeasonID", 1) } for t in traits_raw ] traits_list.sort(key=lambda x: x["id"]) traits_output = { "format_version": 1, "target_client_version": catalog["client_version"], "target_assembly_sha256": assembly, "provenance": { "repository": "https://gaytea.cod-algieba.ts.net/xaodev/Currency-War-SR", "commit": commit, "license": "AGPL-3.0-only", "inputs_sha256": { "traits.json": hashes["resources/grid_fight/traits.json"], "trait_effects.json": hashes["resources/grid_fight/trait_effects.json"], } }, "traits": traits_list, "effects": effects_raw.get("effects", {}), "constants": effects_raw.get("constants", {}) } (root / "resources/CurrencyWarsTraits.json").write_text( json.dumps(traits_output, indent=2, ensure_ascii=False) + "\n", encoding="utf-8" ) # 3. Augments augments_list = [ { "id": a["id"], "quality": a.get("quality", 1), "chapters": a.get("chapters", []), "oc": a.get("oc", False), "grants": a.get("grants", []), "grants_complete": a.get("grants_complete", True) } for a in augments_raw.get("augments", []) ] augments_list.sort(key=lambda x: x["id"]) augments_output = { "format_version": 1, "target_client_version": catalog["client_version"], "target_assembly_sha256": assembly, "provenance": { "repository": "https://gaytea.cod-algieba.ts.net/xaodev/Currency-War-SR", "commit": commit, "license": "AGPL-3.0-only", "inputs_sha256": { "augments.json": hashes["resources/grid_fight/augments.json"], } }, "season_id": augments_raw.get("season_id", 2), "augments": augments_list } (root / "resources/CurrencyWarsAugments.json").write_text( json.dumps(augments_output, indent=2, ensure_ascii=False) + "\n", encoding="utf-8" ) # 4. Node Drops node_drops_output = { "format_version": 1, "target_client_version": catalog["client_version"], "target_assembly_sha256": assembly, "provenance": { "repository": "https://gaytea.cod-algieba.ts.net/xaodev/Currency-War-SR", "commit": commit, "license": "AGPL-3.0-only", "inputs_sha256": { "orb_rewards.json": hashes["resources/grid_fight/orb_rewards.json"], } }, "node_drops": orbs_raw.get("node_drops", {}) } (root / "resources/CurrencyWarsNodeDrops.json").write_text( json.dumps(node_drops_output, indent=2, ensure_ascii=False) + "\n", encoding="utf-8" ) license_dir = root / "third_party/currency-war-sr" license_dir.mkdir(parents=True, exist_ok=True) (license_dir / "LICENSE").write_bytes((source / "LICENSE").read_bytes()) print(f"Imported CW resources:") print(f" - CurrencyWarsXaodev.json: {len(output_cw['node_rules'])} branch rules, {len(divisions)} divisions") print(f" - CurrencyWarsScore.json: {len(score_output['divisions'])} score divisions, {len(offering_s2)} offering levels") print(f" - CurrencyWarsTraits.json: {len(traits_list)} traits") print(f" - CurrencyWarsAugments.json: {len(augments_list)} augments") print(f" - CurrencyWarsNodeDrops.json: {len(node_drops_output['node_drops'])} drop rules") if __name__ == "__main__": main()