package factions import ( "fmt" "sync" ) // MasterFactionList manages all factions in the game type MasterFactionList struct { globalFactionList map[int32]*Faction // Factions by ID factionNameList map[string]*Faction // Factions by name hostileFactions map[int32][]int32 // Hostile faction relationships friendlyFactions map[int32][]int32 // Friendly faction relationships mutex sync.RWMutex // Thread safety } // NewMasterFactionList creates a new master faction list func NewMasterFactionList() *MasterFactionList { return &MasterFactionList{ globalFactionList: make(map[int32]*Faction), factionNameList: make(map[string]*Faction), hostileFactions: make(map[int32][]int32), friendlyFactions: make(map[int32][]int32), } } // Clear removes all factions and relationships func (mfl *MasterFactionList) Clear() { mfl.mutex.Lock() defer mfl.mutex.Unlock() // Clear all maps - Go's garbage collector will handle cleanup mfl.globalFactionList = make(map[int32]*Faction) mfl.factionNameList = make(map[string]*Faction) mfl.hostileFactions = make(map[int32][]int32) mfl.friendlyFactions = make(map[int32][]int32) } // GetDefaultFactionValue returns the default value for a faction func (mfl *MasterFactionList) GetDefaultFactionValue(factionID int32) int32 { mfl.mutex.RLock() defer mfl.mutex.RUnlock() if faction, exists := mfl.globalFactionList[factionID]; exists && faction != nil { return faction.DefaultValue } return 0 } // GetFaction returns a faction by name func (mfl *MasterFactionList) GetFactionByName(name string) *Faction { mfl.mutex.RLock() defer mfl.mutex.RUnlock() return mfl.factionNameList[name] } // GetFaction returns a faction by ID func (mfl *MasterFactionList) GetFaction(id int32) *Faction { mfl.mutex.RLock() defer mfl.mutex.RUnlock() if faction, exists := mfl.globalFactionList[id]; exists { return faction } return nil } // AddFaction adds a faction to the master list func (mfl *MasterFactionList) AddFaction(faction *Faction) error { if faction == nil { return fmt.Errorf("faction cannot be nil") } if !faction.IsValid() { return fmt.Errorf("faction is not valid") } mfl.mutex.Lock() defer mfl.mutex.Unlock() mfl.globalFactionList[faction.ID] = faction mfl.factionNameList[faction.Name] = faction return nil } // GetIncreaseAmount returns the default increase amount for a faction func (mfl *MasterFactionList) GetIncreaseAmount(factionID int32) int32 { mfl.mutex.RLock() defer mfl.mutex.RUnlock() if faction, exists := mfl.globalFactionList[factionID]; exists && faction != nil { return int32(faction.PositiveChange) } return 0 } // GetDecreaseAmount returns the default decrease amount for a faction func (mfl *MasterFactionList) GetDecreaseAmount(factionID int32) int32 { mfl.mutex.RLock() defer mfl.mutex.RUnlock() if faction, exists := mfl.globalFactionList[factionID]; exists && faction != nil { return int32(faction.NegativeChange) } return 0 } // GetFactionCount returns the total number of factions func (mfl *MasterFactionList) GetFactionCount() int32 { mfl.mutex.RLock() defer mfl.mutex.RUnlock() return int32(len(mfl.globalFactionList)) } // AddHostileFaction adds a hostile relationship between factions func (mfl *MasterFactionList) AddHostileFaction(factionID, hostileFactionID int32) { mfl.mutex.Lock() defer mfl.mutex.Unlock() mfl.hostileFactions[factionID] = append(mfl.hostileFactions[factionID], hostileFactionID) } // AddFriendlyFaction adds a friendly relationship between factions func (mfl *MasterFactionList) AddFriendlyFaction(factionID, friendlyFactionID int32) { mfl.mutex.Lock() defer mfl.mutex.Unlock() mfl.friendlyFactions[factionID] = append(mfl.friendlyFactions[factionID], friendlyFactionID) } // GetFriendlyFactions returns all friendly factions for a given faction func (mfl *MasterFactionList) GetFriendlyFactions(factionID int32) []int32 { mfl.mutex.RLock() defer mfl.mutex.RUnlock() if factions, exists := mfl.friendlyFactions[factionID]; exists { // Return a copy to prevent external modification result := make([]int32, len(factions)) copy(result, factions) return result } return nil } // GetHostileFactions returns all hostile factions for a given faction func (mfl *MasterFactionList) GetHostileFactions(factionID int32) []int32 { mfl.mutex.RLock() defer mfl.mutex.RUnlock() if factions, exists := mfl.hostileFactions[factionID]; exists { // Return a copy to prevent external modification result := make([]int32, len(factions)) copy(result, factions) return result } return nil } // GetFactionNameByID returns the faction name for a given ID func (mfl *MasterFactionList) GetFactionNameByID(factionID int32) string { if factionID > 0 { mfl.mutex.RLock() defer mfl.mutex.RUnlock() if faction, exists := mfl.globalFactionList[factionID]; exists { return faction.Name } } return "" } // HasFaction checks if a faction exists by ID func (mfl *MasterFactionList) HasFaction(factionID int32) bool { mfl.mutex.RLock() defer mfl.mutex.RUnlock() _, exists := mfl.globalFactionList[factionID] return exists } // HasFactionByName checks if a faction exists by name func (mfl *MasterFactionList) HasFactionByName(name string) bool { mfl.mutex.RLock() defer mfl.mutex.RUnlock() _, exists := mfl.factionNameList[name] return exists } // GetAllFactions returns a copy of all factions func (mfl *MasterFactionList) GetAllFactions() map[int32]*Faction { mfl.mutex.RLock() defer mfl.mutex.RUnlock() result := make(map[int32]*Faction) for id, faction := range mfl.globalFactionList { result[id] = faction } return result } // GetFactionIDs returns all faction IDs func (mfl *MasterFactionList) GetFactionIDs() []int32 { mfl.mutex.RLock() defer mfl.mutex.RUnlock() ids := make([]int32, 0, len(mfl.globalFactionList)) for id := range mfl.globalFactionList { ids = append(ids, id) } return ids } // GetFactionsByType returns all factions of a specific type func (mfl *MasterFactionList) GetFactionsByType(factionType string) []*Faction { mfl.mutex.RLock() defer mfl.mutex.RUnlock() var result []*Faction for _, faction := range mfl.globalFactionList { if faction.Type == factionType { result = append(result, faction) } } return result } // RemoveFaction removes a faction by ID func (mfl *MasterFactionList) RemoveFaction(factionID int32) bool { mfl.mutex.Lock() defer mfl.mutex.Unlock() faction, exists := mfl.globalFactionList[factionID] if !exists { return false } // Remove from both maps delete(mfl.globalFactionList, factionID) delete(mfl.factionNameList, faction.Name) // Remove from relationship maps delete(mfl.hostileFactions, factionID) delete(mfl.friendlyFactions, factionID) // Remove references to this faction in other faction's relationships for id, hostiles := range mfl.hostileFactions { newHostiles := make([]int32, 0, len(hostiles)) for _, hostileID := range hostiles { if hostileID != factionID { newHostiles = append(newHostiles, hostileID) } } mfl.hostileFactions[id] = newHostiles } for id, friendlies := range mfl.friendlyFactions { newFriendlies := make([]int32, 0, len(friendlies)) for _, friendlyID := range friendlies { if friendlyID != factionID { newFriendlies = append(newFriendlies, friendlyID) } } mfl.friendlyFactions[id] = newFriendlies } return true } // UpdateFaction updates an existing faction func (mfl *MasterFactionList) UpdateFaction(faction *Faction) error { if faction == nil { return fmt.Errorf("faction cannot be nil") } if !faction.IsValid() { return fmt.Errorf("faction is not valid") } mfl.mutex.Lock() defer mfl.mutex.Unlock() // Check if faction exists oldFaction, exists := mfl.globalFactionList[faction.ID] if !exists { return fmt.Errorf("faction with ID %d does not exist", faction.ID) } // If name changed, update name map if oldFaction.Name != faction.Name { delete(mfl.factionNameList, oldFaction.Name) mfl.factionNameList[faction.Name] = faction } // Update faction mfl.globalFactionList[faction.ID] = faction return nil } // ValidateFactions checks all factions for consistency func (mfl *MasterFactionList) ValidateFactions() []string { mfl.mutex.RLock() defer mfl.mutex.RUnlock() issues := make([]string, 0, 10) seenIDs := make(map[int32]*Faction, len(mfl.globalFactionList)) seenNames := make(map[string]*Faction, len(mfl.factionNameList)) // Pass 1: Validate globalFactionList and build seenID map for id, faction := range mfl.globalFactionList { 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)) } seenIDs[id] = faction } // Pass 2: Validate factionNameList and build seenName map for name, faction := range mfl.factionNameList { if faction == nil { issues = append(issues, fmt.Sprintf("Faction name '%s' maps to nil", name)) continue } if faction.Name != name { issues = append(issues, fmt.Sprintf("Faction name mismatch: map key '%s' != faction name '%s'", name, faction.Name)) } if _, ok := seenIDs[faction.ID]; !ok { issues = append(issues, fmt.Sprintf("Faction '%s' (ID %d) exists in name map but not in ID map", name, faction.ID)) } seenNames[name] = faction } // Pass 3: Validate relationships using prebuilt seenIDs validateRelations := func(relations map[int32][]int32, relType string) { for sourceID, targets := range relations { if _, ok := seenIDs[sourceID]; !ok { issues = append(issues, fmt.Sprintf("%s relationship defined for non-existent faction %d", relType, sourceID)) } for _, targetID := range targets { if _, ok := seenIDs[targetID]; !ok { issues = append(issues, fmt.Sprintf("Faction %d has %s relationship with non-existent faction %d", sourceID, relType, targetID)) } } } } validateRelations(mfl.hostileFactions, "Hostile") validateRelations(mfl.friendlyFactions, "Friendly") return issues } // IsValid returns true if all factions are valid func (mfl *MasterFactionList) IsValid() bool { issues := mfl.ValidateFactions() return len(issues) == 0 }