package classes import ( "math/rand" "strings" ) // ClassUtils provides utility functions for class operations type ClassUtils struct { classes *Classes } // NewClassUtils creates a new class utilities instance func NewClassUtils() *ClassUtils { return &ClassUtils{ classes: GetGlobalClasses(), } } // ParseClassName attempts to parse a class name from various input formats func (cu *ClassUtils) ParseClassName(input string) int8 { if input == "" { return -1 } // Try direct lookup first classID := cu.classes.GetClassID(input) if classID != -1 { return classID } // Try with common variations variations := []string{ strings.ToUpper(input), strings.ReplaceAll(strings.ToUpper(input), " ", ""), strings.ReplaceAll(strings.ToUpper(input), "_", ""), strings.ReplaceAll(strings.ToUpper(input), "-", ""), } for _, variation := range variations { if classID := cu.classes.GetClassID(variation); classID != -1 { return classID } } // Try matching against friendly names (case insensitive) inputLower := strings.ToLower(input) allClasses := cu.classes.GetAllClasses() for classID, displayName := range allClasses { if strings.ToLower(displayName) == inputLower { return classID } } return -1 // Not found } // FormatClassName returns a properly formatted class name func (cu *ClassUtils) FormatClassName(classID int8, format string) string { switch strings.ToLower(format) { case "display", "friendly", "proper": return cu.classes.GetClassNameCase(classID) case "upper", "uppercase": return cu.classes.GetClassName(classID) case "lower", "lowercase": return strings.ToLower(cu.classes.GetClassName(classID)) default: return cu.classes.GetClassNameCase(classID) // Default to friendly name } } // GetRandomClassByType returns a random class of the specified type func (cu *ClassUtils) GetRandomClassByType(classType string) int8 { allClasses := cu.classes.GetAllClasses() validClasses := make([]int8, 0) for classID := range allClasses { if cu.classes.GetClassType(classID) == classType { validClasses = append(validClasses, classID) } } if len(validClasses) == 0 { return DefaultClassID } return validClasses[rand.Intn(len(validClasses))] } // GetRandomAdventureClass returns a random adventure class func (cu *ClassUtils) GetRandomAdventureClass() int8 { return cu.GetRandomClassByType(ClassTypeAdventure) } // GetRandomTradeskillClass returns a random tradeskill class func (cu *ClassUtils) GetRandomTradeskillClass() int8 { return cu.GetRandomClassByType(ClassTypeTradeskill) } // ValidateClassForRace checks if a class is valid for a specific race // This is a placeholder for future race-class restrictions func (cu *ClassUtils) ValidateClassForRace(classID, raceID int8) bool { // TODO: Implement race-class restrictions when race system is available // For now, all classes can be all races return cu.classes.IsValidClassID(classID) } // GetClassDescription returns a description of the class func (cu *ClassUtils) GetClassDescription(classID int8) string { // This would typically come from a database or configuration // For now, provide basic descriptions based on class switch classID { case ClassCommoner: return "A starting class for all characters before choosing their path." case ClassFighter: return "Warriors who excel in melee combat and defense." case ClassWarrior: return "Masters of weapons and armor, the ultimate melee combatants." case ClassGuardian: return "Defensive warriors who protect their allies with shield and sword." case ClassBerserker: return "Rage-fueled fighters who sacrifice defense for devastating attacks." case ClassBrawler: return "Hand-to-hand combat specialists who fight with fists and focus." case ClassMonk: return "Disciplined fighters who use martial arts and inner peace." case ClassBruiser: return "Brutal brawlers who overwhelm enemies with raw power." case ClassCrusader: return "Holy warriors who blend combat prowess with divine magic." case ClassShadowknight: return "Dark knights who wield unholy magic alongside martial skill." case ClassPaladin: return "Champions of good who protect the innocent with sword and spell." case ClassPriest: return "Divine casters who channel the power of the gods." case ClassCleric: return "Healers and supporters who keep their allies alive and fighting." case ClassTemplar: return "Protective priests who shield allies from harm." case ClassInquisitor: return "Militant clerics who combine healing with righteous fury." case ClassDruid: return "Nature priests who harness the power of the natural world." case ClassWarden: return "Protective druids who shield allies with nature's blessing." case ClassFury: return "Destructive druids who unleash nature's wrath upon enemies." case ClassShaman: return "Spirit-workers who commune with ancestors and totems." case ClassMystic: return "Supportive shamans who provide wards and spiritual guidance." case ClassDefiler: return "Dark shamans who corrupt and weaken their enemies." case ClassMage: return "Wielders of arcane magic who bend reality to their will." case ClassSorcerer: return "Destructive mages who specialize in damaging spells." case ClassWizard: return "Scholarly sorcerers who master the elements." case ClassWarlock: return "Dark sorcerers who deal in forbidden magic." case ClassEnchanter: return "Mind-controlling mages who manipulate enemies and allies." case ClassIllusionist: return "Deceptive enchanters who confuse and misdirect." case ClassCoercer: return "Dominating enchanters who force enemies to obey." case ClassSummoner: return "Mages who call forth creatures to fight for them." case ClassConjuror: return "Elemental summoners who command earth and air." case ClassNecromancer: return "Death mages who raise undead minions and drain life." case ClassScout: return "Agile fighters who rely on speed and cunning." case ClassRogue: return "Stealthy combatants who strike from the shadows." case ClassSwashbuckler: return "Dashing rogues who fight with finesse and flair." case ClassBrigand: return "Brutal rogues who prefer dirty fighting tactics." case ClassBard: return "Musical combatants who inspire allies and demoralize foes." case ClassTroubador: return "Supportive bards who strengthen their allies." case ClassDirge: return "Dark bards who weaken enemies with haunting melodies." case ClassPredator: return "Hunters who excel at tracking and ranged combat." case ClassRanger: return "Nature-loving predators who protect the wilderness." case ClassAssassin: return "Deadly predators who eliminate targets with precision." case ClassAnimalist: return "Beast masters who fight alongside animal companions." case ClassBeastlord: return "Animalists who have formed powerful bonds with their pets." case ClassShaper: return "Mystic priests who manipulate spiritual energy." case ClassChanneler: return "Shapers who focus spiritual power through channeling." case ClassArtisan: return "Crafters who create useful items for adventurers." case ClassCraftsman: return "Specialized artisans who work with physical materials." case ClassProvisioner: return "Food and drink specialists who create consumables." case ClassWoodworker: return "Crafters who work with wood to create furniture and tools." case ClassCarpenter: return "Master woodworkers who create complex wooden items." case ClassOutfitter: return "Equipment crafters who create armor and weapons." case ClassArmorer: return "Specialists in creating protective armor." case ClassWeaponsmith: return "Masters of weapon crafting and enhancement." case ClassTailor: return "Cloth workers who create clothing and soft armor." case ClassScholar: return "Academic crafters who create magical and scholarly items." case ClassJeweler: return "Specialists in creating jewelry and accessories." case ClassSage: return "Book and scroll crafters who preserve knowledge." case ClassAlchemist: return "Potion makers who brew magical elixirs and potions." default: return "An unknown class with mysterious abilities." } } // GetClassProgression returns the class progression path func (cu *ClassUtils) GetClassProgression(classID int8) []int8 { progression := make([]int8, 0) // Always start with Commoner (except for Commoner itself) if classID != ClassCommoner { progression = append(progression, ClassCommoner) } // Add base class if different from current baseClass := cu.classes.GetBaseClass(classID) if baseClass != classID && baseClass != ClassCommoner { progression = append(progression, baseClass) } // Add secondary base class if different secondaryBase := cu.classes.GetSecondaryBaseClass(classID) if secondaryBase != classID && secondaryBase != baseClass && secondaryBase != ClassCommoner { progression = append(progression, secondaryBase) } // Add the final class progression = append(progression, classID) return progression } // GetClassesForBaseClass returns all classes that belong to a base class func (cu *ClassUtils) GetClassesForBaseClass(baseClassID int8) []int8 { result := make([]int8, 0) allClasses := cu.classes.GetAllClasses() for classID := range allClasses { if cu.classes.GetBaseClass(classID) == baseClassID { result = append(result, classID) } } return result } // GetClassesBySecondaryBase returns all classes that belong to a secondary base class func (cu *ClassUtils) GetClassesBySecondaryBase(secondaryBaseID int8) []int8 { result := make([]int8, 0) allClasses := cu.classes.GetAllClasses() for classID := range allClasses { if cu.classes.GetSecondaryBaseClass(classID) == secondaryBaseID { result = append(result, classID) } } return result } // GetClassesByPattern returns classes matching a name pattern func (cu *ClassUtils) GetClassesByPattern(pattern string) []int8 { pattern = strings.ToLower(pattern) result := make([]int8, 0) allClasses := cu.classes.GetAllClasses() for classID, displayName := range allClasses { if strings.Contains(strings.ToLower(displayName), pattern) { result = append(result, classID) } } return result } // ValidateClassTransition checks if a class change is allowed func (cu *ClassUtils) ValidateClassTransition(fromClassID, toClassID int8) (bool, string) { if !cu.classes.IsValidClassID(fromClassID) { return false, "Invalid source class" } if !cu.classes.IsValidClassID(toClassID) { return false, "Invalid target class" } if fromClassID == toClassID { return false, "Cannot change to the same class" } // Basic progression validation - can only advance, not go backward fromProgression := cu.GetClassProgression(fromClassID) toProgression := cu.GetClassProgression(toClassID) // Check if the target class is a valid advancement if len(toProgression) <= len(fromProgression) { return false, "Cannot regress to a lower tier class" } // Check if the progressions are compatible (share the same base path) for i := 0; i < len(fromProgression); i++ { if i >= len(toProgression) || fromProgression[i] != toProgression[i] { return false, "Incompatible class progression paths" } } return true, "" } // GetClassAliases returns common aliases for a class func (cu *ClassUtils) GetClassAliases(classID int8) []string { aliases := make([]string, 0) switch classID { case ClassShadowknight: aliases = append(aliases, "SK", "Shadow Knight", "Dark Knight") case ClassSwashbuckler: aliases = append(aliases, "Swash", "Swashy") case ClassTroubador: aliases = append(aliases, "Troub", "Troubadour") case ClassIllusionist: aliases = append(aliases, "Illy", "Illusion") case ClassConjuror: aliases = append(aliases, "Conj", "Conjurer") case ClassNecromancer: aliases = append(aliases, "Necro", "Nec") case ClassBeastlord: aliases = append(aliases, "BL", "Beast Lord") case ClassWeaponsmith: aliases = append(aliases, "WS", "Weapon Smith") } // Always include the official names aliases = append(aliases, cu.classes.GetClassName(classID)) aliases = append(aliases, cu.classes.GetClassNameCase(classID)) return aliases } // GetClassStatistics returns statistics about the class system func (cu *ClassUtils) GetClassStatistics() map[string]any { stats := make(map[string]any) allClasses := cu.classes.GetAllClasses() stats["total_classes"] = len(allClasses) adventureCount := 0 tradeskillCount := 0 specialCount := 0 for classID := range allClasses { switch cu.classes.GetClassType(classID) { case ClassTypeAdventure: adventureCount++ case ClassTypeTradeskill: tradeskillCount++ default: specialCount++ } } stats["adventure_classes"] = adventureCount stats["tradeskill_classes"] = tradeskillCount stats["special_classes"] = specialCount // Base class distribution baseClassDistribution := make(map[string][]string) for classID, displayName := range allClasses { if cu.classes.IsAdventureClass(classID) { baseClassID := cu.classes.GetBaseClass(classID) baseClassName := cu.classes.GetClassNameCase(baseClassID) baseClassDistribution[baseClassName] = append(baseClassDistribution[baseClassName], displayName) } } stats["base_class_distribution"] = baseClassDistribution return stats } // FormatClassList returns a formatted string of class names func (cu *ClassUtils) FormatClassList(classIDs []int8, separator string) string { if len(classIDs) == 0 { return "" } names := make([]string, len(classIDs)) for i, classID := range classIDs { names[i] = cu.classes.GetClassNameCase(classID) } return strings.Join(names, separator) } // GetEQClassName returns the EQ-style class name for a given class and level // This is a placeholder for the original C++ GetEQClassName functionality func (cu *ClassUtils) GetEQClassName(classID int8, level int8) string { // TODO: Implement level-based class names when level system is available // For now, just return the display name return cu.classes.GetClassNameCase(classID) } // GetStartingClass returns the appropriate starting class for character creation func (cu *ClassUtils) GetStartingClass() int8 { return ClassCommoner } // IsBaseClass checks if a class is a base class (Fighter, Priest, Mage, Scout) func (cu *ClassUtils) IsBaseClass(classID int8) bool { return classID == ClassFighter || classID == ClassPriest || classID == ClassMage || classID == ClassScout } // IsSecondaryBaseClass checks if a class is a secondary base class func (cu *ClassUtils) IsSecondaryBaseClass(classID int8) bool { // Check if any class has this as their secondary base allClasses := cu.classes.GetAllClasses() for checkClassID := range allClasses { if cu.classes.GetSecondaryBaseClass(checkClassID) == classID && checkClassID != classID { return true } } return false }