package factions import ( "fmt" "sync" ) // Database interface for faction persistence type Database interface { LoadAllFactions() ([]*Faction, error) SaveFaction(faction *Faction) error DeleteFaction(factionID int32) error LoadHostileFactionRelations() ([]*FactionRelation, error) LoadFriendlyFactionRelations() ([]*FactionRelation, error) SaveFactionRelation(relation *FactionRelation) error DeleteFactionRelation(factionID, relatedFactionID int32, isHostile bool) error } // Logger interface for faction logging type Logger interface { LogInfo(message string, args ...any) LogError(message string, args ...any) LogDebug(message string, args ...any) LogWarning(message string, args ...any) } // FactionRelation represents a relationship between two factions type FactionRelation struct { FactionID int32 // Primary faction ID HostileFactionID int32 // Hostile faction ID (if this is a hostile relation) FriendlyFactionID int32 // Friendly faction ID (if this is a friendly relation) } // Client interface for faction-related client operations type Client interface { GetVersion() int16 SendFactionUpdate(factionData []byte) error GetCharacterID() int32 } // Player interface for faction-related player operations type Player interface { GetFactionSystem() *PlayerFaction GetCharacterID() int32 SendMessage(message string) } // FactionAware interface for entities that interact with factions type FactionAware interface { GetFactionID() int32 SetFactionID(factionID int32) GetFactionStanding(playerFaction *PlayerFaction) int8 ShouldAttackPlayer(playerFaction *PlayerFaction) bool } // FactionProvider interface for systems that provide faction information type FactionProvider interface { GetMasterFactionList() *MasterFactionList GetFaction(factionID int32) *Faction GetFactionByName(name string) *Faction CreatePlayerFaction() *PlayerFaction } // EntityFactionAdapter provides faction functionality for entities type EntityFactionAdapter struct { entity Entity factionID int32 manager *Manager logger Logger mutex sync.RWMutex } // Entity interface for things that can have faction affiliations type Entity interface { GetID() int32 GetName() string GetDatabaseID() int32 } // NewEntityFactionAdapter creates a new entity faction adapter func NewEntityFactionAdapter(entity Entity, manager *Manager, logger Logger) *EntityFactionAdapter { return &EntityFactionAdapter{ entity: entity, factionID: 0, manager: manager, logger: logger, } } // GetFactionID returns the entity's faction ID func (efa *EntityFactionAdapter) GetFactionID() int32 { efa.mutex.RLock() defer efa.mutex.RUnlock() return efa.factionID } // SetFactionID sets the entity's faction ID func (efa *EntityFactionAdapter) SetFactionID(factionID int32) { efa.mutex.Lock() defer efa.mutex.Unlock() efa.factionID = factionID if efa.logger != nil { efa.logger.LogDebug("Entity %d (%s): Set faction ID to %d", efa.entity.GetID(), efa.entity.GetName(), factionID) } } // GetFaction returns the entity's faction object func (efa *EntityFactionAdapter) GetFaction() *Faction { factionID := efa.GetFactionID() if factionID == 0 { return nil } if efa.manager == nil { if efa.logger != nil { efa.logger.LogError("Entity %d (%s): No faction manager available", efa.entity.GetID(), efa.entity.GetName()) } return nil } return efa.manager.GetFaction(factionID) } // GetFactionStanding returns the consideration level with a player func (efa *EntityFactionAdapter) GetFactionStanding(playerFaction *PlayerFaction) int8 { factionID := efa.GetFactionID() if factionID == 0 || playerFaction == nil { return ConIndiff // Indifferent if no faction or player faction } return playerFaction.GetCon(factionID) } // ShouldAttackPlayer returns true if the entity should attack the player based on faction func (efa *EntityFactionAdapter) ShouldAttackPlayer(playerFaction *PlayerFaction) bool { factionID := efa.GetFactionID() if factionID == 0 || playerFaction == nil { return false // Don't attack if no faction } return playerFaction.ShouldAttack(factionID) } // GetFactionName returns the name of the entity's faction func (efa *EntityFactionAdapter) GetFactionName() string { faction := efa.GetFaction() if faction == nil { return "" } return faction.Name } // IsHostileToFaction returns true if this entity's faction is hostile to another faction func (efa *EntityFactionAdapter) IsHostileToFaction(otherFactionID int32) bool { factionID := efa.GetFactionID() if factionID == 0 || efa.manager == nil { return false } hostileFactions := efa.manager.GetMasterFactionList().GetHostileFactions(factionID) for _, hostileID := range hostileFactions { if hostileID == otherFactionID { return true } } return false } // IsFriendlyToFaction returns true if this entity's faction is friendly to another faction func (efa *EntityFactionAdapter) IsFriendlyToFaction(otherFactionID int32) bool { factionID := efa.GetFactionID() if factionID == 0 || efa.manager == nil { return false } friendlyFactions := efa.manager.GetMasterFactionList().GetFriendlyFactions(factionID) for _, friendlyID := range friendlyFactions { if friendlyID == otherFactionID { return true } } return false } // ValidateFaction validates that the entity's faction exists and is valid func (efa *EntityFactionAdapter) ValidateFaction() error { factionID := efa.GetFactionID() if factionID == 0 { return nil // No faction is valid } faction := efa.GetFaction() if faction == nil { return fmt.Errorf("faction ID %d not found", factionID) } if !faction.IsValid() { return fmt.Errorf("faction ID %d is invalid", factionID) } return nil } // PlayerFactionManager handles faction interactions for a player type PlayerFactionManager struct { playerFaction *PlayerFaction manager *Manager player Player logger Logger mutex sync.RWMutex } // NewPlayerFactionManager creates a new player faction manager func NewPlayerFactionManager(player Player, manager *Manager, logger Logger) *PlayerFactionManager { return &PlayerFactionManager{ playerFaction: manager.CreatePlayerFaction(), manager: manager, player: player, logger: logger, } } // GetPlayerFaction returns the player's faction system func (pfm *PlayerFactionManager) GetPlayerFaction() *PlayerFaction { return pfm.playerFaction } // IncreaseFaction increases a faction and records statistics func (pfm *PlayerFactionManager) IncreaseFaction(factionID int32, amount int32) bool { result := pfm.playerFaction.IncreaseFaction(factionID, amount) if result { pfm.manager.RecordFactionIncrease(factionID) if pfm.logger != nil { pfm.logger.LogDebug("Player %d: Increased faction %d by %d", pfm.player.GetCharacterID(), factionID, amount) } } return result } // DecreaseFaction decreases a faction and records statistics func (pfm *PlayerFactionManager) DecreaseFaction(factionID int32, amount int32) bool { result := pfm.playerFaction.DecreaseFaction(factionID, amount) if result { pfm.manager.RecordFactionDecrease(factionID) if pfm.logger != nil { pfm.logger.LogDebug("Player %d: Decreased faction %d by %d", pfm.player.GetCharacterID(), factionID, amount) } } return result } // SetFactionValue sets a faction to a specific value func (pfm *PlayerFactionManager) SetFactionValue(factionID int32, value int32) bool { result := pfm.playerFaction.SetFactionValue(factionID, value) if pfm.logger != nil { pfm.logger.LogDebug("Player %d: Set faction %d to %d", pfm.player.GetCharacterID(), factionID, value) } return result } // SendFactionUpdates sends pending faction updates to the client func (pfm *PlayerFactionManager) SendFactionUpdates(client Client) error { if client == nil { return fmt.Errorf("client is nil") } if !pfm.playerFaction.HasPendingUpdates() { return nil // No updates needed } packet, err := pfm.playerFaction.FactionUpdate(client.GetVersion()) if err != nil { return fmt.Errorf("failed to build faction update packet: %w", err) } if packet != nil { if err := client.SendFactionUpdate(packet); err != nil { return fmt.Errorf("failed to send faction update: %w", err) } if pfm.logger != nil { pfm.logger.LogDebug("Player %d: Sent faction updates to client", pfm.player.GetCharacterID()) } } return nil } // GetFactionStanding returns the player's standing with a faction func (pfm *PlayerFactionManager) GetFactionStanding(factionID int32) int8 { return pfm.playerFaction.GetCon(factionID) } // GetFactionValue returns the player's value with a faction func (pfm *PlayerFactionManager) GetFactionValue(factionID int32) int32 { return pfm.playerFaction.GetFactionValue(factionID) } // ShouldAttackFaction returns true if the player should attack entities of a faction func (pfm *PlayerFactionManager) ShouldAttackFaction(factionID int32) bool { return pfm.playerFaction.ShouldAttack(factionID) } // LoadPlayerFactions loads faction data from database func (pfm *PlayerFactionManager) LoadPlayerFactions(database Database) error { if database == nil { return fmt.Errorf("database is nil") } // Load player faction data from database if dbAdapter, ok := database.(*DatabaseAdapter); ok { factionData, err := dbAdapter.LoadPlayerFactions(pfm.player.GetCharacterID()) if err != nil { return fmt.Errorf("failed to load player factions: %w", err) } for factionID, value := range factionData { pfm.playerFaction.SetFactionValue(factionID, value) } } if pfm.logger != nil { pfm.logger.LogInfo("Player %d: Loaded faction data from database", pfm.player.GetCharacterID()) } return nil } // SavePlayerFactions saves faction data to database func (pfm *PlayerFactionManager) SavePlayerFactions(database Database) error { if database == nil { return fmt.Errorf("database is nil") } factionValues := pfm.playerFaction.GetFactionValues() // Save player faction data to database if dbAdapter, ok := database.(*DatabaseAdapter); ok { if err := dbAdapter.SaveAllPlayerFactions(pfm.player.GetCharacterID(), factionValues); err != nil { return fmt.Errorf("failed to save player factions: %w", err) } } if pfm.logger != nil { pfm.logger.LogInfo("Player %d: Saved %d faction values to database", pfm.player.GetCharacterID(), len(factionValues)) } return nil }