381 lines
10 KiB
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
|
|
}
|