package races import ( "fmt" "sync" ) // RaceManager provides high-level race management functionality type RaceManager struct { races *Races utils *RaceUtils integration *RaceIntegration // Statistics tracking raceUsageStats map[int8]int32 // Track how often each race is used // Thread safety mutex sync.RWMutex } // NewRaceManager creates a new race manager func NewRaceManager() *RaceManager { return &RaceManager{ races: GetGlobalRaces(), utils: NewRaceUtils(), integration: NewRaceIntegration(), raceUsageStats: make(map[int8]int32), } } // RegisterRaceUsage tracks race usage for statistics func (rm *RaceManager) RegisterRaceUsage(raceID int8) { if !rm.races.IsValidRaceID(raceID) { return } rm.mutex.Lock() defer rm.mutex.Unlock() rm.raceUsageStats[raceID]++ } // GetRaceUsageStats returns race usage statistics func (rm *RaceManager) GetRaceUsageStats() map[int8]int32 { rm.mutex.RLock() defer rm.mutex.RUnlock() // Return a copy to prevent external modification stats := make(map[int8]int32) for raceID, count := range rm.raceUsageStats { stats[raceID] = count } return stats } // GetMostPopularRace returns the most frequently used race func (rm *RaceManager) GetMostPopularRace() (int8, int32) { rm.mutex.RLock() defer rm.mutex.RUnlock() var mostPopularRace int8 = -1 var maxUsage int32 = 0 for raceID, usage := range rm.raceUsageStats { if usage > maxUsage { maxUsage = usage mostPopularRace = raceID } } return mostPopularRace, maxUsage } // GetLeastPopularRace returns the least frequently used race func (rm *RaceManager) GetLeastPopularRace() (int8, int32) { rm.mutex.RLock() defer rm.mutex.RUnlock() var leastPopularRace int8 = -1 var minUsage int32 = -1 for raceID, usage := range rm.raceUsageStats { if minUsage == -1 || usage < minUsage { minUsage = usage leastPopularRace = raceID } } return leastPopularRace, minUsage } // ResetUsageStats clears all usage statistics func (rm *RaceManager) ResetUsageStats() { rm.mutex.Lock() defer rm.mutex.Unlock() rm.raceUsageStats = make(map[int8]int32) } // ProcessRaceCommand handles race-related commands func (rm *RaceManager) ProcessRaceCommand(command string, args []string) (string, error) { switch command { case "list": return rm.handleListCommand(args) case "info": return rm.handleInfoCommand(args) case "random": return rm.handleRandomCommand(args) case "stats": return rm.handleStatsCommand(args) case "search": return rm.handleSearchCommand(args) default: return "", fmt.Errorf("unknown race command: %s", command) } } // handleListCommand lists races by criteria func (rm *RaceManager) handleListCommand(args []string) (string, error) { if len(args) == 0 { // List all races allRaces := rm.races.GetAllRaces() result := "All Races:\n" for raceID, friendlyName := range allRaces { alignment := rm.races.GetRaceAlignment(raceID) result += fmt.Sprintf("%d: %s (%s)\n", raceID, friendlyName, alignment) } return result, nil } // List races by alignment alignment := args[0] raceIDs := rm.races.GetRacesByAlignment(alignment) if len(raceIDs) == 0 { return fmt.Sprintf("No races found for alignment: %s", alignment), nil } result := fmt.Sprintf("%s Races:\n", alignment) for _, raceID := range raceIDs { friendlyName := rm.races.GetRaceNameCase(raceID) result += fmt.Sprintf("%d: %s\n", raceID, friendlyName) } return result, nil } // handleInfoCommand provides detailed information about a race func (rm *RaceManager) handleInfoCommand(args []string) (string, error) { if len(args) == 0 { return "", fmt.Errorf("race name or ID required") } // Try to parse as race name or ID raceID := rm.utils.ParseRaceName(args[0]) if raceID == -1 { return fmt.Sprintf("Invalid race: %s", args[0]), nil } raceInfo := rm.races.GetRaceInfo(raceID) if !raceInfo["valid"].(bool) { return fmt.Sprintf("Invalid race ID: %d", raceID), nil } result := fmt.Sprintf("Race Information:\n") result += fmt.Sprintf("ID: %d\n", raceID) result += fmt.Sprintf("Name: %s\n", raceInfo["display_name"]) result += fmt.Sprintf("Alignment: %s\n", raceInfo["alignment"]) result += fmt.Sprintf("Description: %s\n", rm.utils.GetRaceDescription(raceID)) result += fmt.Sprintf("Starting Location: %s\n", rm.utils.GetRaceStartingLocation(raceID)) // Add stat modifiers modifiers := rm.utils.GetRaceStatModifiers(raceID) if len(modifiers) > 0 { result += "Stat Modifiers:\n" for stat, modifier := range modifiers { sign := "+" if modifier < 0 { sign = "" } result += fmt.Sprintf(" %s: %s%d\n", stat, sign, modifier) } } // Add usage statistics if available rm.mutex.RLock() usage, hasUsage := rm.raceUsageStats[raceID] rm.mutex.RUnlock() if hasUsage { result += fmt.Sprintf("Usage Count: %d\n", usage) } return result, nil } // handleRandomCommand generates random races func (rm *RaceManager) handleRandomCommand(args []string) (string, error) { alignment := AlignmentNeutral if len(args) > 0 { alignment = args[0] } raceID := rm.utils.GetRandomRaceByAlignment(alignment) if raceID == -1 { return "Failed to generate random race", nil } friendlyName := rm.races.GetRaceNameCase(raceID) raceAlignment := rm.races.GetRaceAlignment(raceID) return fmt.Sprintf("Random %s Race: %s (ID: %d)", raceAlignment, friendlyName, raceID), nil } // handleStatsCommand shows race system statistics func (rm *RaceManager) handleStatsCommand(args []string) (string, error) { systemStats := rm.utils.GetRaceStatistics() usageStats := rm.GetRaceUsageStats() result := "Race System Statistics:\n" result += fmt.Sprintf("Total Races: %d\n", systemStats["total_races"]) result += fmt.Sprintf("Good Races: %d\n", systemStats["good_races"]) result += fmt.Sprintf("Evil Races: %d\n", systemStats["evil_races"]) result += fmt.Sprintf("Neutral Races: %d\n", systemStats["neutral_races"]) if len(usageStats) > 0 { result += "\nUsage Statistics:\n" mostPopular, maxUsage := rm.GetMostPopularRace() leastPopular, minUsage := rm.GetLeastPopularRace() if mostPopular != -1 { mostPopularName := rm.races.GetRaceNameCase(mostPopular) result += fmt.Sprintf("Most Popular: %s (%d uses)\n", mostPopularName, maxUsage) } if leastPopular != -1 { leastPopularName := rm.races.GetRaceNameCase(leastPopular) result += fmt.Sprintf("Least Popular: %s (%d uses)\n", leastPopularName, minUsage) } } return result, nil } // handleSearchCommand searches for races by pattern func (rm *RaceManager) handleSearchCommand(args []string) (string, error) { if len(args) == 0 { return "", fmt.Errorf("search pattern required") } pattern := args[0] matchingRaces := rm.utils.GetRacesByPattern(pattern) if len(matchingRaces) == 0 { return fmt.Sprintf("No races found matching pattern: %s", pattern), nil } result := fmt.Sprintf("Races matching '%s':\n", pattern) for _, raceID := range matchingRaces { friendlyName := rm.races.GetRaceNameCase(raceID) alignment := rm.races.GetRaceAlignment(raceID) result += fmt.Sprintf("%d: %s (%s)\n", raceID, friendlyName, alignment) } return result, nil } // ValidateEntityRaces validates races for a collection of entities func (rm *RaceManager) ValidateEntityRaces(entities []RaceAware) map[string]any { validationResults := make(map[string]any) validCount := 0 invalidCount := 0 raceDistribution := make(map[int8]int) for i, entity := range entities { raceID := entity.GetRace() isValid := rm.races.IsValidRaceID(raceID) if isValid { validCount++ raceDistribution[raceID]++ } 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, "race_id": raceID, }) validationResults["invalid_entities"] = invalidList } } validationResults["total_entities"] = len(entities) validationResults["valid_count"] = validCount validationResults["invalid_count"] = invalidCount validationResults["race_distribution"] = raceDistribution return validationResults } // GetRaceRecommendations returns race recommendations for character creation func (rm *RaceManager) GetRaceRecommendations(preferences map[string]any) []int8 { recommendations := make([]int8, 0) // Check for alignment preference if alignment, exists := preferences["alignment"]; exists { if alignmentStr, ok := alignment.(string); ok { raceIDs := rm.races.GetRacesByAlignment(alignmentStr) recommendations = append(recommendations, raceIDs...) } } // Check for specific stat preferences if preferredStats, exists := preferences["preferred_stats"]; exists { if stats, ok := preferredStats.([]string); ok { allRaces := rm.races.GetAllRaces() for raceID := range allRaces { modifiers := rm.utils.GetRaceStatModifiers(raceID) // Check if this race has bonuses in preferred stats hasPreferredBonus := false for _, preferredStat := range stats { if modifier, exists := modifiers[preferredStat]; exists && modifier > 0 { hasPreferredBonus = true break } } if hasPreferredBonus { recommendations = append(recommendations, raceID) } } } } // If no specific preferences, recommend popular races if len(recommendations) == 0 { // Get usage stats and recommend most popular races usageStats := rm.GetRaceUsageStats() if len(usageStats) > 0 { // Sort by usage and take top races // For simplicity, just return all races with usage > 0 for raceID, usage := range usageStats { if usage > 0 { recommendations = append(recommendations, raceID) } } } // If still no recommendations, return a default set if len(recommendations) == 0 { recommendations = []int8{RaceHuman, RaceHighElf, RaceDwarf, RaceDarkElf} } } return recommendations } // Global race manager instance var globalRaceManager *RaceManager var initRaceManagerOnce sync.Once // GetGlobalRaceManager returns the global race manager (singleton) func GetGlobalRaceManager() *RaceManager { initRaceManagerOnce.Do(func() { globalRaceManager = NewRaceManager() }) return globalRaceManager }