eq2go/internal/factions/interfaces.go

414 lines
12 KiB
Go

package factions
import (
"fmt"
"sync"
"eq2emu/internal/database"
)
// Database interface for faction persistence (simplified)
type Database interface {
LoadAllFactions() ([]*Faction, error)
LoadFactionRelations() (hostile, friendly map[int32][]int32, err error)
SaveFactionRelation(factionID, relatedFactionID int32, isHostile bool) error
DeleteFactionRelation(factionID, relatedFactionID int32, isHostile bool) error
LoadPlayerFactions(playerID int32) (map[int32]int32, error)
SavePlayerFaction(playerID, factionID, factionValue int32) error
SaveAllPlayerFactions(playerID int32, factionValues map[int32]int32) 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)
}
// DatabaseAdapter implements the Database interface using internal/database
type DatabaseAdapter struct {
db *database.Database
}
// NewDatabaseAdapter creates a new database adapter
func NewDatabaseAdapter(db *database.Database) *DatabaseAdapter {
return &DatabaseAdapter{db: db}
}
// LoadAllFactions loads all factions from the database
func (da *DatabaseAdapter) LoadAllFactions() ([]*Faction, error) {
return LoadAllFactions(da.db)
}
// LoadFactionRelations loads faction relationships from the database
func (da *DatabaseAdapter) LoadFactionRelations() (map[int32][]int32, map[int32][]int32, error) {
return LoadFactionRelations(da.db)
}
// SaveFactionRelation saves a faction relationship
func (da *DatabaseAdapter) SaveFactionRelation(factionID, relatedFactionID int32, isHostile bool) error {
return SaveFactionRelation(da.db, factionID, relatedFactionID, isHostile)
}
// DeleteFactionRelation deletes a faction relationship
func (da *DatabaseAdapter) DeleteFactionRelation(factionID, relatedFactionID int32, isHostile bool) error {
return DeleteFactionRelation(da.db, factionID, relatedFactionID, isHostile)
}
// LoadPlayerFactions loads player faction values
func (da *DatabaseAdapter) LoadPlayerFactions(playerID int32) (map[int32]int32, error) {
return LoadPlayerFactions(da.db, playerID)
}
// SavePlayerFaction saves a player faction value
func (da *DatabaseAdapter) SavePlayerFaction(playerID, factionID, factionValue int32) error {
return SavePlayerFaction(da.db, playerID, factionID, factionValue)
}
// SaveAllPlayerFactions saves all player faction values
func (da *DatabaseAdapter) SaveAllPlayerFactions(playerID int32, factionValues map[int32]int32) error {
return SaveAllPlayerFactions(da.db, playerID, factionValues)
}
// 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() *MasterList
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
}