325 lines
9.0 KiB
Go
325 lines
9.0 KiB
Go
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()
|
|
} |