731 lines
20 KiB
Go
731 lines
20 KiB
Go
package factions
|
|
|
|
import (
|
|
"fmt"
|
|
"maps"
|
|
"strings"
|
|
"sync"
|
|
|
|
"eq2emu/internal/database"
|
|
)
|
|
|
|
// MasterList is a specialized faction master list optimized for:
|
|
// - Fast ID-based lookups (O(1))
|
|
// - Fast name-based lookups (O(1))
|
|
// - Fast type-based filtering (indexed)
|
|
// - Efficient faction relationships management
|
|
// - Special faction handling
|
|
// - Value range queries and validation
|
|
type MasterList struct {
|
|
// Core storage
|
|
factions map[int32]*Faction // ID -> Faction
|
|
mutex sync.RWMutex
|
|
|
|
// Specialized indices for O(1) lookups
|
|
byName map[string]*Faction // Lowercase name -> faction
|
|
byType map[string][]*Faction // Type -> factions
|
|
specialFactions map[int32]*Faction // Special factions (ID <= SpecialFactionIDMax)
|
|
regularFactions map[int32]*Faction // Regular factions (ID > SpecialFactionIDMax)
|
|
|
|
// Faction relationships
|
|
hostileFactions map[int32][]int32 // Hostile faction relationships
|
|
friendlyFactions map[int32][]int32 // Friendly faction relationships
|
|
|
|
// Cached metadata
|
|
types []string // Unique types (cached)
|
|
typeStats map[string]int // Type -> count
|
|
metaStale bool // Whether metadata cache needs refresh
|
|
}
|
|
|
|
// NewMasterList creates a new specialized faction master list
|
|
func NewMasterList() *MasterList {
|
|
return &MasterList{
|
|
factions: make(map[int32]*Faction),
|
|
byName: make(map[string]*Faction),
|
|
byType: make(map[string][]*Faction),
|
|
specialFactions: make(map[int32]*Faction),
|
|
regularFactions: make(map[int32]*Faction),
|
|
hostileFactions: make(map[int32][]int32),
|
|
friendlyFactions: make(map[int32][]int32),
|
|
typeStats: make(map[string]int),
|
|
metaStale: true,
|
|
}
|
|
}
|
|
|
|
// refreshMetaCache updates the cached metadata
|
|
func (ml *MasterList) refreshMetaCache() {
|
|
if !ml.metaStale {
|
|
return
|
|
}
|
|
|
|
// Clear and rebuild type stats
|
|
ml.typeStats = make(map[string]int)
|
|
typeSet := make(map[string]struct{})
|
|
|
|
// Collect unique values and stats
|
|
for _, faction := range ml.factions {
|
|
factionType := faction.GetType()
|
|
if factionType != "" {
|
|
ml.typeStats[factionType]++
|
|
typeSet[factionType] = struct{}{}
|
|
}
|
|
}
|
|
|
|
// Clear and rebuild cached slices
|
|
ml.types = ml.types[:0]
|
|
for factionType := range typeSet {
|
|
ml.types = append(ml.types, factionType)
|
|
}
|
|
|
|
ml.metaStale = false
|
|
}
|
|
|
|
// updateFactionIndices updates all indices for a faction
|
|
func (ml *MasterList) updateFactionIndices(faction *Faction, add bool) {
|
|
if add {
|
|
// Add to name index
|
|
ml.byName[strings.ToLower(faction.GetName())] = faction
|
|
|
|
// Add to type index
|
|
factionType := faction.GetType()
|
|
if factionType != "" {
|
|
ml.byType[factionType] = append(ml.byType[factionType], faction)
|
|
}
|
|
|
|
// Add to special/regular index
|
|
if faction.IsSpecialFaction() {
|
|
ml.specialFactions[faction.ID] = faction
|
|
} else {
|
|
ml.regularFactions[faction.ID] = faction
|
|
}
|
|
} else {
|
|
// Remove from name index
|
|
delete(ml.byName, strings.ToLower(faction.GetName()))
|
|
|
|
// Remove from type index
|
|
factionType := faction.GetType()
|
|
if factionType != "" {
|
|
typeFactionsSlice := ml.byType[factionType]
|
|
for i, f := range typeFactionsSlice {
|
|
if f.ID == faction.ID {
|
|
ml.byType[factionType] = append(typeFactionsSlice[:i], typeFactionsSlice[i+1:]...)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove from special/regular index
|
|
delete(ml.specialFactions, faction.ID)
|
|
delete(ml.regularFactions, faction.ID)
|
|
}
|
|
}
|
|
|
|
// AddFaction adds a faction with full indexing
|
|
func (ml *MasterList) AddFaction(faction *Faction) error {
|
|
if faction == nil {
|
|
return fmt.Errorf("faction cannot be nil")
|
|
}
|
|
|
|
if !faction.IsValid() {
|
|
return fmt.Errorf("faction is not valid")
|
|
}
|
|
|
|
ml.mutex.Lock()
|
|
defer ml.mutex.Unlock()
|
|
|
|
// Check if exists
|
|
if _, exists := ml.factions[faction.ID]; exists {
|
|
return fmt.Errorf("faction with ID %d already exists", faction.ID)
|
|
}
|
|
|
|
// Add to core storage
|
|
ml.factions[faction.ID] = faction
|
|
|
|
// Update all indices
|
|
ml.updateFactionIndices(faction, true)
|
|
|
|
// Invalidate metadata cache
|
|
ml.metaStale = true
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetFaction retrieves by ID (O(1))
|
|
func (ml *MasterList) GetFaction(id int32) *Faction {
|
|
ml.mutex.RLock()
|
|
defer ml.mutex.RUnlock()
|
|
return ml.factions[id]
|
|
}
|
|
|
|
// GetFactionSafe retrieves a faction by ID with existence check
|
|
func (ml *MasterList) GetFactionSafe(id int32) (*Faction, bool) {
|
|
ml.mutex.RLock()
|
|
defer ml.mutex.RUnlock()
|
|
faction, exists := ml.factions[id]
|
|
return faction, exists
|
|
}
|
|
|
|
// GetFactionByName retrieves a faction by name (case-insensitive, O(1))
|
|
func (ml *MasterList) GetFactionByName(name string) *Faction {
|
|
ml.mutex.RLock()
|
|
defer ml.mutex.RUnlock()
|
|
return ml.byName[strings.ToLower(name)]
|
|
}
|
|
|
|
// HasFaction checks if a faction exists by ID
|
|
func (ml *MasterList) HasFaction(factionID int32) bool {
|
|
ml.mutex.RLock()
|
|
defer ml.mutex.RUnlock()
|
|
_, exists := ml.factions[factionID]
|
|
return exists
|
|
}
|
|
|
|
// HasFactionByName checks if a faction exists by name
|
|
func (ml *MasterList) HasFactionByName(name string) bool {
|
|
ml.mutex.RLock()
|
|
defer ml.mutex.RUnlock()
|
|
_, exists := ml.byName[strings.ToLower(name)]
|
|
return exists
|
|
}
|
|
|
|
// RemoveFaction removes a faction and updates all indices
|
|
func (ml *MasterList) RemoveFaction(factionID int32) bool {
|
|
ml.mutex.Lock()
|
|
defer ml.mutex.Unlock()
|
|
|
|
faction, exists := ml.factions[factionID]
|
|
if !exists {
|
|
return false
|
|
}
|
|
|
|
// Remove from core storage
|
|
delete(ml.factions, factionID)
|
|
|
|
// Update all indices
|
|
ml.updateFactionIndices(faction, false)
|
|
|
|
// Remove from relationship maps
|
|
delete(ml.hostileFactions, factionID)
|
|
delete(ml.friendlyFactions, factionID)
|
|
|
|
// Remove references to this faction in other faction's relationships
|
|
for id, hostiles := range ml.hostileFactions {
|
|
newHostiles := make([]int32, 0, len(hostiles))
|
|
for _, hostileID := range hostiles {
|
|
if hostileID != factionID {
|
|
newHostiles = append(newHostiles, hostileID)
|
|
}
|
|
}
|
|
ml.hostileFactions[id] = newHostiles
|
|
}
|
|
|
|
for id, friendlies := range ml.friendlyFactions {
|
|
newFriendlies := make([]int32, 0, len(friendlies))
|
|
for _, friendlyID := range friendlies {
|
|
if friendlyID != factionID {
|
|
newFriendlies = append(newFriendlies, friendlyID)
|
|
}
|
|
}
|
|
ml.friendlyFactions[id] = newFriendlies
|
|
}
|
|
|
|
// Invalidate metadata cache
|
|
ml.metaStale = true
|
|
|
|
return true
|
|
}
|
|
|
|
// UpdateFaction updates an existing faction and refreshes indices
|
|
func (ml *MasterList) UpdateFaction(faction *Faction) error {
|
|
if faction == nil {
|
|
return fmt.Errorf("faction cannot be nil")
|
|
}
|
|
|
|
if !faction.IsValid() {
|
|
return fmt.Errorf("faction is not valid")
|
|
}
|
|
|
|
ml.mutex.Lock()
|
|
defer ml.mutex.Unlock()
|
|
|
|
// Check if exists
|
|
old, exists := ml.factions[faction.ID]
|
|
if !exists {
|
|
return fmt.Errorf("faction %d not found", faction.ID)
|
|
}
|
|
|
|
// Remove old faction from indices (but not core storage yet)
|
|
ml.updateFactionIndices(old, false)
|
|
|
|
// Update core storage
|
|
ml.factions[faction.ID] = faction
|
|
|
|
// Add new faction to indices
|
|
ml.updateFactionIndices(faction, true)
|
|
|
|
// Invalidate metadata cache
|
|
ml.metaStale = true
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetFactionCount returns the total number of factions
|
|
func (ml *MasterList) GetFactionCount() int32 {
|
|
ml.mutex.RLock()
|
|
defer ml.mutex.RUnlock()
|
|
return int32(len(ml.factions))
|
|
}
|
|
|
|
// GetAllFactions returns a copy of all factions map
|
|
func (ml *MasterList) GetAllFactions() map[int32]*Faction {
|
|
ml.mutex.RLock()
|
|
defer ml.mutex.RUnlock()
|
|
|
|
// Return a copy to prevent external modification
|
|
result := make(map[int32]*Faction, len(ml.factions))
|
|
maps.Copy(result, ml.factions)
|
|
return result
|
|
}
|
|
|
|
// GetAllFactionsList returns all factions as a slice
|
|
func (ml *MasterList) GetAllFactionsList() []*Faction {
|
|
ml.mutex.RLock()
|
|
defer ml.mutex.RUnlock()
|
|
|
|
result := make([]*Faction, 0, len(ml.factions))
|
|
for _, faction := range ml.factions {
|
|
result = append(result, faction)
|
|
}
|
|
return result
|
|
}
|
|
|
|
// GetFactionIDs returns all faction IDs
|
|
func (ml *MasterList) GetFactionIDs() []int32 {
|
|
ml.mutex.RLock()
|
|
defer ml.mutex.RUnlock()
|
|
|
|
result := make([]int32, 0, len(ml.factions))
|
|
for id := range ml.factions {
|
|
result = append(result, id)
|
|
}
|
|
return result
|
|
}
|
|
|
|
// GetFactionsByType returns all factions of a specific type (O(1))
|
|
func (ml *MasterList) GetFactionsByType(factionType string) []*Faction {
|
|
ml.mutex.RLock()
|
|
defer ml.mutex.RUnlock()
|
|
return ml.byType[factionType]
|
|
}
|
|
|
|
// GetSpecialFactions returns all special factions (ID <= SpecialFactionIDMax)
|
|
func (ml *MasterList) GetSpecialFactions() map[int32]*Faction {
|
|
ml.mutex.RLock()
|
|
defer ml.mutex.RUnlock()
|
|
|
|
// Return a copy to prevent external modification
|
|
result := make(map[int32]*Faction, len(ml.specialFactions))
|
|
maps.Copy(result, ml.specialFactions)
|
|
return result
|
|
}
|
|
|
|
// GetRegularFactions returns all regular factions (ID > SpecialFactionIDMax)
|
|
func (ml *MasterList) GetRegularFactions() map[int32]*Faction {
|
|
ml.mutex.RLock()
|
|
defer ml.mutex.RUnlock()
|
|
|
|
// Return a copy to prevent external modification
|
|
result := make(map[int32]*Faction, len(ml.regularFactions))
|
|
maps.Copy(result, ml.regularFactions)
|
|
return result
|
|
}
|
|
|
|
// Size returns the total number of factions
|
|
func (ml *MasterList) Size() int {
|
|
ml.mutex.RLock()
|
|
defer ml.mutex.RUnlock()
|
|
return len(ml.factions)
|
|
}
|
|
|
|
// IsEmpty returns true if the master list is empty
|
|
func (ml *MasterList) IsEmpty() bool {
|
|
ml.mutex.RLock()
|
|
defer ml.mutex.RUnlock()
|
|
return len(ml.factions) == 0
|
|
}
|
|
|
|
// Clear removes all factions and relationships
|
|
func (ml *MasterList) Clear() {
|
|
ml.mutex.Lock()
|
|
defer ml.mutex.Unlock()
|
|
|
|
// Clear all maps
|
|
ml.factions = make(map[int32]*Faction)
|
|
ml.byName = make(map[string]*Faction)
|
|
ml.byType = make(map[string][]*Faction)
|
|
ml.specialFactions = make(map[int32]*Faction)
|
|
ml.regularFactions = make(map[int32]*Faction)
|
|
ml.hostileFactions = make(map[int32][]int32)
|
|
ml.friendlyFactions = make(map[int32][]int32)
|
|
|
|
// Clear cached metadata
|
|
ml.types = ml.types[:0]
|
|
ml.typeStats = make(map[string]int)
|
|
ml.metaStale = true
|
|
}
|
|
|
|
// GetTypes returns all unique faction types using cached results
|
|
func (ml *MasterList) GetTypes() []string {
|
|
ml.mutex.Lock() // Need write lock to potentially update cache
|
|
defer ml.mutex.Unlock()
|
|
|
|
ml.refreshMetaCache()
|
|
|
|
// Return a copy to prevent external modification
|
|
result := make([]string, len(ml.types))
|
|
copy(result, ml.types)
|
|
return result
|
|
}
|
|
|
|
// GetDefaultFactionValue returns the default value for a faction
|
|
func (ml *MasterList) GetDefaultFactionValue(factionID int32) int32 {
|
|
ml.mutex.RLock()
|
|
defer ml.mutex.RUnlock()
|
|
faction := ml.factions[factionID]
|
|
if faction != nil {
|
|
return faction.DefaultValue
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// GetIncreaseAmount returns the default increase amount for a faction
|
|
func (ml *MasterList) GetIncreaseAmount(factionID int32) int32 {
|
|
ml.mutex.RLock()
|
|
defer ml.mutex.RUnlock()
|
|
faction := ml.factions[factionID]
|
|
if faction != nil {
|
|
return int32(faction.PositiveChange)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// GetDecreaseAmount returns the default decrease amount for a faction
|
|
func (ml *MasterList) GetDecreaseAmount(factionID int32) int32 {
|
|
ml.mutex.RLock()
|
|
defer ml.mutex.RUnlock()
|
|
faction := ml.factions[factionID]
|
|
if faction != nil {
|
|
return int32(faction.NegativeChange)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// GetFactionNameByID returns the faction name for a given ID
|
|
func (ml *MasterList) GetFactionNameByID(factionID int32) string {
|
|
if factionID > 0 {
|
|
ml.mutex.RLock()
|
|
defer ml.mutex.RUnlock()
|
|
faction := ml.factions[factionID]
|
|
if faction != nil {
|
|
return faction.Name
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// AddHostileFaction adds a hostile relationship between factions
|
|
func (ml *MasterList) AddHostileFaction(factionID, hostileFactionID int32) {
|
|
ml.mutex.Lock()
|
|
defer ml.mutex.Unlock()
|
|
ml.hostileFactions[factionID] = append(ml.hostileFactions[factionID], hostileFactionID)
|
|
}
|
|
|
|
// AddFriendlyFaction adds a friendly relationship between factions
|
|
func (ml *MasterList) AddFriendlyFaction(factionID, friendlyFactionID int32) {
|
|
ml.mutex.Lock()
|
|
defer ml.mutex.Unlock()
|
|
ml.friendlyFactions[factionID] = append(ml.friendlyFactions[factionID], friendlyFactionID)
|
|
}
|
|
|
|
// GetFriendlyFactions returns all friendly factions for a given faction
|
|
func (ml *MasterList) GetFriendlyFactions(factionID int32) []int32 {
|
|
ml.mutex.RLock()
|
|
defer ml.mutex.RUnlock()
|
|
|
|
if factions, exists := ml.friendlyFactions[factionID]; exists {
|
|
result := make([]int32, len(factions))
|
|
copy(result, factions)
|
|
return result
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetHostileFactions returns all hostile factions for a given faction
|
|
func (ml *MasterList) GetHostileFactions(factionID int32) []int32 {
|
|
ml.mutex.RLock()
|
|
defer ml.mutex.RUnlock()
|
|
|
|
if factions, exists := ml.hostileFactions[factionID]; exists {
|
|
result := make([]int32, len(factions))
|
|
copy(result, factions)
|
|
return result
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateFactions checks all factions for consistency
|
|
func (ml *MasterList) ValidateFactions() []string {
|
|
ml.mutex.RLock()
|
|
defer ml.mutex.RUnlock()
|
|
|
|
var issues []string
|
|
|
|
// Pass 1: Validate main faction list
|
|
for id, faction := range ml.factions {
|
|
if faction == nil {
|
|
issues = append(issues, fmt.Sprintf("Faction ID %d is nil", id))
|
|
continue
|
|
}
|
|
|
|
if faction.ID <= 0 || faction.Name == "" {
|
|
issues = append(issues, fmt.Sprintf("Faction ID %d is invalid or unnamed", id))
|
|
}
|
|
|
|
if faction.ID != id {
|
|
issues = append(issues, fmt.Sprintf("Faction ID mismatch: map key %d != faction ID %d", id, faction.ID))
|
|
}
|
|
}
|
|
|
|
// Pass 2: Validate byName index
|
|
for name, faction := range ml.byName {
|
|
if faction == nil {
|
|
issues = append(issues, fmt.Sprintf("Faction name '%s' maps to nil", name))
|
|
continue
|
|
}
|
|
|
|
if strings.ToLower(faction.Name) != name {
|
|
issues = append(issues, fmt.Sprintf("Faction name index mismatch: map key '%s' != lowercase faction name '%s'", name, strings.ToLower(faction.Name)))
|
|
}
|
|
|
|
if _, ok := ml.factions[faction.ID]; !ok {
|
|
issues = append(issues, fmt.Sprintf("Faction '%s' (ID %d) exists in name index but not in main storage", faction.Name, faction.ID))
|
|
}
|
|
}
|
|
|
|
// Pass 3: Validate byType index
|
|
for factionType, factions := range ml.byType {
|
|
for _, faction := range factions {
|
|
if faction == nil {
|
|
issues = append(issues, fmt.Sprintf("Type '%s' has nil faction", factionType))
|
|
continue
|
|
}
|
|
|
|
if faction.Type != factionType {
|
|
issues = append(issues, fmt.Sprintf("Faction %d (type '%s') found in wrong type index '%s'", faction.ID, faction.Type, factionType))
|
|
}
|
|
|
|
if _, ok := ml.factions[faction.ID]; !ok {
|
|
issues = append(issues, fmt.Sprintf("Faction %d exists in type index but not in main storage", faction.ID))
|
|
}
|
|
}
|
|
}
|
|
|
|
// Pass 4: Validate special/regular faction indices
|
|
for id, faction := range ml.specialFactions {
|
|
if faction == nil {
|
|
issues = append(issues, fmt.Sprintf("Special faction ID %d is nil", id))
|
|
continue
|
|
}
|
|
|
|
if !faction.IsSpecialFaction() {
|
|
issues = append(issues, fmt.Sprintf("Faction %d is in special index but is not special (ID > %d)", id, SpecialFactionIDMax))
|
|
}
|
|
|
|
if _, ok := ml.factions[id]; !ok {
|
|
issues = append(issues, fmt.Sprintf("Special faction %d exists in special index but not in main storage", id))
|
|
}
|
|
}
|
|
|
|
for id, faction := range ml.regularFactions {
|
|
if faction == nil {
|
|
issues = append(issues, fmt.Sprintf("Regular faction ID %d is nil", id))
|
|
continue
|
|
}
|
|
|
|
if faction.IsSpecialFaction() {
|
|
issues = append(issues, fmt.Sprintf("Faction %d is in regular index but is special (ID <= %d)", id, SpecialFactionIDMax))
|
|
}
|
|
|
|
if _, ok := ml.factions[id]; !ok {
|
|
issues = append(issues, fmt.Sprintf("Regular faction %d exists in regular index but not in main storage", id))
|
|
}
|
|
}
|
|
|
|
// Pass 5: Validate relationships
|
|
for sourceID, targets := range ml.hostileFactions {
|
|
if _, ok := ml.factions[sourceID]; !ok {
|
|
issues = append(issues, fmt.Sprintf("Hostile relationship defined for non-existent faction %d", sourceID))
|
|
}
|
|
for _, targetID := range targets {
|
|
if _, ok := ml.factions[targetID]; !ok {
|
|
issues = append(issues, fmt.Sprintf("Faction %d has hostile relationship with non-existent faction %d", sourceID, targetID))
|
|
}
|
|
}
|
|
}
|
|
|
|
for sourceID, targets := range ml.friendlyFactions {
|
|
if _, ok := ml.factions[sourceID]; !ok {
|
|
issues = append(issues, fmt.Sprintf("Friendly relationship defined for non-existent faction %d", sourceID))
|
|
}
|
|
for _, targetID := range targets {
|
|
if _, ok := ml.factions[targetID]; !ok {
|
|
issues = append(issues, fmt.Sprintf("Faction %d has friendly relationship with non-existent faction %d", sourceID, targetID))
|
|
}
|
|
}
|
|
}
|
|
|
|
return issues
|
|
}
|
|
|
|
// IsValid returns true if all factions are valid
|
|
func (ml *MasterList) IsValid() bool {
|
|
issues := ml.ValidateFactions()
|
|
return len(issues) == 0
|
|
}
|
|
|
|
// ForEach executes a function for each faction
|
|
func (ml *MasterList) ForEach(fn func(int32, *Faction)) {
|
|
ml.mutex.RLock()
|
|
defer ml.mutex.RUnlock()
|
|
|
|
for id, faction := range ml.factions {
|
|
fn(id, faction)
|
|
}
|
|
}
|
|
|
|
// GetStatistics returns statistics about the faction system using cached data
|
|
func (ml *MasterList) GetStatistics() map[string]any {
|
|
ml.mutex.Lock() // Need write lock to potentially update cache
|
|
defer ml.mutex.Unlock()
|
|
|
|
ml.refreshMetaCache()
|
|
|
|
stats := make(map[string]any)
|
|
stats["total_factions"] = len(ml.factions)
|
|
|
|
if len(ml.factions) == 0 {
|
|
return stats
|
|
}
|
|
|
|
// Use cached type stats
|
|
stats["factions_by_type"] = ml.typeStats
|
|
|
|
// Calculate additional stats
|
|
var specialCount, regularCount int
|
|
var minID, maxID int32
|
|
var minDefaultValue, maxDefaultValue int32 = MaxFactionValue, MinFactionValue
|
|
var totalPositiveChange, totalNegativeChange int64
|
|
first := true
|
|
|
|
for id, faction := range ml.factions {
|
|
if faction.IsSpecialFaction() {
|
|
specialCount++
|
|
} else {
|
|
regularCount++
|
|
}
|
|
|
|
if first {
|
|
minID = id
|
|
maxID = id
|
|
minDefaultValue = faction.DefaultValue
|
|
maxDefaultValue = faction.DefaultValue
|
|
first = false
|
|
} else {
|
|
if id < minID {
|
|
minID = id
|
|
}
|
|
if id > maxID {
|
|
maxID = id
|
|
}
|
|
if faction.DefaultValue < minDefaultValue {
|
|
minDefaultValue = faction.DefaultValue
|
|
}
|
|
if faction.DefaultValue > maxDefaultValue {
|
|
maxDefaultValue = faction.DefaultValue
|
|
}
|
|
}
|
|
|
|
totalPositiveChange += int64(faction.PositiveChange)
|
|
totalNegativeChange += int64(faction.NegativeChange)
|
|
}
|
|
|
|
stats["special_factions"] = specialCount
|
|
stats["regular_factions"] = regularCount
|
|
stats["min_id"] = minID
|
|
stats["max_id"] = maxID
|
|
stats["id_range"] = maxID - minID
|
|
stats["min_default_value"] = minDefaultValue
|
|
stats["max_default_value"] = maxDefaultValue
|
|
stats["total_positive_change"] = totalPositiveChange
|
|
stats["total_negative_change"] = totalNegativeChange
|
|
|
|
// Relationship stats
|
|
stats["total_hostile_relationships"] = len(ml.hostileFactions)
|
|
stats["total_friendly_relationships"] = len(ml.friendlyFactions)
|
|
|
|
return stats
|
|
}
|
|
|
|
// LoadAllFactions loads all factions from the database into the master list
|
|
func (ml *MasterList) LoadAllFactions(db *database.Database) error {
|
|
if db == nil {
|
|
return fmt.Errorf("database connection is nil")
|
|
}
|
|
|
|
// Clear existing factions
|
|
ml.Clear()
|
|
|
|
query := `SELECT id, name, type, description, negative_change, positive_change, default_value FROM factions ORDER BY id`
|
|
rows, err := db.Query(query)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to query factions: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
count := 0
|
|
for rows.Next() {
|
|
faction := &Faction{
|
|
db: db,
|
|
isNew: false,
|
|
}
|
|
|
|
err := rows.Scan(&faction.ID, &faction.Name, &faction.Type, &faction.Description,
|
|
&faction.NegativeChange, &faction.PositiveChange, &faction.DefaultValue)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to scan faction: %w", err)
|
|
}
|
|
|
|
if err := ml.AddFaction(faction); err != nil {
|
|
return fmt.Errorf("failed to add faction %d to master list: %w", faction.ID, err)
|
|
}
|
|
|
|
count++
|
|
}
|
|
|
|
if err := rows.Err(); err != nil {
|
|
return fmt.Errorf("error iterating faction rows: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// LoadAllFactionsFromDatabase is a convenience function that creates a master list and loads all factions
|
|
func LoadAllFactionsFromDatabase(db *database.Database) (*MasterList, error) {
|
|
masterList := NewMasterList()
|
|
err := masterList.LoadAllFactions(db)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return masterList, nil
|
|
}
|