386 lines
10 KiB
Go
386 lines
10 KiB
Go
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
|
|
}
|