package catalogs import ( "encoding/json" "os" "path/filepath" "sync" ) var _ = json.RawMessage{} var _ = sync.Mutex{} var _ = os.ReadFile var _ = filepath.Join // // 一条室内房间(IndoorConfig)。占地取自 MiniMapPos[0..3]。 // // IndoorConfig.Id。 // 英文名(如 Spiko Hotel)。 // 中文/策划名(如 亚兰游戏分红)。 // 所属副本 —— 用来把「别的副本的室内」排除掉(见 §护栏)。 // ★ 官方字段:这间室内能不能从外面看见。 // MapFloorList(楼层列表;不是 y 范围,见类注释的警告)。 // Coordinate(多数条目为空数组)。 type IndoorRoomEntry4229938 struct { Id int32 `json:"id"` Name string `json:"name"` NameTag string `json:"nameTag"` ParentRaid uint32 `json:"parentRaid"` SceneId uint32 `json:"sceneId"` VisibleOutside bool `json:"visibleOutside"` MinX float32 `json:"minX"` MinZ float32 `json:"minZ"` MaxX float32 `json:"maxX"` MaxZ float32 `json:"maxZ"` Floors []int32 `json:"floors"` Coordinate []float32 `json:"coordinate"` Width float32 `json:"width"` Depth float32 `json:"depth"` Area float32 `json:"area"` } // // ★★★ 第 50 轮(2026-09-26):室内层判定。读 Configs/IndoorConfig.json。 // 为什么需要它。舞会的官方锚点 // PartySpot(2574.24, 80.135, 1271) 与舞池点 DanceSpot(2605.33, 80, 1269.74) // 横向都落在 IndoorConfig#150 "Spiko Hotel"(NameTag="亚兰游戏分红")的占地里, // 而那间室内的官方字段 VisibleOutside = false —— 从外面看不见。 // 我们却把 10 个 NPC 当露天物摆在 y ≈ 80,于是现场就是「角色全悬在空中」 // (用户 04:56 / 04:58 两张实拍)。 // 为什么不能靠 cell/sector 解决。 // 机关网格(SyncGadgetGridAOIDecrease)是按 40 m cell + SectorIdList 推的, // 而 GadgetCells.json 里 根本没有舞池那两个 cell // ((64,31) / (65,31))⇒ 回落 [0] = 室外 ⇒ 客户端把室内层的道具 // 当室外物造了出来。所以必须走「占地 + VisibleOutside」这条独立判据。 // 占地怎么来的。MiniMapPos 是小地图显示矩形 // [minX, minZ, maxX, maxZ, …4 个精灵像素值]。 // 实测校准(2026-09-26):Spiko Hotel 的 x[2515,2610] z[1162,1285] // 同时含 PartySpot 与 DanceSpot;另外 5 个样本(1UP / Sonic Boom / 保龄球馆 / Dreamy Bay / // 绿岛宴会厅)的 Coordinate 有 4 个精确落在矩形内、2 个只差 < 10 m // ⇒ 它是粗占地(整数、可能外扩几米),不是碰撞体边界。够用来做可见性过滤。 // ⚠ 已知局限(别把它当通用高度表)。 // // y 一律不参与判定。MapFloorList 的语义自相矛盾 —— // Spiko Hotel 写 [-5,-4,-3,-2,-1,1],而三处官方来源(PartyConfig // /PartyDanceSpotConfig/客户端烘焙表)都写 y ≈ 80。这个矛盾未定案 // ⇒ 本类只做 x/z 占地,不猜 y。 // 770 条里只有 143 条有可用矩形,且这 143 条全部 VisibleOutside=false // ⇒ 无法用「可见的室内」反证判据(本类不依赖这一点)。 // 干净拆成三档 ParentRaid:23300888 ×111 / 23300999 ×20 / 23301277 ×12 // —— 最后那档里有 8 条 2000×2000 m 的占位矩形(*_raid1277), // 所以查表时必须给 raidId,再加单边上限双保险。 // // 护栏(只影响「命中率」,不影响「命中后的语义」): // ① ParentRaid 必须等于调用方给的 raidId(0 视为通配); // ② 矩形单边 > MaxSideMeters 视为脏数据并跳过(本副本最大是 773 m 的 Tunnel)。 // 两条都会记进 SkippedCount,可用 Rooms 复查。 // const ( // 单边上限:超过就当地图占位脏数据丢掉(官方表里有 2000×2000 的)。 IndoorRoomCatalog4229938_MaxSideMeters float32 = 1000 ) var ( IndoorRoomCatalog4229938_cacheLock sync.RWMutex IndoorRoomCatalog4229938_cached = false IndoorRoomCatalog4229938_entries []any ) // LoadIndoorRoomCatalog4229938 loads catalog records from client config directory func LoadIndoorRoomCatalog4229938(configDir string) ([]any, error) { IndoorRoomCatalog4229938_cacheLock.RLock() if IndoorRoomCatalog4229938_cached { defer IndoorRoomCatalog4229938_cacheLock.RUnlock() return IndoorRoomCatalog4229938_entries, nil } IndoorRoomCatalog4229938_cacheLock.RUnlock() IndoorRoomCatalog4229938_cacheLock.Lock() defer IndoorRoomCatalog4229938_cacheLock.Unlock() if IndoorRoomCatalog4229938_cached { return IndoorRoomCatalog4229938_entries, nil } filePath := filepath.Join(configDir, "IndoorRoomCatalog.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) } } IndoorRoomCatalog4229938_entries = result IndoorRoomCatalog4229938_cached = true return result, nil } // // 找包含 , 的房间;同一处常有多条记录叠着 ⇒ // 取面积最小的那条(最具体)。 // func IndoorRoomCatalog4229938_TryFindAt(x float32, z float32, raidId uint32, room IndoorRoomEntry4229938) bool { // Catalog helper IndoorRoomCatalog4229938_TryFindAt return false } // // 找包含 , 的、从外面看不见的房间 // (VisibleOutside == false)。命中 = 这个点按露天刷是不对的。 // func IndoorRoomCatalog4229938_TryFindHiddenAt(x float32, z float32, raidId uint32, room IndoorRoomEntry4229938) bool { // Catalog helper IndoorRoomCatalog4229938_TryFindHiddenAt return false } func IndoorRoomCatalog4229938_Str(row any, name string) string { // Catalog helper IndoorRoomCatalog4229938_Str return "" } func IndoorRoomCatalog4229938_U32(row any, name string) uint32 { // Catalog helper IndoorRoomCatalog4229938_U32 return 0 } func IndoorRoomCatalog4229938_Bool(row any, name string) bool { // Catalog helper IndoorRoomCatalog4229938_Bool return false } // 读一个数值:JSON 里同一字段既有整数(2515)也有小数(80.135),两种都要吃。 func IndoorRoomCatalog4229938_Num(el any) *float32 { // Catalog helper IndoorRoomCatalog4229938_Num return nil } func IndoorRoomCatalog4229938_ReadIntArray(row any, name string) []int32 { // Catalog helper IndoorRoomCatalog4229938_ReadIntArray return nil } func IndoorRoomCatalog4229938_ReadFloatArray(row any, name string) []float32 { // Catalog helper IndoorRoomCatalog4229938_ReadFloatArray return nil }