Files
GaCy-GO/tools/import_xaodev_cw.py
T

214 lines
9.7 KiB
Python
Raw Normal View History

"""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()