simplify/ehance classes
This commit is contained in:
parent
32143aab1a
commit
f99343f490
@ -1,359 +0,0 @@
|
|||||||
package classes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// classMap contains the mapping of class names to IDs
|
|
||||||
var classMap = map[string]int8{
|
|
||||||
"COMMONER": ClassCommoner,
|
|
||||||
"FIGHTER": ClassFighter,
|
|
||||||
"WARRIOR": ClassWarrior,
|
|
||||||
"GUARDIAN": ClassGuardian,
|
|
||||||
"BERSERKER": ClassBerserker,
|
|
||||||
"BRAWLER": ClassBrawler,
|
|
||||||
"MONK": ClassMonk,
|
|
||||||
"BRUISER": ClassBruiser,
|
|
||||||
"CRUSADER": ClassCrusader,
|
|
||||||
"SHADOWKNIGHT": ClassShadowknight,
|
|
||||||
"PALADIN": ClassPaladin,
|
|
||||||
"PRIEST": ClassPriest,
|
|
||||||
"CLERIC": ClassCleric,
|
|
||||||
"TEMPLAR": ClassTemplar,
|
|
||||||
"INQUISITOR": ClassInquisitor,
|
|
||||||
"DRUID": ClassDruid,
|
|
||||||
"WARDEN": ClassWarden,
|
|
||||||
"FURY": ClassFury,
|
|
||||||
"SHAMAN": ClassShaman,
|
|
||||||
"MYSTIC": ClassMystic,
|
|
||||||
"DEFILER": ClassDefiler,
|
|
||||||
"MAGE": ClassMage,
|
|
||||||
"SORCERER": ClassSorcerer,
|
|
||||||
"WIZARD": ClassWizard,
|
|
||||||
"WARLOCK": ClassWarlock,
|
|
||||||
"ENCHANTER": ClassEnchanter,
|
|
||||||
"ILLUSIONIST": ClassIllusionist,
|
|
||||||
"COERCER": ClassCoercer,
|
|
||||||
"SUMMONER": ClassSummoner,
|
|
||||||
"CONJUROR": ClassConjuror,
|
|
||||||
"NECROMANCER": ClassNecromancer,
|
|
||||||
"SCOUT": ClassScout,
|
|
||||||
"ROGUE": ClassRogue,
|
|
||||||
"SWASHBUCKLER": ClassSwashbuckler,
|
|
||||||
"BRIGAND": ClassBrigand,
|
|
||||||
"BARD": ClassBard,
|
|
||||||
"TROUBADOR": ClassTroubador,
|
|
||||||
"DIRGE": ClassDirge,
|
|
||||||
"PREDATOR": ClassPredator,
|
|
||||||
"RANGER": ClassRanger,
|
|
||||||
"ASSASSIN": ClassAssassin,
|
|
||||||
"ANIMALIST": ClassAnimalist,
|
|
||||||
"BEASTLORD": ClassBeastlord,
|
|
||||||
"SHAPER": ClassShaper,
|
|
||||||
"CHANNELER": ClassChanneler,
|
|
||||||
"ARTISAN": ClassArtisan,
|
|
||||||
"CRAFTSMAN": ClassCraftsman,
|
|
||||||
"PROVISIONER": ClassProvisioner,
|
|
||||||
"WOODWORKER": ClassWoodworker,
|
|
||||||
"CARPENTER": ClassCarpenter,
|
|
||||||
"OUTFITTER": ClassOutfitter,
|
|
||||||
"ARMORER": ClassArmorer,
|
|
||||||
"WEAPONSMITH": ClassWeaponsmith,
|
|
||||||
"TAILOR": ClassTailor,
|
|
||||||
"SCHOLAR": ClassScholar,
|
|
||||||
"JEWELER": ClassJeweler,
|
|
||||||
"SAGE": ClassSage,
|
|
||||||
"ALCHEMIST": ClassAlchemist,
|
|
||||||
}
|
|
||||||
|
|
||||||
// displayNameMap contains the display names for each class ID
|
|
||||||
var displayNameMap = map[int8]string{
|
|
||||||
ClassCommoner: "Commoner",
|
|
||||||
ClassFighter: "Fighter",
|
|
||||||
ClassWarrior: "Warrior",
|
|
||||||
ClassGuardian: "Guardian",
|
|
||||||
ClassBerserker: "Berserker",
|
|
||||||
ClassBrawler: "Brawler",
|
|
||||||
ClassMonk: "Monk",
|
|
||||||
ClassBruiser: "Bruiser",
|
|
||||||
ClassCrusader: "Crusader",
|
|
||||||
ClassShadowknight: "Shadowknight",
|
|
||||||
ClassPaladin: "Paladin",
|
|
||||||
ClassPriest: "Priest",
|
|
||||||
ClassCleric: "Cleric",
|
|
||||||
ClassTemplar: "Templar",
|
|
||||||
ClassInquisitor: "Inquisitor",
|
|
||||||
ClassDruid: "Druid",
|
|
||||||
ClassWarden: "Warden",
|
|
||||||
ClassFury: "Fury",
|
|
||||||
ClassShaman: "Shaman",
|
|
||||||
ClassMystic: "Mystic",
|
|
||||||
ClassDefiler: "Defiler",
|
|
||||||
ClassMage: "Mage",
|
|
||||||
ClassSorcerer: "Sorcerer",
|
|
||||||
ClassWizard: "Wizard",
|
|
||||||
ClassWarlock: "Warlock",
|
|
||||||
ClassEnchanter: "Enchanter",
|
|
||||||
ClassIllusionist: "Illusionist",
|
|
||||||
ClassCoercer: "Coercer",
|
|
||||||
ClassSummoner: "Summoner",
|
|
||||||
ClassConjuror: "Conjuror",
|
|
||||||
ClassNecromancer: "Necromancer",
|
|
||||||
ClassScout: "Scout",
|
|
||||||
ClassRogue: "Rogue",
|
|
||||||
ClassSwashbuckler: "Swashbuckler",
|
|
||||||
ClassBrigand: "Brigand",
|
|
||||||
ClassBard: "Bard",
|
|
||||||
ClassTroubador: "Troubador",
|
|
||||||
ClassDirge: "Dirge",
|
|
||||||
ClassPredator: "Predator",
|
|
||||||
ClassRanger: "Ranger",
|
|
||||||
ClassAssassin: "Assassin",
|
|
||||||
ClassAnimalist: "Animalist",
|
|
||||||
ClassBeastlord: "Beastlord",
|
|
||||||
ClassShaper: "Shaper",
|
|
||||||
ClassChanneler: "Channeler",
|
|
||||||
ClassArtisan: "Artisan",
|
|
||||||
ClassCraftsman: "Craftsman",
|
|
||||||
ClassProvisioner: "Provisioner",
|
|
||||||
ClassWoodworker: "Woodworker",
|
|
||||||
ClassCarpenter: "Carpenter",
|
|
||||||
ClassOutfitter: "Outfitter",
|
|
||||||
ClassArmorer: "Armorer",
|
|
||||||
ClassWeaponsmith: "Weaponsmith",
|
|
||||||
ClassTailor: "Tailor",
|
|
||||||
ClassScholar: "Scholar",
|
|
||||||
ClassJeweler: "Jeweler",
|
|
||||||
ClassSage: "Sage",
|
|
||||||
ClassAlchemist: "Alchemist",
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetClassID returns the class ID for a given class name
|
|
||||||
// Converted from C++ Classes::GetClassID
|
|
||||||
func GetClassID(name string) int8 {
|
|
||||||
className := strings.ToUpper(strings.TrimSpace(name))
|
|
||||||
if classID, exists := classMap[className]; exists {
|
|
||||||
return classID
|
|
||||||
}
|
|
||||||
return -1 // Invalid class
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetClassName returns the uppercase class name for a given ID
|
|
||||||
// Converted from C++ Classes::GetClassName
|
|
||||||
func GetClassName(classID int8) string {
|
|
||||||
// Search through class map to find the name
|
|
||||||
for name, id := range classMap {
|
|
||||||
if id == classID {
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "" // Invalid class ID
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetClassNameCase returns the friendly display name for a given class ID
|
|
||||||
// Converted from C++ Classes::GetClassNameCase
|
|
||||||
func GetClassNameCase(classID int8) string {
|
|
||||||
if displayName, exists := displayNameMap[classID]; exists {
|
|
||||||
return displayName
|
|
||||||
}
|
|
||||||
return "" // Invalid class ID
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBaseClass returns the base class ID for a given class
|
|
||||||
// Converted from C++ Classes::GetBaseClass
|
|
||||||
func GetBaseClass(classID int8) int8 {
|
|
||||||
if classID >= ClassWarrior && classID <= ClassPaladin {
|
|
||||||
return ClassFighter
|
|
||||||
}
|
|
||||||
if (classID >= ClassCleric && classID <= ClassDefiler) || (classID == ClassShaper || classID == ClassChanneler) {
|
|
||||||
return ClassPriest
|
|
||||||
}
|
|
||||||
if classID >= ClassSorcerer && classID <= ClassNecromancer {
|
|
||||||
return ClassMage
|
|
||||||
}
|
|
||||||
if classID >= ClassRogue && classID <= ClassBeastlord {
|
|
||||||
return ClassScout
|
|
||||||
}
|
|
||||||
return ClassCommoner // Default for unknown classes
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSecondaryBaseClass returns the secondary base class ID for specialized classes
|
|
||||||
// Converted from C++ Classes::GetSecondaryBaseClass
|
|
||||||
func GetSecondaryBaseClass(classID int8) int8 {
|
|
||||||
switch classID {
|
|
||||||
case ClassGuardian, ClassBerserker:
|
|
||||||
return ClassWarrior
|
|
||||||
case ClassMonk, ClassBruiser:
|
|
||||||
return ClassBrawler
|
|
||||||
case ClassShadowknight, ClassPaladin:
|
|
||||||
return ClassCrusader
|
|
||||||
case ClassTemplar, ClassInquisitor:
|
|
||||||
return ClassCleric
|
|
||||||
case ClassWarden, ClassFury:
|
|
||||||
return ClassDruid
|
|
||||||
case ClassMystic, ClassDefiler:
|
|
||||||
return ClassShaman
|
|
||||||
case ClassWizard, ClassWarlock:
|
|
||||||
return ClassSorcerer
|
|
||||||
case ClassIllusionist, ClassCoercer:
|
|
||||||
return ClassEnchanter
|
|
||||||
case ClassConjuror, ClassNecromancer:
|
|
||||||
return ClassSummoner
|
|
||||||
case ClassSwashbuckler, ClassBrigand:
|
|
||||||
return ClassRogue
|
|
||||||
case ClassTroubador, ClassDirge:
|
|
||||||
return ClassBard
|
|
||||||
case ClassRanger, ClassAssassin:
|
|
||||||
return ClassPredator
|
|
||||||
case ClassBeastlord:
|
|
||||||
return ClassAnimalist
|
|
||||||
case ClassChanneler:
|
|
||||||
return ClassShaper
|
|
||||||
}
|
|
||||||
return ClassCommoner // Default for unknown classes
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTSBaseClass returns the tradeskill base class ID
|
|
||||||
// Converted from C++ Classes::GetTSBaseClass
|
|
||||||
func GetTSBaseClass(classID int8) int8 {
|
|
||||||
// This function maps tradeskill class IDs to their base tradeskill progression levels
|
|
||||||
// The C++ code uses offset of 42 between adventure and tradeskill class systems
|
|
||||||
if classID+42 >= ClassArtisan {
|
|
||||||
return ClassArtisan - 44 // Returns 1 (base artisan level)
|
|
||||||
}
|
|
||||||
return classID // For non-tradeskill classes, return as-is
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSecondaryTSBaseClass returns the secondary tradeskill base class ID
|
|
||||||
// Converted from C++ Classes::GetSecondaryTSBaseClass
|
|
||||||
func GetSecondaryTSBaseClass(classID int8) int8 {
|
|
||||||
ret := classID + 42
|
|
||||||
|
|
||||||
if ret == ClassArtisan {
|
|
||||||
return ClassArtisan - 44
|
|
||||||
} else if ret >= ClassCraftsman && ret < ClassOutfitter {
|
|
||||||
return ClassCraftsman - 44
|
|
||||||
} else if ret >= ClassOutfitter && ret < ClassScholar {
|
|
||||||
return ClassOutfitter - 44
|
|
||||||
} else if ret >= ClassScholar {
|
|
||||||
return ClassScholar - 44
|
|
||||||
}
|
|
||||||
|
|
||||||
return classID
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsValidClassID checks if a class ID is valid
|
|
||||||
func IsValidClassID(classID int8) bool {
|
|
||||||
return classID >= MinClassID && classID <= MaxClassID
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsAdventureClass checks if a class is an adventure class
|
|
||||||
func IsAdventureClass(classID int8) bool {
|
|
||||||
return classID >= ClassCommoner && classID <= ClassChanneler
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsTradeskillClass checks if a class is a tradeskill class
|
|
||||||
func IsTradeskillClass(classID int8) bool {
|
|
||||||
return classID >= ClassArtisan && classID <= ClassAlchemist
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetClassType returns the type of class (adventure, tradeskill, etc.)
|
|
||||||
func GetClassType(classID int8) string {
|
|
||||||
if IsAdventureClass(classID) {
|
|
||||||
return ClassTypeAdventure
|
|
||||||
}
|
|
||||||
if IsTradeskillClass(classID) {
|
|
||||||
return ClassTypeTradeskill
|
|
||||||
}
|
|
||||||
return ClassTypeSpecial
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAllClasses returns all class IDs and their display names
|
|
||||||
func GetAllClasses() map[int8]string {
|
|
||||||
result := make(map[int8]string)
|
|
||||||
for classID, displayName := range displayNameMap {
|
|
||||||
result[classID] = displayName
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetClassInfo returns comprehensive information about a class
|
|
||||||
func GetClassInfo(classID int8) map[string]any {
|
|
||||||
info := make(map[string]any)
|
|
||||||
|
|
||||||
if !IsValidClassID(classID) {
|
|
||||||
info["valid"] = false
|
|
||||||
return info
|
|
||||||
}
|
|
||||||
|
|
||||||
info["valid"] = true
|
|
||||||
info["class_id"] = classID
|
|
||||||
info["name"] = GetClassName(classID)
|
|
||||||
info["display_name"] = GetClassNameCase(classID)
|
|
||||||
info["base_class"] = GetBaseClass(classID)
|
|
||||||
info["secondary_base_class"] = GetSecondaryBaseClass(classID)
|
|
||||||
info["type"] = GetClassType(classID)
|
|
||||||
info["is_adventure"] = IsAdventureClass(classID)
|
|
||||||
info["is_tradeskill"] = IsTradeskillClass(classID)
|
|
||||||
|
|
||||||
return info
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetClassHierarchy returns the full class hierarchy for a given class
|
|
||||||
func GetClassHierarchy(classID int8) []int8 {
|
|
||||||
if !IsValidClassID(classID) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
hierarchy := []int8{classID}
|
|
||||||
|
|
||||||
// Add secondary base class if it exists
|
|
||||||
secondary := GetSecondaryBaseClass(classID)
|
|
||||||
if secondary != ClassCommoner && secondary != classID {
|
|
||||||
hierarchy = append(hierarchy, secondary)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add base class
|
|
||||||
base := GetBaseClass(classID)
|
|
||||||
if base != ClassCommoner && base != classID {
|
|
||||||
// Check if base is already in hierarchy (from secondary)
|
|
||||||
found := false
|
|
||||||
for _, id := range hierarchy {
|
|
||||||
if id == base {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
hierarchy = append(hierarchy, base)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always add Commoner as the root
|
|
||||||
if classID != ClassCommoner {
|
|
||||||
hierarchy = append(hierarchy, ClassCommoner)
|
|
||||||
}
|
|
||||||
|
|
||||||
return hierarchy
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSameArchetype checks if two classes share the same base archetype
|
|
||||||
func IsSameArchetype(classID1, classID2 int8) bool {
|
|
||||||
if !IsValidClassID(classID1) || !IsValidClassID(classID2) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return GetBaseClass(classID1) == GetBaseClass(classID2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetArchetypeClasses returns all classes of a given archetype
|
|
||||||
func GetArchetypeClasses(archetypeID int8) []int8 {
|
|
||||||
var classes []int8
|
|
||||||
|
|
||||||
for classID := MinClassID; classID <= MaxClassID; classID++ {
|
|
||||||
if GetBaseClass(classID) == archetypeID {
|
|
||||||
classes = append(classes, classID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return classes
|
|
||||||
}
|
|
540
internal/classes/classes.go
Normal file
540
internal/classes/classes.go
Normal file
@ -0,0 +1,540 @@
|
|||||||
|
package classes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Class represents a character class with all its properties
|
||||||
|
type Class struct {
|
||||||
|
ID int8 `json:"id"`
|
||||||
|
Name string `json:"name"` // Uppercase name (WARRIOR)
|
||||||
|
DisplayName string `json:"display_name"` // Friendly name (Warrior)
|
||||||
|
BaseClass int8 `json:"base_class"` // Primary archetype
|
||||||
|
SecondaryBaseClass int8 `json:"secondary_base"` // Secondary archetype
|
||||||
|
ClassType string `json:"type"` // adventure/tradeskill/special
|
||||||
|
IsAdventure bool `json:"is_adventure"`
|
||||||
|
IsTradeskill bool `json:"is_tradeskill"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetID returns the class ID
|
||||||
|
func (c *Class) GetID() int8 {
|
||||||
|
return c.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName returns the uppercase class name (C++ API compatibility)
|
||||||
|
func (c *Class) GetName() string {
|
||||||
|
return c.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDisplayName returns the friendly display name
|
||||||
|
func (c *Class) GetDisplayName() string {
|
||||||
|
return c.DisplayName
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBaseClass returns the base archetype (C++ API compatibility)
|
||||||
|
func (c *Class) GetBaseClass() int8 {
|
||||||
|
return c.BaseClass
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSecondaryBaseClass returns the secondary archetype (C++ API compatibility)
|
||||||
|
func (c *Class) GetSecondaryBaseClass() int8 {
|
||||||
|
return c.SecondaryBaseClass
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid returns true if the class ID is valid
|
||||||
|
func (c *Class) IsValid() bool {
|
||||||
|
return c.ID >= MinClassID && c.ID <= MaxClassID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manager provides centralized class management with caching and lookup optimization
|
||||||
|
type Manager struct {
|
||||||
|
classes map[int8]*Class // ID -> Class
|
||||||
|
nameToID map[string]int8 // Uppercase name -> ID
|
||||||
|
displayNameMap map[int8]string // ID -> Display name
|
||||||
|
mutex sync.RWMutex
|
||||||
|
|
||||||
|
// Statistics
|
||||||
|
stats struct {
|
||||||
|
ClassesLoaded int32
|
||||||
|
LookupsPerformed int32
|
||||||
|
CacheHits int32
|
||||||
|
CacheMisses int32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewManager creates a new class manager
|
||||||
|
func NewManager() *Manager {
|
||||||
|
manager := &Manager{
|
||||||
|
classes: make(map[int8]*Class),
|
||||||
|
nameToID: make(map[string]int8),
|
||||||
|
displayNameMap: make(map[int8]string),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize with all classes
|
||||||
|
manager.initializeClasses()
|
||||||
|
return manager
|
||||||
|
}
|
||||||
|
|
||||||
|
// initializeClasses populates the manager with all class data
|
||||||
|
func (m *Manager) initializeClasses() {
|
||||||
|
m.mutex.Lock()
|
||||||
|
defer m.mutex.Unlock()
|
||||||
|
|
||||||
|
// Define all classes with their properties
|
||||||
|
classDefinitions := []struct {
|
||||||
|
id int8
|
||||||
|
name string
|
||||||
|
displayName string
|
||||||
|
baseClass int8
|
||||||
|
secondaryBase int8
|
||||||
|
classType string
|
||||||
|
isAdventure bool
|
||||||
|
isTradeskill bool
|
||||||
|
}{
|
||||||
|
// Base classes
|
||||||
|
{ClassCommoner, "COMMONER", "Commoner", ClassCommoner, ClassCommoner, ClassTypeAdventure, true, false},
|
||||||
|
{ClassFighter, "FIGHTER", "Fighter", ClassFighter, ClassCommoner, ClassTypeAdventure, true, false},
|
||||||
|
{ClassPriest, "PRIEST", "Priest", ClassPriest, ClassCommoner, ClassTypeAdventure, true, false},
|
||||||
|
{ClassMage, "MAGE", "Mage", ClassMage, ClassCommoner, ClassTypeAdventure, true, false},
|
||||||
|
{ClassScout, "SCOUT", "Scout", ClassScout, ClassCommoner, ClassTypeAdventure, true, false},
|
||||||
|
|
||||||
|
// Fighter subclasses
|
||||||
|
{ClassWarrior, "WARRIOR", "Warrior", ClassFighter, ClassCommoner, ClassTypeAdventure, true, false},
|
||||||
|
{ClassGuardian, "GUARDIAN", "Guardian", ClassFighter, ClassWarrior, ClassTypeAdventure, true, false},
|
||||||
|
{ClassBerserker, "BERSERKER", "Berserker", ClassFighter, ClassWarrior, ClassTypeAdventure, true, false},
|
||||||
|
{ClassBrawler, "BRAWLER", "Brawler", ClassFighter, ClassCommoner, ClassTypeAdventure, true, false},
|
||||||
|
{ClassMonk, "MONK", "Monk", ClassFighter, ClassBrawler, ClassTypeAdventure, true, false},
|
||||||
|
{ClassBruiser, "BRUISER", "Bruiser", ClassFighter, ClassBrawler, ClassTypeAdventure, true, false},
|
||||||
|
{ClassCrusader, "CRUSADER", "Crusader", ClassFighter, ClassCommoner, ClassTypeAdventure, true, false},
|
||||||
|
{ClassShadowknight, "SHADOWKNIGHT", "Shadowknight", ClassFighter, ClassCrusader, ClassTypeAdventure, true, false},
|
||||||
|
{ClassPaladin, "PALADIN", "Paladin", ClassFighter, ClassCrusader, ClassTypeAdventure, true, false},
|
||||||
|
|
||||||
|
// Priest subclasses
|
||||||
|
{ClassCleric, "CLERIC", "Cleric", ClassPriest, ClassCommoner, ClassTypeAdventure, true, false},
|
||||||
|
{ClassTemplar, "TEMPLAR", "Templar", ClassPriest, ClassCleric, ClassTypeAdventure, true, false},
|
||||||
|
{ClassInquisitor, "INQUISITOR", "Inquisitor", ClassPriest, ClassCleric, ClassTypeAdventure, true, false},
|
||||||
|
{ClassDruid, "DRUID", "Druid", ClassPriest, ClassCommoner, ClassTypeAdventure, true, false},
|
||||||
|
{ClassWarden, "WARDEN", "Warden", ClassPriest, ClassDruid, ClassTypeAdventure, true, false},
|
||||||
|
{ClassFury, "FURY", "Fury", ClassPriest, ClassDruid, ClassTypeAdventure, true, false},
|
||||||
|
{ClassShaman, "SHAMAN", "Shaman", ClassPriest, ClassCommoner, ClassTypeAdventure, true, false},
|
||||||
|
{ClassMystic, "MYSTIC", "Mystic", ClassPriest, ClassShaman, ClassTypeAdventure, true, false},
|
||||||
|
{ClassDefiler, "DEFILER", "Defiler", ClassPriest, ClassShaman, ClassTypeAdventure, true, false},
|
||||||
|
|
||||||
|
// Mage subclasses
|
||||||
|
{ClassSorcerer, "SORCERER", "Sorcerer", ClassMage, ClassCommoner, ClassTypeAdventure, true, false},
|
||||||
|
{ClassWizard, "WIZARD", "Wizard", ClassMage, ClassSorcerer, ClassTypeAdventure, true, false},
|
||||||
|
{ClassWarlock, "WARLOCK", "Warlock", ClassMage, ClassSorcerer, ClassTypeAdventure, true, false},
|
||||||
|
{ClassEnchanter, "ENCHANTER", "Enchanter", ClassMage, ClassCommoner, ClassTypeAdventure, true, false},
|
||||||
|
{ClassIllusionist, "ILLUSIONIST", "Illusionist", ClassMage, ClassEnchanter, ClassTypeAdventure, true, false},
|
||||||
|
{ClassCoercer, "COERCER", "Coercer", ClassMage, ClassEnchanter, ClassTypeAdventure, true, false},
|
||||||
|
{ClassSummoner, "SUMMONER", "Summoner", ClassMage, ClassCommoner, ClassTypeAdventure, true, false},
|
||||||
|
{ClassConjuror, "CONJUROR", "Conjuror", ClassMage, ClassSummoner, ClassTypeAdventure, true, false},
|
||||||
|
{ClassNecromancer, "NECROMANCER", "Necromancer", ClassMage, ClassSummoner, ClassTypeAdventure, true, false},
|
||||||
|
|
||||||
|
// Scout subclasses
|
||||||
|
{ClassRogue, "ROGUE", "Rogue", ClassScout, ClassCommoner, ClassTypeAdventure, true, false},
|
||||||
|
{ClassSwashbuckler, "SWASHBUCKLER", "Swashbuckler", ClassScout, ClassRogue, ClassTypeAdventure, true, false},
|
||||||
|
{ClassBrigand, "BRIGAND", "Brigand", ClassScout, ClassRogue, ClassTypeAdventure, true, false},
|
||||||
|
{ClassBard, "BARD", "Bard", ClassScout, ClassCommoner, ClassTypeAdventure, true, false},
|
||||||
|
{ClassTroubador, "TROUBADOR", "Troubador", ClassScout, ClassBard, ClassTypeAdventure, true, false},
|
||||||
|
{ClassDirge, "DIRGE", "Dirge", ClassScout, ClassBard, ClassTypeAdventure, true, false},
|
||||||
|
{ClassPredator, "PREDATOR", "Predator", ClassScout, ClassCommoner, ClassTypeAdventure, true, false},
|
||||||
|
{ClassRanger, "RANGER", "Ranger", ClassScout, ClassPredator, ClassTypeAdventure, true, false},
|
||||||
|
{ClassAssassin, "ASSASSIN", "Assassin", ClassScout, ClassPredator, ClassTypeAdventure, true, false},
|
||||||
|
{ClassAnimalist, "ANIMALIST", "Animalist", ClassScout, ClassCommoner, ClassTypeAdventure, true, false},
|
||||||
|
{ClassBeastlord, "BEASTLORD", "Beastlord", ClassScout, ClassAnimalist, ClassTypeAdventure, true, false},
|
||||||
|
|
||||||
|
// Special classes
|
||||||
|
{ClassShaper, "SHAPER", "Shaper", ClassPriest, ClassCommoner, ClassTypeAdventure, true, false},
|
||||||
|
{ClassChanneler, "CHANNELER", "Channeler", ClassPriest, ClassShaper, ClassTypeAdventure, true, false},
|
||||||
|
|
||||||
|
// Tradeskill classes
|
||||||
|
{ClassArtisan, "ARTISAN", "Artisan", ClassArtisan, ClassCommoner, ClassTypeTradeskill, false, true},
|
||||||
|
{ClassCraftsman, "CRAFTSMAN", "Craftsman", ClassArtisan, ClassCommoner, ClassTypeTradeskill, false, true},
|
||||||
|
{ClassProvisioner, "PROVISIONER", "Provisioner", ClassArtisan, ClassCraftsman, ClassTypeTradeskill, false, true},
|
||||||
|
{ClassWoodworker, "WOODWORKER", "Woodworker", ClassArtisan, ClassCraftsman, ClassTypeTradeskill, false, true},
|
||||||
|
{ClassCarpenter, "CARPENTER", "Carpenter", ClassArtisan, ClassCraftsman, ClassTypeTradeskill, false, true},
|
||||||
|
{ClassOutfitter, "OUTFITTER", "Outfitter", ClassArtisan, ClassCommoner, ClassTypeTradeskill, false, true},
|
||||||
|
{ClassArmorer, "ARMORER", "Armorer", ClassArtisan, ClassOutfitter, ClassTypeTradeskill, false, true},
|
||||||
|
{ClassWeaponsmith, "WEAPONSMITH", "Weaponsmith", ClassArtisan, ClassOutfitter, ClassTypeTradeskill, false, true},
|
||||||
|
{ClassTailor, "TAILOR", "Tailor", ClassArtisan, ClassOutfitter, ClassTypeTradeskill, false, true},
|
||||||
|
{ClassScholar, "SCHOLAR", "Scholar", ClassArtisan, ClassCommoner, ClassTypeTradeskill, false, true},
|
||||||
|
{ClassJeweler, "JEWELER", "Jeweler", ClassArtisan, ClassScholar, ClassTypeTradeskill, false, true},
|
||||||
|
{ClassSage, "SAGE", "Sage", ClassArtisan, ClassScholar, ClassTypeTradeskill, false, true},
|
||||||
|
{ClassAlchemist, "ALCHEMIST", "Alchemist", ClassArtisan, ClassScholar, ClassTypeTradeskill, false, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and store class objects
|
||||||
|
for _, def := range classDefinitions {
|
||||||
|
class := &Class{
|
||||||
|
ID: def.id,
|
||||||
|
Name: def.name,
|
||||||
|
DisplayName: def.displayName,
|
||||||
|
BaseClass: def.baseClass,
|
||||||
|
SecondaryBaseClass: def.secondaryBase,
|
||||||
|
ClassType: def.classType,
|
||||||
|
IsAdventure: def.isAdventure,
|
||||||
|
IsTradeskill: def.isTradeskill,
|
||||||
|
}
|
||||||
|
|
||||||
|
m.classes[def.id] = class
|
||||||
|
m.nameToID[def.name] = def.id
|
||||||
|
m.displayNameMap[def.id] = def.displayName
|
||||||
|
}
|
||||||
|
|
||||||
|
m.stats.ClassesLoaded = int32(len(classDefinitions))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetClass gets class by ID
|
||||||
|
func (m *Manager) GetClass(classID int8) *Class {
|
||||||
|
m.mutex.RLock()
|
||||||
|
defer m.mutex.RUnlock()
|
||||||
|
m.stats.LookupsPerformed++
|
||||||
|
|
||||||
|
if class, exists := m.classes[classID]; exists {
|
||||||
|
m.stats.CacheHits++
|
||||||
|
return class
|
||||||
|
}
|
||||||
|
|
||||||
|
m.stats.CacheMisses++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetClassID returns the class ID for a given class name (C++ API compatibility)
|
||||||
|
func (m *Manager) GetClassID(name string) int8 {
|
||||||
|
m.mutex.RLock()
|
||||||
|
defer m.mutex.RUnlock()
|
||||||
|
m.stats.LookupsPerformed++
|
||||||
|
|
||||||
|
className := strings.ToUpper(strings.TrimSpace(name))
|
||||||
|
if classID, exists := m.nameToID[className]; exists {
|
||||||
|
m.stats.CacheHits++
|
||||||
|
return classID
|
||||||
|
}
|
||||||
|
|
||||||
|
m.stats.CacheMisses++
|
||||||
|
return -1 // Invalid class
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetClassName returns the uppercase class name for a given ID (C++ API compatibility)
|
||||||
|
func (m *Manager) GetClassName(classID int8) string {
|
||||||
|
m.mutex.RLock()
|
||||||
|
defer m.mutex.RUnlock()
|
||||||
|
m.stats.LookupsPerformed++
|
||||||
|
|
||||||
|
if class := m.classes[classID]; class != nil {
|
||||||
|
m.stats.CacheHits++
|
||||||
|
return class.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
m.stats.CacheMisses++
|
||||||
|
return "" // Invalid class ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetClassNameCase returns the friendly display name for a given class ID (C++ API compatibility)
|
||||||
|
func (m *Manager) GetClassNameCase(classID int8) string {
|
||||||
|
m.mutex.RLock()
|
||||||
|
defer m.mutex.RUnlock()
|
||||||
|
m.stats.LookupsPerformed++
|
||||||
|
|
||||||
|
if displayName, exists := m.displayNameMap[classID]; exists {
|
||||||
|
m.stats.CacheHits++
|
||||||
|
return displayName
|
||||||
|
}
|
||||||
|
|
||||||
|
m.stats.CacheMisses++
|
||||||
|
return "" // Invalid class ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBaseClass returns the base class ID for a given class (C++ API compatibility)
|
||||||
|
func (m *Manager) GetBaseClass(classID int8) int8 {
|
||||||
|
m.mutex.RLock()
|
||||||
|
defer m.mutex.RUnlock()
|
||||||
|
m.stats.LookupsPerformed++
|
||||||
|
|
||||||
|
if class := m.classes[classID]; class != nil {
|
||||||
|
m.stats.CacheHits++
|
||||||
|
return class.BaseClass
|
||||||
|
}
|
||||||
|
|
||||||
|
m.stats.CacheMisses++
|
||||||
|
return ClassCommoner // Default for unknown classes
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSecondaryBaseClass returns the secondary base class ID (C++ API compatibility)
|
||||||
|
func (m *Manager) GetSecondaryBaseClass(classID int8) int8 {
|
||||||
|
m.mutex.RLock()
|
||||||
|
defer m.mutex.RUnlock()
|
||||||
|
m.stats.LookupsPerformed++
|
||||||
|
|
||||||
|
if class := m.classes[classID]; class != nil {
|
||||||
|
m.stats.CacheHits++
|
||||||
|
return class.SecondaryBaseClass
|
||||||
|
}
|
||||||
|
|
||||||
|
m.stats.CacheMisses++
|
||||||
|
return ClassCommoner // Default for unknown classes
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTSBaseClass returns the tradeskill base class ID (C++ API compatibility)
|
||||||
|
func (m *Manager) GetTSBaseClass(classID int8) int8 {
|
||||||
|
// This function maps tradeskill class IDs to their base tradeskill progression levels
|
||||||
|
// The C++ code uses offset of 42 between adventure and tradeskill class systems
|
||||||
|
if classID+42 >= ClassArtisan {
|
||||||
|
return ClassArtisan - 44 // Returns 1 (base artisan level)
|
||||||
|
}
|
||||||
|
return classID // For non-tradeskill classes, return as-is
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSecondaryTSBaseClass returns the secondary tradeskill base class ID (C++ API compatibility)
|
||||||
|
func (m *Manager) GetSecondaryTSBaseClass(classID int8) int8 {
|
||||||
|
ret := classID + 42
|
||||||
|
|
||||||
|
if ret == ClassArtisan {
|
||||||
|
return ClassArtisan - 44
|
||||||
|
} else if ret >= ClassCraftsman && ret < ClassOutfitter {
|
||||||
|
return ClassCraftsman - 44
|
||||||
|
} else if ret >= ClassOutfitter && ret < ClassScholar {
|
||||||
|
return ClassOutfitter - 44
|
||||||
|
} else if ret >= ClassScholar {
|
||||||
|
return ClassScholar - 44
|
||||||
|
}
|
||||||
|
|
||||||
|
return classID
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValidClassID checks if a class ID is valid
|
||||||
|
func (m *Manager) IsValidClassID(classID int8) bool {
|
||||||
|
return classID >= MinClassID && classID <= MaxClassID
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAdventureClass checks if a class is an adventure class
|
||||||
|
func (m *Manager) IsAdventureClass(classID int8) bool {
|
||||||
|
if class := m.GetClass(classID); class != nil {
|
||||||
|
return class.IsAdventure
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsTradeskillClass checks if a class is a tradeskill class
|
||||||
|
func (m *Manager) IsTradeskillClass(classID int8) bool {
|
||||||
|
if class := m.GetClass(classID); class != nil {
|
||||||
|
return class.IsTradeskill
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetClassType returns the type of class (adventure, tradeskill, etc.)
|
||||||
|
func (m *Manager) GetClassType(classID int8) string {
|
||||||
|
if class := m.GetClass(classID); class != nil {
|
||||||
|
return class.ClassType
|
||||||
|
}
|
||||||
|
return ClassTypeSpecial
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllClasses returns all class IDs and their display names
|
||||||
|
func (m *Manager) GetAllClasses() map[int8]string {
|
||||||
|
m.mutex.RLock()
|
||||||
|
defer m.mutex.RUnlock()
|
||||||
|
|
||||||
|
result := make(map[int8]string)
|
||||||
|
for classID, displayName := range m.displayNameMap {
|
||||||
|
result[classID] = displayName
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetClassInfo returns comprehensive information about a class
|
||||||
|
func (m *Manager) GetClassInfo(classID int8) map[string]any {
|
||||||
|
class := m.GetClass(classID)
|
||||||
|
info := make(map[string]any)
|
||||||
|
|
||||||
|
if class == nil {
|
||||||
|
info["valid"] = false
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
info["valid"] = true
|
||||||
|
info["class_id"] = class.ID
|
||||||
|
info["name"] = class.Name
|
||||||
|
info["display_name"] = class.DisplayName
|
||||||
|
info["base_class"] = class.BaseClass
|
||||||
|
info["secondary_base_class"] = class.SecondaryBaseClass
|
||||||
|
info["type"] = class.ClassType
|
||||||
|
info["is_adventure"] = class.IsAdventure
|
||||||
|
info["is_tradeskill"] = class.IsTradeskill
|
||||||
|
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetClassHierarchy returns the full class hierarchy for a given class
|
||||||
|
func (m *Manager) GetClassHierarchy(classID int8) []int8 {
|
||||||
|
class := m.GetClass(classID)
|
||||||
|
if class == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hierarchy := []int8{classID}
|
||||||
|
|
||||||
|
// Add secondary base class if it exists
|
||||||
|
if class.SecondaryBaseClass != ClassCommoner && class.SecondaryBaseClass != classID {
|
||||||
|
hierarchy = append(hierarchy, class.SecondaryBaseClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add base class
|
||||||
|
if class.BaseClass != ClassCommoner && class.BaseClass != classID {
|
||||||
|
// Check if base is already in hierarchy (from secondary)
|
||||||
|
found := false
|
||||||
|
for _, id := range hierarchy {
|
||||||
|
if id == class.BaseClass {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
hierarchy = append(hierarchy, class.BaseClass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always add Commoner as the root
|
||||||
|
if classID != ClassCommoner {
|
||||||
|
hierarchy = append(hierarchy, ClassCommoner)
|
||||||
|
}
|
||||||
|
|
||||||
|
return hierarchy
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSameArchetype checks if two classes share the same base archetype
|
||||||
|
func (m *Manager) IsSameArchetype(classID1, classID2 int8) bool {
|
||||||
|
class1 := m.GetClass(classID1)
|
||||||
|
class2 := m.GetClass(classID2)
|
||||||
|
|
||||||
|
if class1 == nil || class2 == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return class1.BaseClass == class2.BaseClass
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetArchetypeClasses returns all classes of a given archetype
|
||||||
|
func (m *Manager) GetArchetypeClasses(archetypeID int8) []int8 {
|
||||||
|
m.mutex.RLock()
|
||||||
|
defer m.mutex.RUnlock()
|
||||||
|
|
||||||
|
var classes []int8
|
||||||
|
for classID, class := range m.classes {
|
||||||
|
if class.BaseClass == archetypeID {
|
||||||
|
classes = append(classes, classID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return classes
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStatistics returns current manager statistics
|
||||||
|
func (m *Manager) GetStatistics() map[string]interface{} {
|
||||||
|
m.mutex.RLock()
|
||||||
|
defer m.mutex.RUnlock()
|
||||||
|
|
||||||
|
return map[string]interface{}{
|
||||||
|
"classes_loaded": m.stats.ClassesLoaded,
|
||||||
|
"lookups_performed": m.stats.LookupsPerformed,
|
||||||
|
"cache_hits": m.stats.CacheHits,
|
||||||
|
"cache_misses": m.stats.CacheMisses,
|
||||||
|
"total_classes": int32(len(m.classes)),
|
||||||
|
"adventure_classes": m.countClassesByType(ClassTypeAdventure),
|
||||||
|
"tradeskill_classes": m.countClassesByType(ClassTypeTradeskill),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// countClassesByType counts classes of a specific type
|
||||||
|
func (m *Manager) countClassesByType(classType string) int32 {
|
||||||
|
count := int32(0)
|
||||||
|
for _, class := range m.classes {
|
||||||
|
if class.ClassType == classType {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global manager instance
|
||||||
|
var globalManager *Manager
|
||||||
|
|
||||||
|
// InitializeManager initializes the global class manager
|
||||||
|
func InitializeManager() {
|
||||||
|
globalManager = NewManager()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetManager returns the global class manager
|
||||||
|
func GetManager() *Manager {
|
||||||
|
if globalManager == nil {
|
||||||
|
InitializeManager()
|
||||||
|
}
|
||||||
|
return globalManager
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global functions for C++ API compatibility
|
||||||
|
func GetClassID(name string) int8 {
|
||||||
|
return GetManager().GetClassID(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetClassName(classID int8) string {
|
||||||
|
return GetManager().GetClassName(classID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetClassNameCase(classID int8) string {
|
||||||
|
return GetManager().GetClassNameCase(classID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetBaseClass(classID int8) int8 {
|
||||||
|
return GetManager().GetBaseClass(classID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSecondaryBaseClass(classID int8) int8 {
|
||||||
|
return GetManager().GetSecondaryBaseClass(classID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTSBaseClass(classID int8) int8 {
|
||||||
|
return GetManager().GetTSBaseClass(classID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSecondaryTSBaseClass(classID int8) int8 {
|
||||||
|
return GetManager().GetSecondaryTSBaseClass(classID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsValidClassID(classID int8) bool {
|
||||||
|
return GetManager().IsValidClassID(classID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsAdventureClass(classID int8) bool {
|
||||||
|
return GetManager().IsAdventureClass(classID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsTradeskillClass(classID int8) bool {
|
||||||
|
return GetManager().IsTradeskillClass(classID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetClassType(classID int8) string {
|
||||||
|
return GetManager().GetClassType(classID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllClasses() map[int8]string {
|
||||||
|
return GetManager().GetAllClasses()
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetClassInfo(classID int8) map[string]any {
|
||||||
|
return GetManager().GetClassInfo(classID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetClassHierarchy(classID int8) []int8 {
|
||||||
|
return GetManager().GetClassHierarchy(classID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsSameArchetype(classID1, classID2 int8) bool {
|
||||||
|
return GetManager().IsSameArchetype(classID1, classID2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetArchetypeClasses(archetypeID int8) []int8 {
|
||||||
|
return GetManager().GetArchetypeClasses(archetypeID)
|
||||||
|
}
|
578
internal/classes/classes_test.go
Normal file
578
internal/classes/classes_test.go
Normal file
@ -0,0 +1,578 @@
|
|||||||
|
package classes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestClassBasics(t *testing.T) {
|
||||||
|
class := &Class{
|
||||||
|
ID: ClassWarrior,
|
||||||
|
Name: "WARRIOR",
|
||||||
|
DisplayName: "Warrior",
|
||||||
|
BaseClass: ClassFighter,
|
||||||
|
SecondaryBaseClass: ClassCommoner,
|
||||||
|
ClassType: ClassTypeAdventure,
|
||||||
|
IsAdventure: true,
|
||||||
|
IsTradeskill: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
if class.GetID() != ClassWarrior {
|
||||||
|
t.Errorf("Expected ID %d, got %d", ClassWarrior, class.GetID())
|
||||||
|
}
|
||||||
|
|
||||||
|
if class.GetName() != "WARRIOR" {
|
||||||
|
t.Errorf("Expected name 'WARRIOR', got %s", class.GetName())
|
||||||
|
}
|
||||||
|
|
||||||
|
if class.GetDisplayName() != "Warrior" {
|
||||||
|
t.Errorf("Expected display name 'Warrior', got %s", class.GetDisplayName())
|
||||||
|
}
|
||||||
|
|
||||||
|
if class.GetBaseClass() != ClassFighter {
|
||||||
|
t.Errorf("Expected base class %d, got %d", ClassFighter, class.GetBaseClass())
|
||||||
|
}
|
||||||
|
|
||||||
|
if class.GetSecondaryBaseClass() != ClassCommoner {
|
||||||
|
t.Errorf("Expected secondary base class %d, got %d", ClassCommoner, class.GetSecondaryBaseClass())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !class.IsValid() {
|
||||||
|
t.Error("Expected class to be valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test invalid class
|
||||||
|
invalidClass := &Class{ID: int8(99)}
|
||||||
|
if invalidClass.IsValid() {
|
||||||
|
t.Error("Expected invalid class to be invalid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManagerCreation(t *testing.T) {
|
||||||
|
manager := NewManager()
|
||||||
|
|
||||||
|
if manager == nil {
|
||||||
|
t.Fatal("Manager creation failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
stats := manager.GetStatistics()
|
||||||
|
if stats["classes_loaded"].(int32) == 0 {
|
||||||
|
t.Error("Expected classes to be loaded during initialization")
|
||||||
|
}
|
||||||
|
|
||||||
|
if stats["total_classes"].(int32) != MaxClasses {
|
||||||
|
t.Errorf("Expected %d total classes, got %d", MaxClasses, stats["total_classes"])
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Manager initialized with %d classes", stats["total_classes"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetClassID(t *testing.T) {
|
||||||
|
manager := NewManager()
|
||||||
|
|
||||||
|
// Test valid class names (C++ API compatibility)
|
||||||
|
testCases := map[string]int8{
|
||||||
|
"WARRIOR": ClassWarrior,
|
||||||
|
"GUARDIAN": ClassGuardian,
|
||||||
|
"TEMPLAR": ClassTemplar,
|
||||||
|
"WIZARD": ClassWizard,
|
||||||
|
"RANGER": ClassRanger,
|
||||||
|
"BEASTLORD": ClassBeastlord,
|
||||||
|
"CHANNELER": ClassChanneler,
|
||||||
|
"ARTISAN": ClassArtisan,
|
||||||
|
"PROVISIONER": ClassProvisioner,
|
||||||
|
"ALCHEMIST": ClassAlchemist,
|
||||||
|
"COMMONER": ClassCommoner,
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, expectedID := range testCases {
|
||||||
|
id := manager.GetClassID(name)
|
||||||
|
if id != expectedID {
|
||||||
|
t.Errorf("GetClassID('%s'): expected %d, got %d", name, expectedID, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case insensitivity and whitespace handling
|
||||||
|
id = manager.GetClassID(" " + strings.ToLower(name) + " ")
|
||||||
|
if id != expectedID {
|
||||||
|
t.Errorf("GetClassID('%s') with whitespace and lowercase: expected %d, got %d", name, expectedID, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test invalid class name
|
||||||
|
invalidID := manager.GetClassID("INVALID_CLASS")
|
||||||
|
if invalidID != -1 {
|
||||||
|
t.Errorf("Expected -1 for invalid class name, got %d", invalidID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test empty string
|
||||||
|
emptyID := manager.GetClassID("")
|
||||||
|
if emptyID != -1 {
|
||||||
|
t.Errorf("Expected -1 for empty class name, got %d", emptyID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetClassName(t *testing.T) {
|
||||||
|
manager := NewManager()
|
||||||
|
|
||||||
|
// Test valid class IDs (C++ API compatibility)
|
||||||
|
testCases := map[int8]string{
|
||||||
|
ClassWarrior: "WARRIOR",
|
||||||
|
ClassGuardian: "GUARDIAN",
|
||||||
|
ClassTemplar: "TEMPLAR",
|
||||||
|
ClassWizard: "WIZARD",
|
||||||
|
ClassRanger: "RANGER",
|
||||||
|
ClassBeastlord: "BEASTLORD",
|
||||||
|
ClassChanneler: "CHANNELER",
|
||||||
|
ClassArtisan: "ARTISAN",
|
||||||
|
ClassProvisioner: "PROVISIONER",
|
||||||
|
ClassAlchemist: "ALCHEMIST",
|
||||||
|
ClassCommoner: "COMMONER",
|
||||||
|
}
|
||||||
|
|
||||||
|
for classID, expectedName := range testCases {
|
||||||
|
name := manager.GetClassName(classID)
|
||||||
|
if name != expectedName {
|
||||||
|
t.Errorf("GetClassName(%d): expected '%s', got '%s'", classID, expectedName, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test invalid class ID
|
||||||
|
invalidName := manager.GetClassName(int8(99))
|
||||||
|
if invalidName != "" {
|
||||||
|
t.Errorf("Expected empty string for invalid class ID, got '%s'", invalidName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetClassNameCase(t *testing.T) {
|
||||||
|
manager := NewManager()
|
||||||
|
|
||||||
|
// Test display names (C++ API compatibility)
|
||||||
|
testCases := map[int8]string{
|
||||||
|
ClassWarrior: "Warrior",
|
||||||
|
ClassGuardian: "Guardian",
|
||||||
|
ClassTemplar: "Templar",
|
||||||
|
ClassWizard: "Wizard",
|
||||||
|
ClassRanger: "Ranger",
|
||||||
|
ClassBeastlord: "Beastlord",
|
||||||
|
ClassChanneler: "Channeler",
|
||||||
|
ClassArtisan: "Artisan",
|
||||||
|
ClassProvisioner: "Provisioner",
|
||||||
|
ClassAlchemist: "Alchemist",
|
||||||
|
ClassCommoner: "Commoner",
|
||||||
|
}
|
||||||
|
|
||||||
|
for classID, expectedDisplayName := range testCases {
|
||||||
|
displayName := manager.GetClassNameCase(classID)
|
||||||
|
if displayName != expectedDisplayName {
|
||||||
|
t.Errorf("GetClassNameCase(%d): expected '%s', got '%s'", classID, expectedDisplayName, displayName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test invalid class ID
|
||||||
|
invalidDisplayName := manager.GetClassNameCase(int8(99))
|
||||||
|
if invalidDisplayName != "" {
|
||||||
|
t.Errorf("Expected empty string for invalid class ID, got '%s'", invalidDisplayName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetBaseClass(t *testing.T) {
|
||||||
|
manager := NewManager()
|
||||||
|
|
||||||
|
// Test base class mappings (C++ API compatibility)
|
||||||
|
testCases := map[int8]int8{
|
||||||
|
// Fighter archetype
|
||||||
|
ClassWarrior: ClassFighter,
|
||||||
|
ClassGuardian: ClassFighter,
|
||||||
|
ClassBerserker: ClassFighter,
|
||||||
|
ClassMonk: ClassFighter,
|
||||||
|
ClassShadowknight: ClassFighter,
|
||||||
|
ClassPaladin: ClassFighter,
|
||||||
|
|
||||||
|
// Priest archetype
|
||||||
|
ClassCleric: ClassPriest,
|
||||||
|
ClassTemplar: ClassPriest,
|
||||||
|
ClassInquisitor: ClassPriest,
|
||||||
|
ClassWarden: ClassPriest,
|
||||||
|
ClassFury: ClassPriest,
|
||||||
|
ClassMystic: ClassPriest,
|
||||||
|
ClassDefiler: ClassPriest,
|
||||||
|
ClassChanneler: ClassPriest,
|
||||||
|
|
||||||
|
// Mage archetype
|
||||||
|
ClassSorcerer: ClassMage,
|
||||||
|
ClassWizard: ClassMage,
|
||||||
|
ClassWarlock: ClassMage,
|
||||||
|
ClassIllusionist: ClassMage,
|
||||||
|
ClassCoercer: ClassMage,
|
||||||
|
ClassConjuror: ClassMage,
|
||||||
|
ClassNecromancer: ClassMage,
|
||||||
|
|
||||||
|
// Scout archetype
|
||||||
|
ClassRogue: ClassScout,
|
||||||
|
ClassSwashbuckler: ClassScout,
|
||||||
|
ClassBrigand: ClassScout,
|
||||||
|
ClassTroubador: ClassScout,
|
||||||
|
ClassDirge: ClassScout,
|
||||||
|
ClassRanger: ClassScout,
|
||||||
|
ClassAssassin: ClassScout,
|
||||||
|
ClassBeastlord: ClassScout,
|
||||||
|
|
||||||
|
// Base classes should return themselves
|
||||||
|
ClassFighter: ClassFighter,
|
||||||
|
ClassPriest: ClassPriest,
|
||||||
|
ClassMage: ClassMage,
|
||||||
|
ClassScout: ClassScout,
|
||||||
|
ClassCommoner: ClassCommoner,
|
||||||
|
}
|
||||||
|
|
||||||
|
for classID, expectedBase := range testCases {
|
||||||
|
base := manager.GetBaseClass(classID)
|
||||||
|
if base != expectedBase {
|
||||||
|
t.Errorf("GetBaseClass(%d): expected %d, got %d", classID, expectedBase, base)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test invalid class ID
|
||||||
|
invalidBase := manager.GetBaseClass(int8(99))
|
||||||
|
if invalidBase != ClassCommoner {
|
||||||
|
t.Errorf("Expected %d (Commoner) for invalid class ID, got %d", ClassCommoner, invalidBase)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetSecondaryBaseClass(t *testing.T) {
|
||||||
|
manager := NewManager()
|
||||||
|
|
||||||
|
// Test secondary base class mappings (C++ API compatibility)
|
||||||
|
testCases := map[int8]int8{
|
||||||
|
ClassGuardian: ClassWarrior,
|
||||||
|
ClassBerserker: ClassWarrior,
|
||||||
|
ClassMonk: ClassBrawler,
|
||||||
|
ClassBruiser: ClassBrawler,
|
||||||
|
ClassShadowknight: ClassCrusader,
|
||||||
|
ClassPaladin: ClassCrusader,
|
||||||
|
ClassTemplar: ClassCleric,
|
||||||
|
ClassInquisitor: ClassCleric,
|
||||||
|
ClassWarden: ClassDruid,
|
||||||
|
ClassFury: ClassDruid,
|
||||||
|
ClassMystic: ClassShaman,
|
||||||
|
ClassDefiler: ClassShaman,
|
||||||
|
ClassWizard: ClassSorcerer,
|
||||||
|
ClassWarlock: ClassSorcerer,
|
||||||
|
ClassIllusionist: ClassEnchanter,
|
||||||
|
ClassCoercer: ClassEnchanter,
|
||||||
|
ClassConjuror: ClassSummoner,
|
||||||
|
ClassNecromancer: ClassSummoner,
|
||||||
|
ClassSwashbuckler: ClassRogue,
|
||||||
|
ClassBrigand: ClassRogue,
|
||||||
|
ClassTroubador: ClassBard,
|
||||||
|
ClassDirge: ClassBard,
|
||||||
|
ClassRanger: ClassPredator,
|
||||||
|
ClassAssassin: ClassPredator,
|
||||||
|
ClassBeastlord: ClassAnimalist,
|
||||||
|
ClassChanneler: ClassShaper,
|
||||||
|
}
|
||||||
|
|
||||||
|
for classID, expectedSecondary := range testCases {
|
||||||
|
secondary := manager.GetSecondaryBaseClass(classID)
|
||||||
|
if secondary != expectedSecondary {
|
||||||
|
t.Errorf("GetSecondaryBaseClass(%d): expected %d, got %d", classID, expectedSecondary, secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test base classes (should return Commoner)
|
||||||
|
baseClasses := []int8{ClassCommoner, ClassFighter, ClassPriest, ClassMage, ClassScout}
|
||||||
|
for _, classID := range baseClasses {
|
||||||
|
secondary := manager.GetSecondaryBaseClass(classID)
|
||||||
|
if secondary != ClassCommoner {
|
||||||
|
t.Errorf("GetSecondaryBaseClass(%d): expected %d (Commoner), got %d", classID, ClassCommoner, secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClassValidation(t *testing.T) {
|
||||||
|
manager := NewManager()
|
||||||
|
|
||||||
|
// Test valid class IDs
|
||||||
|
validClasses := []int8{
|
||||||
|
ClassCommoner, ClassFighter, ClassWarrior, ClassGuardian,
|
||||||
|
ClassTemplar, ClassWizard, ClassRanger, ClassBeastlord,
|
||||||
|
ClassChanneler, ClassArtisan, ClassAlchemist,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, classID := range validClasses {
|
||||||
|
if !manager.IsValidClassID(classID) {
|
||||||
|
t.Errorf("Expected class ID %d to be valid", classID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test invalid class IDs
|
||||||
|
invalidClasses := []int8{-1, -10, 58, 99, 127}
|
||||||
|
for _, classID := range invalidClasses {
|
||||||
|
if manager.IsValidClassID(classID) {
|
||||||
|
t.Errorf("Expected class ID %d to be invalid", classID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test boundary conditions
|
||||||
|
if !manager.IsValidClassID(MinClassID) {
|
||||||
|
t.Errorf("Expected MinClassID (%d) to be valid", MinClassID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !manager.IsValidClassID(MaxClassID) {
|
||||||
|
t.Errorf("Expected MaxClassID (%d) to be valid", MaxClassID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClassTypeChecking(t *testing.T) {
|
||||||
|
manager := NewManager()
|
||||||
|
|
||||||
|
// Test adventure classes
|
||||||
|
adventureClasses := []int8{
|
||||||
|
ClassCommoner, ClassFighter, ClassWarrior, ClassGuardian,
|
||||||
|
ClassTemplar, ClassWizard, ClassRanger, ClassBeastlord,
|
||||||
|
ClassChanneler,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, classID := range adventureClasses {
|
||||||
|
if !manager.IsAdventureClass(classID) {
|
||||||
|
t.Errorf("Expected class ID %d to be an adventure class", classID)
|
||||||
|
}
|
||||||
|
if manager.IsTradeskillClass(classID) {
|
||||||
|
t.Errorf("Expected class ID %d to not be a tradeskill class", classID)
|
||||||
|
}
|
||||||
|
if manager.GetClassType(classID) != ClassTypeAdventure {
|
||||||
|
t.Errorf("Expected class ID %d to have type '%s', got '%s'", classID, ClassTypeAdventure, manager.GetClassType(classID))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test tradeskill classes
|
||||||
|
tradeskillClasses := []int8{
|
||||||
|
ClassArtisan, ClassCraftsman, ClassProvisioner,
|
||||||
|
ClassOutfitter, ClassArmorer, ClassScholar,
|
||||||
|
ClassJeweler, ClassAlchemist,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, classID := range tradeskillClasses {
|
||||||
|
if manager.IsAdventureClass(classID) {
|
||||||
|
t.Errorf("Expected class ID %d to not be an adventure class", classID)
|
||||||
|
}
|
||||||
|
if !manager.IsTradeskillClass(classID) {
|
||||||
|
t.Errorf("Expected class ID %d to be a tradeskill class", classID)
|
||||||
|
}
|
||||||
|
if manager.GetClassType(classID) != ClassTypeTradeskill {
|
||||||
|
t.Errorf("Expected class ID %d to have type '%s', got '%s'", classID, ClassTypeTradeskill, manager.GetClassType(classID))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClassHierarchy(t *testing.T) {
|
||||||
|
manager := NewManager()
|
||||||
|
|
||||||
|
// Test Guardian hierarchy: Guardian -> Warrior -> Fighter -> Commoner
|
||||||
|
guardianHierarchy := manager.GetClassHierarchy(ClassGuardian)
|
||||||
|
expectedGuardian := []int8{ClassGuardian, ClassWarrior, ClassFighter, ClassCommoner}
|
||||||
|
|
||||||
|
if len(guardianHierarchy) != len(expectedGuardian) {
|
||||||
|
t.Errorf("Guardian hierarchy length: expected %d, got %d", len(expectedGuardian), len(guardianHierarchy))
|
||||||
|
} else {
|
||||||
|
for i, expected := range expectedGuardian {
|
||||||
|
if i < len(guardianHierarchy) && guardianHierarchy[i] != expected {
|
||||||
|
t.Errorf("Guardian hierarchy[%d]: expected %d, got %d", i, expected, guardianHierarchy[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Templar hierarchy: Templar -> Cleric -> Priest -> Commoner
|
||||||
|
templarHierarchy := manager.GetClassHierarchy(ClassTemplar)
|
||||||
|
expectedTemplar := []int8{ClassTemplar, ClassCleric, ClassPriest, ClassCommoner}
|
||||||
|
|
||||||
|
if len(templarHierarchy) != len(expectedTemplar) {
|
||||||
|
t.Errorf("Templar hierarchy length: expected %d, got %d", len(expectedTemplar), len(templarHierarchy))
|
||||||
|
} else {
|
||||||
|
for i, expected := range expectedTemplar {
|
||||||
|
if i < len(templarHierarchy) && templarHierarchy[i] != expected {
|
||||||
|
t.Errorf("Templar hierarchy[%d]: expected %d, got %d", i, expected, templarHierarchy[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Commoner hierarchy: just Commoner
|
||||||
|
commonerHierarchy := manager.GetClassHierarchy(ClassCommoner)
|
||||||
|
if len(commonerHierarchy) != 1 || commonerHierarchy[0] != ClassCommoner {
|
||||||
|
t.Errorf("Commoner hierarchy: expected [%d], got %v", ClassCommoner, commonerHierarchy)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test invalid class
|
||||||
|
invalidHierarchy := manager.GetClassHierarchy(int8(99))
|
||||||
|
if invalidHierarchy != nil {
|
||||||
|
t.Errorf("Expected nil hierarchy for invalid class, got %v", invalidHierarchy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestArchetypeOperations(t *testing.T) {
|
||||||
|
manager := NewManager()
|
||||||
|
|
||||||
|
// Test same archetype checking
|
||||||
|
if !manager.IsSameArchetype(ClassWarrior, ClassGuardian) {
|
||||||
|
t.Error("Expected Warrior and Guardian to be same archetype (Fighter)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !manager.IsSameArchetype(ClassTemplar, ClassMystic) {
|
||||||
|
t.Error("Expected Templar and Mystic to be same archetype (Priest)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if manager.IsSameArchetype(ClassWarrior, ClassTemplar) {
|
||||||
|
t.Error("Expected Warrior and Templar to be different archetypes")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test archetype class listing
|
||||||
|
fighterClasses := manager.GetArchetypeClasses(ClassFighter)
|
||||||
|
expectedFighterCount := 10 // Including base Fighter class (Fighter, Warrior, Guardian, Berserker, Brawler, Monk, Bruiser, Crusader, Shadowknight, Paladin)
|
||||||
|
if len(fighterClasses) != expectedFighterCount {
|
||||||
|
t.Errorf("Expected %d Fighter archetype classes, got %d", expectedFighterCount, len(fighterClasses))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify Fighter is in the list
|
||||||
|
foundFighter := false
|
||||||
|
for _, classID := range fighterClasses {
|
||||||
|
if classID == ClassFighter {
|
||||||
|
foundFighter = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundFighter {
|
||||||
|
t.Error("Expected Fighter base class to be in Fighter archetype list")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTradeskillClassOperations(t *testing.T) {
|
||||||
|
manager := NewManager()
|
||||||
|
|
||||||
|
// Test tradeskill base class operations (C++ API compatibility)
|
||||||
|
// Note: These functions use specific C++ logic with offsets
|
||||||
|
|
||||||
|
// Test with adventure class (should return as-is for most)
|
||||||
|
warriorTS := manager.GetTSBaseClass(ClassWarrior)
|
||||||
|
if warriorTS != ClassWarrior {
|
||||||
|
t.Errorf("Expected GetTSBaseClass(%d) to return %d, got %d", ClassWarrior, ClassWarrior, warriorTS)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test secondary tradeskill operations
|
||||||
|
warriorSecondaryTS := manager.GetSecondaryTSBaseClass(ClassWarrior)
|
||||||
|
// This should follow the C++ logic with +42 offset calculations
|
||||||
|
t.Logf("GetSecondaryTSBaseClass(ClassWarrior) = %d", warriorSecondaryTS)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManagerStatistics(t *testing.T) {
|
||||||
|
manager := NewManager()
|
||||||
|
|
||||||
|
// Perform some operations to generate statistics
|
||||||
|
manager.GetClassID("WARRIOR")
|
||||||
|
manager.GetClassName(ClassTemplar)
|
||||||
|
manager.GetClassNameCase(ClassRanger)
|
||||||
|
manager.GetBaseClass(ClassGuardian)
|
||||||
|
manager.GetClass(ClassBeastlord)
|
||||||
|
manager.GetClassID("INVALID_CLASS") // This should increment cache misses
|
||||||
|
|
||||||
|
stats := manager.GetStatistics()
|
||||||
|
|
||||||
|
// Check basic statistics
|
||||||
|
if stats["classes_loaded"].(int32) == 0 {
|
||||||
|
t.Error("Expected classes to be loaded")
|
||||||
|
}
|
||||||
|
|
||||||
|
if stats["lookups_performed"].(int32) == 0 {
|
||||||
|
t.Error("Expected lookups to be performed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if stats["total_classes"].(int32) != MaxClasses {
|
||||||
|
t.Errorf("Expected %d total classes, got %d", MaxClasses, stats["total_classes"])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check adventure vs tradeskill counts
|
||||||
|
adventureCount := stats["adventure_classes"].(int32)
|
||||||
|
tradeskillCount := stats["tradeskill_classes"].(int32)
|
||||||
|
|
||||||
|
if adventureCount == 0 {
|
||||||
|
t.Error("Expected some adventure classes")
|
||||||
|
}
|
||||||
|
|
||||||
|
if tradeskillCount == 0 {
|
||||||
|
t.Error("Expected some tradeskill classes")
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Statistics: %+v", stats)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGlobalFunctions(t *testing.T) {
|
||||||
|
// Test global functions that should initialize manager automatically
|
||||||
|
|
||||||
|
// Test basic operations
|
||||||
|
id := GetClassID("WARRIOR")
|
||||||
|
if id != ClassWarrior {
|
||||||
|
t.Errorf("GetClassID('WARRIOR'): expected %d, got %d", ClassWarrior, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
name := GetClassName(ClassTemplar)
|
||||||
|
if name != "TEMPLAR" {
|
||||||
|
t.Errorf("GetClassName(%d): expected 'TEMPLAR', got '%s'", ClassTemplar, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
displayName := GetClassNameCase(ClassRanger)
|
||||||
|
if displayName != "Ranger" {
|
||||||
|
t.Errorf("GetClassNameCase(%d): expected 'Ranger', got '%s'", ClassRanger, displayName)
|
||||||
|
}
|
||||||
|
|
||||||
|
base := GetBaseClass(ClassGuardian)
|
||||||
|
if base != ClassFighter {
|
||||||
|
t.Errorf("GetBaseClass(%d): expected %d, got %d", ClassGuardian, ClassFighter, base)
|
||||||
|
}
|
||||||
|
|
||||||
|
secondary := GetSecondaryBaseClass(ClassGuardian)
|
||||||
|
if secondary != ClassWarrior {
|
||||||
|
t.Errorf("GetSecondaryBaseClass(%d): expected %d, got %d", ClassGuardian, ClassWarrior, secondary)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test utility functions
|
||||||
|
if !IsValidClassID(ClassBeastlord) {
|
||||||
|
t.Errorf("Expected ClassBeastlord (%d) to be valid", ClassBeastlord)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !IsAdventureClass(ClassChanneler) {
|
||||||
|
t.Errorf("Expected ClassChanneler (%d) to be adventure class", ClassChanneler)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !IsTradeskillClass(ClassAlchemist) {
|
||||||
|
t.Errorf("Expected ClassAlchemist (%d) to be tradeskill class", ClassAlchemist)
|
||||||
|
}
|
||||||
|
|
||||||
|
classType := GetClassType(ClassWarrior)
|
||||||
|
if classType != ClassTypeAdventure {
|
||||||
|
t.Errorf("Expected ClassWarrior type to be '%s', got '%s'", ClassTypeAdventure, classType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test collection functions
|
||||||
|
allClasses := GetAllClasses()
|
||||||
|
if len(allClasses) != MaxClasses {
|
||||||
|
t.Errorf("Expected %d classes in GetAllClasses(), got %d", MaxClasses, len(allClasses))
|
||||||
|
}
|
||||||
|
|
||||||
|
info := GetClassInfo(ClassTemplar)
|
||||||
|
if !info["valid"].(bool) {
|
||||||
|
t.Error("Expected ClassTemplar info to be valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
hierarchy := GetClassHierarchy(ClassGuardian)
|
||||||
|
if len(hierarchy) == 0 {
|
||||||
|
t.Error("Expected non-empty hierarchy for ClassGuardian")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !IsSameArchetype(ClassWarrior, ClassGuardian) {
|
||||||
|
t.Error("Expected Warrior and Guardian to be same archetype")
|
||||||
|
}
|
||||||
|
|
||||||
|
fighterClasses := GetArchetypeClasses(ClassFighter)
|
||||||
|
if len(fighterClasses) == 0 {
|
||||||
|
t.Error("Expected non-empty list of Fighter archetype classes")
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user