package factions import ( "fmt" "sync" "eq2emu/internal/common" ) // MasterList manages all factions using the generic MasterList base type MasterList struct { *common.MasterList[int32, *Faction] factionNameList map[string]*Faction // Factions by name lookup hostileFactions map[int32][]int32 // Hostile faction relationships friendlyFactions map[int32][]int32 // Friendly faction relationships mutex sync.RWMutex // Additional mutex for relationships } // NewMasterList creates a new master faction list func NewMasterList() *MasterList { return &MasterList{ MasterList: common.NewMasterList[int32, *Faction](), factionNameList: make(map[string]*Faction), hostileFactions: make(map[int32][]int32), friendlyFactions: make(map[int32][]int32), } } // AddFaction adds a faction to the master list 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") } // Use generic base for main storage if !ml.MasterList.Add(faction) { return fmt.Errorf("faction with ID %d already exists", faction.ID) } // Update name lookup ml.mutex.Lock() ml.factionNameList[faction.Name] = faction ml.mutex.Unlock() return nil } // GetFaction returns a faction by ID func (ml *MasterList) GetFaction(id int32) *Faction { return ml.MasterList.Get(id) } // GetFactionByName returns a faction by name func (ml *MasterList) GetFactionByName(name string) *Faction { ml.mutex.RLock() defer ml.mutex.RUnlock() return ml.factionNameList[name] } // HasFaction checks if a faction exists by ID func (ml *MasterList) HasFaction(factionID int32) bool { return ml.MasterList.Exists(factionID) } // HasFactionByName checks if a faction exists by name func (ml *MasterList) HasFactionByName(name string) bool { ml.mutex.RLock() defer ml.mutex.RUnlock() _, exists := ml.factionNameList[name] return exists } // RemoveFaction removes a faction by ID func (ml *MasterList) RemoveFaction(factionID int32) bool { faction := ml.MasterList.Get(factionID) if faction == nil { return false } // Remove from generic base if !ml.MasterList.Remove(factionID) { return false } ml.mutex.Lock() defer ml.mutex.Unlock() // Remove from name lookup delete(ml.factionNameList, faction.Name) // 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 } return true } // UpdateFaction updates an existing faction 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") } oldFaction := ml.MasterList.Get(faction.ID) if oldFaction == nil { return fmt.Errorf("faction with ID %d does not exist", faction.ID) } // Update in generic base if err := ml.MasterList.Update(faction); err != nil { return err } ml.mutex.Lock() defer ml.mutex.Unlock() // If name changed, update name map if oldFaction.Name != faction.Name { delete(ml.factionNameList, oldFaction.Name) ml.factionNameList[faction.Name] = faction } return nil } // GetFactionCount returns the total number of factions func (ml *MasterList) GetFactionCount() int32 { return int32(ml.MasterList.Size()) } // GetAllFactions returns a copy of all factions func (ml *MasterList) GetAllFactions() map[int32]*Faction { return ml.MasterList.GetAll() } // GetFactionIDs returns all faction IDs func (ml *MasterList) GetFactionIDs() []int32 { return ml.MasterList.GetAllIDs() } // GetFactionsByType returns all factions of a specific type func (ml *MasterList) GetFactionsByType(factionType string) []*Faction { return ml.MasterList.Filter(func(f *Faction) bool { return f.Type == factionType }) } // Clear removes all factions and relationships func (ml *MasterList) Clear() { ml.MasterList.Clear() ml.mutex.Lock() defer ml.mutex.Unlock() ml.factionNameList = make(map[string]*Faction) ml.hostileFactions = make(map[int32][]int32) ml.friendlyFactions = make(map[int32][]int32) } // GetDefaultFactionValue returns the default value for a faction func (ml *MasterList) GetDefaultFactionValue(factionID int32) int32 { faction := ml.MasterList.Get(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 { faction := ml.MasterList.Get(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 { faction := ml.MasterList.Get(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 { faction := ml.MasterList.Get(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 // Use WithReadLock to avoid copying the entire map var seenIDs map[int32]*Faction ml.MasterList.WithReadLock(func(allFactions map[int32]*Faction) { seenIDs = make(map[int32]*Faction, len(allFactions)) // Pass 1: Validate main faction list and build seenID map for id, faction := range allFactions { if faction == nil { if issues == nil { issues = make([]string, 0, 10) } issues = append(issues, fmt.Sprintf("Faction ID %d is nil", id)) continue } if faction.ID <= 0 || faction.Name == "" { if issues == nil { issues = make([]string, 0, 10) } issues = append(issues, fmt.Sprintf("Faction ID %d is invalid or unnamed", id)) } if faction.ID != id { if issues == nil { issues = make([]string, 0, 10) } issues = append(issues, fmt.Sprintf("Faction ID mismatch: map key %d != faction ID %d", id, faction.ID)) } seenIDs[id] = faction } }) // Pass 2: Validate factionNameList for name, faction := range ml.factionNameList { if faction == nil { if issues == nil { issues = make([]string, 0, 10) } issues = append(issues, fmt.Sprintf("Faction name '%s' maps to nil", name)) continue } if faction.Name != name { if issues == nil { issues = make([]string, 0, 10) } issues = append(issues, fmt.Sprintf("Faction name mismatch: map key '%s' != faction name '%s'", name, faction.Name)) } if _, ok := seenIDs[faction.ID]; !ok { if issues == nil { issues = make([]string, 0, 10) } issues = append(issues, fmt.Sprintf("Faction '%s' (ID %d) exists in name map but not in ID map", name, faction.ID)) } } // Pass 3: Validate relationships using prebuilt seenIDs for sourceID, targets := range ml.hostileFactions { if _, ok := seenIDs[sourceID]; !ok { if issues == nil { issues = make([]string, 0, 10) } issues = append(issues, fmt.Sprintf("Hostile relationship defined for non-existent faction %d", sourceID)) } for _, targetID := range targets { if _, ok := seenIDs[targetID]; !ok { if issues == nil { issues = make([]string, 0, 10) } 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 := seenIDs[sourceID]; !ok { if issues == nil { issues = make([]string, 0, 10) } issues = append(issues, fmt.Sprintf("Friendly relationship defined for non-existent faction %d", sourceID)) } for _, targetID := range targets { if _, ok := seenIDs[targetID]; !ok { if issues == nil { issues = make([]string, 0, 10) } issues = append(issues, fmt.Sprintf("Faction %d has Friendly relationship with non-existent faction %d", sourceID, targetID)) } } } if issues == nil { return []string{} } return issues } // IsValid returns true if all factions are valid func (ml *MasterList) IsValid() bool { issues := ml.ValidateFactions() return len(issues) == 0 }