456 lines
13 KiB
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 := "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]any {
|
|
validationResults := make(map[string]any)
|
|
|
|
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]any, 0)
|
|
}
|
|
|
|
invalidList := validationResults["invalid_entities"].([]map[string]any)
|
|
invalidList = append(invalidList, map[string]any{
|
|
"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]any) []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.GetClassesForBaseClass(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
|
|
}
|