Complete Cocos Creator port with level bundles, themes, and tooling.
Adds level prefabs, theme assets, audio, extensions, and deployment scripts for the Unity WebGL migration. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
142
tools/renumber-level-prefabs.py
Normal file
142
tools/renumber-level-prefabs.py
Normal file
@@ -0,0 +1,142 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
将 level-prefabs/Level{N}.prefab 重命名为 Level{91600+N}.prefab,并同步 JSON 数据库。
|
||||
|
||||
与 Unity / 主站 config.js BEGINNING_REAL_LVID=91601 对齐:游戏 ID 从 91601 起(首关 Level91601)。
|
||||
|
||||
用法:
|
||||
python3 tools/renumber-level-prefabs.py --dry-run
|
||||
python3 tools/renumber-level-prefabs.py
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
from level_id import (
|
||||
LEVEL_ID_BASE,
|
||||
internal_level_index as to_internal_id,
|
||||
prefab_resource_path,
|
||||
touch_database,
|
||||
)
|
||||
|
||||
|
||||
def to_external_id(internal: int) -> int:
|
||||
return LEVEL_ID_BASE + internal
|
||||
|
||||
PREFAB_DIR_NAME = "level-prefabs"
|
||||
LEVEL_RE = re.compile(r"^Level(\d+)\.prefab$", re.I)
|
||||
|
||||
|
||||
def rename_prefabs(prefab_dir: Path, dry_run: bool) -> dict[int, int]:
|
||||
"""internal -> external mapping for renamed files."""
|
||||
mapping: dict[int, int] = {}
|
||||
files = sorted(
|
||||
[p for p in prefab_dir.glob("Level*.prefab") if LEVEL_RE.match(p.name)],
|
||||
key=lambda p: int(LEVEL_RE.match(p.name).group(1)), # type: ignore
|
||||
reverse=True,
|
||||
)
|
||||
for src in files:
|
||||
m = LEVEL_RE.match(src.name)
|
||||
if not m:
|
||||
continue
|
||||
internal = int(m.group(1))
|
||||
if internal > LEVEL_ID_BASE:
|
||||
continue
|
||||
external = to_external_id(internal)
|
||||
dst = prefab_dir / f"Level{external}.prefab"
|
||||
meta_src = Path(str(src) + ".meta")
|
||||
meta_dst = Path(str(dst) + ".meta")
|
||||
mapping[internal] = external
|
||||
if dry_run:
|
||||
if internal <= 3 or internal in (60, 80, 4188):
|
||||
print(f" {src.name} -> {dst.name}")
|
||||
continue
|
||||
if dst.exists():
|
||||
raise SystemExit(f"目标已存在,中止: {dst}")
|
||||
src.rename(dst)
|
||||
if meta_src.is_file():
|
||||
meta_src.rename(meta_dst)
|
||||
return mapping
|
||||
|
||||
|
||||
def migrate_levels_database(db_path: Path, mapping: dict[int, int], dry_run: bool) -> None:
|
||||
if not db_path.is_file():
|
||||
print(f"skip database (not found): {db_path}")
|
||||
return
|
||||
data = json.loads(db_path.read_text(encoding="utf-8"))
|
||||
old_levels = data.get("levels") or {}
|
||||
new_levels: dict[str, dict] = {}
|
||||
for key, cfg in old_levels.items():
|
||||
try:
|
||||
old_id = int(key)
|
||||
except ValueError:
|
||||
old_id = int(cfg.get("levelID", key))
|
||||
internal = to_internal_id(old_id) if old_id > LEVEL_ID_BASE else old_id
|
||||
external = mapping.get(internal, to_external_id(internal))
|
||||
entry = dict(cfg)
|
||||
entry["levelID"] = external
|
||||
unity_prefab = entry.get("unityPrefab", "")
|
||||
if isinstance(unity_prefab, str) and unity_prefab:
|
||||
entry["unityPrefab"] = re.sub(
|
||||
r"Level\d+\.prefab$",
|
||||
f"Level{external}.prefab",
|
||||
unity_prefab.replace("\\", "/"),
|
||||
flags=re.I,
|
||||
)
|
||||
entry["cocosPrefab"] = prefab_resource_path(external)
|
||||
new_levels[str(external)] = entry
|
||||
data["levels"] = new_levels
|
||||
if dry_run:
|
||||
print(f"database: {len(old_levels)} -> {len(new_levels)} keys:", sorted(new_levels.keys()))
|
||||
return
|
||||
db_path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
touch_database(db_path)
|
||||
print(f"updated {db_path}")
|
||||
|
||||
|
||||
def rebuild_prefab_index(prefab_dir: Path, index_path: Path, dry_run: bool) -> None:
|
||||
index: dict[str, str] = {}
|
||||
for p in sorted(
|
||||
prefab_dir.glob("Level*.prefab"),
|
||||
key=lambda x: int(LEVEL_RE.match(x.name).group(1)), # type: ignore
|
||||
):
|
||||
m = LEVEL_RE.match(p.name)
|
||||
if not m:
|
||||
continue
|
||||
lid = int(m.group(1))
|
||||
index[str(lid)] = f"{PREFAB_DIR_NAME}/Level{lid}"
|
||||
if dry_run:
|
||||
print(f"index: {len(index)} entries")
|
||||
return
|
||||
index_path.write_text(json.dumps(index, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
print(f"updated {index_path} ({len(index)} entries)")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--project", type=Path, default=Path(__file__).resolve().parents[1])
|
||||
ap.add_argument("--dry-run", action="store_true")
|
||||
args = ap.parse_args()
|
||||
project = args.project
|
||||
prefab_dir = project / "assets" / "resources" / PREFAB_DIR_NAME
|
||||
db_path = project / "assets" / "level-data" / "levels-database.json"
|
||||
index_path = project / "tools" / "level-prefab-index.json"
|
||||
|
||||
if not prefab_dir.is_dir():
|
||||
raise SystemExit(f"prefab dir not found: {prefab_dir}")
|
||||
|
||||
print(f"{'[dry-run] ' if args.dry_run else ''}Renaming prefabs in {prefab_dir} …")
|
||||
mapping = rename_prefabs(prefab_dir, args.dry_run)
|
||||
print(f" mapped {len(mapping)} prefabs (1 -> {to_external_id(1)})")
|
||||
|
||||
migrate_levels_database(db_path, mapping, args.dry_run)
|
||||
rebuild_prefab_index(prefab_dir, index_path, args.dry_run)
|
||||
print("done.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user