177 lines
6.6 KiB
Go
177 lines
6.6 KiB
Go
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 个只差 < 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 视为通配);
|
|||
|
|
// ② 矩形单边 > 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
|
|||
|
|
}
|
|||
|
|
|