package catalogs import ( "encoding/json" "os" "path/filepath" "sync" ) var _ = json.RawMessage{} var _ = sync.Mutex{} var _ = os.ReadFile var _ = filepath.Join // // One achievement row, projected from AchievementConfig.json by _gen_ach_cfg.py. // FirstCategoryType / SecCategoryType mirror the table's // FirstCategoryType / SecCategoryType columns, which is how NewAchievementMgr buckets the ids // into tabs during its ctor (it reads the client's own AchievementConfig, so we only need the ids // and their completion state to line up). // type AchievementEntry4229938 struct { Id uint32 `json:"id"` Quality uint32 `json:"quality"` Hide bool `json:"hide"` FirstCategoryType uint32 `json:"firstCategoryType"` SecCategoryType uint32 `json:"secCategoryType"` MaxProgressBarNum uint32 `json:"maxProgressBarNum"` } // // Build-4229938 achievement catalog. The source table is AchievementConfig.json, which does not // ship in the v0.3 Configs folder; it comes from 补丁和工具\ConfigDump.zip. Regenerate with // _gen_ach_cfg.py. // Achievements travel in the login payload, and the client does read them: // lua/LX6/Data/Player/PlayerData/PlayerInfoAchievementData.lua:21 // t.AchievementInfos = info.InfoAchievement.AchievementInfos // lua/LX6/Manager/GameSystem/NewAchievementMgr.lua:45,99,115 // for k, v in pairs(gPlayerManager.infoAchievement.bindData.AchievementInfos) do // self:OnAchievementFinished(k, v) // The panel opens only when SystemUnlockConfig id 119 ("成就") is unlocked; the login payload is // already granted every authored SystemUnlockConfig row, so nothing extra is needed here. // const ( // UX.Game.AchievementStatus.None (dump TypeDefIndex 33405). AchievementCatalog4229938_StatusNone byte = 0 // UX.Game.AchievementStatus.Completed. AchievementCatalog4229938_StatusCompleted byte = 1 // UX.Game.AchievementStatus.Rewarded. AchievementCatalog4229938_StatusRewarded byte = 2 // // Completed | Rewarded — the state a fully settled achievement ends up in. The Lua enum names // are obfuscated in the decompile, so only the dump values are trustworthy. // AchievementCatalog4229938_StatusSettled byte = AchievementCatalog4229938_StatusCompleted | AchievementCatalog4229938_StatusRewarded ) var ( AchievementCatalog4229938_cacheLock sync.RWMutex AchievementCatalog4229938_cached = false AchievementCatalog4229938_entries []any ) // LoadAchievementCatalog4229938 loads catalog records from client config directory func LoadAchievementCatalog4229938(configDir string) ([]any, error) { AchievementCatalog4229938_cacheLock.RLock() if AchievementCatalog4229938_cached { defer AchievementCatalog4229938_cacheLock.RUnlock() return AchievementCatalog4229938_entries, nil } AchievementCatalog4229938_cacheLock.RUnlock() AchievementCatalog4229938_cacheLock.Lock() defer AchievementCatalog4229938_cacheLock.Unlock() if AchievementCatalog4229938_cached { return AchievementCatalog4229938_entries, nil } filePath := filepath.Join(configDir, "AchievementCatalog.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) } } AchievementCatalog4229938_entries = result AchievementCatalog4229938_cached = true return result, nil } func AchievementCatalog4229938_UInt(row any, name string) uint32 { // Catalog helper AchievementCatalog4229938_UInt return 0 }