eq2go/internal/races/manager.go

381 lines
10 KiB
Go

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
}