414 lines
12 KiB
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
|
|
}
|