package catalogs import ( "encoding/json" "os" "path/filepath" "sync" ) var _ = json.RawMessage{} var _ = sync.Mutex{} var _ = os.ReadFile var _ = filepath.Join // // 龙栖村的一个互动摊位机关:打气球 / 套圈 / 刮刮乐 / 五子棋 / 打年糕 …。 // 读自 Configs/MiniGameStalls.json。 // // // ★ 为什么需要它(2026-09-27,第 89 轮): // 这批摊位机关**不在**客户端会自己实例化的清单里。它们的真身有两半: // ① 客户端 Spoon 图里有一个 GADGET 节点(StartBalloonNode / StartRingTossNode …) // ② 关卡块里有一份官方世界摆位 // 和 GymEquipment / PartyCamp 完全同因:grid-AOI 下发**不驱动** LX6.Item.GadgetBlob, // 所以客户端一件都不会自己出现 ⇒ 必须服务端拿 pathId + 官方坐标显式摆一次。 // ★ pathId 空间是「服务端 catalog」那一套(Configs/GadgetCatalog.json,2935 条), // ⛔ 不是客户端 GadgetConfig.json 的 1367 条(那是另一套 id, // 例:同一件 Gadget_DaQiQiu 在那边是 79304020,在服务端 catalog 是 912387408)。 // 服务端下发的 pathId 必须取**能被服务端 catalog 查到名字**的那个,否则 // BuildGadgetEntityInfo 会 Warn「not in GadgetCatalog.json」并原样发出。 // ★ 官方世界坐标逐条解自关卡块(工具 gym_tools/_r87_minigame_gadget.py, // 结果 _r87_minigame_gadget.txt;记录结构 = 命中点 - 28 是记录头, // +0/+4/+8 = f32 x/y/z,+16 = yaw,+28 = pathId)。 // type MiniGameStallItem4229938 struct { PathId int32 `json:"pathId"` Name string `json:"name"` X float32 `json:"x"` Y float32 `json:"y"` Z float32 `json:"z"` Yaw float32 `json:"yaw"` UniqueId uint64 `json:"uniqueId"` } // 一个摊位片区:一个锚点、一个触发半径,和半径内的摊位机关。 type MiniGameStallSpot4229938 struct { Name string `json:"name"` Raid uint32 `json:"raid"` X float32 `json:"x"` Z float32 `json:"z"` Radius float32 `json:"radius"` Items []MiniGameStallItem4229938 `json:"items"` } // // Reads Configs/MiniGameStalls.json. Tiny file, parsed once behind a Lazy. // A missing or malformed file is not fatal: the catalog goes empty and // LoadError explains why, so the caller can log it instead of throwing. // var ( MiniGameStallCatalog4229938_cacheLock sync.RWMutex MiniGameStallCatalog4229938_cached = false MiniGameStallCatalog4229938_entries []any ) // LoadMiniGameStallCatalog4229938 loads catalog records from client config directory func LoadMiniGameStallCatalog4229938(configDir string) ([]any, error) { MiniGameStallCatalog4229938_cacheLock.RLock() if MiniGameStallCatalog4229938_cached { defer MiniGameStallCatalog4229938_cacheLock.RUnlock() return MiniGameStallCatalog4229938_entries, nil } MiniGameStallCatalog4229938_cacheLock.RUnlock() MiniGameStallCatalog4229938_cacheLock.Lock() defer MiniGameStallCatalog4229938_cacheLock.Unlock() if MiniGameStallCatalog4229938_cached { return MiniGameStallCatalog4229938_entries, nil } filePath := filepath.Join(configDir, "MiniGameStallCatalog.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) } } MiniGameStallCatalog4229938_entries = result MiniGameStallCatalog4229938_cached = true return result, nil } // Spots whose anchor is within <radius> metres of (x, z), nearest first. func MiniGameStallCatalog4229938_WithinRadius(x float32, z float32, radius float32) []MiniGameStallSpot4229938 { // Catalog helper MiniGameStallCatalog4229938_WithinRadius return nil } func MiniGameStallCatalog4229938_Dist2(spot MiniGameStallSpot4229938, x float32, z float32) float32 { // Catalog helper MiniGameStallCatalog4229938_Dist2 return 0 } // // u64 that the capture script emits as a JSON string (no double round-trip). System.Text.Json // throws InvalidOperationException ("requires an element of type 'Number'") if you call // TryGetUInt64 on a string element, so accept both shapes — one bad uid must not kill the file. // func MiniGameStallCatalog4229938_U64(source any, name string) uint64 { // Catalog helper MiniGameStallCatalog4229938_U64 return 0 } func MiniGameStallCatalog4229938_Str(source any, name string) string { // Catalog helper MiniGameStallCatalog4229938_Str return "" } func MiniGameStallCatalog4229938_Floats(source any, name string, take int32) []float32 { // Catalog helper MiniGameStallCatalog4229938_Floats return nil }