package catalogs import ( "encoding/json" "os" "path/filepath" "sync" ) var _ = json.RawMessage{} var _ = sync.Mutex{} var _ = os.ReadFile var _ = filepath.Join // One TaskRoleConfig row: a switchable "role" the character wheel can offer. type TaskRoleCatalogEntry4229938 struct { Id uint32 `json:"id"` Name string `json:"name"` IsDefault bool `json:"isDefault"` FightSpiritId uint32 `json:"fightSpiritId"` SpiritCaseId uint32 `json:"spiritCaseId"` } // // TaskRoleConfig projection — the id space the client's character wheel speaks in. // Background (see TOPIC_切换角色.md): pressing Z never sends a packet, so the rotation list is // built client-side. The only server-facing handle on it is SyncTaskRoleTeam // (64534724, void SyncTaskRoleTeam(uint[] roleTeam, uint[] enableRoleIds, uint tipRoleId, bool enableSwitch) // per dump.cs:1732654), which the retail server never called — so the client has always fallen // back to its own default set (the FightSpiritConfig rows with CanJoin=true). // Two id spaces get mixed here, and they are NOT the same thing: // • TaskRoleConfig.Id (102000 / 113000 / 106000 …) — what the wheel wants. // • NpcCultivationId (91050014 / 91050030 …) — what the player knows the character by. // Resolve() accepts either, so config can stay readable. // var ( TaskRoleCatalog4229938_cacheLock sync.RWMutex TaskRoleCatalog4229938_cached = false TaskRoleCatalog4229938_entries []any ) // LoadTaskRoleCatalog4229938 loads catalog records from client config directory func LoadTaskRoleCatalog4229938(configDir string) ([]any, error) { TaskRoleCatalog4229938_cacheLock.RLock() if TaskRoleCatalog4229938_cached { defer TaskRoleCatalog4229938_cacheLock.RUnlock() return TaskRoleCatalog4229938_entries, nil } TaskRoleCatalog4229938_cacheLock.RUnlock() TaskRoleCatalog4229938_cacheLock.Lock() defer TaskRoleCatalog4229938_cacheLock.Unlock() if TaskRoleCatalog4229938_cached { return TaskRoleCatalog4229938_entries, nil } filePath := filepath.Join(configDir, "TaskRoleCatalog.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) } } TaskRoleCatalog4229938_entries = result TaskRoleCatalog4229938_cached = true return result, nil } func TaskRoleCatalog4229938_TryGet(id uint32, entry TaskRoleCatalogEntry4229938) bool { // Catalog helper TaskRoleCatalog4229938_TryGet return false } // Every role entry bound to a FightSpiritConfig id (usually exactly one). func TaskRoleCatalog4229938_ByFightSpirit(fightSpiritId uint32) []TaskRoleCatalogEntry4229938 { // Catalog helper TaskRoleCatalog4229938_ByFightSpirit return nil } // NpcCultivationId -> FightSpiritId. func TaskRoleCatalog4229938_TryGetFightSpirit(cultivationId uint32, fightSpiritId uint32) bool { // Catalog helper TaskRoleCatalog4229938_TryGetFightSpirit return false } // // Turn raw configured numbers into a role-id list. 8-digit values (>= 10,000,000) are read as // NpcCultivationIds and translated through NpcCultivationCatalog; anything smaller is taken as // a TaskRoleConfig.Id verbatim. Unresolvable ids are dropped and reported so config typos do // not silently shrink the wheel. // func TaskRoleCatalog4229938_Resolve(raw []uint32) any { // Catalog helper TaskRoleCatalog4229938_Resolve var zero any; return zero } func TaskRoleCatalog4229938_LoadCultivation() map[uint32]uint32 { // Catalog helper TaskRoleCatalog4229938_LoadCultivation return nil } func TaskRoleCatalog4229938_U32(row any, name string) uint32 { // Catalog helper TaskRoleCatalog4229938_U32 return 0 }