package titles import ( "fmt" "sync" ) // MasterTitlesList manages all available titles in the game type MasterTitlesList struct { titles map[int32]*Title // All titles indexed by ID categorized map[string][]*Title // Titles grouped by category bySource map[int32][]*Title // Titles grouped by source byRarity map[int32][]*Title // Titles grouped by rarity byAchievement map[uint32]*Title // Titles indexed by achievement ID nextID int32 // Next available title ID mutex sync.RWMutex // Thread safety } // NewMasterTitlesList creates a new master titles list func NewMasterTitlesList() *MasterTitlesList { mtl := &MasterTitlesList{ titles: make(map[int32]*Title), categorized: make(map[string][]*Title), bySource: make(map[int32][]*Title), byRarity: make(map[int32][]*Title), byAchievement: make(map[uint32]*Title), nextID: 1, } // Initialize default titles mtl.initializeDefaultTitles() return mtl } // initializeDefaultTitles creates the basic system titles func (mtl *MasterTitlesList) initializeDefaultTitles() { // System titles with negative IDs citizen := NewTitle(TitleIDCitizen, "Citizen") citizen.SetDescription("Default citizen title") citizen.SetFlag(FlagStarter) citizen.Position = TitlePositionSuffix mtl.addTitleInternal(citizen) visitor := NewTitle(TitleIDVisitor, "Visitor") visitor.SetDescription("Temporary visitor status") visitor.SetFlag(FlagTemporary) visitor.Position = TitlePositionSuffix mtl.addTitleInternal(visitor) newcomer := NewTitle(TitleIDNewcomer, "Newcomer") newcomer.SetDescription("New player welcome title") newcomer.SetFlag(FlagStarter) newcomer.ExpirationHours = 168 // 1 week newcomer.Position = TitlePositionPrefix mtl.addTitleInternal(newcomer) returning := NewTitle(TitleIDReturning, "Returning") returning.SetDescription("Welcome back title for returning players") returning.SetFlag(FlagTemporary) returning.ExpirationHours = 72 // 3 days returning.Position = TitlePositionPrefix mtl.addTitleInternal(returning) } // AddTitle adds a new title to the master list func (mtl *MasterTitlesList) AddTitle(title *Title) error { mtl.mutex.Lock() defer mtl.mutex.Unlock() if title == nil { return fmt.Errorf("cannot add nil title") } // Assign ID if not set if title.ID == 0 { title.ID = mtl.nextID mtl.nextID++ } // Check for duplicate ID if _, exists := mtl.titles[title.ID]; exists { return fmt.Errorf("title with ID %d already exists", title.ID) } // Validate title name length if len(title.Name) > MaxTitleNameLength { return fmt.Errorf("title name exceeds maximum length of %d characters", MaxTitleNameLength) } // Validate description length if len(title.Description) > MaxTitleDescriptionLength { return fmt.Errorf("title description exceeds maximum length of %d characters", MaxTitleDescriptionLength) } // Check for unique titles if title.IsUnique() { // TODO: Check if any player already has this unique title } return mtl.addTitleInternal(title) } // addTitleInternal adds a title without validation (used internally) func (mtl *MasterTitlesList) addTitleInternal(title *Title) error { // Add to main map mtl.titles[title.ID] = title // Add to category index if mtl.categorized[title.Category] == nil { mtl.categorized[title.Category] = make([]*Title, 0) } mtl.categorized[title.Category] = append(mtl.categorized[title.Category], title) // Add to source index if mtl.bySource[title.Source] == nil { mtl.bySource[title.Source] = make([]*Title, 0) } mtl.bySource[title.Source] = append(mtl.bySource[title.Source], title) // Add to rarity index if mtl.byRarity[title.Rarity] == nil { mtl.byRarity[title.Rarity] = make([]*Title, 0) } mtl.byRarity[title.Rarity] = append(mtl.byRarity[title.Rarity], title) // Add to achievement index if applicable if title.AchievementID > 0 { mtl.byAchievement[title.AchievementID] = title } // Update next ID if necessary if title.ID >= mtl.nextID { mtl.nextID = title.ID + 1 } return nil } // GetTitle retrieves a title by ID func (mtl *MasterTitlesList) GetTitle(id int32) (*Title, bool) { mtl.mutex.RLock() defer mtl.mutex.RUnlock() title, exists := mtl.titles[id] if !exists { return nil, false } return title.Clone(), true } // GetTitleByName retrieves a title by name (case-sensitive) func (mtl *MasterTitlesList) GetTitleByName(name string) (*Title, bool) { mtl.mutex.RLock() defer mtl.mutex.RUnlock() for _, title := range mtl.titles { if title.Name == name { return title.Clone(), true } } return nil, false } // GetTitleByAchievement retrieves a title associated with an achievement func (mtl *MasterTitlesList) GetTitleByAchievement(achievementID uint32) (*Title, bool) { mtl.mutex.RLock() defer mtl.mutex.RUnlock() title, exists := mtl.byAchievement[achievementID] if !exists { return nil, false } return title.Clone(), true } // GetTitlesByCategory retrieves all titles in a specific category func (mtl *MasterTitlesList) GetTitlesByCategory(category string) []*Title { mtl.mutex.RLock() defer mtl.mutex.RUnlock() titles := mtl.categorized[category] if titles == nil { return make([]*Title, 0) } // Return clones to prevent external modification result := make([]*Title, len(titles)) for i, title := range titles { result[i] = title.Clone() } return result } // GetTitlesBySource retrieves all titles from a specific source func (mtl *MasterTitlesList) GetTitlesBySource(source int32) []*Title { mtl.mutex.RLock() defer mtl.mutex.RUnlock() titles := mtl.bySource[source] if titles == nil { return make([]*Title, 0) } // Return clones to prevent external modification result := make([]*Title, len(titles)) for i, title := range titles { result[i] = title.Clone() } return result } // GetTitlesByRarity retrieves all titles of a specific rarity func (mtl *MasterTitlesList) GetTitlesByRarity(rarity int32) []*Title { mtl.mutex.RLock() defer mtl.mutex.RUnlock() titles := mtl.byRarity[rarity] if titles == nil { return make([]*Title, 0) } // Return clones to prevent external modification result := make([]*Title, len(titles)) for i, title := range titles { result[i] = title.Clone() } return result } // GetAllTitles retrieves all titles (excluding hidden ones by default) func (mtl *MasterTitlesList) GetAllTitles(includeHidden bool) []*Title { mtl.mutex.RLock() defer mtl.mutex.RUnlock() result := make([]*Title, 0, len(mtl.titles)) for _, title := range mtl.titles { if !includeHidden && title.IsHidden() { continue } result = append(result, title.Clone()) } return result } // GetAvailableCategories returns all categories that have titles func (mtl *MasterTitlesList) GetAvailableCategories() []string { mtl.mutex.RLock() defer mtl.mutex.RUnlock() categories := make([]string, 0, len(mtl.categorized)) for category := range mtl.categorized { categories = append(categories, category) } return categories } // RemoveTitle removes a title from the master list func (mtl *MasterTitlesList) RemoveTitle(id int32) error { mtl.mutex.Lock() defer mtl.mutex.Unlock() title, exists := mtl.titles[id] if !exists { return fmt.Errorf("title with ID %d does not exist", id) } // Remove from main map delete(mtl.titles, id) // Remove from category index mtl.removeFromSlice(&mtl.categorized[title.Category], title) if len(mtl.categorized[title.Category]) == 0 { delete(mtl.categorized, title.Category) } // Remove from source index mtl.removeFromSlice(&mtl.bySource[title.Source], title) if len(mtl.bySource[title.Source]) == 0 { delete(mtl.bySource, title.Source) } // Remove from rarity index mtl.removeFromSlice(&mtl.byRarity[title.Rarity], title) if len(mtl.byRarity[title.Rarity]) == 0 { delete(mtl.byRarity, title.Rarity) } // Remove from achievement index if applicable if title.AchievementID > 0 { delete(mtl.byAchievement, title.AchievementID) } return nil } // removeFromSlice removes a title from a slice func (mtl *MasterTitlesList) removeFromSlice(slice *[]*Title, title *Title) { for i, t := range *slice { if t.ID == title.ID { *slice = append((*slice)[:i], (*slice)[i+1:]...) break } } } // UpdateTitle updates an existing title func (mtl *MasterTitlesList) UpdateTitle(title *Title) error { mtl.mutex.Lock() defer mtl.mutex.Unlock() if title == nil { return fmt.Errorf("cannot update with nil title") } existing, exists := mtl.titles[title.ID] if !exists { return fmt.Errorf("title with ID %d does not exist", title.ID) } // Remove old title from indices mtl.removeFromSlice(&mtl.categorized[existing.Category], existing) mtl.removeFromSlice(&mtl.bySource[existing.Source], existing) mtl.removeFromSlice(&mtl.byRarity[existing.Rarity], existing) if existing.AchievementID > 0 { delete(mtl.byAchievement, existing.AchievementID) } // Update the title mtl.titles[title.ID] = title // Re-add to indices with new values if mtl.categorized[title.Category] == nil { mtl.categorized[title.Category] = make([]*Title, 0) } mtl.categorized[title.Category] = append(mtl.categorized[title.Category], title) if mtl.bySource[title.Source] == nil { mtl.bySource[title.Source] = make([]*Title, 0) } mtl.bySource[title.Source] = append(mtl.bySource[title.Source], title) if mtl.byRarity[title.Rarity] == nil { mtl.byRarity[title.Rarity] = make([]*Title, 0) } mtl.byRarity[title.Rarity] = append(mtl.byRarity[title.Rarity], title) if title.AchievementID > 0 { mtl.byAchievement[title.AchievementID] = title } return nil } // GetTitleCount returns the total number of titles func (mtl *MasterTitlesList) GetTitleCount() int { mtl.mutex.RLock() defer mtl.mutex.RUnlock() return len(mtl.titles) } // ValidateTitle checks if a title meets all requirements func (mtl *MasterTitlesList) ValidateTitle(title *Title) error { if title == nil { return fmt.Errorf("title cannot be nil") } if len(title.Name) == 0 { return fmt.Errorf("title name cannot be empty") } if len(title.Name) > MaxTitleNameLength { return fmt.Errorf("title name exceeds maximum length of %d characters", MaxTitleNameLength) } if len(title.Description) > MaxTitleDescriptionLength { return fmt.Errorf("title description exceeds maximum length of %d characters", MaxTitleDescriptionLength) } if len(title.Requirements) > MaxTitleRequirements { return fmt.Errorf("title has too many requirements (max %d)", MaxTitleRequirements) } // Validate position if title.Position != TitlePositionPrefix && title.Position != TitlePositionSuffix { return fmt.Errorf("invalid title position: %d", title.Position) } // Validate rarity if title.Rarity < TitleRarityCommon || title.Rarity > TitleRarityUnique { return fmt.Errorf("invalid title rarity: %d", title.Rarity) } return nil } // LoadFromDatabase would load titles from the database // TODO: Implement database integration with zone/database package func (mtl *MasterTitlesList) LoadFromDatabase() error { // TODO: Implement database loading return fmt.Errorf("LoadFromDatabase not yet implemented - requires database integration") } // SaveToDatabase would save titles to the database // TODO: Implement database integration with zone/database package func (mtl *MasterTitlesList) SaveToDatabase() error { // TODO: Implement database saving return fmt.Errorf("SaveToDatabase not yet implemented - requires database integration") }