388 lines
9.7 KiB
Go
388 lines
9.7 KiB
Go
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
|
|
}
|