200 lines
7.8 KiB
Python
200 lines
7.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Export first-milestone source evidence for the Cocos 4 rebuild.
|
|
|
|
The extractor consumes the already decoded source-first inventory from the
|
|
original Cocos package. It does not read Godot output and does not infer layout
|
|
from screenshots.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
PROJECT = Path(__file__).resolve().parents[1]
|
|
SOURCE_ROOT = Path("/Users/hy/Documents/dev/qq_farm_godot_rebuild_20260616")
|
|
SOURCE_INVENTORY = SOURCE_ROOT / "source_first/out/main_scene_elements.json"
|
|
OUT = PROJECT / "source_export"
|
|
|
|
|
|
def read_json(path: Path) -> Any:
|
|
with path.open("r", encoding="utf-8") as fh:
|
|
return json.load(fh)
|
|
|
|
|
|
def write_json(path: Path, value: Any) -> None:
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(json.dumps(value, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
|
|
|
|
|
def write_text(path: Path, value: str) -> None:
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(value, encoding="utf-8")
|
|
|
|
|
|
def find_default_records(config_assets: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
for item in config_assets:
|
|
if item.get("id") == "skin_cfg":
|
|
return item.get("default_records", [])
|
|
return []
|
|
|
|
|
|
def only_paths(elements: list[dict[str, Any]], *needles: str) -> list[dict[str, Any]]:
|
|
return [item for item in elements if any(needle in str(item.get("full_path")) for needle in needles)]
|
|
|
|
|
|
def build_stage1(data: dict[str, Any]) -> dict[str, Any]:
|
|
sprite_prefab = data["prefabs"]["farm_scene_v3_sprite"]
|
|
mount_prefab = data["prefabs"]["farm_scene_v3"]
|
|
main_ui = data["prefabs"]["main_ui_v2"]
|
|
|
|
land_candidates = [
|
|
item for item in sprite_prefab["elements"]
|
|
if item.get("category") == "visual_land_cell"
|
|
]
|
|
background_nodes = [
|
|
item for item in sprite_prefab["elements"]
|
|
if item.get("category") == "world_background"
|
|
or str(item.get("full_path")).endswith("/Scene/bg")
|
|
or "BgLayer" in str(item.get("full_path"))
|
|
]
|
|
bottom_menu_nodes = only_paths(
|
|
main_ui["elements"],
|
|
"main_ui_v2/Menu",
|
|
"main_ui_v2/Menu/btn_layout",
|
|
"main_ui_v2/Menu/Node_task",
|
|
"main_ui_v2/Menu/Node_Friend",
|
|
)
|
|
default_skin_records = find_default_records(data.get("config_assets", []))
|
|
house_records = [
|
|
item for item in default_skin_records
|
|
if item.get("skin_type") in {1, 5}
|
|
]
|
|
doghouse_nodes = only_paths(sprite_prefab["elements"], "dogHouse") + only_paths(mount_prefab["elements"], "dogHouse")
|
|
|
|
return {
|
|
"module": "main_scene_first_milestone",
|
|
"generated_at": datetime.now(timezone.utc).isoformat(),
|
|
"source_inventory": str(SOURCE_INVENTORY),
|
|
"design_resolution": data["summary"]["design_resolution"],
|
|
"scope": [
|
|
"Canvas",
|
|
"main Camera",
|
|
"root scene",
|
|
"background layers",
|
|
"visible land plots",
|
|
"house",
|
|
"doghouse",
|
|
"bottom menu buttons",
|
|
],
|
|
"startup_scene": data["startup_scene"],
|
|
"prefab_sources": {
|
|
"farm_scene_v3_sprite": sprite_prefab["source"],
|
|
"farm_scene_v3": mount_prefab["source"],
|
|
"main_ui_v2": main_ui["source"],
|
|
},
|
|
"canvas_and_cameras": data["startup_scene"]["elements"],
|
|
"background_nodes": background_nodes,
|
|
"land_candidates": {
|
|
"source_count": len(land_candidates),
|
|
"visible_count_required_by_spec": 24,
|
|
"selection_status": "blocked_missing_runtime_visible_land_rule",
|
|
"nodes": land_candidates,
|
|
},
|
|
"house_and_doghouse": {
|
|
"skin_cfg_default_records": house_records,
|
|
"doghouse_nodes": doghouse_nodes,
|
|
},
|
|
"bottom_menu_nodes": bottom_menu_nodes,
|
|
"asset_inputs": {
|
|
"backgrounds": data.get("source_assets", {}).get("backgrounds", []),
|
|
"config_assets": data.get("config_assets", []),
|
|
},
|
|
"blockers": [
|
|
{
|
|
"id": "visible_land_count",
|
|
"status": "blocked",
|
|
"detail": "Static source prefab has 36 visual grid cells. The first-milestone spec requires 24 visible land plots, but the runtime rule selecting those 24 is not yet decoded.",
|
|
},
|
|
{
|
|
"id": "background_texture_binding",
|
|
"status": "blocked",
|
|
"detail": "Background ImageAsset entries are located, but final texture/SpriteFrame binding for Cocos 4 import is not yet resolved from metadata.",
|
|
},
|
|
{
|
|
"id": "cocos4_cli_build",
|
|
"status": "blocked",
|
|
"detail": "Cocos 4 alpha CLI reaches engine init, then fails because node_modules/gl/build/Release/webgl.node requires NODE_MODULE_VERSION 140 while local Node 24 provides 137.",
|
|
},
|
|
],
|
|
}
|
|
|
|
|
|
def main() -> None:
|
|
data = read_json(SOURCE_INVENTORY)
|
|
stage1 = build_stage1(data)
|
|
|
|
sprite_prefab = data["prefabs"]["farm_scene_v3_sprite"]
|
|
mount_prefab = data["prefabs"]["farm_scene_v3"]
|
|
main_ui = data["prefabs"]["main_ui_v2"]
|
|
|
|
write_json(OUT / "source_nodes.json", stage1)
|
|
write_json(OUT / "source_prefabs.json", {
|
|
"farm_scene_v3_sprite": {
|
|
"source": sprite_prefab["source"],
|
|
"node_count": len(sprite_prefab["elements"]),
|
|
"category_counts": sprite_prefab.get("category_counts", {}),
|
|
},
|
|
"farm_scene_v3": {
|
|
"source": mount_prefab["source"],
|
|
"node_count": len(mount_prefab["elements"]),
|
|
"category_counts": mount_prefab.get("category_counts", {}),
|
|
},
|
|
"main_ui_v2": {
|
|
"source": main_ui["source"],
|
|
"node_count": len(main_ui["elements"]),
|
|
"category_counts": main_ui.get("category_counts", {}),
|
|
},
|
|
})
|
|
write_json(OUT / "source_assets.json", stage1["asset_inputs"])
|
|
write_json(OUT / "source_spriteframes.json", {
|
|
"status": "metadata_manifest_available_but_stage1_bindings_unresolved",
|
|
"manifest": str(SOURCE_ROOT / "downloads/spriteframes/sprite_manifest.json"),
|
|
})
|
|
write_json(OUT / "source_spine.json", {
|
|
"status": "default house and doghouse reference Spine assets through SkinCfg; Cocos 4 import binding not generated yet",
|
|
"manifest": str(SOURCE_ROOT / "downloads/spine_regions/spine_region_manifest.json"),
|
|
"records": stage1["house_and_doghouse"]["skin_cfg_default_records"],
|
|
})
|
|
write_json(OUT / "source_animations.json", {
|
|
"status": "not_in_first_stage_scope",
|
|
"animation_clips": data.get("source_assets", {}).get("animation_clips", []),
|
|
})
|
|
|
|
runtime_notes = """# Source Runtime Notes
|
|
|
|
- Startup scene source has Canvas/root/ui/scene and two cameras.
|
|
- `farm_scene_v3_sprite` contains 36 static visual land cells under `Scaled/Rotate/GridOrigin`.
|
|
- First-stage spec requires 24 visible land plots, but the runtime rule selecting visible or unlocked plots is not decoded yet.
|
|
- Default house and doghouse come from `SkinCfg` records `201001` and `205001`, not only from the static visual prefab.
|
|
- Bottom menu source nodes are under `main_ui_v2/Menu`; feature logic for warehouse/shop/friend/task is outside first-stage scope.
|
|
"""
|
|
write_text(OUT / "source_runtime_notes.md", runtime_notes)
|
|
|
|
blocker_lines = ["# Source Blockers", ""]
|
|
for item in stage1["blockers"]:
|
|
blocker_lines.append(f"- `{item['id']}`: {item['detail']}")
|
|
blocker_lines.append("")
|
|
write_text(OUT / "source_blockers.md", "\n".join(blocker_lines))
|
|
|
|
print(f"wrote {OUT / 'source_nodes.json'}")
|
|
print(f"land candidates: {stage1['land_candidates']['source_count']}")
|
|
print("visible land selection: blocked_missing_runtime_visible_land_rule")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|