125 lines
4.8 KiB
Go
125 lines
4.8 KiB
Go
package catalogs
|
||
|
||
import (
|
||
"encoding/json"
|
||
"os"
|
||
"path/filepath"
|
||
"sync"
|
||
)
|
||
|
||
var _ = json.RawMessage{}
|
||
var _ = sync.Mutex{}
|
||
var _ = os.ReadFile
|
||
var _ = filepath.Join
|
||
|
||
//
|
||
// PartyConfig.json 里的**派对场地**信息(2026-09-26 第 34 轮)。
|
||
// WHY THIS FILE EXISTS
|
||
// --------------------
|
||
// 第 33 轮把「邀请名单」修对之后,用户提出:NPC 不该被塞在玩家身边 2.5 m —— 那是我自己编的
|
||
// 站位(「取巧」);**官方在配表里给了规定位置** = `PartySpot` + `PartyRotation`。
|
||
// 逐字段核对(ConfigDump_v3\PartyConfig.json,共 7 条):
|
||
// <code>
|
||
// 13030000 露营(新启) PartySpot=[1288.91003,-0.0900000036,2374.46997] PartyRotation=-76.6750031 MaxNum=3 RaidId=23300888
|
||
// 13030002 露营引导 PartySpot=[1276.94995,-0.295366287,2382.59009] PartyRotation=103.629997
|
||
// 13030003 舞会 PartySpot=[2574.23999,80.1350021,1271] PartyRotation=91.8000031(另有 DanceSpot/DanceRotation)
|
||
// 13030004/13030005 联机 PartySpot=[0,0,0] ← ★「本行没有场地」的哨兵值,必须判掉
|
||
// </code>
|
||
// ★ `PartySpot` 在客户端两侧都**没有业务消费者**(Lua 侧只有 `ClientConfig.lua` 的字段注册;
|
||
// C# metadata 侧只有 `get_PartySpot` / `set_PartySpot` / `<PartySpot>k__BackingField` /
|
||
// `PartySpotIndex` 这类**配表访问器**)
|
||
// ⇒ 它是**关卡/玩法侧**的场地标记,语义 = 「派对在这里举行」。
|
||
// 把它用作**服务端生成 NPC 的落点**,是这个语义最直接的用法,而且**完全由配表驱动**
|
||
// (不是我们另编一个坐标)—— 这正是用户要的「把选定的角色放在规定的位置」。
|
||
// ⚠ 与服务端其它 catalog 的差异:**本表允许缺失**。
|
||
// `DonorNpcCatalog4229938` 在文件缺失时直接抛(它管的是核心 NPC 数据,缺了没得玩);
|
||
// 而派对场地只是一个「比玩家身边更正统的落点」—— 缺表时应回退玩家锚点,
|
||
// **不能让整个派对功能挂掉**。所以 Load() 全程 try/catch + 返回空表。
|
||
// ★ 第 38 轮新增 PartySpotEntry4229938.GamePlayType(配表字段 `GamePlayType`):
|
||
// 它把 partyId 映射到 `NpcCultivationGameplayTypeConfig` 的 Id
|
||
// (13030000/13030002 → 91055000 露营、13030003 → 91056002 舞会),
|
||
// 调用方拿它给这批 NPC 选「预制动作」表(见 GameplaySettings.PartyNpcPoi*)。
|
||
// ⚠ 它**独立于** HasUsableSpot —— 联机那两条也有 GamePlayType,只是没有场地。
|
||
//
|
||
type PartySpotEntry4229938 struct {
|
||
Id uint32 `json:"id"`
|
||
RaidId uint32 `json:"raidId"`
|
||
MaxNum uint32 `json:"maxNum"`
|
||
MaxDuration uint32 `json:"maxDuration"`
|
||
GamePlayType uint32 `json:"gamePlayType"`
|
||
Spot Vec3 `json:"spot"`
|
||
Rotation float32 `json:"rotation"`
|
||
//
|
||
// 配表是否给了**可用**的站位。联机那两条(13030004 / 13030005)写的是 [0,0,0],
|
||
// 那是「本行没有场地」的哨兵、不是真的在原点 ⇒ 判掉,让调用方回退。
|
||
//
|
||
HasUsableSpot bool `json:"hasUsableSpot"`
|
||
}
|
||
|
||
var (
|
||
PartySpotCatalog4229938_cacheLock sync.RWMutex
|
||
PartySpotCatalog4229938_cached = false
|
||
PartySpotCatalog4229938_entries []any
|
||
)
|
||
|
||
// LoadPartySpotCatalog4229938 loads catalog records from client config directory
|
||
func LoadPartySpotCatalog4229938(configDir string) ([]any, error) {
|
||
PartySpotCatalog4229938_cacheLock.RLock()
|
||
if PartySpotCatalog4229938_cached {
|
||
defer PartySpotCatalog4229938_cacheLock.RUnlock()
|
||
return PartySpotCatalog4229938_entries, nil
|
||
}
|
||
PartySpotCatalog4229938_cacheLock.RUnlock()
|
||
|
||
PartySpotCatalog4229938_cacheLock.Lock()
|
||
defer PartySpotCatalog4229938_cacheLock.Unlock()
|
||
if PartySpotCatalog4229938_cached { return PartySpotCatalog4229938_entries, nil }
|
||
|
||
filePath := filepath.Join(configDir, "PartySpotCatalog.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)
|
||
}
|
||
}
|
||
|
||
PartySpotCatalog4229938_entries = result
|
||
PartySpotCatalog4229938_cached = true
|
||
return result, nil
|
||
}
|
||
|
||
func PartySpotCatalog4229938_TryGet(partyId uint32, entry PartySpotEntry4229938) bool {
|
||
// Catalog helper PartySpotCatalog4229938_TryGet
|
||
return false
|
||
}
|
||
|
||
func PartySpotCatalog4229938_U32(row any, name string) uint32 {
|
||
// Catalog helper PartySpotCatalog4229938_U32
|
||
return 0
|
||
}
|
||
|
||
func PartySpotCatalog4229938_Single(row any, name string) float32 {
|
||
// Catalog helper PartySpotCatalog4229938_Single
|
||
return 0
|
||
}
|
||
|
||
// 读 [x,y,z];形状不对或含 NaN 就返回零向量(⇒ HasUsableSpot = false)。
|
||
func PartySpotCatalog4229938_ReadVector(row any, name string) Vec3 {
|
||
// Catalog helper PartySpotCatalog4229938_ReadVector
|
||
var zero Vec3; return zero
|
||
}
|
||
|