package titles import ( "sync" "time" ) // Title represents a single character title with all its properties type Title struct { ID int32 `json:"id"` // Unique title identifier Name string `json:"name"` // Display name of the title Description string `json:"description"` // Description shown in UI // Positioning and display Position int32 `json:"position"` // TitlePositionPrefix or TitlePositionSuffix DisplayFormat int32 `json:"display_format"` // How the title is formatted Color uint32 `json:"color"` // Color code for display // Classification Source int32 `json:"source"` // How the title is obtained (TitleSource*) Category string `json:"category"` // Category for organization Rarity int32 `json:"rarity"` // Title rarity level // Requirements and restrictions Requirements []TitleRequirement `json:"requirements"` // What's needed to unlock Flags uint32 `json:"flags"` // Various title flags // Metadata CreatedDate time.Time `json:"created_date"` // When title was added LastModified time.Time `json:"last_modified"` // When title was last updated MinLevel int32 `json:"min_level"` // Minimum character level MaxLevel int32 `json:"max_level"` // Maximum character level (0 = no limit) ExpansionID int32 `json:"expansion_id"` // Required expansion AchievementID uint32 `json:"achievement_id"` // Associated achievement if any // Expiration (for temporary titles) ExpirationHours int32 `json:"expiration_hours"` // Hours until expiration (0 = permanent) mutex sync.RWMutex // Thread safety } // TitleRequirement represents a single requirement for unlocking a title type TitleRequirement struct { Type int32 `json:"type"` // RequirementType* constant Value int32 `json:"value"` // Required value/amount StringValue string `json:"string_value"` // For string-based requirements Description string `json:"description"` // Human-readable requirement description } // PlayerTitle represents a title owned by a player type PlayerTitle struct { TitleID int32 `json:"title_id"` // Reference to Title.ID PlayerID int32 `json:"player_id"` // Character ID who owns it EarnedDate time.Time `json:"earned_date"` // When the title was earned ExpiresAt time.Time `json:"expires_at"` // When temporary title expires (zero for permanent) IsActive bool `json:"is_active"` // Whether title is currently displayed IsPrefix bool `json:"is_prefix"` // True if used as prefix, false for suffix // Achievement context AchievementID uint32 `json:"achievement_id"` // Achievement that granted this title QuestID uint32 `json:"quest_id"` // Quest that granted this title mutex sync.RWMutex // Thread safety } // NewTitle creates a new title with default values func NewTitle(id int32, name string) *Title { return &Title{ ID: id, Name: name, Position: TitlePositionSuffix, DisplayFormat: DisplayFormatSimple, Color: ColorCommon, Source: TitleSourceMiscellaneous, Category: CategoryMiscellaneous, Rarity: TitleRarityCommon, Requirements: make([]TitleRequirement, 0), Flags: 0, CreatedDate: time.Now(), LastModified: time.Now(), MinLevel: 1, MaxLevel: 0, // No limit ExpansionID: 0, // Base game AchievementID: 0, ExpirationHours: 0, // Permanent } } // NewPlayerTitle creates a new player title entry func NewPlayerTitle(titleID, playerID int32) *PlayerTitle { return &PlayerTitle{ TitleID: titleID, PlayerID: playerID, EarnedDate: time.Now(), ExpiresAt: time.Time{}, // Zero value = permanent IsActive: false, IsPrefix: false, AchievementID: 0, QuestID: 0, } } // IsExpired checks if a temporary title has expired func (pt *PlayerTitle) IsExpired() bool { pt.mutex.RLock() defer pt.mutex.RUnlock() if pt.ExpiresAt.IsZero() { return false // Permanent title } return time.Now().After(pt.ExpiresAt) } // SetExpiration sets when a temporary title expires func (pt *PlayerTitle) SetExpiration(hours int32) { pt.mutex.Lock() defer pt.mutex.Unlock() if hours <= 0 { pt.ExpiresAt = time.Time{} // Make permanent } else { pt.ExpiresAt = time.Now().Add(time.Duration(hours) * time.Hour) } } // Activate makes this title the active one for the player func (pt *PlayerTitle) Activate(isPrefix bool) { pt.mutex.Lock() defer pt.mutex.Unlock() pt.IsActive = true pt.IsPrefix = isPrefix } // Deactivate removes this title from active display func (pt *PlayerTitle) Deactivate() { pt.mutex.Lock() defer pt.mutex.Unlock() pt.IsActive = false } // Clone creates a deep copy of the title func (t *Title) Clone() *Title { t.mutex.RLock() defer t.mutex.RUnlock() clone := &Title{ ID: t.ID, Name: t.Name, Description: t.Description, Position: t.Position, DisplayFormat: t.DisplayFormat, Color: t.Color, Source: t.Source, Category: t.Category, Rarity: t.Rarity, Requirements: make([]TitleRequirement, len(t.Requirements)), Flags: t.Flags, CreatedDate: t.CreatedDate, LastModified: t.LastModified, MinLevel: t.MinLevel, MaxLevel: t.MaxLevel, ExpansionID: t.ExpansionID, AchievementID: t.AchievementID, ExpirationHours: t.ExpirationHours, } copy(clone.Requirements, t.Requirements) return clone } // Clone creates a deep copy of the player title func (pt *PlayerTitle) Clone() *PlayerTitle { pt.mutex.RLock() defer pt.mutex.RUnlock() return &PlayerTitle{ TitleID: pt.TitleID, PlayerID: pt.PlayerID, EarnedDate: pt.EarnedDate, ExpiresAt: pt.ExpiresAt, IsActive: pt.IsActive, IsPrefix: pt.IsPrefix, AchievementID: pt.AchievementID, QuestID: pt.QuestID, } } // HasFlag checks if the title has a specific flag set func (t *Title) HasFlag(flag uint32) bool { t.mutex.RLock() defer t.mutex.RUnlock() return (t.Flags & flag) != 0 } // SetFlag sets a specific flag on the title func (t *Title) SetFlag(flag uint32) { t.mutex.Lock() defer t.mutex.Unlock() t.Flags |= flag t.LastModified = time.Now() } // ClearFlag removes a specific flag from the title func (t *Title) ClearFlag(flag uint32) { t.mutex.Lock() defer t.mutex.Unlock() t.Flags &^= flag t.LastModified = time.Now() } // AddRequirement adds a new requirement to the title func (t *Title) AddRequirement(reqType int32, value int32, stringValue, description string) { t.mutex.Lock() defer t.mutex.Unlock() req := TitleRequirement{ Type: reqType, Value: value, StringValue: stringValue, Description: description, } t.Requirements = append(t.Requirements, req) t.LastModified = time.Now() } // ClearRequirements removes all requirements from the title func (t *Title) ClearRequirements() { t.mutex.Lock() defer t.mutex.Unlock() t.Requirements = make([]TitleRequirement, 0) t.LastModified = time.Now() } // IsHidden checks if the title is hidden from normal display func (t *Title) IsHidden() bool { return t.HasFlag(FlagHidden) } // IsAccountWide checks if the title is available to all characters on the account func (t *Title) IsAccountWide() bool { return t.HasFlag(FlagAccountWide) } // IsUnique checks if only one player can have this title func (t *Title) IsUnique() bool { return t.HasFlag(FlagUnique) } // IsTemporary checks if the title expires after a certain time func (t *Title) IsTemporary() bool { return t.HasFlag(FlagTemporary) } // IsGMOnly checks if the title is restricted to Game Masters func (t *Title) IsGMOnly() bool { return t.HasFlag(FlagGMOnly) } // GetDisplayName returns the formatted title name for display func (t *Title) GetDisplayName() string { t.mutex.RLock() defer t.mutex.RUnlock() switch t.DisplayFormat { case DisplayFormatWithBrackets: return "[" + t.Name + "]" case DisplayFormatWithQuotes: return "\"" + t.Name + "\"" case DisplayFormatWithCommas: return "," + t.Name + "," default: return t.Name } } // SetDescription updates the title description func (t *Title) SetDescription(description string) { t.mutex.Lock() defer t.mutex.Unlock() t.Description = description t.LastModified = time.Now() } // SetCategory updates the title category func (t *Title) SetCategory(category string) { t.mutex.Lock() defer t.mutex.Unlock() t.Category = category t.LastModified = time.Now() } // SetRarity updates the title rarity and adjusts color accordingly func (t *Title) SetRarity(rarity int32) { t.mutex.Lock() defer t.mutex.Unlock() t.Rarity = rarity // Update color based on rarity switch rarity { case TitleRarityCommon: t.Color = ColorCommon case TitleRarityUncommon: t.Color = ColorUncommon case TitleRarityRare: t.Color = ColorRare case TitleRarityEpic: t.Color = ColorEpic case TitleRarityLegendary: t.Color = ColorLegendary case TitleRarityUnique: t.Color = ColorUnique } t.LastModified = time.Now() }