eq2go/internal/factions/master.go

339 lines
9.1 KiB
Go

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()
issues := make([]string, 0, 10)
allFactions := ml.MasterList.GetAll()
seenIDs := make(map[int32]*Faction, len(allFactions))
seenNames := make(map[string]*Faction, len(ml.factionNameList))
// Pass 1: Validate main faction list and build seenID map
for id, faction := range allFactions {
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 ml.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(ml.hostileFactions, "Hostile")
validateRelations(ml.friendlyFactions, "Friendly")
return issues
}
// IsValid returns true if all factions are valid
func (ml *MasterList) IsValid() bool {
issues := ml.ValidateFactions()
return len(issues) == 0
}