#!/usr/bin/env python3 """为 mapLayer 格子层所有 MapGrid 实例统一 digSprite / putSprite(参考 grid0)。""" from __future__ import annotations import argparse import json import sys from copy import deepcopy from pathlib import Path MAP_GRID_PREFAB_UUID = "5d1d936e-4aad-4dea-a4ed-65596d036a4e" MAP_GRID_LOCAL_ID = "a5Czjqnj1DIa/1qXv6uwzF" GRID_PARENT_NAME = "格子" def load_prefab(path: Path) -> list: return json.loads(path.read_text(encoding="utf-8")) def save_prefab(path: Path, data: list) -> None: path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") def resolve(data: list, ref) -> dict | None: if ref is None: return None if isinstance(ref, dict) and "__id__" in ref: return data[ref["__id__"]] return None def find_grid_parent_id(data: list) -> int | None: for i, obj in enumerate(data): if obj.get("__type__") == "cc.Node" and obj.get("_name") == GRID_PARENT_NAME: return i return None def collect_instance_overrides(data: list, instance: dict) -> list[dict]: overrides: list[dict] = [] for ref in instance.get("propertyOverrides", []): ov = resolve(data, ref) if ov is not None: overrides.append(ov) return overrides def get_grid_name(data: list, overrides: list[dict]) -> str | None: for ov in overrides: if ov.get("propertyPath") == ["_name"]: return ov.get("value") return None def find_mapgrid_target_id(data: list, overrides: list[dict]) -> int | None: for ov in overrides: target = resolve(data, ov.get("targetInfo")) if target and target.get("localID") == [MAP_GRID_LOCAL_ID]: return ov["targetInfo"]["__id__"] return None def sprite_value(uuid: str) -> dict: return { "__uuid__": uuid, "__expectedType__": "cc.SpriteFrame", } def patch_prefab(data: list) -> tuple[int, int, int]: parent_id = find_grid_parent_id(data) if parent_id is None: raise RuntimeError(f"未找到节点「{GRID_PARENT_NAME}」") parent = data[parent_id] dig_ref: dict | None = None put_ref: dict | None = None added = 0 updated = 0 skipped = 0 for child_ref in parent.get("_children", []): child = resolve(data, child_ref) if child is None: continue prefab_info = resolve(data, child.get("_prefab")) if prefab_info is None: continue asset = prefab_info.get("asset", {}) if asset.get("__uuid__") != MAP_GRID_PREFAB_UUID: continue instance = resolve(data, prefab_info.get("instance")) if instance is None: continue overrides = collect_instance_overrides(data, instance) grid_name = get_grid_name(data, overrides) mapgrid_target_id = find_mapgrid_target_id(data, overrides) if mapgrid_target_id is None: print(f"warn: {grid_name or child_ref} 无 MapGrid TargetInfo,跳过", file=sys.stderr) skipped += 1 continue if grid_name == "grid0": for ov in overrides: path = ov.get("propertyPath", []) if path == ["digSprite"]: dig_ref = deepcopy(ov.get("value")) elif path == ["putSprite"]: put_ref = deepcopy(ov.get("value")) continue if dig_ref is None or put_ref is None: raise RuntimeError("grid0 缺少 digSprite 或 putSprite 引用") existing: dict[str, dict] = {} for ov in overrides: path = ov.get("propertyPath", []) if path in (["digSprite"], ["putSprite"]): existing[path[0]] = ov for prop, ref_value in (("digSprite", dig_ref), ("putSprite", put_ref)): if prop in existing: if existing[prop].get("value") != ref_value: existing[prop]["value"] = deepcopy(ref_value) updated += 1 else: new_ov = { "__type__": "CCPropertyOverrideInfo", "targetInfo": {"__id__": mapgrid_target_id}, "propertyPath": [prop], "value": deepcopy(ref_value), } data.append(new_ov) instance.setdefault("propertyOverrides", []).append({"__id__": len(data) - 1}) added += 1 return added, updated, skipped def main() -> int: parser = argparse.ArgumentParser(description="统一 mapLayer 格子 MapGrid 的 digSprite / putSprite") parser.add_argument( "prefab", nargs="?", default="mapLayer1.prefab", help="预制体文件名,如 mapLayer1.prefab / mapLayer2.prefab", ) args = parser.parse_args() prefab_path = Path(__file__).resolve().parents[1] / "assets/bundles/battleMap/prefab/地图" / args.prefab if not prefab_path.is_file(): print(f"错误: 找不到 {prefab_path}", file=sys.stderr) return 1 data = load_prefab(prefab_path) added, updated, skipped = patch_prefab(data) save_prefab(prefab_path, data) print(f"{prefab_path.name}: 新增 {added} 条, 更新 {updated} 条, 跳过 {skipped} 个实例") return 0 if __name__ == "__main__": raise SystemExit(main())