451 lines
14 KiB
Go

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]interface{} {
stats := make(map[string]interface{})
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
}