eq2go/internal/classes/manager.go

456 lines
13 KiB
Go

package classes
import (
"fmt"
"sync"
)
// ClassManager provides high-level class management functionality
type ClassManager struct {
classes *Classes
utils *ClassUtils
integration *ClassIntegration
// Statistics tracking
classUsageStats map[int8]int32 // Track how often each class is used
// Thread safety
mutex sync.RWMutex
}
// NewClassManager creates a new class manager
func NewClassManager() *ClassManager {
return &ClassManager{
classes: GetGlobalClasses(),
utils: NewClassUtils(),
integration: NewClassIntegration(),
classUsageStats: make(map[int8]int32),
}
}
// RegisterClassUsage tracks class usage for statistics
func (cm *ClassManager) RegisterClassUsage(classID int8) {
if !cm.classes.IsValidClassID(classID) {
return
}
cm.mutex.Lock()
defer cm.mutex.Unlock()
cm.classUsageStats[classID]++
}
// GetClassUsageStats returns class usage statistics
func (cm *ClassManager) GetClassUsageStats() map[int8]int32 {
cm.mutex.RLock()
defer cm.mutex.RUnlock()
// Return a copy to prevent external modification
stats := make(map[int8]int32)
for classID, count := range cm.classUsageStats {
stats[classID] = count
}
return stats
}
// GetMostPopularClass returns the most frequently used class
func (cm *ClassManager) GetMostPopularClass() (int8, int32) {
cm.mutex.RLock()
defer cm.mutex.RUnlock()
var mostPopularClass int8 = -1
var maxUsage int32 = 0
for classID, usage := range cm.classUsageStats {
if usage > maxUsage {
maxUsage = usage
mostPopularClass = classID
}
}
return mostPopularClass, maxUsage
}
// GetLeastPopularClass returns the least frequently used class
func (cm *ClassManager) GetLeastPopularClass() (int8, int32) {
cm.mutex.RLock()
defer cm.mutex.RUnlock()
var leastPopularClass int8 = -1
var minUsage int32 = -1
for classID, usage := range cm.classUsageStats {
if minUsage == -1 || usage < minUsage {
minUsage = usage
leastPopularClass = classID
}
}
return leastPopularClass, minUsage
}
// ResetUsageStats clears all usage statistics
func (cm *ClassManager) ResetUsageStats() {
cm.mutex.Lock()
defer cm.mutex.Unlock()
cm.classUsageStats = make(map[int8]int32)
}
// ProcessClassCommand handles class-related commands
func (cm *ClassManager) ProcessClassCommand(command string, args []string) (string, error) {
switch command {
case "list":
return cm.handleListCommand(args)
case "info":
return cm.handleInfoCommand(args)
case "random":
return cm.handleRandomCommand(args)
case "stats":
return cm.handleStatsCommand(args)
case "search":
return cm.handleSearchCommand(args)
case "progression":
return cm.handleProgressionCommand(args)
default:
return "", fmt.Errorf("unknown class command: %s", command)
}
}
// handleListCommand lists classes by criteria
func (cm *ClassManager) handleListCommand(args []string) (string, error) {
if len(args) == 0 {
// List all classes
allClasses := cm.classes.GetAllClasses()
result := "All Classes:\n"
for classID, displayName := range allClasses {
classType := cm.classes.GetClassType(classID)
baseClass := cm.classes.GetBaseClass(classID)
baseClassName := cm.classes.GetClassNameCase(baseClass)
result += fmt.Sprintf("%d: %s (%s, Base: %s)\n", classID, displayName, classType, baseClassName)
}
return result, nil
}
// List classes by type
classType := args[0]
allClasses := cm.classes.GetAllClasses()
result := fmt.Sprintf("%s Classes:\n", classType)
count := 0
for classID, displayName := range allClasses {
if cm.classes.GetClassType(classID) == classType {
baseClass := cm.classes.GetBaseClass(classID)
baseClassName := cm.classes.GetClassNameCase(baseClass)
result += fmt.Sprintf("%d: %s (Base: %s)\n", classID, displayName, baseClassName)
count++
}
}
if count == 0 {
return fmt.Sprintf("No classes found for type: %s", classType), nil
}
return result, nil
}
// handleInfoCommand provides detailed information about a class
func (cm *ClassManager) handleInfoCommand(args []string) (string, error) {
if len(args) == 0 {
return "", fmt.Errorf("class name or ID required")
}
// Try to parse as class name or ID
classID := cm.utils.ParseClassName(args[0])
if classID == -1 {
return fmt.Sprintf("Invalid class: %s", args[0]), nil
}
classInfo := cm.classes.GetClassInfo(classID)
if !classInfo["valid"].(bool) {
return fmt.Sprintf("Invalid class ID: %d", classID), nil
}
result := fmt.Sprintf("Class Information:\n")
result += fmt.Sprintf("ID: %d\n", classID)
result += fmt.Sprintf("Name: %s\n", classInfo["display_name"])
result += fmt.Sprintf("Type: %s\n", classInfo["type"])
result += fmt.Sprintf("Base Class: %s\n", cm.classes.GetClassNameCase(classInfo["base_class"].(int8)))
if secondaryBase := classInfo["secondary_base_class"].(int8); secondaryBase != DefaultClassID {
result += fmt.Sprintf("Secondary Base: %s\n", cm.classes.GetClassNameCase(secondaryBase))
}
result += fmt.Sprintf("Description: %s\n", cm.utils.GetClassDescription(classID))
// Add progression path
progression := cm.utils.GetClassProgression(classID)
if len(progression) > 1 {
result += "Progression Path: "
progressionNames := make([]string, len(progression))
for i, progClassID := range progression {
progressionNames[i] = cm.classes.GetClassNameCase(progClassID)
}
result += fmt.Sprintf("%s\n", cm.utils.FormatClassList(progression, " → "))
}
// Add starting stats
startingStats := cm.integration.GetClassStartingStats(classID)
if len(startingStats) > 0 {
result += "Starting Stats:\n"
for stat, value := range startingStats {
result += fmt.Sprintf(" %s: %d\n", stat, value)
}
}
// Add usage statistics if available
cm.mutex.RLock()
usage, hasUsage := cm.classUsageStats[classID]
cm.mutex.RUnlock()
if hasUsage {
result += fmt.Sprintf("Usage Count: %d\n", usage)
}
return result, nil
}
// handleRandomCommand generates random classes
func (cm *ClassManager) handleRandomCommand(args []string) (string, error) {
classType := ClassTypeAdventure
if len(args) > 0 {
classType = args[0]
}
classID := cm.utils.GetRandomClassByType(classType)
if classID == -1 {
return "Failed to generate random class", nil
}
displayName := cm.classes.GetClassNameCase(classID)
actualType := cm.classes.GetClassType(classID)
return fmt.Sprintf("Random %s Class: %s (ID: %d)", actualType, displayName, classID), nil
}
// handleStatsCommand shows class system statistics
func (cm *ClassManager) handleStatsCommand(args []string) (string, error) {
systemStats := cm.utils.GetClassStatistics()
usageStats := cm.GetClassUsageStats()
result := "Class System Statistics:\n"
result += fmt.Sprintf("Total Classes: %d\n", systemStats["total_classes"])
result += fmt.Sprintf("Adventure Classes: %d\n", systemStats["adventure_classes"])
result += fmt.Sprintf("Tradeskill Classes: %d\n", systemStats["tradeskill_classes"])
result += fmt.Sprintf("Special Classes: %d\n", systemStats["special_classes"])
if len(usageStats) > 0 {
result += "\nUsage Statistics:\n"
mostPopular, maxUsage := cm.GetMostPopularClass()
leastPopular, minUsage := cm.GetLeastPopularClass()
if mostPopular != -1 {
mostPopularName := cm.classes.GetClassNameCase(mostPopular)
result += fmt.Sprintf("Most Popular: %s (%d uses)\n", mostPopularName, maxUsage)
}
if leastPopular != -1 {
leastPopularName := cm.classes.GetClassNameCase(leastPopular)
result += fmt.Sprintf("Least Popular: %s (%d uses)\n", leastPopularName, minUsage)
}
}
// Show base class distribution
if baseDistribution, exists := systemStats["base_class_distribution"]; exists {
result += "\nBase Class Distribution:\n"
distribution := baseDistribution.(map[string][]string)
for baseClass, subClasses := range distribution {
result += fmt.Sprintf("%s: %d subclasses\n", baseClass, len(subClasses))
}
}
return result, nil
}
// handleSearchCommand searches for classes by pattern
func (cm *ClassManager) handleSearchCommand(args []string) (string, error) {
if len(args) == 0 {
return "", fmt.Errorf("search pattern required")
}
pattern := args[0]
matchingClasses := cm.utils.GetClassesByPattern(pattern)
if len(matchingClasses) == 0 {
return fmt.Sprintf("No classes found matching pattern: %s", pattern), nil
}
result := fmt.Sprintf("Classes matching '%s':\n", pattern)
for _, classID := range matchingClasses {
displayName := cm.classes.GetClassNameCase(classID)
classType := cm.classes.GetClassType(classID)
baseClass := cm.classes.GetBaseClass(classID)
baseClassName := cm.classes.GetClassNameCase(baseClass)
result += fmt.Sprintf("%d: %s (%s, Base: %s)\n", classID, displayName, classType, baseClassName)
}
return result, nil
}
// handleProgressionCommand shows class progression information
func (cm *ClassManager) handleProgressionCommand(args []string) (string, error) {
if len(args) == 0 {
return "", fmt.Errorf("class name or ID required")
}
classID := cm.utils.ParseClassName(args[0])
if classID == -1 {
return fmt.Sprintf("Invalid class: %s", args[0]), nil
}
progression := cm.utils.GetClassProgression(classID)
if len(progression) <= 1 {
return fmt.Sprintf("Class %s has no progression path", cm.classes.GetClassNameCase(classID)), nil
}
result := fmt.Sprintf("Progression Path for %s:\n", cm.classes.GetClassNameCase(classID))
for i, stepClassID := range progression {
stepName := cm.classes.GetClassNameCase(stepClassID)
if i == 0 {
result += fmt.Sprintf("1. %s (Starting Class)\n", stepName)
} else if i == len(progression)-1 {
result += fmt.Sprintf("%d. %s (Final Class)\n", i+1, stepName)
} else {
result += fmt.Sprintf("%d. %s\n", i+1, stepName)
}
}
return result, nil
}
// ValidateEntityClasses validates classes for a collection of entities
func (cm *ClassManager) ValidateEntityClasses(entities []ClassAware) map[string]interface{} {
validationResults := make(map[string]interface{})
validCount := 0
invalidCount := 0
classDistribution := make(map[int8]int)
for i, entity := range entities {
classID := entity.GetClass()
isValid := cm.classes.IsValidClassID(classID)
if isValid {
validCount++
classDistribution[classID]++
} else {
invalidCount++
}
// Track invalid entities
if !isValid {
if validationResults["invalid_entities"] == nil {
validationResults["invalid_entities"] = make([]map[string]interface{}, 0)
}
invalidList := validationResults["invalid_entities"].([]map[string]interface{})
invalidList = append(invalidList, map[string]interface{}{
"index": i,
"class_id": classID,
})
validationResults["invalid_entities"] = invalidList
}
}
validationResults["total_entities"] = len(entities)
validationResults["valid_count"] = validCount
validationResults["invalid_count"] = invalidCount
validationResults["class_distribution"] = classDistribution
return validationResults
}
// GetClassRecommendations returns class recommendations for character creation
func (cm *ClassManager) GetClassRecommendations(preferences map[string]interface{}) []int8 {
recommendations := make([]int8, 0)
// Check for class type preference
if classType, exists := preferences["class_type"]; exists {
if typeStr, ok := classType.(string); ok {
allClasses := cm.classes.GetAllClasses()
for classID := range allClasses {
if cm.classes.GetClassType(classID) == typeStr {
recommendations = append(recommendations, classID)
}
}
}
}
// Check for base class preference
if baseClass, exists := preferences["base_class"]; exists {
if baseClassID, ok := baseClass.(int8); ok {
subClasses := cm.utils.GetClassesByBaseClass(baseClassID)
recommendations = append(recommendations, subClasses...)
}
}
// Check for specific stat preferences
if preferredStats, exists := preferences["preferred_stats"]; exists {
if stats, ok := preferredStats.([]string); ok {
allClasses := cm.classes.GetAllClasses()
for classID := range allClasses {
startingStats := cm.integration.GetClassStartingStats(classID)
// Check if this class has bonuses in preferred stats
hasPreferredBonus := false
for _, preferredStat := range stats {
if statValue, exists := startingStats[preferredStat]; exists && statValue > 52 { // Above base of 50 + minor bonus
hasPreferredBonus = true
break
}
}
if hasPreferredBonus {
recommendations = append(recommendations, classID)
}
}
}
}
// If no specific preferences, recommend popular classes
if len(recommendations) == 0 {
// Get usage stats and recommend most popular classes
usageStats := cm.GetClassUsageStats()
if len(usageStats) > 0 {
// Sort by usage and take top classes
// For simplicity, just return all classes with usage > 0
for classID, usage := range usageStats {
if usage > 0 {
recommendations = append(recommendations, classID)
}
}
}
// If still no recommendations, return a default set of beginner-friendly classes
if len(recommendations) == 0 {
recommendations = []int8{ClassWarrior, ClassCleric, ClassWizard, ClassRogue}
}
}
return recommendations
}
// Global class manager instance
var globalClassManager *ClassManager
var initClassManagerOnce sync.Once
// GetGlobalClassManager returns the global class manager (singleton)
func GetGlobalClassManager() *ClassManager {
initClassManagerOnce.Do(func() {
globalClassManager = NewClassManager()
})
return globalClassManager
}