package catalogs import ( "encoding/json" "os" "path/filepath" "sync" ) var _ = json.RawMessage{} var _ = sync.Mutex{} var _ = os.ReadFile var _ = filepath.Join // // One urban badge row, projected from UrbanBadgeConfig.json by _gen_ach_cfg.py. // Rows flagged OnlyServer are already filtered out at projection time: the client's // SpiritManager.FilterBadge drops them, so shipping them in the login payload would be pointless // payload weight. 148 of the authored 207 rows survive that filter. // type BadgeEntry4229938 struct { Id uint32 `json:"id"` Type uint32 `json:"type"` JobId uint32 `json:"jobId"` Name string `json:"name"` Buffs []uint32 `json:"buffs"` } // // Build-4229938 badge catalog. The source table is UrbanBadgeConfig.json — note it is NOT called // "BadgeConfig"; the login payload carries BadgeInfo rows whose TemplateId indexes this table. // It does not ship in the v0.3 Configs folder either; it comes from 补丁和工具\ConfigDump.zip. // Regenerate with _gen_ach_cfg.py. // The client reads badges straight out of the login payload: // lua/LX6/Data/Player/PlayerData/PlayerInfoMinorData.lua:94 // t.Badges = gSpiritManager:FilterBadge(info.InfoMinor.Badges) // lua/LX6/GUI/Spirit/SpiritManager.lua:200 // if cfg and not cfg.OnlyServer then filterBadge[v.TemplateId] = v end // Two consequences for how the payload must be built: // 1. the dictionary key is expected to be the UrbanBadgeConfig id (FilterBadge re-keys by // TemplateId, and consumers look up badges[cfg.Id]); // 2. every consumer additionally tests BadgeInfo.Active — UrbanAbilityBadgePanelStore:325, // UrbanAbilityBadgeV2PanelStore:228, TalentTreeMgr:1528, AkxFuxiExtraArgFuncs:28. // The badge UI is gated on SystemUnlockConfig id 148 ("徽章"), which the login payload already // unlocks along with every other authored system. // var ( BadgeCatalog4229938_cacheLock sync.RWMutex BadgeCatalog4229938_cached = false BadgeCatalog4229938_entries []any ) // LoadBadgeCatalog4229938 loads catalog records from client config directory func LoadBadgeCatalog4229938(configDir string) ([]any, error) { BadgeCatalog4229938_cacheLock.RLock() if BadgeCatalog4229938_cached { defer BadgeCatalog4229938_cacheLock.RUnlock() return BadgeCatalog4229938_entries, nil } BadgeCatalog4229938_cacheLock.RUnlock() BadgeCatalog4229938_cacheLock.Lock() defer BadgeCatalog4229938_cacheLock.Unlock() if BadgeCatalog4229938_cached { return BadgeCatalog4229938_entries, nil } filePath := filepath.Join(configDir, "BadgeCatalog.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) } } BadgeCatalog4229938_entries = result BadgeCatalog4229938_cached = true return result, nil } // // Capability ids granted by the badges that belong to any of , i.e. the // data-driven answer to "what does having this career actually let me do". UrbanBadgeConfig // is the only table that links a job to a buff list; rows flagged OnlyServer were dropped // at projection time, which keeps the two 低声望 negative debuffs (52606230/52606231) out. // Only jobs 7 / 401 / 501 carry buffs today — 301/302/303 (police) are all OnlyServer or empty. // func BadgeCatalog4229938_BuffsForJobs(jobIds []uint32) []uint32 { // Catalog helper BadgeCatalog4229938_BuffsForJobs return nil } func BadgeCatalog4229938_UInt(row any, name string) uint32 { // Catalog helper BadgeCatalog4229938_UInt return 0 } func BadgeCatalog4229938_ReadUInt32Array(row any, name string) []uint32 { // Catalog helper BadgeCatalog4229938_ReadUInt32Array return nil }