package catalogs import ( "encoding/json" "os" "path/filepath" "sync" ) var _ = json.RawMessage{} var _ = sync.Mutex{} var _ = os.ReadFile var _ = filepath.Join // // PartyDanceSpotConfig.json 里的**舞池点位**(2026-09-26 第 49 轮,舞会线)。 // WHY THIS FILE EXISTS // -------------------- // 第 48 轮用户两条硬指令:「**舞会有舞会的摆法,不要参考露营**」+「**走官方路径**」。 // 露营的摆法是「以圆心为心、半径 r 的圆周,人人**朝圆心**」;舞会不是。 // 官方给舞会单独开了一张表 PartyDanceSpotConfig,字段只有四个: // PartyId / MusicId / DanceSpot / DanceRotation。 // 客户端唯一消费者 ChooseDanceMusicPanelStore.lua:319-332 的用法是 // Vector3(DanceSpot) + Vector3(0, DanceRotation, 0) 当作 // **演出 timeline 的世界位姿**(PlayLiveHouseV2 的 timelinePos/timelineRot) // ⇒ 语义就是「**舞池在世界的哪里、朝哪边**」。 // 于是「舞会的摆法」= 站位落在官方 DanceSpot、朝向取官方 DanceRotation // (**所有人面向同一个方向**,而不是各自朝圆心)。 // 逐字段核对(ConfigDump_v3\PartyDanceSpotConfig.json,共 10 条): // // Id 1~5 PartyId=13030003 新启舞会 MusicId=91131019/20/21/22/23 // DanceSpot=[2605.33008, 80, 1269.73999] DanceRotation=-84.1142578 ← 五条完全同值 // Id 6~10 PartyId=13030006 凌云舞会 同名五首 // DanceSpot=[77.1600037, 19.0752449, -285.609985] DanceRotation=-129.262299 // // ⇒ 按 PartyId 索引即可(同一 party 的五首曲子共用一个舞池); // TryGet 保留 MusicId 维度,供将来「一曲一舞池」用。 // ⚠ 与 PartySpotCatalog4229938 的 PartySpot **不是一个点**,别互相顶替: // 13030003 的 PartySpot = [2574.24, 80.135, 1271](场地锚,与 // PartyCamp.json 的 danceOnly.pos 一致,舞会大门在 2569.63); // 而 DanceSpot = [2605.33, 80, 1269.74](舞池,灯光控制台在 2593.94), // 两者相距 ≈ 31 m。前者是「场地入口」,后者是「舞池」。 // type PartyDanceSpotEntry4229938 struct { Id uint32 `json:"id"` PartyId uint32 `json:"partyId"` MusicId uint32 `json:"musicId"` DanceSpot Vec3 `json:"danceSpot"` DanceRotation float32 `json:"danceRotation"` // // 配表是否给了**可用**的舞池点。[0,0,0] 是哨兵(「本行没有舞池」)不是真原点; // 再叠一层有限性检查(NaN/Inf 一律判不可用)⇒ 让调用方回退。 // HasUsableSpot bool `json:"hasUsableSpot"` } var ( PartyDanceSpotCatalog4229938_cacheLock sync.RWMutex PartyDanceSpotCatalog4229938_cached = false PartyDanceSpotCatalog4229938_entries []any ) // LoadPartyDanceSpotCatalog4229938 loads catalog records from client config directory func LoadPartyDanceSpotCatalog4229938(configDir string) ([]any, error) { PartyDanceSpotCatalog4229938_cacheLock.RLock() if PartyDanceSpotCatalog4229938_cached { defer PartyDanceSpotCatalog4229938_cacheLock.RUnlock() return PartyDanceSpotCatalog4229938_entries, nil } PartyDanceSpotCatalog4229938_cacheLock.RUnlock() PartyDanceSpotCatalog4229938_cacheLock.Lock() defer PartyDanceSpotCatalog4229938_cacheLock.Unlock() if PartyDanceSpotCatalog4229938_cached { return PartyDanceSpotCatalog4229938_entries, nil } filePath := filepath.Join(configDir, "PartyDanceSpotCatalog.json.json") data, err := os.ReadFile(filePath) if err != nil { return nil, err } var raw struct { Records []json.RawMessage `json:"records"` } if err := json.Unmarshal(data, &raw); err != nil { return nil, err } var result []any for _, item := range raw.Records { var m map[string]any if err := json.Unmarshal(item, &m); err == nil { result = append(result, m) } } PartyDanceSpotCatalog4229938_entries = result PartyDanceSpotCatalog4229938_cached = true return result, nil } // 按 partyId 取舞池(同一 party 的五首曲子共用一个舞池 ⇒ 取第一条可用行)。 func PartyDanceSpotCatalog4229938_TryGetByParty(partyId uint32, entry PartyDanceSpotEntry4229938) bool { // Catalog helper PartyDanceSpotCatalog4229938_TryGetByParty return false } // 按 partyId + musicId 精确取(保留维度,当前配表下与 TryGetByParty 同值)。 func PartyDanceSpotCatalog4229938_TryGet(partyId uint32, musicId uint32, entry PartyDanceSpotEntry4229938) bool { // Catalog helper PartyDanceSpotCatalog4229938_TryGet return false } func PartyDanceSpotCatalog4229938_U32(row any, name string) uint32 { // Catalog helper PartyDanceSpotCatalog4229938_U32 return 0 } func PartyDanceSpotCatalog4229938_Single(row any, name string) float32 { // Catalog helper PartyDanceSpotCatalog4229938_Single return 0 } // 读 [x,y,z];形状不对或含 NaN/Inf 就返回零向量(⇒ HasUsableSpot = false)。 func PartyDanceSpotCatalog4229938_ReadVector(row any, name string) Vec3 { // Catalog helper PartyDanceSpotCatalog4229938_ReadVector var zero Vec3; return zero }