package races import ( "math/rand" "strings" "sync" ) // Races manages race information and lookups // Converted from C++ Races class type Races struct { // Race name to ID mapping (uppercase keys) raceMap map[string]int8 // ID to friendly name mapping friendlyNameMap map[int8]string // Alignment-based race lists for randomization goodRaces []string evilRaces []string // Thread safety mutex sync.RWMutex } // NewRaces creates a new races manager with all EQ2 races // Converted from C++ Races::Races constructor func NewRaces() *Races { races := &Races{ raceMap: make(map[string]int8), friendlyNameMap: make(map[int8]string), goodRaces: make([]string, 0), evilRaces: make([]string, 0), } races.initializeRaces() return races } // initializeRaces sets up all race mappings func (r *Races) initializeRaces() { // Initialize race name to ID mappings (from C++ constructor) r.raceMap[RaceNameBarbarian] = RaceBarbarian r.raceMap[RaceNameDarkElf] = RaceDarkElf r.raceMap[RaceNameDwarf] = RaceDwarf r.raceMap[RaceNameErudite] = RaceErudite r.raceMap[RaceNameFroglok] = RaceFroglok r.raceMap[RaceNameGnome] = RaceGnome r.raceMap[RaceNameHalfElf] = RaceHalfElf r.raceMap[RaceNameHalfling] = RaceHalfling r.raceMap[RaceNameHighElf] = RaceHighElf r.raceMap[RaceNameHuman] = RaceHuman r.raceMap[RaceNameIksar] = RaceIksar r.raceMap[RaceNameKerra] = RaceKerra r.raceMap[RaceNameOgre] = RaceOgre r.raceMap[RaceNameRatonga] = RaceRatonga r.raceMap[RaceNameTroll] = RaceTroll r.raceMap[RaceNameWoodElf] = RaceWoodElf r.raceMap[RaceNameFaeLight] = RaceFae r.raceMap[RaceNameFaeDark] = RaceArasai r.raceMap[RaceNameSarnak] = RaceSarnak r.raceMap[RaceNameVampire] = RaceVampire r.raceMap[RaceNameAerakyn] = RaceAerakyn // Initialize friendly display names (from C++ constructor) r.friendlyNameMap[RaceBarbarian] = DisplayNameBarbarian r.friendlyNameMap[RaceDarkElf] = DisplayNameDarkElf r.friendlyNameMap[RaceDwarf] = DisplayNameDwarf r.friendlyNameMap[RaceErudite] = DisplayNameErudite r.friendlyNameMap[RaceFroglok] = DisplayNameFroglok r.friendlyNameMap[RaceGnome] = DisplayNameGnome r.friendlyNameMap[RaceHalfElf] = DisplayNameHalfElf r.friendlyNameMap[RaceHalfling] = DisplayNameHalfling r.friendlyNameMap[RaceHighElf] = DisplayNameHighElf r.friendlyNameMap[RaceHuman] = DisplayNameHuman r.friendlyNameMap[RaceIksar] = DisplayNameIksar r.friendlyNameMap[RaceKerra] = DisplayNameKerra r.friendlyNameMap[RaceOgre] = DisplayNameOgre r.friendlyNameMap[RaceRatonga] = DisplayNameRatonga r.friendlyNameMap[RaceTroll] = DisplayNameTroll r.friendlyNameMap[RaceWoodElf] = DisplayNameWoodElf r.friendlyNameMap[RaceFae] = DisplayNameFae r.friendlyNameMap[RaceArasai] = DisplayNameArasai r.friendlyNameMap[RaceSarnak] = DisplayNameSarnak r.friendlyNameMap[RaceVampire] = DisplayNameVampire r.friendlyNameMap[RaceAerakyn] = DisplayNameAerakyn // Initialize good races (from C++ race_map_good) // "Neutral" races appear in both lists for /randomize functionality r.goodRaces = []string{ RaceNameDwarf, // 0 RaceNameFaeLight, // 1 RaceNameFroglok, // 2 RaceNameHalfling, // 3 RaceNameHighElf, // 4 RaceNameWoodElf, // 5 RaceNameBarbarian, // 6 (neutral) RaceNameErudite, // 7 (neutral) RaceNameGnome, // 8 (neutral) RaceNameHalfElf, // 9 (neutral) RaceNameHuman, // 10 (neutral) RaceNameKerra, // 11 (neutral) RaceNameVampire, // 12 (neutral) RaceNameAerakyn, // 13 (neutral) } // Initialize evil races (from C++ race_map_evil) r.evilRaces = []string{ RaceNameFaeDark, // 0 RaceNameDarkElf, // 1 RaceNameIksar, // 2 RaceNameOgre, // 3 RaceNameRatonga, // 4 RaceNameSarnak, // 5 RaceNameTroll, // 6 RaceNameBarbarian, // 7 (neutral) RaceNameErudite, // 8 (neutral) RaceNameGnome, // 9 (neutral) RaceNameHalfElf, // 10 (neutral) RaceNameHuman, // 11 (neutral) RaceNameKerra, // 12 (neutral) RaceNameVampire, // 13 (neutral) RaceNameAerakyn, // 14 (neutral) } } // GetRaceID returns the race ID for a given race name // Converted from C++ Races::GetRaceID func (r *Races) GetRaceID(name string) int8 { r.mutex.RLock() defer r.mutex.RUnlock() raceName := strings.ToUpper(strings.TrimSpace(name)) if raceID, exists := r.raceMap[raceName]; exists { return raceID } return -1 // Invalid race } // GetRaceName returns the uppercase race name for a given ID // Converted from C++ Races::GetRaceName func (r *Races) GetRaceName(raceID int8) string { r.mutex.RLock() defer r.mutex.RUnlock() // Search through race map to find the name for name, id := range r.raceMap { if id == raceID { return name } } return "" // Invalid race ID } // GetRaceNameCase returns the friendly display name for a given race ID // Converted from C++ Races::GetRaceNameCase func (r *Races) GetRaceNameCase(raceID int8) string { r.mutex.RLock() defer r.mutex.RUnlock() if friendlyName, exists := r.friendlyNameMap[raceID]; exists { return friendlyName } return "" // Invalid race ID } // GetRandomGoodRace returns a random good-aligned race ID // Converted from C++ Races::GetRaceNameGood func (r *Races) GetRandomGoodRace() int8 { r.mutex.RLock() defer r.mutex.RUnlock() if len(r.goodRaces) == 0 { return DefaultRaceID } randomIndex := rand.Intn(len(r.goodRaces)) raceName := r.goodRaces[randomIndex] if raceID, exists := r.raceMap[raceName]; exists { return raceID } return DefaultRaceID // Default to Human if error } // GetRandomEvilRace returns a random evil-aligned race ID // Converted from C++ Races::GetRaceNameEvil func (r *Races) GetRandomEvilRace() int8 { r.mutex.RLock() defer r.mutex.RUnlock() if len(r.evilRaces) == 0 { return DefaultRaceID } randomIndex := rand.Intn(len(r.evilRaces)) raceName := r.evilRaces[randomIndex] if raceID, exists := r.raceMap[raceName]; exists { return raceID } return DefaultRaceID // Default to Human if error } // IsValidRaceID checks if a race ID is valid func (r *Races) IsValidRaceID(raceID int8) bool { return raceID >= MinRaceID && raceID <= MaxRaceID } // GetAllRaces returns all race IDs and their friendly names func (r *Races) GetAllRaces() map[int8]string { r.mutex.RLock() defer r.mutex.RUnlock() result := make(map[int8]string) for raceID, friendlyName := range r.friendlyNameMap { result[raceID] = friendlyName } return result } // GetRacesByAlignment returns races filtered by alignment func (r *Races) GetRacesByAlignment(alignment string) []int8 { r.mutex.RLock() defer r.mutex.RUnlock() var raceNames []string switch strings.ToLower(alignment) { case AlignmentGood: raceNames = r.goodRaces case AlignmentEvil: raceNames = r.evilRaces default: // Return all races for neutral or unknown alignment result := make([]int8, 0, len(r.friendlyNameMap)) for raceID := range r.friendlyNameMap { result = append(result, raceID) } return result } result := make([]int8, 0, len(raceNames)) for _, raceName := range raceNames { if raceID, exists := r.raceMap[raceName]; exists { result = append(result, raceID) } } return result } // IsGoodRace checks if a race is considered good-aligned func (r *Races) IsGoodRace(raceID int8) bool { r.mutex.RLock() defer r.mutex.RUnlock() raceName := "" for name, id := range r.raceMap { if id == raceID { raceName = name break } } if raceName == "" { return false } for _, goodRace := range r.goodRaces { if goodRace == raceName { return true } } return false } // IsEvilRace checks if a race is considered evil-aligned func (r *Races) IsEvilRace(raceID int8) bool { r.mutex.RLock() defer r.mutex.RUnlock() raceName := "" for name, id := range r.raceMap { if id == raceID { raceName = name break } } if raceName == "" { return false } for _, evilRace := range r.evilRaces { if evilRace == raceName { return true } } return false } // IsNeutralRace checks if a race appears in both good and evil lists (neutral) func (r *Races) IsNeutralRace(raceID int8) bool { return r.IsGoodRace(raceID) && r.IsEvilRace(raceID) } // GetRaceAlignment returns the primary alignment of a race func (r *Races) GetRaceAlignment(raceID int8) string { if r.IsNeutralRace(raceID) { return AlignmentNeutral } else if r.IsGoodRace(raceID) { return AlignmentGood } else if r.IsEvilRace(raceID) { return AlignmentEvil } return AlignmentNeutral // Default for invalid races } // GetRaceCount returns the total number of races func (r *Races) GetRaceCount() int { r.mutex.RLock() defer r.mutex.RUnlock() return len(r.friendlyNameMap) } // GetGoodRaceCount returns the number of good-aligned races func (r *Races) GetGoodRaceCount() int { r.mutex.RLock() defer r.mutex.RUnlock() return len(r.goodRaces) } // GetEvilRaceCount returns the number of evil-aligned races func (r *Races) GetEvilRaceCount() int { r.mutex.RLock() defer r.mutex.RUnlock() return len(r.evilRaces) } // GetRaceInfo returns comprehensive information about a race func (r *Races) GetRaceInfo(raceID int8) map[string]interface{} { r.mutex.RLock() defer r.mutex.RUnlock() info := make(map[string]interface{}) if !r.IsValidRaceID(raceID) { info["valid"] = false return info } info["valid"] = true info["race_id"] = raceID info["name"] = r.GetRaceName(raceID) info["display_name"] = r.GetRaceNameCase(raceID) info["alignment"] = r.GetRaceAlignment(raceID) info["is_good"] = r.IsGoodRace(raceID) info["is_evil"] = r.IsEvilRace(raceID) info["is_neutral"] = r.IsNeutralRace(raceID) return info } // Global races instance var globalRaces *Races var initRacesOnce sync.Once // GetGlobalRaces returns the global races manager (singleton) func GetGlobalRaces() *Races { initRacesOnce.Do(func() { globalRaces = NewRaces() }) return globalRaces }