This repository has been archived on 2026-10-31. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files

177 lines
6.6 KiB
Go
Raw Permalink Normal View History

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。
// <para>为什么需要它。舞会的官方锚点
// 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 两张实拍)。</para>
// <para>为什么不能靠 cell/sector 解决。
// 机关网格(SyncGadgetGridAOIDecrease)是按 40 m cell + SectorIdList 推的,
// 而 GadgetCells.json 里 根本没有舞池那两个 cell
// ((64,31) / (65,31))⇒ 回落 [0] = 室外 ⇒ 客户端把室内层的道具
// 当室外物造了出来。所以必须走「占地 + VisibleOutside」这条独立判据。</para>
// <para>占地怎么来的。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 个只差 &lt; 10 m
// ⇒ 它是粗占地(整数、可能外扩几米),不是碰撞体边界。够用来做可见性过滤。</para>
// <para>⚠ 已知局限(别把它当通用高度表)。
// <list type="bullet">
// <item>y 一律不参与判定。MapFloorList 的语义自相矛盾 ——
// Spiko Hotel 写 [-5,-4,-3,-2,-1,1],而三处官方来源(PartyConfig
// /PartyDanceSpotConfig/客户端烘焙表)都写 y ≈ 80。这个矛盾未定案
// ⇒ 本类只做 x/z 占地,不猜 y。</item>
// <item>770 条里只有 143 条有可用矩形,且这 143 条全部 VisibleOutside=false
// ⇒ 无法用「可见的室内」反证判据(本类不依赖这一点)。</item>
// <item>干净拆成三档 ParentRaid:23300888 ×111 / 23300999 ×20 / 23301277 ×12
// —— 最后那档里有 8 条 2000×2000 m 的占位矩形(*_raid1277),
// 所以查表时必须给 raidId,再加单边上限双保险。</item>
// </list></para>
// <para>护栏(只影响「命中率」,不影响「命中后的语义」):
// ① ParentRaid 必须等于调用方给的 raidId(0 视为通配);
// ② 矩形单边 &gt; MaxSideMeters 视为脏数据并跳过(本副本最大是 773 m 的 Tunnel)。
// 两条都会记进 SkippedCount,可用 Rooms 复查。</para>
//
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
}