package common import ( "encoding/json" "fmt" ) // Scene metadata is exported from the installed Client's RuntimeFloor tables. // A complete group list allows us to exclude deleted/client-owned groups // without maintaining special cases for individual maps or group IDs. type sceneMetadata struct { ClientVersion string `json:"client_version"` Floors map[uint32]struct { StartAnchor *AnchorItem `json:"start_anchor"` Monsters map[uint32]map[uint32]TeleportInfo `json:"monsters"` GroupCount int `json:"group_count"` Groups map[uint32]struct { LoadSide string `json:"load_side"` Deleted bool `json:"is_deleted"` } `json:"groups"` } `json:"floors"` } func applySceneMetadata(res *JsonConfig) { raw, err := ReadResourceFile("resources/SceneMetadata.json") if err != nil { return } var metadata sceneMetadata if err := json.Unmarshal(raw, &metadata); err != nil { LogInfo("[SCENE] Invalid SceneMetadata: %v", err) return } config, err := LoadRuntimeConfig() if err != nil || metadata.ClientVersion != config.ClientVersion { LogInfo("[SCENE] SceneMetadata Client revision does not match runtime config; not applied") return } for entryID, configs := range res.LevelOutputConfigs { for name, scene := range configs { var planeID, floorID uint32 if n, _ := fmt.Sscanf(name, "P%d_F%d", &planeID, &floorID); n != 2 { continue } floor, ok := metadata.Floors[floorID] if !ok { continue } if floor.GroupCount == 0 || len(floor.Groups) != floor.GroupCount { LogInfo("[SCENE] Incomplete group metadata for floor %d; not applied", floorID) continue } if floor.StartAnchor != nil { res.SceneAnchors[entryID] = []AnchorItem{*floor.StartAnchor} } for groupID := range scene.Scenes { group, exists := floor.Groups[groupID] if !exists || group.Deleted || group.LoadSide == "Client" { delete(scene.Scenes, groupID) LogInfo("[SCENE] Excluded group entry=%d floor=%d group=%d present=%t deleted=%t side=%s", entryID, floorID, groupID, exists, group.Deleted, group.LoadSide) continue } data := scene.Scenes[groupID] for index := range data.Monsters { monster := &data.Monsters[index] if placement, ok := floor.Monsters[groupID][monster.InstID]; ok { monster.Pos, monster.Rot = placement.Pos, placement.Rot } } scene.Scenes[groupID] = data } } } }