138 lines
5.0 KiB
Go
138 lines
5.0 KiB
Go
package catalogs
|
||||
|
|
|
|||
|
|
import (
|
|||
|
|
"encoding/json"
|
|||
|
|
"os"
|
|||
|
|
"path/filepath"
|
|||
|
|
"sync"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
var _ = json.RawMessage{}
|
|||
|
|
var _ = sync.Mutex{}
|
|||
|
|
var _ = os.ReadFile
|
|||
|
|
var _ = filepath.Join
|
|||
|
|
|
|||
|
|
// One party-camp prop the server places itself (grill / tent / bonfire / mat / lamp…).
|
|||
|
|
type PartyCampItem4229938 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"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// One party venue's camp: the official PartySpot anchor, a trigger radius, its props.
|
|||
|
|
type PartyCampSite4229938 struct {
|
|||
|
|
Name string `json:"name"`
|
|||
|
|
PartyId uint32 `json:"partyId"`
|
|||
|
|
Raid uint32 `json:"raid"`
|
|||
|
|
X float32 `json:"x"`
|
|||
|
|
Y float32 `json:"y"`
|
|||
|
|
Z float32 `json:"z"`
|
|||
|
|
Rotation float32 `json:"rotation"`
|
|||
|
|
Radius float32 `json:"radius"`
|
|||
|
|
Items []PartyCampItem4229938 `json:"items"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//
|
|||
|
|
// Reads Configs/PartyCamp.json — the official campground furniture (露营家具) for each party venue.
|
|||
|
|
//
|
|||
|
|
//
|
|||
|
|
// WHY THIS FILE EXISTS (2026-09-26, round 44)
|
|||
|
|
// -------------------------------------------
|
|||
|
|
// 用户在游戏里站在露营点 2.8 m 处实测「家具全无」——烤炉、帐篷、篝火、野餐垫一件都没有。
|
|||
|
|
// 他问的那句「是不是和健身器材一样」是**对的**:这批 `Gadgets_Party_*` 和 `wodegym_*` 同类,
|
|||
|
|
// 客户端不会自己把 blob 里的它们实例化出来(grid-AOI 下发 68956797 不驱动
|
|||
|
|
// `GadgetBlob.OnAoiAddBlobCfgByGridIndex`),必须服务端用 SyncGadgetAdd 显式摆。
|
|||
|
|
// ★ 关键判据(回答「是不是开派对才刷」):**不是**。
|
|||
|
|
// 1. 这套家具烘焙在**主城 free-roam 副本 23300888** 的关卡数据里(记录前挂着 raid 字符串
|
|||
|
|
// `23300888` 与实例键 `23300888_60000023_60000023`),不是派对专用副本;
|
|||
|
|
// 2. 露营/舞会的 `PartyConfig.RaidId` 就是 23300888 —— **派对是就地举行,不切副本**,
|
|||
|
|
// 所以场地家具只能是预先烘焙在开放世界里的;
|
|||
|
|
// 3. 营地 400 m 圈内是改车店 / 公共厕所 / 时装店 / 象棋摊 / wodegym 等**主城常驻机关**
|
|||
|
|
// ⇒ 营地就是主城里的一块地,不是单独场景;
|
|||
|
|
// 4. 同一批 prefab(`Gadgets_Party_BBQGrill`)还被当作**天台夜市**的常驻装点摆在
|
|||
|
|
// (-378.28, 130.50, -497.87),那一处与任何派对点位无关。
|
|||
|
|
// ⇒ 所以服务端应当**按场地坐标常驻摆放**,而不是「开派对时才摆」。
|
|||
|
|
// 坐标来源:逐条解自关卡块 `Blocks/vfc_1479` 的 88 B 机关摆位表(pathId 在记录头 +28,
|
|||
|
|
// 记录头 = pathId 命中点 - 28)。同一套家具在块里被 3~4 张表各列一遍(顺序与个别 y 略有差异),
|
|||
|
|
// PartyCamp.json 只取**带 raid 实例键的那一张**。生成脚本 `gym_tools/_camp_list.py`。
|
|||
|
|
// 缺失/损坏不致命:表空 + LoadError 说明原因,调用方跳过即可。
|
|||
|
|
//
|
|||
|
|
var (
|
|||
|
|
PartyCampCatalog4229938_cacheLock sync.RWMutex
|
|||
|
|
PartyCampCatalog4229938_cached = false
|
|||
|
|
PartyCampCatalog4229938_entries []any
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// LoadPartyCampCatalog4229938 loads catalog records from client config directory
|
|||
|
|
func LoadPartyCampCatalog4229938(configDir string) ([]any, error) {
|
|||
|
|
PartyCampCatalog4229938_cacheLock.RLock()
|
|||
|
|
if PartyCampCatalog4229938_cached {
|
|||
|
|
defer PartyCampCatalog4229938_cacheLock.RUnlock()
|
|||
|
|
return PartyCampCatalog4229938_entries, nil
|
|||
|
|
}
|
|||
|
|
PartyCampCatalog4229938_cacheLock.RUnlock()
|
|||
|
|
|
|||
|
|
PartyCampCatalog4229938_cacheLock.Lock()
|
|||
|
|
defer PartyCampCatalog4229938_cacheLock.Unlock()
|
|||
|
|
if PartyCampCatalog4229938_cached { return PartyCampCatalog4229938_entries, nil }
|
|||
|
|
|
|||
|
|
filePath := filepath.Join(configDir, "PartyCampCatalog.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)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
PartyCampCatalog4229938_entries = result
|
|||
|
|
PartyCampCatalog4229938_cached = true
|
|||
|
|
return result, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Camp sites whose anchor is within metres of (x, z).
|
|||
|
|
func PartyCampCatalog4229938_WithinRadius(x float32, z float32, radius float32) []PartyCampSite4229938 {
|
|||
|
|
// Catalog helper PartyCampCatalog4229938_WithinRadius
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func PartyCampCatalog4229938_Dist2(site PartyCampSite4229938, x float32, z float32) float32 {
|
|||
|
|
// Catalog helper PartyCampCatalog4229938_Dist2
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//
|
|||
|
|
// u64 written as a JSON string (no double round-trip). TryGetUInt64 throws on a String element,
|
|||
|
|
// so accept both shapes — one bad uid must not kill the file.
|
|||
|
|
//
|
|||
|
|
func PartyCampCatalog4229938_U64(source any, name string) uint64 {
|
|||
|
|
// Catalog helper PartyCampCatalog4229938_U64
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func PartyCampCatalog4229938_Str(source any, name string) string {
|
|||
|
|
// Catalog helper PartyCampCatalog4229938_Str
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func PartyCampCatalog4229938_Floats(source any, name string, take int32) []float32 {
|
|||
|
|
// Catalog helper PartyCampCatalog4229938_Floats
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|