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:
2026-06-16 15:30:58 +08:00
parent cba5105908
commit d393302388
6248 changed files with 17322729 additions and 11036 deletions

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