package catalogs import ( "encoding/json" "os" "path/filepath" "sync" ) var _ = json.RawMessage{} var _ = sync.Mutex{} var _ = os.ReadFile var _ = filepath.Join // // One functional NPC placed at a fixed spot: a shop clerk, a ticket seller, a mahjong host... // Read from Configs/FunctionalNpcSpots.json — see _build_spots.py. // // // Where the data comes from (three hops, none of which lives in the NPC table itself): // 1. AgentDataSetsInteractSettingConfig says what the F key does for an AgentConfig id // (OpenNpcShop(94100024), BuyBoatTicket(), ShowDialog(...)); // 2. ShopConfig.Position / IndoorMapFunctionPointConfig.Coordinate / IndoorConfig // give the venue's world position — that is the anchor; // 3. the anchor XZ is snapped onto the nearest CivilianDistricts pavement spot so the NPC // stands on a walkable surface at a real ground height instead of on a roof or in mid-air. // The server's only job is to announce the entity. Everything the F key does afterwards is client // lua — the server neither validates nor pays for a purchase. // type FunctionalNpcSpot4229938 struct { NpcFormworkId uint32 `json:"npcFormworkId"` Name string `json:"name"` PoiActionId uint32 `json:"poiActionId"` X float32 `json:"x"` Y float32 `json:"y"` Z float32 `json:"z"` Facing float32 `json:"facing"` Function string `json:"function"` Raid uint32 `json:"raid"` Anchor string `json:"anchor"` Actions []string `json:"actions"` Labels []string `json:"labels"` Note string `json:"note"` } // // Reads Configs/FunctionalNpcSpots.json. Small file (36 spots), parsed once behind a Lazy. // A missing or malformed file is not fatal: the catalog goes empty and // LoadError explains why, so the panel can say so instead of throwing on load. // var ( FunctionalNpcSpotCatalog4229938_cacheLock sync.RWMutex FunctionalNpcSpotCatalog4229938_cached = false FunctionalNpcSpotCatalog4229938_entries []any ) // LoadFunctionalNpcSpotCatalog4229938 loads catalog records from client config directory func LoadFunctionalNpcSpotCatalog4229938(configDir string) ([]any, error) { FunctionalNpcSpotCatalog4229938_cacheLock.RLock() if FunctionalNpcSpotCatalog4229938_cached { defer FunctionalNpcSpotCatalog4229938_cacheLock.RUnlock() return FunctionalNpcSpotCatalog4229938_entries, nil } FunctionalNpcSpotCatalog4229938_cacheLock.RUnlock() FunctionalNpcSpotCatalog4229938_cacheLock.Lock() defer FunctionalNpcSpotCatalog4229938_cacheLock.Unlock() if FunctionalNpcSpotCatalog4229938_cached { return FunctionalNpcSpotCatalog4229938_entries, nil } filePath := filepath.Join(configDir, "FunctionalNpcSpotCatalog.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) } } FunctionalNpcSpotCatalog4229938_entries = result FunctionalNpcSpotCatalog4229938_cached = true return result, nil } // // 规则 199:这个模板能不能当"人"铺出去。 // ⛑ 判据是 DonorNpcCatalogEntry4229938.AgentPersonaId == 0 —— 物件和敌人都是 0,真人都非 0。 // 模型段位(8695xxxx…)不能当判据:869xxxxx 段里混着 621 个真人(BVB-阿姆斯 / Yun Feng / Tie Shan)。 // 案由:铺出来的「NPC」按 F 显示「厄反广播」,实际是 40120273「大喇叭音箱(完成后)」—— 一个音箱。 // ⚠️ fail-open:查不到 NpcCatalog 时放行,别因为这道保险把整张点位表清空。 // func FunctionalNpcSpotCatalog4229938_IsPlaceablePerson(npcFormworkId uint32) bool { // Catalog helper FunctionalNpcSpotCatalog4229938_IsPlaceablePerson return false } // Every spot belonging to <raidId>; an unknown raid falls back to none. func FunctionalNpcSpotCatalog4229938_ForRaid(raidId uint32, includeSemantic bool) []FunctionalNpcSpot4229938 { // Catalog helper FunctionalNpcSpotCatalog4229938_ForRaid return nil } // // Spots of <raidId> inside <radius> metres of (x, z), nearest first. radius <= 0 // means "every spot of that raid", which is what you want for a 36-item list anyway. // func FunctionalNpcSpotCatalog4229938_WithinRadius(raidId uint32, x float32, z float32, radius float32, includeSemantic bool) []FunctionalNpcSpot4229938 { // Catalog helper FunctionalNpcSpotCatalog4229938_WithinRadius return nil } func FunctionalNpcSpotCatalog4229938_Strings(source any, name string) []string { // Catalog helper FunctionalNpcSpotCatalog4229938_Strings return nil }